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 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
20
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
21

22
#include <string.h>
23 24
#include <stdlib.h>

25
#include "winbase.h"
26 27
#include "windef.h"
#include "wingdi.h"
28
#include "wine/winuser16.h"
29
#include "controls.h"
30
#include "user.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
31

32 33 34 35 36
/* GetWindowLong offsets for window extra information */
#define STATE_GWL_OFFSET  0
#define HFONT_GWL_OFFSET  (sizeof(LONG))
#define HIMAGE_GWL_OFFSET (2*sizeof(LONG))
#define NB_EXTRA_BYTES    (3*sizeof(LONG))
37 38 39 40 41 42 43 44 45 46 47 48 49

  /* 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

50
static UINT BUTTON_CalcLabelRect( HWND hwnd, HDC hdc, RECT *rc );
51 52 53 54 55 56
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 );
57 58
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
59

Alexandre Julliard's avatar
Alexandre Julliard committed
60
#define MAX_BTN_TYPE  12
Alexandre Julliard's avatar
Alexandre Julliard committed
61

Alexandre Julliard's avatar
Alexandre Julliard committed
62
static const WORD maxCheckState[MAX_BTN_TYPE] =
Alexandre Julliard's avatar
Alexandre Julliard committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
{
    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 */
};

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

Alexandre Julliard's avatar
Alexandre Julliard committed
80
static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
Alexandre Julliard's avatar
Alexandre Julliard committed
81 82 83 84 85 86 87 88 89 90 91 92 93
{
    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
94 95
};

96
static HBITMAP hbitmapCheckBoxes = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
97 98
static WORD checkBoxWidth = 0, checkBoxHeight = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
99

100 101 102 103 104 105 106 107 108
/*********************************************************************
 * button class descriptor
 */
const struct builtin_class_descr BUTTON_builtin_class =
{
    "Button",            /* name */
    CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style  */
    ButtonWndProcA,      /* procA */
    ButtonWndProcW,      /* procW */
109
    NB_EXTRA_BYTES,      /* extra */
110 111 112 113 114
    IDC_ARROWA,          /* cursor */
    0                    /* brush */
};


115 116 117 118 119 120 121 122 123 124 125 126
inline static LONG get_button_state( HWND hwnd )
{
    return GetWindowLongA( hwnd, STATE_GWL_OFFSET );
}

inline static void set_button_state( HWND hwnd, LONG state )
{
    SetWindowLongA( hwnd, STATE_GWL_OFFSET, state );
}

inline static HFONT get_button_font( HWND hwnd )
{
127
    return (HFONT)GetWindowLongA( hwnd, HFONT_GWL_OFFSET );
128 129 130 131
}

