[CALC] Add theming support, requires Windows XP or later. CORE-13343
authorCarlo-Bramini <carlo_bramini@users.sourceforge.net>
Mon, 6 Aug 2018 18:23:12 +0000 (20:23 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 18 Mar 2019 00:34:02 +0000 (01:34 +0100)
- Fix errors if a theme api is missing.
- Add callback to functions for drawing themed transparent background.
- Fix drawing glitch when theming is applied.
- Redraw on theme change: automatically redraw the window if the
  theme is changed while the application is active.
- Colours are now declared though RGB() macro.
- Removed safe DS_SHELLFONT declaration.

base/applications/calc/CMakeLists.txt
base/applications/calc/calc.h
base/applications/calc/res/calc_sm.ico [deleted file]
base/applications/calc/resource.h
base/applications/calc/resource.rc
base/applications/calc/theme.c [new file with mode: 0644]
base/applications/calc/winmain.c
base/applications/calc/wmmsg.h [deleted file]

index ffe0ee3..d273f10 100644 (file)
@@ -8,6 +8,7 @@ list(APPEND SOURCE
     utl_ieee.c
     winmain.c
     htmlhelp.c
+    theme.c
     calc.h)
 
 file(GLOB calc_rc_deps res/*.*)
index db984f0..f6c2c7c 100644 (file)
@@ -17,6 +17,9 @@
 #endif
 #include <limits.h>
 
+/* RESOURCES */
+#include "resource.h"
+
 /* Messages reserved for the main dialog */
 #define WM_CLOSE_STATS      (WM_APP+1)
 #define WM_HANDLE_CLIPBOARD (WM_APP+2)
 
 #endif
 
-#include "resource.h"
-
-#ifndef IDC_STATIC
-#define IDC_STATIC  ((DWORD)-1)
-#endif
-
 #define CALC_VERSION        _T("1.12")
 
 #define MAX_CALC_SIZE       256
@@ -67,6 +64,34 @@ extern type_HtmlHelpW calc_HtmlHelpW;
 void HtmlHelp_Start(HINSTANCE hInstance);
 void HtmlHelp_Stop(void);
 
+/* THEMING SUPPORT */
+#if (_WIN32_WINNT >= 0x0600)
+#include <vssym32.h>
+#include <vsstyle.h>
+#else
+#include <tmschema.h>
+#endif
+#include <uxtheme.h>
+
+void Theme_Start(HINSTANCE hInstance);
+void Theme_Stop(void);
+
+typedef HTHEME   (WINAPI* type_OpenThemeData)(HWND,const WCHAR*);
+typedef HRESULT  (WINAPI* type_CloseThemeData)(HTHEME);
+typedef HRESULT  (WINAPI* type_DrawThemeBackground)(HTHEME,HDC,int,int,const RECT*,const RECT*);
+typedef BOOL     (WINAPI* type_IsAppThemed)(void);
+typedef BOOL     (WINAPI* type_IsThemeActive)(void);
+typedef BOOL     (WINAPI* type_IsThemeBackgroundPartiallyTransparent)(HTHEME, int, int);
+typedef HRESULT  (WINAPI* type_DrawThemeParentBackground)(HWND, HDC, RECT *);
+
+extern type_OpenThemeData                   calc_OpenThemeData;
+extern type_CloseThemeData                  calc_CloseThemeData;
+extern type_DrawThemeBackground             calc_DrawThemeBackground;
+extern type_IsAppThemed                     calc_IsAppThemed;
+extern type_IsThemeActive                   calc_IsThemeActive;
+extern type_IsThemeBackgroundPartiallyTransparent calc_IsThemeBackgroundPartiallyTransparent;
+extern type_DrawThemeParentBackground       calc_DrawThemeParentBackground;
+
 /*#define USE_KEYBOARD_HOOK*/
 
 #define SIZEOF(_ar)     (sizeof(_ar)/sizeof(_ar[1]))
@@ -144,6 +169,8 @@ typedef struct {
     HHOOK         hKeyboardHook;
 #endif
     HWND          hWnd;
+    HICON         hBgIcon;
+    HICON         hSmIcon;
     DWORD         layout;
     TCHAR         buffer[MAX_CALC_SIZE];
     TCHAR         source[MAX_CALC_SIZE];
diff --git a/base/applications/calc/res/calc_sm.ico b/base/applications/calc/res/calc_sm.ico
deleted file mode 100644 (file)
index c6e151d..0000000
Binary files a/base/applications/calc/res/calc_sm.ico and /dev/null differ
index c9a3ac0..08e985c 100644 (file)
@@ -1,5 +1,9 @@
 #pragma once
 
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+
 #define IDS_CALC_NAME         1
 #define IDS_MATH_ERROR        2
 #define IDS_QUICKHELP         3
@@ -11,8 +15,7 @@
 #define IDR_MENU_SCIENTIFIC_1 106
 #define IDR_MENU_SCIENTIFIC_2 107
 #define IDR_MENU_STANDARD     108
-#define IDI_CALC_BIG          110
-#define IDI_CALC_SMALL        111
+#define IDI_CALC              110
 #define IDC_RADIO_HEX         1002
 #define IDC_RADIO_DEC         1003
 #define IDC_RADIO_OCT         1004
index 7272276..9bb5620 100644 (file)
 
 #include "resource.h"
 
-#ifndef IDC_STATIC
-#define IDC_STATIC -1
-#endif
-
 /* Common resources */
 
 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 
 /* Icons */
-IDI_CALC_BIG ICON "res/calc.ico"
-IDI_CALC_SMALL ICON "res/calc_sm.ico"
+IDI_CALC ICON DISCARDABLE "res/calc.ico"
 
+/* Manifest */
 #include <reactos/manifest_exe.rc>
 
 /* UTF-8 */
diff --git a/base/applications/calc/theme.c b/base/applications/calc/theme.c
new file mode 100644 (file)
index 0000000..8055b36
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * ReactOS Calc (Theming support)
+ *
+ * Copyright 2007-2017, Carlo Bramini
+ *
+ * This program 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 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "calc.h"
+
+#define GET_CB(name) \
+    calc_##name = (type_##name)GetProcAddress(hUxTheme, #name); \
+    if (calc_##name == NULL) calc_##name = dummy_##name;
+
+static HTHEME WINAPI
+dummy_OpenThemeData(HWND hwnd, const WCHAR *pszClassList);
+
+static HRESULT WINAPI
+dummy_CloseThemeData(HTHEME hTheme);
+
+static HRESULT WINAPI
+dummy_DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
+    const RECT *prc, const RECT *prcClip);
+
+static BOOL WINAPI
+dummy_IsAppThemed(void);
+
+static BOOL WINAPI
+dummy_IsThemeActive(void);
+
+static BOOL WINAPI
+dummy_IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId, int iStateId);
+
+static HRESULT WINAPI
+dummy_DrawThemeParentBackground(HWND hWnd, HDC hdc, RECT *prc);
+
+
+type_OpenThemeData       calc_OpenThemeData       = dummy_OpenThemeData;
+type_CloseThemeData      calc_CloseThemeData      = dummy_CloseThemeData;
+type_DrawThemeBackground calc_DrawThemeBackground = dummy_DrawThemeBackground;
+type_IsAppThemed         calc_IsAppThemed         = dummy_IsAppThemed;
+type_IsThemeActive       calc_IsThemeActive       = dummy_IsThemeActive;
+type_IsThemeBackgroundPartiallyTransparent calc_IsThemeBackgroundPartiallyTransparent = \
+    dummy_IsThemeBackgroundPartiallyTransparent;
+type_DrawThemeParentBackground calc_DrawThemeParentBackground = \
+    dummy_DrawThemeParentBackground;
+
+static HMODULE hUxTheme;
+
+static HTHEME WINAPI
+dummy_OpenThemeData(HWND hwnd, const WCHAR* pszClassList)
+{
+    return NULL;
+}
+
+static HRESULT WINAPI
+dummy_CloseThemeData(HTHEME hTheme)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+dummy_DrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
+            const RECT* prc, const RECT* prcClip)
+{
+    return E_NOTIMPL;
+}
+
+static BOOL WINAPI
+dummy_IsAppThemed(void)
+{
+    return FALSE;
+}
+
+static BOOL WINAPI
+dummy_IsThemeActive(void)
+{
+    return FALSE;
+}
+
+static BOOL WINAPI
+dummy_IsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId, int iStateId)
+{
+    return FALSE;
+}
+
+static HRESULT WINAPI
+dummy_DrawThemeParentBackground(HWND hWnd, HDC hdc, RECT *prc)
+{
+    return E_NOTIMPL;
+}
+
+void Theme_Start(HINSTANCE hInstance)
+{
+    hUxTheme = LoadLibrary(_T("UXTHEME"));
+    if (hUxTheme == NULL)
+        return;
+
+    GET_CB(OpenThemeData)
+    GET_CB(CloseThemeData)
+    GET_CB(DrawThemeBackground)
+    GET_CB(IsAppThemed)
+    GET_CB(IsThemeActive)
+    GET_CB(IsThemeBackgroundPartiallyTransparent)
+    GET_CB(DrawThemeParentBackground)
+}
+
+void Theme_Stop(void)
+{
+    if(hUxTheme == NULL)
+        return;
+
+    FreeLibrary(hUxTheme);
+    hUxTheme = NULL;
+}
index 696e3ee..fceb41f 100644 (file)
@@ -48,9 +48,9 @@
 #define BITMASK_OCT_MASK    0x02
 #define BITMASK_BIN_MASK    0x01
 
