[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