inline static void set_button_font( HWND hwnd, HFONT font )
{
132
    SetWindowLongA( hwnd, HFONT_GWL_OFFSET, (LONG)font );
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
}

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 )
{
    INT len = GetWindowTextLengthW( hwnd );
    WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
    if (buffer) GetWindowTextW( hwnd, buffer, len + 1 );
    return buffer;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
160
/***********************************************************************
161
 *           ButtonWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
162
 */
163 164
static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
                                           WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
165
{
166
    RECT rect;
167
    POINT pt;
168 169 170
    LONG style = GetWindowLongA( hWnd, GWL_STYLE );
    UINT btn_type = get_button_type( style );
    LONG state;
171
    HANDLE oldHbitmap;
Alexandre Julliard's avatar
Alexandre Julliard committed
172

173 174 175
    pt.x = LOWORD(lParam);
    pt.y = HIWORD(lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
176 177 178
    switch (uMsg)
    {
    case WM_GETDLGCODE:
179
        switch(btn_type)
Alexandre Julliard's avatar
Alexandre Julliard committed
180 181 182 183 184 185 186
        {
        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;
        default:                 return DLGC_BUTTON;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
187

Alexandre Julliard's avatar
Alexandre Julliard committed
188
    case WM_ENABLE:
189
        paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
Alexandre Julliard's avatar
Alexandre Julliard committed
190
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
191

Alexandre Julliard's avatar
Alexandre Julliard committed
192 193 194
    case WM_CREATE:
        if (!hbitmapCheckBoxes)
        {
195
            BITMAP bmp;
196 197
            hbitmapCheckBoxes = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CHECKBOXES));
            GetObjectW( hbitmapCheckBoxes, sizeof(bmp), &bmp );
Alexandre Julliard's avatar
Alexandre Julliard committed
198 199 200
            checkBoxWidth  = bmp.bmWidth / 4;
            checkBoxHeight = bmp.bmHeight / 3;
        }
201
        if (btn_type >= MAX_BTN_TYPE)
202
            return -1; /* abort */
203
        set_button_state( hWnd, BUTTON_UNCHECKED );
Alexandre Julliard's avatar
Alexandre Julliard committed
204
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
205

Alexandre Julliard's avatar
Alexandre Julliard committed
206
    case WM_ERASEBKGND:
Alexandre Julliard's avatar
Alexandre Julliard committed
207
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
208

Alexandre Julliard's avatar
Alexandre Julliard committed
209
    case WM_PAINT:
210
        if (btnPaintFunc[btn_type])
Alexandre Julliard's avatar
Alexandre Julliard committed
211
        {
212 213
            PAINTSTRUCT ps;
            HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
214
            int nOldMode = SetBkMode( hdc, OPAQUE );
215
            (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE );
216
            SetBkMode(hdc, nOldMode); /*  reset painting mode */
217
            if( !wParam ) EndPaint( hWnd, &ps );
Alexandre Julliard's avatar
Alexandre Julliard committed
218 219 220
        }
        break;

221 222 223
    case WM_KEYDOWN:
	if (wParam == VK_SPACE)
	{
224
	    SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
225
            set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
226 227
	}
	break;
228

229
    case WM_LBUTTONDBLCLK:
230 231 232 233 234
        if(style & BS_NOTIFY ||
           btn_type == BS_RADIOBUTTON ||
           btn_type == BS_USERBUTTON ||
           btn_type == BS_OWNERDRAW)
        {
235
            SendMessageW( GetParent(hWnd), WM_COMMAND,
236 237
                          MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_DOUBLECLICKED ),
                          (LPARAM)hWnd);
238 239 240
            break;
        }
        /* fall through */
241
    case WM_LBUTTONDOWN:
242
        SetCapture( hWnd );
243
        SetFocus( hWnd );
244
        SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
245
        set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
Alexandre Julliard's avatar
Alexandre Julliard committed
246 247
        break;

248 249 250 251
    case WM_KEYUP:
	if (wParam != VK_SPACE)
	    break;
	/* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
252
    case WM_LBUTTONUP:
253 254 255 256 257 258
        state = get_button_state( hWnd );
        if (!(state & BUTTON_BTNPRESSED)) break;
        state &= BUTTON_NSTATES;
        set_button_state( hWnd, state );
        if (!(state & BUTTON_HIGHLIGHTED))
        {
259 260 261
            ReleaseCapture();
            break;
        }
262
        SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
263
        ReleaseCapture();
264
        GetClientRect( hWnd, &rect );
265
	if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
Alexandre Julliard's avatar
Alexandre Julliard committed
266
        {
267 268
            state = get_button_state( hWnd );
            switch(btn_type)
Alexandre Julliard's avatar
Alexandre Julliard committed
269 270
            {
            case BS_AUTOCHECKBOX:
271
                SendMessageW( hWnd, BM_SETCHECK, !(state & BUTTON_CHECKED), 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
272
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
273
            case BS_AUTORADIOBUTTON:
274
                SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
275 276
                break;
            case BS_AUTO3STATE:
277
                SendMessageW( hWnd, BM_SETCHECK,
278
                                (state & BUTTON_3STATE) ? 0 : ((state & 3) + 1), 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
279
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
280
            }
281
            SendMessageW( GetParent(hWnd), WM_COMMAND,
282
                          MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), (LPARAM)hWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
283 284 285
        }
        break;

286
    case WM_CAPTURECHANGED:
287 288 289 290 291 292
        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 );
293 294 295
        }
        break;

Alexandre Julliard's avatar
Alexandre Julliard committed
296
    case WM_MOUSEMOVE:
297
        if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
298
        {
299
            GetClientRect( hWnd, &rect );
300
            SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
301 302 303 304
        }
        break;

    case WM_SETTEXT:
305 306 307 308 309 310
    {
        /* Clear an old text here as Windows does */
        HDC hdc = GetDC(hWnd);
        HBRUSH hbrush;
        RECT client, rc;

311 312
        hbrush = (HBRUSH)SendMessageW(GetParent(hWnd), WM_CTLCOLORSTATIC,
				      (WPARAM)hdc, (LPARAM)hWnd);
313
        if (!hbrush) /* did the app forget to call DefWindowProc ? */
314 315
            hbrush = (HBRUSH)DefWindowProcW(GetParent(hWnd), WM_CTLCOLORSTATIC,
					    (WPARAM)hdc, (LPARAM)hWnd);
316 317 318 319 320 321 322 323 324 325

        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);

326 327
        if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
        else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
328 329 330 331
        if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */
            InvalidateRect( hWnd, NULL, TRUE );
        else
            paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
332
        return 1; /* success. FIXME: check text length */
333
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
334 335

    case WM_SETFONT:
336
        set_button_font( hWnd, (HFONT)wParam );
337
        if (lParam) paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
Alexandre Julliard's avatar
Alexandre Julliard committed
338 339 340
        break;

    case WM_GETFONT:
341
        return (LRESULT)get_button_font( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
342 343

    case WM_SETFOCUS:
344
        if ((btn_type == BS_RADIOBUTTON || btn_type == BS_AUTORADIOBUTTON) && (GetCapture() != hWnd) &&
345
            !(SendMessageW(hWnd, BM_GETCHECK, 0, 0) & BST_CHECKED))
346
	{
347
            /* The notification is sent when the button (BS_AUTORADIOBUTTON)
Andreas Mohr's avatar
Andreas Mohr committed
348
               is unchecked and the focus was not given by a mouse click. */
349 350 351
            if (btn_type == BS_AUTORADIOBUTTON)
                SendMessageW( hWnd, BM_SETCHECK, BUTTON_CHECKED, 0 );
            SendMessageW( GetParent(hWnd), WM_COMMAND,
352
                          MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), (LPARAM)hWnd);
353
        }
354 355
        set_button_state( hWnd, get_button_state(hWnd) | BUTTON_HASFOCUS );
        paint_button( hWnd, btn_type, ODA_FOCUS );
Alexandre Julliard's avatar
Alexandre Julliard committed
356 357 358
        break;

    case WM_KILLFOCUS:
359 360
        set_button_state( hWnd, get_button_state(hWnd) & ~BUTTON_HASFOCUS );
	paint_button( hWnd, btn_type, ODA_FOCUS );
361
	InvalidateRect( hWnd, NULL, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
362 363 364
        break;

    case WM_SYSCOLORCHANGE:
365
        InvalidateRect( hWnd, NULL, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
366 367 368
        break;

    case BM_SETSTYLE16:
369
    case BM_SETSTYLE:
Alexandre Julliard's avatar
Alexandre Julliard committed
370
        if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
371 372 373
        btn_type = wParam & 0x0f;
        style = (style & ~0x0f) | btn_type;
        SetWindowLongA( hWnd, GWL_STYLE, style );
374 375 376

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

Alexandre Julliard's avatar
Alexandre Julliard committed
379 380
        break;

381
    case BM_CLICK:
382 383
	SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
	SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
384 385
	break;

386
    case BM_SETIMAGE:
387 388 389 390 391 392 393 394 395 396 397 398
        /* 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;
        }
399
        oldHbitmap = (HBITMAP)SetWindowLongA( hWnd, HIMAGE_GWL_OFFSET, lParam );
400
	InvalidateRect( hWnd, NULL, FALSE );
401
	return (LRESULT)oldHbitmap;
402 403

    case BM_GETIMAGE:
404
        return GetWindowLongA( hWnd, HIMAGE_GWL_OFFSET );
405

Alexandre Julliard's avatar
Alexandre Julliard committed
406
    case BM_GETCHECK16:
407
    case BM_GETCHECK:
408
        return get_button_state( hWnd ) & 3;
Alexandre Julliard's avatar
Alexandre Julliard committed
409 410

    case BM_SETCHECK16:
411
    case BM_SETCHECK:
412 413
        if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
        state = get_button_state( hWnd );
414 415 416 417 418 419
        if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
        {
            if (wParam) style |= WS_TABSTOP;
            else style &= ~WS_TABSTOP;
            SetWindowLongA( hWnd, GWL_STYLE, style );
        }
420
        if ((state & 3) != wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
421
        {
422 423
            set_button_state( hWnd, (state & ~3) | wParam );
            paint_button( hWnd, btn_type, ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
424
        }
425 426
        if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BUTTON_CHECKED) && (style & WS_CHILD))
            BUTTON_CheckAutoRadioButton( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
427
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
428

Alexandre Julliard's avatar
Alexandre Julliard committed
429
    case BM_GETSTATE16:
430
    case BM_GETSTATE:
431
        return get_button_state( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
432

Alexandre Julliard's avatar
Alexandre Julliard committed
433
    case BM_SETSTATE16:
434
    case BM_SETSTATE:
435
        state = get_button_state( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
436 437
        if (wParam)
        {
438 439
            if (state & BUTTON_HIGHLIGHTED) break;
            set_button_state( hWnd, state | BUTTON_HIGHLIGHTED );
Alexandre Julliard's avatar
Alexandre Julliard committed
440 441 442
        }
        else
        {
443 444
            if (!(state & BUTTON_HIGHLIGHTED)) break;
            set_button_state( hWnd, state & ~BUTTON_HIGHLIGHTED );
Alexandre Julliard's avatar
Alexandre Julliard committed
445
        }
446
        paint_button( hWnd, btn_type, ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
447 448
        break;

449
    case WM_NCHITTEST:
450
        if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
451
        /* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
452
    default:
453 454
        return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
                         DefWindowProcA(hWnd, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
455 456
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
457 458
}

459
/***********************************************************************
460
 *           ButtonWndProcW
461 462 463 464
 * 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).
 */
465
static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
466
{
467 468
    if (!IsWindow( hWnd )) return 0;
    return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, TRUE );
469
}
Alexandre Julliard's avatar
Alexandre Julliard committed
470

471 472 473 474

/***********************************************************************
 *           ButtonWndProcA
 */
475
static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
476
{
477 478
    if (!IsWindow( hWnd )) return 0;
    return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, FALSE );
479 480 481
}


482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
/**********************************************************************
 * 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 */
506
         if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
507 508 509 510 511 512
         /* all other flavours have left aligned text */
   }

   /* DrawText ignores vertical alignment for multiline text,
    * but we use these flags to align label manualy.
    */