-#define CALC_CLR_RED        0x000000FF
-#define CALC_CLR_BLUE       0x00FF0000
-#define CALC_CLR_PURP       0x00FF00FF
+#define CALC_CLR_RED        RGB(0xFF, 0x00, 0x00)
+#define CALC_CLR_BLUE       RGB(0x00, 0x00, 0xFF)
+#define CALC_CLR_PURP       RGB(0xFF, 0x00, 0xFF)
 
 typedef struct {
     CHAR key; // Virtual key identifier
@@ -233,10 +233,22 @@ static const function_table_t function_table[] = {
     { IDC_BUTTON_LEFTPAR,  NO_CHAIN,              0, run_lpar,    NULL,        NULL,     NULL,     },
 };
 
+/* Sub-classing information for theming support */
+typedef struct{
+    BOOL    bHover;
+    WNDPROC oldProc;
+} BTNINFO,*LPBTNINFO;
+
+
 /*
-*/
+ * Global variable declaration
+ */
+
+calc_t  calc;
 
-calc_t calc;
+/* Hot-state info for theming support */
+BTNINFO BtnInfo[255];
+UINT    BtnCount;
 
 static void load_config(void)
 {
@@ -326,7 +338,7 @@ static LRESULT post_key_press(LPARAM lParam, WORD idc)
     if (!GetClassName(hCtlWnd, ClassName, SIZEOF(ClassName)))
         return 1;
 
-    if (!_tcscmp(ClassName, TEXT("Button"))) {
+    if (!_tcscmp(ClassName, WC_BUTTON)) {
         DWORD dwStyle = GetWindowLongPtr(hCtlWnd, GWL_STYLE) & 0xF;
 
         /* Set states for press/release, but only for push buttons */
@@ -1213,14 +1225,58 @@ static void run_lpar(calc_number_t *c)
 static LRESULT CALLBACK SubclassButtonProc(HWND hWnd, WPARAM wp, LPARAM lp)
 {
     LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lp;
-    DWORD            dwStyle;
     UINT             dwText;
     TCHAR            text[64];
     int              dx, dy, len;
     SIZE             size;
     POINT            pt;
 
-    if(dis->CtlType == ODT_BUTTON) {
+    if(dis->CtlType == ODT_BUTTON)
+    {
+        HTHEME hTheme = NULL;
+        LPBTNINFO lpBtnInfo;
+
+        if (calc_IsAppThemed() && calc_IsThemeActive())
+            hTheme = calc_OpenThemeData(hWnd, L"Button");
+
+        if (hTheme)
+        {
+            int iState = 0;
+
+            if ((dis->itemState & ODS_DISABLED))
+                iState |= PBS_DISABLED;
+            if ((dis->itemState & ODS_SELECTED))
+                iState |= PBS_PRESSED;
+
+            lpBtnInfo = (LPBTNINFO)GetWindowLongPtr(dis->hwndItem, GWLP_USERDATA);
+            if (lpBtnInfo != NULL)
+            {
+                if (lpBtnInfo->bHover)
+                    iState |= PBS_HOT;
+            }
+
+            if (calc_IsThemeBackgroundPartiallyTransparent(hTheme, BP_PUSHBUTTON, iState))
+            {
+                calc_DrawThemeParentBackground(dis->hwndItem, dis->hDC, &dis->rcItem);
+            }
+
+            // Draw the frame around the control
+            calc_DrawThemeBackground(hTheme, dis->hDC, BP_PUSHBUTTON, iState, &dis->rcItem, NULL);
+
+            calc_CloseThemeData(hTheme);
+        } else {
+            /* default state: unpushed */
+            DWORD dwStyle = 0;
+
+            if ((dis->itemState & ODS_SELECTED))
+                dwStyle = DFCS_PUSHED;
+
+            DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | dwStyle);
+        }
+
+        /* button text to write */
+        len = GetWindowText(dis->hwndItem, text, SIZEOF(text));
+
         /*
          * little exception: 1/x has different color
          * in standard and scientific modes
@@ -1236,21 +1292,20 @@ static LRESULT CALLBACK SubclassButtonProc(HWND hWnd, WPARAM wp, LPARAM lp)
                 break;
             }
         }
-        /* button text to write */
-        len = GetWindowText(dis->hwndItem, text, SIZEOF(text));
-        /* default state: unpushed & enabled */
-        dwStyle = 0;
+
+        /* No background, to avoid corruption of the texture */
+        SetBkMode(dis->hDC, TRANSPARENT);
+
+        /* Default state: enabled */
         dwText = 0;
         if ((dis->itemState & ODS_DISABLED))
             dwText = DSS_DISABLED;
-        if ((dis->itemState & ODS_SELECTED))
-            dwStyle = DFCS_PUSHED;
 
-        DrawFrameControl(dis->hDC, &dis->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH | dwStyle);
+        /* Draw the text in the button */
         GetTextExtentPoint32(dis->hDC, text, len, &size);
         dx = ((dis->rcItem.right-dis->rcItem.left) - size.cx) >> 1;
         dy = ((dis->rcItem.bottom-dis->rcItem.top) - size.cy) >> 1;
-        if ((dwStyle & DFCS_PUSHED)) {
+        if ((dis->itemState & ODS_SELECTED)) {
             dx++;
             dy++;
         }
@@ -1261,6 +1316,61 @@ static LRESULT CALLBACK SubclassButtonProc(HWND hWnd, WPARAM wp, LPARAM lp)
     return 1L;
 }
 
+static INT_PTR CALLBACK HotButtonProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+    LPBTNINFO lpBtnInfo = (LPBTNINFO)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+    TRACKMOUSEEVENT mouse_event;
+
+    switch (msg) {
+    case WM_MOUSEMOVE:
+        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 WM_MOUSEHOVER:
+        lpBtnInfo->bHover = TRUE;
+        InvalidateRect(hWnd, NULL, FALSE);
+        break;
+
+    case WM_MOUSELEAVE:
+        lpBtnInfo->bHover = FALSE;
+        InvalidateRect(hWnd, NULL, FALSE);
+        break;
+    }
+
+    return CallWindowProc(lpBtnInfo->oldProc, hWnd, msg, wp, lp);
+}
+
+static BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam)
+{
+    TCHAR szClass[64];
+
+    if (!GetClassName(hWnd, szClass, SIZEOF(szClass)))
+        return TRUE;
+
+    if (!_tcscmp(szClass, WC_BUTTON))
+    {
+        int *pnCtrls = (int *)lParam;
+        int nCtrls = *pnCtrls;
+
+        BtnInfo[nCtrls].oldProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+        BtnInfo[nCtrls].bHover  = FALSE;
+
+        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&BtnInfo[nCtrls]);
+        SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)HotButtonProc);
+
+        *pnCtrls = ++nCtrls;
+    }
+    return TRUE;
+}
+
 static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
 {
     unsigned int x;
@@ -1276,6 +1386,9 @@ static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
         EnableMenuItem(GetMenu(hWnd), IDM_HELP_HELP, MF_BYCOMMAND | MF_GRAYED);
 #endif
         calc.hWnd=hWnd;
+        /* Enumerate children and apply hover function */
+        BtnCount = 0;
+        EnumChildWindows(hWnd, EnumChildProc, (LPARAM)&BtnCount);
 
 #ifdef USE_KEYBOARD_HOOK
         calc.hKeyboardHook=SetWindowsHookEx(
@@ -1300,8 +1413,8 @@ static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
         /* remove keyboard focus */
         SetFocus(GetDlgItem(hWnd, IDC_BUTTON_FOCUS));
         /* set our calc icon */
-        SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_BIG)));
-        SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_SMALL)));
+        SendMessage(hWnd, WM_SETICON, ICON_BIG,   (LPARAM)calc.hBgIcon);
+        SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)calc.hSmIcon);
 
         /* Sets the state of the option to group digits */
         hMenu = GetSubMenu(GetMenu(hWnd), 1);
