[PATCH 2/2] user32: Implement BS_SPLITBUTTON and BS_DEFSPLITBUTTON.
Shu Yokoyama
yokoyama at graco.c.u-tokyo.ac.jp
Sun Feb 5 18:54:21 CST 2017
Signed-off-by: Shu Yokoyama <yokoyama at graco.c.u-tokyo.ac.jp>
---
dlls/user32/button.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 142 insertions(+), 10 deletions(-)
diff --git a/dlls/user32/button.c b/dlls/user32/button.c
index e85e30d..135fb1b 100644
--- a/dlls/user32/button.c
+++ b/dlls/user32/button.c
@@ -29,6 +29,8 @@
*
* TODO
* Styles
+ * - BS_COMMANDLINK
+ * - BS_DEFCOMMANDLINK
* - BS_NOTIFY: is it complete?
* - BS_RIGHTBUTTON: same as BS_LEFTTEXT
*
@@ -42,6 +44,8 @@
* - BCM_GETTEXTMARGIN
* - BCM_SETIMAGELIST
* - BCM_SETTEXTMARGIN
+ * - BCM_SETSPLITINFO
+ * - BCM_GETSPLITINFO
*
* Notifications
* - BCN_HOTITEMCHANGE
@@ -61,6 +65,8 @@
* - Button_GetTextMargin
* - Button_SetImageList
* - Button_SetTextMargin
+ * - Button_SetSplitInfo
+ * - Button_GetSplitInfo
*/
#include <stdarg.h>
@@ -73,6 +79,7 @@
#include "winbase.h"
#include "wingdi.h"
#include "controls.h"
+#include "commctrl.h"
#include "win.h"
#include "user_private.h"
#include "wine/debug.h"
@@ -101,6 +108,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(button);
static UINT BUTTON_CalcLabelRect( HWND hwnd, HDC hdc, RECT *rc );
static void PB_Paint( HWND hwnd, HDC hDC, UINT action );
+static void BUTTON_ClickSplitDropdown( HWND hwnd );
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 );
@@ -122,7 +130,11 @@ static const WORD maxCheckState[MAX_BTN_TYPE] =
BST_UNCHECKED, /* BS_USERBUTTON */
BST_CHECKED, /* BS_AUTORADIOBUTTON */
BST_UNCHECKED, /* BS_PUSHBOX */
- BST_UNCHECKED /* BS_OWNERDRAW */
+ BST_UNCHECKED, /* BS_OWNERDRAW */
+ BST_UNCHECKED, /* BS_SPLITBUTTON */
+ BST_UNCHECKED, /* BS_DEFSPLITBUTTON */
+ BST_UNCHECKED, /* BS_COMMANDLINK */
+ BST_UNCHECKED /* BS_DEFCOMMANDLINK */
};
typedef void (*pfPaint)( HWND hwnd, HDC hdc, UINT action );
@@ -140,7 +152,11 @@ static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
UB_Paint, /* BS_USERBUTTON */
CB_Paint, /* BS_AUTORADIOBUTTON */
NULL, /* BS_PUSHBOX */
- OB_Paint /* BS_OWNERDRAW */
+ OB_Paint, /* BS_OWNERDRAW */
+ PB_Paint, /* BS_SPLITBUTTON */
+ PB_Paint, /* BS_DEFSPLITBUTTON */
+ NULL, /* BS_COMMANDLINK */
+ NULL /* BS_DEFCOMMANDLINK */
};
/*********************************************************************
@@ -203,6 +219,12 @@ static inline WCHAR *get_button_text( HWND hwnd )
return buffer;
}
+/* TODO: stub */
+static inline LONG get_button_split_width(void)
+{
+ return 17;
+}
+
/***********************************************************************
* ButtonWndProc_common
*/
@@ -231,6 +253,8 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
case BS_RADIOBUTTON:
case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
case BS_GROUPBOX: return DLGC_STATIC;
+ case BS_SPLITBUTTON: return DLGC_WANTARROWS | DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
+ case BS_DEFSPLITBUTTON: return DLGC_WANTARROWS | DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
default: return DLGC_BUTTON;
}
@@ -290,6 +314,11 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
SetCapture( hWnd );
}
+ if ((btn_type == BS_SPLITBUTTON || btn_type == BS_DEFSPLITBUTTON) &&
+ (wParam == VK_UP || wParam == VK_DOWN) )
+ {
+ BUTTON_ClickSplitDropdown(hWnd);
+ }
break;
case WM_LBUTTONDBLCLK:
@@ -303,8 +332,20 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
}
/* fall through */
case WM_LBUTTONDOWN:
- SetCapture( hWnd );
+ /* TODO: ignore events during closing the dropdown menu */
SetFocus( hWnd );
+
+ GetClientRect( hWnd, &rect );
+ if (btn_type == BS_SPLITBUTTON || btn_type == BS_DEFSPLITBUTTON)
+ {
+ if ( PtInRect( &rect, pt ) && ((rect.right - pt.x) <= get_button_split_width()) )
+ {
+ BUTTON_ClickSplitDropdown(hWnd);
+ break;
+ }
+ }
+
+ SetCapture( hWnd );
set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
break;
@@ -514,6 +555,37 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
paint_button( hWnd, btn_type, ODA_SELECT );
break;
+ case BCM_SETSPLITINFO:
+ FIXME("SplitButton BCM_SETSPLITINFO not supported yet\n");
+ return FALSE;
+ case BCM_GETSPLITINFO:
+ if (btn_type == BS_SPLITBUTTON || btn_type == BS_DEFSPLITBUTTON)
+ {
+ /* FIXME stub */
+ BUTTON_SPLITINFO *info = (BUTTON_SPLITINFO*)lParam;
+ if ( info->mask & BCSIF_SIZE ) {
+ info->size.cx = get_button_split_width();
+ info->size.cy = 0;
+ }
+ if ( info->mask & BCSIF_STYLE )
+ info->uSplitStyle = 0;
+ if ( info->mask & BCSIF_IMAGE )
+ info->himlGlyph = NULL;
+ if ( info->mask & BCSIF_GLYPH )
+ info->himlGlyph = (HIMAGELIST)0x36; /* Glyph: BLACK DOWN-POINTING TRIANGLE */
+ return TRUE;
+ } else
+ return FALSE;
+ case BCM_SETDROPDOWNSTATE:
+ if (btn_type == BS_SPLITBUTTON || btn_type == BS_DEFSPLITBUTTON)
+ {
+ if ( wParam )
+ set_button_state( hWnd, get_button_state(hWnd) | BST_DROPDOWNPUSHED );
+ else
+ set_button_state( hWnd, get_button_state(hWnd) & ~BST_DROPDOWNPUSHED );
+ return TRUE;
+ }
+ return FALSE;
case WM_NCHITTEST:
if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
/* fall through */
@@ -547,7 +619,10 @@ static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style )
case BS_CENTER: dtStyle |= DT_CENTER; break;
default:
/* Pushbutton's text is centered by default */
- if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
+ if (get_button_type(style) <= BS_DEFPUSHBUTTON ||
+ get_button_type(style) == BS_SPLITBUTTON ||
+ get_button_type(style) == BS_DEFSPLITBUTTON)
+ dtStyle |= DT_CENTER;
/* all other flavours have left aligned text */
}
@@ -746,7 +821,8 @@ static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc)
*/
static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
{
- RECT rc, r;
+ RECT rc, r, rfcs;
+ LONG widthSplit = get_button_split_width();
UINT dtFlags, uState;
HPEN hOldPen;
HBRUSH hOldBrush;
@@ -756,10 +832,18 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
LONG state = get_button_state( hwnd );
LONG style = GetWindowLongW( hwnd, GWL_STYLE );
BOOL pushedState = (state & BST_PUSHED);
+ BOOL isDef = (get_button_type(style) == BS_DEFPUSHBUTTON) ||
+ (get_button_type(style) == BS_DEFSPLITBUTTON);
+ BOOL isSplit = (get_button_type(style) == BS_SPLITBUTTON) ||
+ (get_button_type(style) == BS_DEFSPLITBUTTON);
HWND parent;
HRGN hrgn;
GetClientRect( hwnd, &rc );
+ rfcs = rc;
+
+ if (isSplit)
+ rfcs.right -= widthSplit;
/* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
@@ -773,7 +857,7 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
oldBkMode = SetBkMode(hDC, TRANSPARENT);
- if (get_button_type(style) == BS_DEFPUSHBUTTON)
+ if (isDef)
{
if (action != ODA_FOCUS)
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
@@ -789,7 +873,7 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
uState |= DFCS_MONO;
else if (pushedState)
{
- if (get_button_type(style) == BS_DEFPUSHBUTTON )
+ if (isDef)
uState |= DFCS_FLAT;
else
uState |= DFCS_PUSHED;
@@ -800,8 +884,37 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
+ if (isSplit)
+ {
+ POINT triangle[3];
+ /* draw split line */
+ RECT rdd = rc;
+ rdd.left = rc.right - widthSplit;
+ rdd.top += 3;
+ rdd.bottom -= 3;
+ rdd.right = rdd.left + 3;
+ if (pushedState)
+ OffsetRect(&rdd, 1, 1);
+
+ SelectObject(hDC, GetStockObject(NULL_PEN));
+ SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW));
+ Rectangle( hDC, rdd.left, rdd.top, rdd.right, rdd.bottom);
+
+
+ /* draw drop down glyph */
+ triangle[0].x = rdd.left + 3;
+ triangle[0].y = (rdd.top + rdd.bottom)/2 - 3;
+ triangle[1].x = triangle[0].x + 11;
+ triangle[1].y = triangle[0].y;
+ triangle[2].x = triangle[0].x + 5;
+ triangle[2].y = triangle[0].y + 6;
+
+ SelectObject(hDC, GetSysColorBrush(COLOR_BTNTEXT));
+ Polygon(hDC, triangle, 3);
+ }
+
/* draw button label */
- r = rc;
+ r = rfcs;
dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
if (dtFlags == (UINT)-1L)
@@ -819,8 +932,8 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
draw_focus:
if (action == ODA_FOCUS || (state & BST_FOCUS))
{
- InflateRect( &rc, -2, -2 );
- DrawFocusRect( hDC, &rc );
+ InflateRect( &rfcs, -2, -2 );
+ DrawFocusRect( hDC, &rfcs );
}
cleanup:
@@ -832,6 +945,25 @@ draw_focus:
}
/**********************************************************************
+ * Notify parent of clicking the drop down arrow
+ */
+static void BUTTON_ClickSplitDropdown( HWND hwnd )
+{
+ RECT rect;
+ NMBCDROPDOWN nmbcdd;
+ GetClientRect( hwnd, &rect );
+ nmbcdd.hdr.hwndFrom = hwnd;
+ nmbcdd.hdr.idFrom = GetWindowLongPtrW(hwnd,GWLP_ID);
+ nmbcdd.hdr.code = BCN_DROPDOWN;
+ nmbcdd.rcButton = rect;
+
+ SendMessageW(hwnd, BCM_SETDROPDOWNSTATE, (WPARAM)1, 0);
+ TRACE("notification(WM_NOTIFY) BCN_DROPDOWN sent to hwnd=%p\n", GetParent(hwnd));
+ FORWARD_WM_NOTIFY(GetParent(hwnd), GetWindowLongPtrW(hwnd,GWLP_ID), &nmbcdd, SendMessageW);
+ SendMessageW(hwnd, BCM_SETDROPDOWNSTATE, (WPARAM)0, 0);
+}
+
+/**********************************************************************
* Check Box & Radio Button Functions
*/
--
2.9.3
More information about the wine-patches
mailing list