513
   if (get_button_type(style) != BS_GROUPBOX)
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
   {
      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.
 */
540
static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
541
{
542 543
   LONG style = GetWindowLongA( hwnd, GWL_STYLE );
   WCHAR *text;
544 545
   ICONINFO    iconInfo;
   BITMAP      bm;
546
   UINT        dtStyle = BUTTON_BStoDT(style);
547 548 549 550
   RECT        r = *rc;
   INT         n;

   /* Calculate label rectangle according to label type */
551
   switch (style & (BS_ICON|BS_BITMAP))
552 553
   {
      case BS_TEXT:
554 555 556 557 558 559 560 561 562
          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;
563 564

      case BS_ICON:
565
         if (!GetIconInfo((HICON)GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
566 567
            goto empty_rect;

568
         GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
569 570 571 572 573 574 575 576 577

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

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

      case BS_BITMAP:
578
         if (!GetObjectW( (HANDLE)GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
579 580 581 582 583 584 585
            goto empty_rect;

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

      default:
586
      empty_rect:
587 588 589 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 616 617 618 619 620 621 622 623 624 625 626 627 628
         r.right = r.left;
         r.bottom = r.top;
         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
 *
629
 *   Callback function used by DrawStateW function.
630 631 632
 */
static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
{
633 634 635 636 637
   RECT rc;
   rc.left = 0;
   rc.top = 0;
   rc.right = cx;
   rc.bottom = cy;
638 639 640 641 642 643 644 645 646 647 648

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


/**********************************************************************
 *       BUTTON_DrawLabel
 *
 *   Common function for drawing button label.
 */
649
static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
650 651 652 653 654
{
   DRAWSTATEPROC lpOutputProc = NULL;
   LPARAM lp;
   WPARAM wp = 0;
   HBRUSH hbr = 0;
655 656 657 658
   UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
   LONG state = get_button_state( hwnd );
   LONG style = GetWindowLongA( hwnd, GWL_STYLE );
   WCHAR *text = NULL;
659

660
   /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
661 662 663 664
    * 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.
    */

665
   if ((style & BS_PUSHLIKE) && (state & BUTTON_3STATE))
666 667 668 669 670
   {
      hbr = GetSysColorBrush(COLOR_GRAYTEXT);
      flags |= DSS_MONO;
   }

671
   switch (style & (BS_ICON|BS_BITMAP))
672 673 674 675
   {
      case BS_TEXT:
         /* DST_COMPLEX -- is 0 */
         lpOutputProc = BUTTON_DrawTextCallback;
676 677
         if (!(text = get_button_text( hwnd ))) return;
         lp = (LPARAM)text;
678 679 680 681 682
         wp = (WPARAM)dtFlags;
         break;

      case BS_ICON:
         flags |= DST_ICON;
683
         lp = GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET );
684 685 686 687
         break;

      case BS_BITMAP:
         flags |= DST_BITMAP;
688
         lp = GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET );
689 690 691 692 693 694 695 696
         break;

      default:
         return;
   }

   DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
              rc->right - rc->left, rc->bottom - rc->top, flags);
697
   if (text) HeapFree( GetProcessHeap(), 0, text );
698 699
}

700
/**********************************************************************
701
 *       Push Button Functions
702
 */
703
static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
704
{
705 706 707 708 709 710 711
    RECT     rc, focus_rect, r;
    UINT     dtFlags;
    HRGN     hRgn;
    HPEN     hOldPen;
    HBRUSH   hOldBrush;
    INT      oldBkMode;
    COLORREF oldTxtColor;
712 713 714 715
    HFONT hFont;
    LONG state = get_button_state( hwnd );
    LONG style = GetWindowLongA( hwnd, GWL_STYLE );
    BOOL pushedState = (state & BUTTON_HIGHLIGHTED);
Alexandre Julliard's avatar
Alexandre Julliard committed
716

717
    GetClientRect( hwnd, &rc );
Alexandre Julliard's avatar
Alexandre Julliard committed
718

719
    /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
720
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
721
    SendMessageW( GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
722
    hOldPen = (HPEN)SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
723
    hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
724
    oldBkMode = SetBkMode(hDC, TRANSPARENT);
725 726

    if ( TWEAK_WineLook == WIN31_LOOK)
Alexandre Julliard's avatar
Alexandre Julliard committed
727
    {
728
        COLORREF clr_wnd = GetSysColor(COLOR_WINDOW);
729 730
        Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);

731 732 733 734
        SetPixel( hDC, rc.left, rc.top, clr_wnd);
        SetPixel( hDC, rc.left, rc.bottom-1, clr_wnd);
        SetPixel( hDC, rc.right-1, rc.top, clr_wnd);
        SetPixel( hDC, rc.right-1, rc.bottom-1, clr_wnd);
735
	InflateRect( &rc, -1, -1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
736
    }
737

738
    if (get_button_type(style) == BS_DEFPUSHBUTTON)
Alexandre Julliard's avatar
Alexandre Julliard committed
739
    {
740
        Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
741
	InflateRect( &rc, -1, -1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
742 743
    }

744
    if (TWEAK_WineLook == WIN31_LOOK)
Alexandre Julliard's avatar
Alexandre Julliard committed
745
    {
746
        if (pushedState)
747 748 749 750 751 752 753 754 755 756 757 758 759
	{
	    /* draw button shadow: */
	    SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW));
	    PatBlt(hDC, rc.left, rc.top, 1, rc.bottom-rc.top, PATCOPY );
	    PatBlt(hDC, rc.left, rc.top, rc.right-rc.left, 1, PATCOPY );
	} else {
	   rc.right++, rc.bottom++;
	   DrawEdge( hDC, &rc, EDGE_RAISED, BF_RECT );
	   rc.right--, rc.bottom--;
	}
    }
    else
    {
760
        UINT uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
761

762
        if (style & BS_FLAT)
763 764
            uState |= DFCS_MONO;
        else if (pushedState)
765
	{
766
	    if (get_button_type(style) == BS_DEFPUSHBUTTON )
767 768 769 770
	        uState |= DFCS_FLAT;
	    else
	        uState |= DFCS_PUSHED;
	}
771

772
        if (state & (BUTTON_CHECKED | BUTTON_3STATE))
773 774
            uState |= DFCS_CHECKED;

775
	DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
776

777
	focus_rect = rc;
Alexandre Julliard's avatar
Alexandre Julliard committed
778
    }
779

780 781
    /* draw button label */
    r = rc;
782
    dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
783

784 785
    if (dtFlags == (UINT)-1L)
       goto cleanup;
786

787 788
    if (pushedState)
       OffsetRect(&r, 1, 1);
789

790 791 792 793 794
    if(TWEAK_WineLook == WIN31_LOOK)
    {
       focus_rect = r;
       InflateRect(&focus_rect, 2, 0);
    }
795

796 797
    hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
    SelectClipRgn(hDC, hRgn);
798

799
    oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
800

801
    BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
802

803 804 805
    SetTextColor( hDC, oldTxtColor );
    SelectClipRgn(hDC, 0);
    DeleteObject(hRgn);
806

807
    if (state & BUTTON_HASFOCUS)
808 809
    {
        InflateRect( &focus_rect, -1, -1 );
810
        IntersectRect(&focus_rect, &focus_rect, &rc);
811 812 813
        DrawFocusRect( hDC, &focus_rect );
    }

814
 cleanup:
815 816
    SelectObject( hDC, hOldPen );
    SelectObject( hDC, hOldBrush );
817
    SetBkMode(hDC, oldBkMode);
Alexandre Julliard's avatar
Alexandre Julliard committed
818 819 820
}

/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
821
 *       Check Box & Radio Button Functions
Alexandre Julliard's avatar
Alexandre Julliard committed
822
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
823

824
static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
825
{
826 827
    RECT rbox, rtext, client;
    HBRUSH hBrush;
828 829 830
    int delta;
    UINT dtFlags;
    HRGN hRgn;
831 832 833
    HFONT hFont;
    LONG state = get_button_state( hwnd );
    LONG style = GetWindowLongA( hwnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
834

835
    if (style & BS_PUSHLIKE)
836
    {
837
        PB_Paint( hwnd, hDC, action );
838
	return;
839 840
    }

841
    GetClientRect(hwnd, &client);
Alexandre Julliard's avatar
Alexandre Julliard committed
842
    rbox = rtext = client;
Alexandre Julliard's avatar
Alexandre Julliard committed
843

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

846 847
    hBrush = (HBRUSH)SendMessageW(GetParent(hwnd), WM_CTLCOLORSTATIC,
				  (WPARAM)hDC, (LPARAM)hwnd);
848
    if (!hBrush) /* did the app forget to call defwindowproc ? */
849 850
        hBrush = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORSTATIC,
					(WPARAM)hDC, (LPARAM)hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
851

852
    if (style & BS_LEFTTEXT)
Alexandre Julliard's avatar
Alexandre Julliard committed
853 854
    {
	/* magic +4 is what CTL3D expects */
Alexandre Julliard's avatar
Alexandre Julliard committed
855

Alexandre Julliard's avatar
Alexandre Julliard committed
856 857 858
        rtext.right -= checkBoxWidth + 4;
        rbox.left = rbox.right - checkBoxWidth;
    }
859
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
860 861 862 863
    {
        rtext.left += checkBoxWidth + 4;
        rbox.right = checkBoxWidth;
    }
864 865 866 867
 
    /* 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
868

869 870 871 872 873 874
    /* Draw label */
    client = rtext;
    dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext);
    
    rbox.top = rtext.top;
    rbox.bottom = rtext.bottom;
875
    /* Draw the check-box bitmap */
Alexandre Julliard's avatar
Alexandre Julliard committed
876
    if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
877
    {
878 879
        if( TWEAK_WineLook == WIN31_LOOK )
        {
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
	    HDC hMemDC = CreateCompatibleDC( hDC );
	    int x = 0, y = 0;
	    delta = (rbox.bottom - rbox.top - checkBoxHeight) / 2;
	    
	    /* Check in case the client area is smaller than the checkbox bitmap */
	    if (delta < 0) delta = 0;
	    
	    if (state & BUTTON_HIGHLIGHTED) x += 2 * checkBoxWidth;
	    if (state & (BUTTON_CHECKED | BUTTON_3STATE)) x += checkBoxWidth;
	    if ((get_button_type(style) == BS_RADIOBUTTON) ||
		(get_button_type(style) == BS_AUTORADIOBUTTON)) y += checkBoxHeight;
	    else if (state & BUTTON_3STATE) y += 2 * checkBoxHeight;
	    
	    /* The bitmap for the radio button is not aligned with the
	     * left of the window, it is 1 pixel off. */
	    if ((get_button_type(style) == BS_RADIOBUTTON) ||
		(get_button_type(style) == BS_AUTORADIOBUTTON))
	      rbox.left += 1;
	    
	    SelectObject( hMemDC, hbitmapCheckBoxes );
	    BitBlt( hDC, rbox.left, rbox.top + delta, checkBoxWidth,
		    checkBoxHeight, hMemDC, x, y, SRCCOPY );
	    DeleteDC( hMemDC );
903 904 905
        }
        else
        {
906
            UINT flags;
907

908 909 910 911
            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;
912

913 914
            if (state & (BUTTON_CHECKED | BUTTON_3STATE)) flags |= DFCS_CHECKED;
	    if (state & BUTTON_HIGHLIGHTED) flags |= DFCS_PUSHED;
915

916
	    if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
917

918
	    /* rbox must have the correct height */
919 920 921 922
	    delta = rbox.bottom - rbox.top - checkBoxHeight;
	    
	    if (style & BS_TOP) {
	      if (delta > 0) {
923
		rbox.bottom = rbox.top + checkBoxHeight;
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
	      } else {
		rbox.top -= -delta/2 + 1;
		rbox.bottom += rbox.top + checkBoxHeight;
	      }
	    } else if (style & BS_BOTTOM) {
	      if (delta > 0) {
		rbox.top = rbox.bottom - checkBoxHeight;
	      } else {
		rbox.bottom += -delta/2 + 1;
		rbox.top = rbox.bottom -= checkBoxHeight;
	      }
	    } 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;
		}
948 949
	    }

950
	    DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags );
951
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
952
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
953

954 955 956 957 958 959 960
    if (dtFlags == (UINT)-1L) /* Noting to draw */
	return;
    hRgn = CreateRectRgn(client.left, client.top, client.right, client.bottom);
    SelectClipRgn(hDC, hRgn);
    DeleteObject(hRgn);

    if (action == ODA_DRAWENTIRE)
961
	BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
962 963

    /* ... and focus */
Alexandre Julliard's avatar
Alexandre Julliard committed
964
    if ((action == ODA_FOCUS) ||
965
        ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
Alexandre Julliard's avatar
Alexandre Julliard committed
966
    {
967 968 969 970
	rtext.left--;
	rtext.right++;
	IntersectRect(&rtext, &rtext, &client);
	DrawFocusRect( hDC, &rtext );
Alexandre Julliard's avatar
Alexandre Julliard committed
971
    }
972
    SelectClipRgn(hDC, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
973 974 975
}


Alexandre Julliard's avatar
Alexandre Julliard committed
976 977 978
/**********************************************************************
 *       BUTTON_CheckAutoRadioButton
 *
979
 * hwnd is checked, uncheck every other auto radio button in group
Alexandre Julliard's avatar
Alexandre Julliard committed
980
 */
981
static void BUTTON_CheckAutoRadioButton( HWND hwnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
982
{
983
    HWND parent, sibling, start;
984 985 986 987

    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
988 989 990
    do
    {
        if (!sibling) break;
991 992
        if ((hwnd != sibling) &&
            ((GetWindowLongA( sibling, GWL_STYLE) & 0x0f) == BS_AUTORADIOBUTTON))
993
            SendMessageW( sibling, BM_SETCHECK, BUTTON_UNCHECKED, 0 );
994
        sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
995
    } while (sibling != start);
Alexandre Julliard's avatar
Alexandre Julliard committed
996 997 998
}


Alexandre Julliard's avatar
Alexandre Julliard committed
999 1000 1001 1002
/**********************************************************************
 *       Group Box Functions
 */

1003
static void GB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
1004
{
1005
    RECT rc, rcFrame;
1006
    HBRUSH hbr;
1007
    HFONT hFont;
1008
    UINT dtFlags;
1009
    LONG style = GetWindowLongA( hwnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1010

Alexandre Julliard's avatar
Alexandre Julliard committed
1011
    if (action != ODA_DRAWENTIRE) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1012

1013
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1014
    /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
1015
    hbr = (HBRUSH)SendMessageW(GetParent(hwnd), WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd);
1016
    if (!hbr) /* did the app forget to call defwindowproc ? */
1017 1018
        hbr = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORSTATIC,
				     (WPARAM)hDC, (LPARAM)hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1019

1020
    GetClientRect( hwnd, &rc);
1021
    if (TWEAK_WineLook == WIN31_LOOK) {
1022
        HPEN hPrevPen = SelectObject( hDC,
1023
					  SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
1024 1025 1026 1027 1028 1029
	HBRUSH hPrevBrush = SelectObject( hDC,
					      GetStockObject(NULL_BRUSH) );

	Rectangle( hDC, rc.left, rc.top + 2, rc.right - 1, rc.bottom - 1 );
	SelectObject( hDC, hPrevBrush );
	SelectObject( hDC, hPrevPen );
1030
    } else {
1031
	TEXTMETRICW tm;
Alexandre Julliard's avatar
Alexandre Julliard committed
1032 1033
	rcFrame = rc;

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

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

    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--;

1055
    BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
Alexandre Julliard's avatar
Alexandre Julliard committed
1056 1057 1058 1059 1060 1061 1062
}


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

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

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

1072
    GetClientRect( hwnd, &rc);
Alexandre Julliard's avatar
Alexandre Julliard committed
1073

1074
    if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1075

1076
    hBrush = (HBRUSH)SendMessageW(GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd);
1077
    if (!hBrush) /* did the app forget to call defwindowproc ? */
1078 1079
        hBrush = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORBTN,
					(WPARAM)hDC, (LPARAM)hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1080

1081
    FillRect( hDC, &rc, hBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
1082
    if ((action == ODA_FOCUS) ||
1083
        ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
1084
        DrawFocusRect( hDC, &rc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1085
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1086

Alexandre Julliard's avatar
Alexandre Julliard committed
1087 1088 1089 1090 1091

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

1092
static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
1093
{
1094
    LONG state = get_button_state( hwnd );
1095
    DRAWITEMSTRUCT dis;
1096 1097
    HRGN clipRegion;
    RECT clipRect;
1098
    UINT id = GetWindowLongA( hwnd, GWL_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
1099

Alexandre Julliard's avatar
Alexandre Julliard committed
1100
    dis.CtlType    = ODT_BUTTON;
1101
    dis.CtlID      = id;
Alexandre Julliard's avatar
Alexandre Julliard committed
1102 1103
    dis.itemID     = 0;
    dis.itemAction = action;
1104 1105 1106 1107
    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
1108 1109
    dis.hDC        = hDC;
    dis.itemData   = 0;
1110
    GetClientRect( hwnd, &dis.rcItem );
1111

1112
    clipRegion = CreateRectRgnIndirect(&dis.rcItem);
1113 1114 1115
    if (GetClipRgn(hDC, clipRegion) != 1)
    {
	DeleteObject(clipRegion);
1116
	clipRegion=NULL;
1117 1118
    }
    clipRect = dis.rcItem;
1119
    DPtoLP(hDC, (LPPOINT) &clipRect, 2);
1120 1121
    IntersectClipRect(hDC, clipRect.left,  clipRect.top, clipRect.right, clipRect.bottom);

1122
    SetBkColor( hDC, GetSysColor( COLOR_BTNFACE ) );
1123 1124
    SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
    SelectClipRgn(hDC, clipRegion);
Alexandre Julliard's avatar
Alexandre Julliard committed
1125
}