@@ -1354,7 +1467,7 @@ static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
             TCHAR infotext[200];
             LoadString(calc.hInstance, IDS_CALC_NAME, infotitle, SIZEOF(infotitle));
             LoadString(calc.hInstance, IDS_AUTHOR, infotext, SIZEOF(infotext));
-            ShellAbout(hWnd, infotitle, infotext, (HICON)LoadIcon(calc.hInstance, MAKEINTRESOURCE(IDI_CALC_BIG)));
+            ShellAbout(hWnd, infotitle, infotext, calc.hBgIcon);
             return TRUE;
         }
         case IDM_HELP_HELP:
@@ -1791,6 +1904,10 @@ static INT_PTR CALLBACK DlgMainProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
     case WM_EXITMENULOOP:
         calc.is_menu_on = FALSE;
         break;
+
+    case WM_THEMECHANGED:
+        InvalidateRect(hWnd, NULL, FALSE);
+        break;
     }
     return FALSE;
 }
@@ -1804,6 +1921,7 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL
     MSG msg;
     DWORD dwLayout;
 
+    /* Initialize controls for theming & manifest support */
     InitCommonControls();
 
     calc.hInstance = hInstance;
@@ -1816,6 +1934,24 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL
 
     HtmlHelp_Start(hInstance);
 
