[COMCTL32] -Implement BCM_GETTEXTMARGIN, BCM_SETTEXTMARGIN, BCM_SETIMAGELIST and...
[reactos.git] / reactos / dll / win32 / comctl32 / button.c
index 2a4eda4..89ec414 100644 (file)
@@ -135,6 +135,7 @@ static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
     OB_Paint     /* BS_OWNERDRAW */
 };
 
+/* The original code from user32 was kept in order to make it easier to bring changes from user32 */
 #ifdef _USER32_
 /*********************************************************************
  * button class descriptor
@@ -154,7 +155,6 @@ const struct builtin_class_descr BUTTON_builtin_class =
     IDC_ARROW,           /* cursor */
     0                    /* brush */
 };
-#endif
 
 
 static inline LONG get_button_state( HWND hwnd )
@@ -181,9 +181,41 @@ static __inline LONG get_ui_state( HWND hwnd )
 
 #endif /* __REACTOS__ */
 
-#ifndef _USER32_
+static inline HFONT get_button_font( HWND hwnd )
+{
+    return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
+}
+
+static inline void set_button_font( HWND hwnd, HFONT font )
+{
+    SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font );
+}
+
+static inline UINT get_button_type( LONG window_style )
+{
+    return (window_style & BS_TYPEMASK);
+}
+
+/* paint a button of any type */
+static inline 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 );
+    }
+}
+
+#else
+
 #define NtUserAlterWindowStyle SetWindowLongPtrW
 
+static inline void _SetButtonData(HWND hwnd, PBUTTON_DATA data)
+{
+    SetWindowLongPtrW( hwnd, 0, (LONG)data );
+}
+
 HRGN set_control_clipping( HDC hdc, const RECT *rect )
 {
     RECT rc = *rect;
@@ -204,18 +236,49 @@ HRGN set_control_clipping( HDC hdc, const RECT *rect )
     return hrgn;
 }
 
-BOOL BUTTON_Paint(HTHEME theme, HWND hwnd, HDC hParamDC, UINT action);
+BOOL BUTTON_PaintWithTheme(HTHEME theme, HWND hwnd, HDC hParamDC, LPARAM prfFlag);
 
-#endif
+static inline LONG_PTR get_button_image(HWND hwnd)
+{
+    return _GetButtonData(hwnd)->image;
+}
+
+static inline LONG_PTR set_button_image(HWND hwnd, LONG_PTR image)
+{
+    PBUTTON_DATA data = _GetButtonData(hwnd);
+    LONG_PTR ret = data->image;
+    data->image = image;
+    return ret;
+}
+
+static inline LONG get_button_state( HWND hwnd )
+{
+    return _GetButtonData(hwnd)->state;
+}
+
+static inline void set_button_state( HWND hwnd, LONG state )
+{
+    _GetButtonData(hwnd)->state = state;
+}
+
+static __inline void set_ui_state( HWND hwnd, LONG flags )
+{
+    _GetButtonData(hwnd)->ui_state = flags;
+}
+
+static __inline LONG get_ui_state( HWND hwnd )
+{
+    return _GetButtonData(hwnd)->ui_state;
+}
 
 static inline HFONT get_button_font( HWND hwnd )
 {
-    return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
+    return (HFONT)_GetButtonData(hwnd)->font;
 }
 
 static inline void set_button_font( HWND hwnd, HFONT font )
 {
-    SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font );
+    _GetButtonData(hwnd)->font = font;
 }
 
 static inline UINT get_button_type( LONG window_style )
@@ -226,14 +289,27 @@ static inline UINT get_button_type( LONG window_style )
 /* paint a button of any type */
 static inline void paint_button( HWND hwnd, LONG style, UINT action )
 {
+    HTHEME theme = GetWindowTheme(hwnd);
+    RECT rc;
+    HDC hdc = GetDC( hwnd );
+    /* GetDC appears to give a dc with a clip rect that includes the whoe parent, not sure if it is correct or not. */
+    GetClientRect(hwnd, &rc);
+    IntersectClipRect (hdc, rc.left, rc. top, rc.right, rc.bottom);
+    if (theme && BUTTON_PaintWithTheme(theme, hwnd, hdc, 0))
+    {
+        ReleaseDC( hwnd, hdc );
+        return;
+    }
     if (btnPaintFunc[style] && IsWindowVisible(hwnd))
     {
-        HDC hdc = GetDC( hwnd );
         btnPaintFunc[style]( hwnd, hdc, action );
-        ReleaseDC( hwnd, hdc );
     }
+    ReleaseDC( hwnd, hdc );
 }
 
