#include <user32.h>
#include <wine/debug.h>
+WINE_DEFAULT_DEBUG_CHANNEL(button);
/* GetWindowLong offsets for window extra information */
#define STATE_GWL_OFFSET 0
#define HFONT_GWL_OFFSET (sizeof(LONG))
#define HIMAGE_GWL_OFFSET (HFONT_GWL_OFFSET+sizeof(HFONT))
-#define NB_EXTRA_BYTES (HIMAGE_GWL_OFFSET+sizeof(HANDLE))
+#define UISTATE_GWL_OFFSET (HIMAGE_GWL_OFFSET+sizeof(HFONT))
+#define NB_EXTRA_BYTES (UISTATE_GWL_OFFSET+sizeof(LONG))
/* Button state values */
#define BUTTON_UNCHECKED 0x00
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 );
-static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
-static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
#define MAX_BTN_TYPE 12
/*********************************************************************
* button class descriptor
*/
+static const WCHAR buttonW[] = {'B','u','t','t','o','n',0};
const struct builtin_class_descr BUTTON_builtin_class =
{
-#ifdef __REACTOS__
- L"Button", /* name */
- CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */
- ButtonWndProcW, /* procW */
- ButtonWndProcA, /* procA */
- NB_EXTRA_BYTES, /* extra */
- (LPWSTR)IDC_ARROW, /* cursor */
- 0 /* brush */
-#else
- "Button", /* name */
+ buttonW, /* name */
CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */
ButtonWndProcA, /* procA */
ButtonWndProcW, /* procW */
NB_EXTRA_BYTES, /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
-#endif
};
-__inline static LONG get_button_state( HWND hwnd )
+static inline LONG get_button_state( HWND hwnd )
{
- return GetWindowLongW( hwnd, STATE_GWL_OFFSET );
+ return GetWindowLongPtrW( hwnd, STATE_GWL_OFFSET );
}
-__inline static void set_button_state( HWND hwnd, LONG state )
+static inline void set_button_state( HWND hwnd, LONG state )
{
- SetWindowLongW( hwnd, STATE_GWL_OFFSET, state );
+ SetWindowLongPtrW( hwnd, STATE_GWL_OFFSET, state );
+}
+
+static __inline void set_ui_state( HWND hwnd, LONG flags )
+{
+ SetWindowLongPtrW( hwnd, UISTATE_GWL_OFFSET, flags );
+}
+
+static __inline LONG get_ui_state( HWND hwnd )
+{
+ return GetWindowLongPtrW( hwnd, UISTATE_GWL_OFFSET );
}
__inline static HFONT get_button_font( HWND hwnd )
return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
}
-__inline static void set_button_font( HWND hwnd, HFONT font )
+static inline void set_button_font( HWND hwnd, HFONT font )
{
SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font );
}
-__inline static UINT get_button_type( LONG window_style )
+static inline 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 )
+static inline void paint_button( HWND hwnd, LONG style, UINT action )
{
if (btnPaintFunc[style] && IsWindowVisible(hwnd))
{
}
/* retrieve the button text; returned buffer must be freed by caller */
-__inline static WCHAR *get_button_text( HWND hwnd )
+static inline WCHAR *get_button_text( HWND hwnd )
{
INT len = 512;
WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
return buffer;
}
+static void setup_clipping( HWND hwnd, HDC hdc )
+{
+ RECT rc;
+
+ GetClientRect( hwnd, &rc );
+ DPtoLP( hdc, (POINT *)&rc, 2 );
+ IntersectClipRect( hdc, rc.left, rc.top, rc.right, rc.bottom );
+}
+
+/* Retrieve the UI state for the control */
+static BOOL button_update_uistate(HWND hwnd, BOOL unicode)
+{
+ LONG flags, prevflags;
+
+ if (unicode)
+ flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0);
+ else
+ flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0);
+
+ prevflags = get_ui_state(hwnd);
+
+ if (prevflags != flags)
+ {
+ set_ui_state(hwnd, flags);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/***********************************************************************
* ButtonWndProc_common
*/
-static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
- WPARAM wParam, LPARAM lParam, BOOL unicode )
+LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam, BOOL unicode )
{
RECT rect;
POINT pt;
- LONG style = GetWindowLongW( hWnd, GWL_STYLE );
+ LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE );
UINT btn_type = get_button_type( style );
LONG state;
HANDLE oldHbitmap;
- pt.x = LOWORD(lParam);
- pt.y = HIWORD(lParam);
+ pt.x = (short)LOWORD(lParam);
+ pt.y = (short)HIWORD(lParam);
switch (uMsg)
{
}
if (btn_type >= MAX_BTN_TYPE)
return -1; /* abort */
+
+ /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
+ if (btn_type == BS_USERBUTTON )
+ {
+ style = (style & ~0x0f) | BS_PUSHBUTTON;
+ SetWindowLongPtrW( hWnd, GWL_STYLE, style );
+ }
set_button_state( hWnd, BUTTON_UNCHECKED );
+ button_update_uistate( hWnd, unicode );
return 0;
case WM_ERASEBKGND:
{
SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
+ SetCapture( hWnd );
}
break;
case WM_SETFONT:
set_button_font( hWnd, (HFONT)wParam );
- if (lParam) paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
+ if (lParam) InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_GETFONT:
if (style & BS_NOTIFY)
BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS);
+ InvalidateRect( hWnd, NULL, FALSE );
break;
case WM_SYSCOLORCHANGE:
InvalidateRect( hWnd, NULL, FALSE );
break;
-#ifndef __REACTOS__
- case BM_SETSTYLE16:
-#endif
case BM_SETSTYLE:
if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
btn_type = wParam & 0x0f;
style = (style & ~0x0f) | btn_type;
- SetWindowLongW( hWnd, GWL_STYLE, style );
+ SetWindowLongPtrW( hWnd, GWL_STYLE, style );
/* Only redraw if lParam flag is set.*/
if (lParam)
- paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
+ InvalidateRect( hWnd, NULL, TRUE );
break;
case BM_GETIMAGE:
return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
-#ifndef __REACTOS__
- case BM_GETCHECK16:
-#endif
case BM_GETCHECK:
return get_button_state( hWnd ) & 3;
-#ifndef __REACTOS__
- case BM_SETCHECK16:
-#endif
case BM_SETCHECK:
if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
state = get_button_state( hWnd );
{
if (wParam) style |= WS_TABSTOP;
else style &= ~WS_TABSTOP;
- SetWindowLongW( hWnd, GWL_STYLE, style );
+ SetWindowLongPtrW( hWnd, GWL_STYLE, style );
}
if ((state & 3) != wParam)
{
BUTTON_CheckAutoRadioButton( hWnd );
break;
-#ifndef __REACTOS__
- case BM_GETSTATE16:
-#endif
case BM_GETSTATE:
return get_button_state( hWnd );
-#ifndef __REACTOS__
- case BM_SETSTATE16:
-#endif
case BM_SETSTATE:
state = get_button_state( hWnd );
if (wParam)
paint_button( hWnd, btn_type, ODA_SELECT );
break;
+ case WM_UPDATEUISTATE:
+ if (unicode)
+ DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ else
+ DefWindowProcA(hWnd, uMsg, wParam, lParam);
+
+ if (button_update_uistate(hWnd, unicode))
+ paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
+ break;
+
case WM_NCHITTEST:
if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
/* fall through */
* the passed HWND and calls the real window procedure (with a WND*
* pointer pointing to the locked windowstructure).
*/
-static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if (!IsWindow( hWnd )) return 0;
return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, TRUE );
/***********************************************************************
* ButtonWndProcA
*/
-static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if (!IsWindow( hWnd )) return 0;
return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, FALSE );
/**********************************************************************
* Convert button styles to flags used by DrawText.
- * TODO: handle WS_EX_RIGHT extended style.
*/
-static UINT BUTTON_BStoDT(DWORD style)
+static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style )
{
UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */
/* all other flavours have left aligned text */
}
+ if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER));
+
/* DrawText ignores vertical alignment for multiline text,
- * but we use these flags to align label manualy.
+ * but we use these flags to align label manually.
*/
if (get_button_type(style) != BS_GROUPBOX)
{
*/
static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
{
- LONG style = GetWindowLongW( hwnd, GWL_STYLE );
+ LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
+ LONG ex_style = GetWindowLongPtrW( hwnd, GWL_EXSTYLE );
WCHAR *text;
ICONINFO iconInfo;
BITMAP bm;
- UINT dtStyle = BUTTON_BStoDT(style);
+ UINT dtStyle = BUTTON_BStoDT( style, ex_style );
RECT r = *rc;
INT n;
}
DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
HeapFree( GetProcessHeap(), 0, text );
+
+ if (get_ui_state( hwnd ) & UISF_HIDEACCEL)
+ dtStyle |= DT_HIDEPREFIX;
break;
case BS_ICON:
empty_rect:
rc->right = r.left;
rc->bottom = r.top;
- return (UINT)(LONG)-1;
+ return (UINT)-1;
}
/* Position label inside bounding rectangle according to
*
* Common function for drawing button label.
*/
-static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
+static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc)
{
DRAWSTATEPROC lpOutputProc = NULL;
LPARAM lp;
HBRUSH hbr = 0;
UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
LONG state = get_button_state( hwnd );
- LONG style = GetWindowLongW( hwnd, GWL_STYLE );
+ LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
WCHAR *text = NULL;
/* FIXME: To draw disabled label in Win31 look-and-feel, we probably
if (!(text = get_button_text( hwnd ))) return;
lp = (LPARAM)text;
wp = (WPARAM)dtFlags;
+
+ if (dtFlags & DT_HIDEPREFIX)
+ flags |= DSS_HIDEPREFIX;
break;
case BS_ICON:
*/
static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
{
- RECT rc, focus_rect, r;
+ RECT rc, r;
UINT dtFlags, uState;
HPEN hOldPen;
HBRUSH hOldBrush;
COLORREF oldTxtColor;
HFONT hFont;
LONG state = get_button_state( hwnd );
- LONG style = GetWindowLongW( hwnd, GWL_STYLE );
+ LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
BOOL pushedState = (state & BUTTON_HIGHLIGHTED);
HWND parent;
parent = GetParent(hwnd);
if (!parent) parent = hwnd;
SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
+
+ setup_clipping( hwnd, hDC );
#ifdef __REACTOS__
- hOldPen = (HPEN)SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
+ hOldPen = SelectObject(hDC, GetStockObject(DC_PEN));
+ SetDCPenColor(hDC, GetSysColor(COLOR_WINDOWFRAME));
#else
- hOldPen = (HPEN)SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
+ hOldPen = SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
#endif
- hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
+ hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
oldBkMode = SetBkMode(hDC, TRANSPARENT);
if (get_button_type(style) == BS_DEFPUSHBUTTON)
{
- Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
+ if (action != ODA_FOCUS)
+ Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
InflateRect( &rc, -1, -1 );
}
- uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
+ /* completely skip the drawing if only focus has changed */
+ if (action == ODA_FOCUS) goto draw_focus;
+
+ uState = DFCS_BUTTONPUSH;
if (style & BS_FLAT)
uState |= DFCS_MONO;
DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
- focus_rect = rc;
-
/* draw button label */
r = rc;
dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
if (pushedState)
OffsetRect(&r, 1, 1);
- IntersectClipRect(hDC, rc.left, rc.top, rc.right, rc.bottom);
-
oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
SetTextColor( hDC, oldTxtColor );
- if (state & BUTTON_HASFOCUS)
+draw_focus:
+ if ((action == ODA_FOCUS) ||
+ ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
{
- InflateRect( &focus_rect, -1, -1 );
- IntersectRect(&focus_rect, &focus_rect, &rc);
- DrawFocusRect( hDC, &focus_rect );
+ if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
+ {
+ InflateRect( &rc, -2, -2 );
+ DrawFocusRect( hDC, &rc );
+ }
}
cleanup:
UINT dtFlags;
HFONT hFont;
LONG state = get_button_state( hwnd );
- LONG style = GetWindowLongW( hwnd, GWL_STYLE );
+ LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
HWND parent;
if (style & BS_PUSHLIKE)
if (!hBrush) /* did the app forget to call defwindowproc ? */
hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
(WPARAM)hDC, (LPARAM)hwnd );
+ setup_clipping( hwnd, hDC );
if (style & BS_LEFTTEXT)
{
rtext.left += checkBoxWidth + 4;
rbox.right = checkBoxWidth;
}
-
+
/* Since WM_ERASEBKGND does nothing, first prepare background */
if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
if (dtFlags == (UINT)-1L) /* Noting to draw */
return;
- IntersectClipRect(hDC, client.left, client.top, client.right, client.bottom);
-
if (action == ODA_DRAWENTIRE)
BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
if ((action == ODA_FOCUS) ||
((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
{
- rtext.left--;
- rtext.right++;
- IntersectRect(&rtext, &rtext, &client);
- DrawFocusRect( hDC, &rtext );
+ if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
+ {
+ rtext.left--;
+ rtext.right++;
+ IntersectRect(&rtext, &rtext, &client);
+ DrawFocusRect( hDC, &rtext );
+ }
}
}
{
if (!sibling) break;
if ((hwnd != sibling) &&
- ((GetWindowLongW( sibling, GWL_STYLE) & 0x0f) == BS_AUTORADIOBUTTON))
+ ((GetWindowLongPtrW( sibling, GWL_STYLE) & 0x0f) == BS_AUTORADIOBUTTON))
SendMessageW( sibling, BM_SETCHECK, BUTTON_UNCHECKED, 0 );
sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
} while (sibling != start);
HFONT hFont;
UINT dtFlags;
TEXTMETRICW tm;
- LONG style = GetWindowLongW( hwnd, GWL_STYLE );
+ LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
HWND parent;
if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
if (!hbr) /* did the app forget to call defwindowproc ? */
hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
(WPARAM)hDC, (LPARAM)hwnd);
+ setup_clipping( hwnd, hDC );
GetClientRect( hwnd, &rc);
rcFrame = rc;
* But Windows doesn't clip label's rect, so do I.
*/
- /* There is 1-pixel marging at the left, right, and bottom */
+ /* There is 1-pixel margin at the left, right, and bottom */
rc.left--; rc.right++; rc.bottom++;
FillRect(hDC, &rc, hbr);
rc.left++; rc.right--; rc.bottom--;
FillRect( hDC, &rc, hBrush );
if ((action == ODA_FOCUS) ||
((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
- DrawFocusRect( hDC, &rc );
+ {
+ if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
+ DrawFocusRect( hDC, &rc );
+ }
+
+ BUTTON_NOTIFY_PARENT( hwnd, BN_PAINT );
}
{
LONG state = get_button_state( hwnd );
DRAWITEMSTRUCT dis;
- HRGN clipRegion;
- RECT clipRect;
LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID );
HWND parent;
HFONT hFont, hPrevFont = 0;
dis.itemData = 0;
GetClientRect( hwnd, &dis.rcItem );
- clipRegion = CreateRectRgnIndirect(&dis.rcItem);
- if (GetClipRgn(hDC, clipRegion) != 1)
- {
- DeleteObject(clipRegion);
- clipRegion=NULL;
- }
- clipRect = dis.rcItem;
- DPtoLP(hDC, (LPPOINT) &clipRect, 2);
- IntersectClipRect(hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
-
if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont );
parent = GetParent(hwnd);
if (!parent) parent = hwnd;
SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
+
+ setup_clipping( hwnd, hDC );
+
SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
if (hPrevFont) SelectObject(hDC, hPrevFont);
- SelectClipRgn(hDC, clipRegion);
}