+    Theme_Start(hInstance);
+
+    calc.hBgIcon = LoadImage(
+                    hInstance,
+                    MAKEINTRESOURCE(IDI_CALC),
+                    IMAGE_ICON,
+                    GetSystemMetrics(SM_CXICON),
+                    GetSystemMetrics(SM_CYICON),
+                    0);
+
+    calc.hSmIcon = LoadImage(
+                    hInstance,
+                    MAKEINTRESOURCE(IDI_CALC),
+                    IMAGE_ICON,
+                    GetSystemMetrics(SM_CXSMICON),
+                    GetSystemMetrics(SM_CYSMICON),
+                    0);
+
     do {
         /* ignore hwnd: dialogs are already visible! */
         if (calc.layout == CALC_LAYOUT_SCIENTIFIC)
@@ -1841,8 +1977,15 @@ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdL
         }
     } while (calc.action != IDC_STATIC);
 
+    if (calc.hBgIcon != NULL)
+        DestroyIcon(calc.hBgIcon);
+
+    if (calc.hSmIcon != NULL)
+        DestroyIcon(calc.hSmIcon);
+
     stop_rpn_engine();
 
+    Theme_Stop();
     HtmlHelp_Stop();
 
     return 0;
diff --git a/base/applications/calc/wmmsg.h b/base/applications/calc/wmmsg.h
deleted file mode 100644 (file)
index e69de29..0000000