+#endif
+
+
 /* retrieve the button text; returned buffer must be freed by caller */
 static inline WCHAR *get_button_text( HWND hwnd )
 {
@@ -306,6 +382,121 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
     pt.x = (short)LOWORD(lParam);
     pt.y = (short)HIWORD(lParam);
 
+#ifndef _USER32_
+    switch (uMsg)
+    {
+        case WM_NCCREATE:
+        {
+            PBUTTON_DATA data = HeapAlloc( GetProcessHeap(), 0, sizeof(BUTTON_DATA) );
+            if (!data)
+            {
+                ERR("Failed to alloc internal button data\n");
+                return -1;
+            }
+
+            memset(data, 0, sizeof(BUTTON_DATA));
+            SetRect(&data->rcTextMargin, 1,1,1,1);
+
+            _SetButtonData(hWnd, data);
+            break;
+        }
+        case WM_NCDESTROY:
+        {
+            PBUTTON_DATA data = _GetButtonData(hWnd);
+            if (!data)
+            {
+                ERR("No data");
+                return 0;
+            }
+            HeapFree( GetProcessHeap(), 0, data );
+            _SetButtonData(hWnd, NULL);
+        }
+        case WM_CREATE:
+            OpenThemeData(hWnd, WC_BUTTONW);
+            break;
+        case WM_DESTROY:
+            CloseThemeData (GetWindowTheme(hWnd));
+            break;
+        case WM_THEMECHANGED:
+            CloseThemeData (GetWindowTheme(hWnd));
+            OpenThemeData(hWnd, WC_BUTTONW);
+            InvalidateRect(hWnd, NULL, FALSE);
+            break;
+        case WM_MOUSEHOVER:
+        {
+            int state = (int)SendMessageW(hWnd, BM_GETSTATE, 0, 0);
+            set_button_state(hWnd, state|BST_HOT);
+            InvalidateRect(hWnd, NULL, FALSE);
+            break;
+        }
+        case WM_MOUSELEAVE:
+        {
+            int state = (int)SendMessageW(hWnd, BM_GETSTATE, 0, 0);
+            set_button_state(hWnd, state&(~BST_HOT));
+            InvalidateRect(hWnd, NULL, FALSE);
+            break;
+        }
+        case WM_MOUSEMOVE:
+        {
+            TRACKMOUSEEVENT mouse_event;
+            mouse_event.cbSize = sizeof(TRACKMOUSEEVENT);
+            mouse_event.dwFlags = TME_QUERY;
+            if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&(TME_HOVER|TME_LEAVE)))
+            {
+                mouse_event.dwFlags = TME_HOVER|TME_LEAVE;
+                mouse_event.hwndTrack = hWnd;
+                mouse_event.dwHoverTime = 1;
+                TrackMouseEvent(&mouse_event);
+            }
+            break;
+        }
+        case BCM_GETTEXTMARGIN:
+        {
+            RECT* prc = (RECT*)lParam;
+            PBUTTON_DATA data = _GetButtonData(hWnd);
+            if (!prc || !data)
+                return FALSE;
+            *prc = data->rcTextMargin;
+            return TRUE;
+        }
+        case BCM_SETTEXTMARGIN:
+        {
+            RECT* prc = (RECT*)lParam;
+            PBUTTON_DATA data = _GetButtonData(hWnd);
+            if (!prc || !data)
+                return FALSE;
+            data->rcTextMargin = *prc;
+            return TRUE;
+        }
+        case BCM_SETIMAGELIST:
+        {
+            BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam;
+            PBUTTON_DATA data = _GetButtonData(hWnd);
+            if (!data || !pimldata || !pimldata->himl)
+                return FALSE;
+            data->imlData = *pimldata;
+            return TRUE;
+        }
+        case BCM_GETIMAGELIST:
+        {
+            BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam;
+            PBUTTON_DATA data = _GetButtonData(hWnd);
+            if (!data|| !pimldata)
+                return FALSE;
+            *pimldata = data->imlData;
+            return TRUE;
+        }
+    }
+
+    if (!_GetButtonData(hWnd))
+    {
+        ERR("no data!\n");
+        return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
+                         DefWindowProcA(hWnd, uMsg, wParam, lParam);
+    }
+
+#endif
+
     switch (uMsg)
     {
     case WM_GETDLGCODE:
@@ -350,7 +541,6 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
     case WM_DESTROY:
         break;
 #endif
-
     case WM_ERASEBKGND:
         if (btn_type == BS_OWNERDRAW)
         {
@@ -377,6 +567,14 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
     {
         PAINTSTRUCT ps;
         HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
+#ifndef _USER32_
+        HTHEME theme = GetWindowTheme(hWnd);
+        if (theme && BUTTON_PaintWithTheme(theme, hWnd, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0))
+        {
+            if ( !wParam ) EndPaint( hWnd, &ps );
+            return 0;
+        }
+#endif
         if (btnPaintFunc[btn_type])
         {
             int nOldMode = SetBkMode( hdc, OPAQUE );
@@ -556,6 +754,11 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
     case WM_SETFOCUS:
         TRACE("WM_SETFOCUS %p\n",hWnd);
         set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS );
+#ifndef _USER32_
+        if (btn_type != BS_OWNERDRAW)
+            InvalidateRect(hWnd, NULL, FALSE);
+        else
+#endif
         paint_button( hWnd, btn_type, ODA_FOCUS );
         if (style & BS_NOTIFY)
             BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS);
@@ -565,7 +768,9 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
         TRACE("WM_KILLFOCUS %p\n",hWnd);
         state = get_button_state( hWnd );
         set_button_state( hWnd, state & ~BST_FOCUS );
+#ifdef _USER32_
        paint_button( hWnd, btn_type, ODA_FOCUS );
+#endif
 
         if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd)
             ReleaseCapture();
@@ -624,12 +829,20 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
         default:
             return 0;
         }
+#ifdef _USER32_
         oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam );
+#else
+        oldHbitmap = (HBITMAP)set_button_image(hWnd, lParam );
+#endif
        InvalidateRect( hWnd, NULL, FALSE );
        return (LRESULT)oldHbitmap;
 
     case BM_GETIMAGE:
+#ifdef _USER32_
         return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
+#else
+        return get_button_image(hWnd);
+#endif
 
     case BM_GETCHECK:
         return get_button_state( hWnd ) & 3;
@@ -651,7 +864,11 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
         if ((state & 3) != wParam)
         {
             set_button_state( hWnd, (state & ~3) | wParam );
+#ifdef _USER32
             paint_button( hWnd, btn_type, ODA_SELECT );
+#else
+            InvalidateRect(hWnd, NULL, FALSE);
+#endif
         }
         if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD))
             BUTTON_CheckAutoRadioButton( hWnd );
@@ -667,7 +884,11 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
         else
             set_button_state( hWnd, state & ~BST_PUSHED );
 
+#ifdef _USER32_
         paint_button( hWnd, btn_type, ODA_SELECT );
+#else
+        InvalidateRect(hWnd, NULL, FALSE);
+#endif
         break;
 
 #ifdef __REACTOS__
@@ -813,7 +1034,11 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
       }
 
       case BS_ICON:
+#ifdef _USER32_
          if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
+#else
+         if (!GetIconInfo((HICON)get_button_image(hwnd), &iconInfo))
+#endif
             goto empty_rect;
 
          GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
@@ -826,7 +1051,11 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
          break;
 
       case BS_BITMAP:
+#ifdef _USER32_
          if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
+#else
+         if (!GetObjectW( (HANDLE)get_button_image(hwnd), sizeof(BITMAP), &bm))
+#endif
             goto empty_rect;
 
          r.right  = r.left + bm.bmWidth;
@@ -933,12 +1162,20 @@ static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc)
 
       case BS_ICON:
          flags |= DST_ICON;
+#ifdef _USER32_
          lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
+#else
+         lp = get_button_image(hwnd);
+#endif
          break;
 
       case BS_BITMAP:
          flags |= DST_BITMAP;
+#ifdef _USER32_
          lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
+#else
+         lp = get_button_image(hwnd);
+#endif
          break;
 
       default:
@@ -1388,7 +1625,7 @@ void BUTTON_Register()
     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC;
     wndClass.lpfnWndProc   = ButtonWndProcW;
     wndClass.cbClsExtra    = 0;
-    wndClass.cbWndExtra    = NB_EXTRA_BYTES;
+    wndClass.cbWndExtra    = sizeof(PBUTTON_DATA);
     wndClass.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
     wndClass.lpszClassName = WC_BUTTONW;