comdlg32: itemdlg split button implementation

Vincent Povirk madewokherd at gmail.com
Tue Aug 25 15:52:39 CDT 2015


These patches apply on top of my series that implements the open
dropdown menu in the common item dialog, based on BS_SPLITBUTTON. They
work in my testing on Windows, but they do not yet work on Wine
because BS_SPLITBUTTON is unimplemented.

This is mostly to show how to do this properly (and hopefully make the
case that the deficiency won't hinder development in the mean time),
but if I'm not around when BS_SPLITBUTTON is implemented, I consider
this a contribution to Wine under the LGPL just like anything I send
to wine-patches.

I'm interested in feedback in terms of whether this is as correct and
clean as it could be, but it's sort of academic until Wine has the
necessary architecture to support it.
-------------- next part --------------
From 2ce90e00efa9c970e2e47cb0f002037a582e98cc Mon Sep 17 00:00:00 2001
From: Vincent Povirk <vincent at codeweavers.com>
Date: Tue, 25 Aug 2015 14:16:55 -0500
Subject: [PATCH 6/6] comdlg32: Use a real split button in the item dialog.

---
 dlls/comdlg32/comdlg32.rc |   7 +--
 dlls/comdlg32/itemdlg.c   | 139 ++++++++--------------------------------------
 include/commctrl.h        |   1 +
 3 files changed, 24 insertions(+), 123 deletions(-)

diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc
index 240972e..fba1373 100644
--- a/dlls/comdlg32/comdlg32.rc
+++ b/dlls/comdlg32/comdlg32.rc
@@ -490,12 +490,7 @@ FONT 8, "MS Shell Dlg"
     COMBOBOX        IDC_FILETYPE, 226, 256, 100, 12,  WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL |
                     WS_CLIPSIBLINGS | CBS_HASSTRINGS | CBS_DROPDOWNLIST
 
-    DEFPUSHBUTTON   "&Open",  IDOK,     350, 240, 32, 14, WS_GROUP | WS_CLIPSIBLINGS
-
-    /* drop-down menu for open button */
-    CONTROL         "6", psh1, "Button", WS_CHILD | WS_CLIPSIBLINGS | WS_GROUP | WS_TABSTOP |
-                    BS_CHECKBOX | BS_PUSHLIKE, 342, 240, 8, 14
-
+    DEFPUSHBUTTON   "&Open",  IDOK,     350, 240, 40, 14, WS_GROUP | WS_CLIPSIBLINGS
     PUSHBUTTON      "Cancel", IDCANCEL, 395, 240, 40, 14, WS_CLIPSIBLINGS
     PUSHBUTTON      "&Help",  pshHelp,  350, 272, 40, 14, WS_CLIPSIBLINGS
 }
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c
index aba4fc4..803e9b2 100644
--- a/dlls/comdlg32/itemdlg.c
+++ b/dlls/comdlg32/itemdlg.c
@@ -1446,7 +1446,8 @@ static BOOL update_open_dropdown(FileDialogImpl *This)
 {
     /* Show or hide the open dropdown button as appropriate */
     BOOL show=FALSE, showing;
-    HWND open_hwnd, dropdown_hwnd;
+    HWND open_hwnd;
+    LONG_PTR style;
 
     if (This->hmenu_opendropdown)
     {
@@ -1468,33 +1469,19 @@ static BOOL update_open_dropdown(FileDialogImpl *This)
     }
 
     open_hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
-    dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
 
-    showing = (GetWindowLongPtrW(dropdown_hwnd, GWL_STYLE) & WS_VISIBLE) != 0;
+    style = GetWindowLongPtrW(open_hwnd, GWL_STYLE);
+    showing = (style & BS_SPLITBUTTON) != 0;
 
     if (showing != show)
     {
-        RECT open_rc, dropdown_rc;
-
-        GetWindowRect(open_hwnd, &open_rc);
-        GetWindowRect(dropdown_hwnd, &dropdown_rc);
-
         if (show)
-        {
-            ShowWindow(dropdown_hwnd, SW_SHOW);
-
-            SetWindowPos(open_hwnd, NULL, 0, 0,
-                (open_rc.right - open_rc.left) - (dropdown_rc.right - dropdown_rc.left),
-                open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
-        }
+            style |= BS_SPLITBUTTON;
         else
-        {
-            ShowWindow(dropdown_hwnd, SW_HIDE);
+            style &= ~BS_SPLITBUTTON;
 
-            SetWindowPos(open_hwnd, NULL, 0, 0,
-                (open_rc.right - open_rc.left) + (dropdown_rc.right - dropdown_rc.left),
-                open_rc.bottom - open_rc.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
-        }
+        SetWindowLongPtrW(open_hwnd, GWL_STYLE, style);
+        InvalidateRect(open_hwnd, NULL, TRUE);
     }
 
     return show;
@@ -1505,12 +1492,11 @@ static void update_layout(FileDialogImpl *This)
     HDWP hdwp;
     HWND hwnd;
     RECT dialog_rc;
-    RECT cancel_rc, dropdown_rc, open_rc;
+    RECT cancel_rc, open_rc;
     RECT filetype_rc, filename_rc, filenamelabel_rc;
     RECT toolbar_rc, ebrowser_rc, customctrls_rc;
     static const UINT vspacing = 4, hspacing = 4;
     static const UINT min_width = 320, min_height = 200;
-    BOOL show_dropdown;
 
     if (!GetClientRect(This->dlg_hwnd, &dialog_rc))
     {
@@ -1543,31 +1529,9 @@ static void update_layout(FileDialogImpl *This)
         cancel_rc.bottom = cancel_rc.top + cancel_height;
     }
 
-    /* Open/Save dropdown */
-    show_dropdown = update_open_dropdown(This);
-
-    if(show_dropdown)
-    {
-        int dropdown_width, dropdown_height;
-        hwnd = GetDlgItem(This->dlg_hwnd, psh1);
-
-        GetWindowRect(hwnd, &dropdown_rc);
-        dropdown_width = dropdown_rc.right - dropdown_rc.left;
-        dropdown_height = dropdown_rc.bottom - dropdown_rc.top;
-
-        dropdown_rc.left = cancel_rc.left - dropdown_width - hspacing;
-        dropdown_rc.top = cancel_rc.top;
-        dropdown_rc.right = dropdown_rc.left + dropdown_width;
-        dropdown_rc.bottom = dropdown_rc.top + dropdown_height;
-    }
-    else
-    {
-        dropdown_rc.left = dropdown_rc.right = cancel_rc.left - hspacing;
-        dropdown_rc.top = cancel_rc.top;
-        dropdown_rc.bottom = cancel_rc.bottom;
-    }
-
     /* Open/Save button */
+    update_open_dropdown(This);
+
     hwnd = GetDlgItem(This->dlg_hwnd, IDOK);
     if(hwnd)
     {
@@ -1576,8 +1540,8 @@ static void update_layout(FileDialogImpl *This)
         open_width = open_rc.right - open_rc.left;
         open_height = open_rc.bottom - open_rc.top;
 
-        open_rc.left = dropdown_rc.left - open_width;
-        open_rc.top = dropdown_rc.top;
+        open_rc.left = cancel_rc.left - open_width;
+        open_rc.top = cancel_rc.top;
         open_rc.right = open_rc.left + open_width;
         open_rc.bottom = open_rc.top + open_height;
     }
@@ -1688,10 +1652,6 @@ static void update_layout(FileDialogImpl *This)
         DeferWindowPos(hdwp, hwnd, NULL, open_rc.left, open_rc.top, 0, 0,
                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
 
-    if(hdwp && This->hmenu_opendropdown && (hwnd = GetDlgItem(This->dlg_hwnd, psh1)))
-        DeferWindowPos(hdwp, hwnd, NULL, dropdown_rc.left, dropdown_rc.top, 0, 0,
-                       SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
-
     if(hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDCANCEL)) )
         DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0,
                        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
@@ -1829,25 +1789,6 @@ static void update_control_text(FileDialogImpl *This)
     }
 }
 
-static LRESULT CALLBACK dropdown_subclass_proc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
-{
-    static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
-    static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
-
-    if (umessage == WM_LBUTTONDOWN)
-    {
-        FileDialogImpl *This = (FileDialogImpl*)GetPropW(hwnd, prop_this);
-
-        SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
-        show_opendropdown(This);
-        SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
-
-        return 0;
-    }
-
-    return CallWindowProcW((WNDPROC)GetPropW(hwnd, prop_oldwndproc), hwnd, umessage, wparam, lparam);
-}
-
 static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
 {
     FileDialogImpl *This = (FileDialogImpl*)lParam;
@@ -1909,37 +1850,6 @@ static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
        (hitem = GetDlgItem(This->dlg_hwnd, IDC_FILENAME)) )
         SendMessageW(hitem, WM_SETTEXT, 0, (LPARAM)This->set_filename);
 
-    if(This->hmenu_opendropdown)
-    {
-        HWND dropdown_hwnd;
-        LOGFONTW lfw, lfw_marlett;
-        HFONT dialog_font;
-        static const WCHAR marlett[] = {'M','a','r','l','e','t','t',0};
-        static const WCHAR prop_this[] = {'i','t','e','m','d','l','g','_','T','h','i','s',0};
-        static const WCHAR prop_oldwndproc[] = {'i','t','e','m','d','l','g','_','o','l','d','w','n','d','p','r','o','c',0};
-
-        dropdown_hwnd = GetDlgItem(This->dlg_hwnd, psh1);
-
-        /* Change dropdown button font to Marlett */
-        dialog_font = (HFONT)SendMessageW(dropdown_hwnd, WM_GETFONT, 0, 0);
-
-        GetObjectW(dialog_font, sizeof(lfw), &lfw);
-
-        memset(&lfw_marlett, 0, sizeof(lfw_marlett));
-        lstrcpyW(lfw_marlett.lfFaceName, marlett);
-        lfw_marlett.lfHeight = lfw.lfHeight;
-        lfw_marlett.lfCharSet = SYMBOL_CHARSET;
-
-        This->hfont_opendropdown = CreateFontIndirectW(&lfw_marlett);
-
-        SendMessageW(dropdown_hwnd, WM_SETFONT, (LPARAM)This->hfont_opendropdown, 0);
-
-        /* Subclass button so we can handle LBUTTONDOWN */
-        SetPropW(dropdown_hwnd, prop_this, (HANDLE)This);
-        SetPropW(dropdown_hwnd, prop_oldwndproc,
-            (HANDLE)SetWindowLongPtrW(dropdown_hwnd, GWLP_WNDPROC, (LONG_PTR)dropdown_subclass_proc));
-    }
-
     ctrl_container_reparent(This, This->dlg_hwnd);
     init_explorerbrowser(This);
     init_toolbar(This, hwnd);
@@ -2009,19 +1919,6 @@ static LRESULT on_idcancel(FileDialogImpl *This)
     return FALSE;
 }
 
-static LRESULT on_command_opendropdown(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
-{
-    if(HIWORD(wparam) == BN_CLICKED)
-    {
-        HWND hwnd = (HWND)lparam;
-        SendMessageW(hwnd, BM_SETCHECK, BST_CHECKED, 0);
-        show_opendropdown(This);
-        SendMessageW(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
-    }
-
-    return FALSE;
-}
-
 static LRESULT on_browse_back(FileDialogImpl *This)
 {
     TRACE("%p\n", This);
@@ -2091,7 +1988,6 @@ static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
     {
     case IDOK:                return on_idok(This);
     case IDCANCEL:            return on_idcancel(This);
-    case psh1:                return on_command_opendropdown(This, wparam, lparam);
     case IDC_NAVBACK:         return on_browse_back(This);
     case IDC_NAVFORWARD:      return on_browse_forward(This);
     case IDC_FILETYPE:        return on_command_filetype(This, wparam, lparam);
@@ -2100,6 +1996,14 @@ static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
     return FALSE;
 }
 
+static LRESULT on_wm_notify(FileDialogImpl *This, WPARAM wparam, LPARAM lparam)
+{
+    NMHDR* nmhdr = (NMHDR*)lparam;
+    if (nmhdr->idFrom == IDOK && nmhdr->code == BCN_DROPDOWN)
+        show_opendropdown(This);
+    return FALSE;
+}
+
 static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
 {
     FileDialogImpl *This = (FileDialogImpl*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
@@ -2108,6 +2012,7 @@ static LRESULT CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam,
     {
     case WM_INITDIALOG:       return on_wm_initdialog(hwnd, lparam);
     case WM_COMMAND:          return on_wm_command(This, wparam, lparam);
+    case WM_NOTIFY:           return on_wm_notify(This, wparam, lparam);
     case WM_SIZE:             return on_wm_size(This);
     case WM_GETMINMAXINFO:    return on_wm_getminmaxinfo(This, lparam);
     case WM_DESTROY:          return on_wm_destroy(This);
diff --git a/include/commctrl.h b/include/commctrl.h
index 0bcaeb6..1c79aec 100644
--- a/include/commctrl.h
+++ b/include/commctrl.h
@@ -1018,6 +1018,7 @@ static const WCHAR WC_BUTTONW[] = { 'B','u','t','t','o','n',0 };
 #define BCN_LAST                (0U-1350U)
 
 #define BCN_HOTITEMCHANGE       (BCN_FIRST + 0x0001)
+#define BCN_DROPDOWN            (BCN_FIRST + 0x0002)
 
 typedef struct tagNMBCHOTITEM
 {
-- 
2.1.4

-------------- next part --------------
From 227cebabacc42b438133dcae8b0d70041d3ae8d9 Mon Sep 17 00:00:00 2001
From: Vincent Povirk <vincent at codeweavers.com>
Date: Tue, 25 Aug 2015 12:25:51 -0500
Subject: [PATCH 5/6] comdlg32: Enable visual styles in item dialog.

---
 dlls/comdlg32/cdlg.h      |  2 ++
 dlls/comdlg32/cdlg32.c    | 14 +++++++++++++-
 dlls/comdlg32/comdlg32.rc |  3 +++
 dlls/comdlg32/itemdlg.c   | 22 ++++++++++++++++++++++
 4 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/dlls/comdlg32/cdlg.h b/dlls/comdlg32/cdlg.h
index 886804c..2fd1d81 100644
--- a/dlls/comdlg32/cdlg.h
+++ b/dlls/comdlg32/cdlg.h
@@ -28,6 +28,8 @@
 
 extern HINSTANCE	COMDLG32_hInstance DECLSPEC_HIDDEN;
 
+extern HANDLE	COMDLG32_ActCtx DECLSPEC_HIDDEN;
+
 void	COMDLG32_SetCommDlgExtendedError(DWORD err) DECLSPEC_HIDDEN;
 LPVOID	COMDLG32_AllocMem(int size) __WINE_ALLOC_SIZE(1) DECLSPEC_HIDDEN;
 
diff --git a/dlls/comdlg32/cdlg32.c b/dlls/comdlg32/cdlg32.c
index e794f94..e7c331c 100644
--- a/dlls/comdlg32/cdlg32.c
+++ b/dlls/comdlg32/cdlg32.c
@@ -40,6 +40,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
 
 DECLSPEC_HIDDEN HINSTANCE	COMDLG32_hInstance = 0;
 
+DECLSPEC_HIDDEN HANDLE	COMDLG32_ActCtx = 0;
+
 static DWORD COMDLG32_TlsIndex = TLS_OUT_OF_INDEXES;
 
 static HINSTANCE	SHELL32_hInstance;
@@ -83,6 +85,9 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
 	switch(Reason)
 	{
 	case DLL_PROCESS_ATTACH:
+	{
+        ACTCTXW actctx;
+
 		COMDLG32_hInstance = hInstance;
 		DisableThreadLibraryCalls(hInstance);
 
@@ -109,12 +114,19 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
 		  GPA(COMDLG32_SHGetFolderPathW, SHFOLDER_hInstance,"SHGetFolderPathW");
 		}
 
-		break;
+        actctx.cbSize = sizeof(actctx);
+        actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID|ACTCTX_FLAG_HMODULE_VALID;
+        actctx.lpResourceName = MAKEINTRESOURCEW(ISOLATIONAWARE_MANIFEST_RESOURCE_ID);
+        actctx.hModule = hInstance;
+        COMDLG32_ActCtx = CreateActCtxW(&actctx);
 
+		break;
+    }
 	case DLL_PROCESS_DETACH:
             if (Reserved) break;
             if (COMDLG32_TlsIndex != TLS_OUT_OF_INDEXES) TlsFree(COMDLG32_TlsIndex);
             if(SHFOLDER_hInstance) FreeLibrary(SHFOLDER_hInstance);
+            if (COMDLG32_ActCtx) ReleaseActCtx(COMDLG32_ActCtx);
             break;
 	}
 	return TRUE;
diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc
index bd19cf3..240972e 100644
--- a/dlls/comdlg32/comdlg32.rc
+++ b/dlls/comdlg32/comdlg32.rc
@@ -540,6 +540,9 @@ STRINGTABLE
 }
 
 
+/* @makedep: comdlg32.manifest */
+ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST comdlg32.manifest
+
 /* @makedep: pd32_collate.ico */
 PD32_COLLATE ICON pd32_collate.ico
 
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c
index ef4da2b..aba4fc4 100644
--- a/dlls/comdlg32/itemdlg.c
+++ b/dlls/comdlg32/itemdlg.c
@@ -146,6 +146,8 @@ typedef struct FileDialogImpl {
     BOOL opendropdown_has_selection;
     DWORD opendropdown_selection;
 
+    ULONG_PTR actctx_cookie;
+
     GUID client_guid;
 } FileDialogImpl;
 
@@ -1105,6 +1107,7 @@ static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
     HWND ns_hwnd, control_hwnd, parent_hwnd;
     DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
     customctrl *ctrl;
+    ULONG_PTR cookie;
 
     if(get_cctrl(This, id))
         return E_UNEXPECTED; /* Duplicate id */
@@ -1114,6 +1117,8 @@ static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
     else
         parent_hwnd = This->cctrls_hwnd;
 
+    ActivateActCtx(COMDLG32_ActCtx, &cookie);
+
     ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
                               0, 0, This->cctrl_width, height, parent_hwnd,
                               (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
@@ -1121,6 +1126,8 @@ static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
                                    0, 0, This->cctrl_width, height, ns_hwnd,
                                    (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
 
+    DeactivateActCtx(0, cookie);
+
     if(!ns_hwnd || !control_hwnd)
     {
         ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
@@ -1851,6 +1858,12 @@ static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam)
     SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
     This->dlg_hwnd = hwnd;
 
+    if (This->actctx_cookie)
+    {
+        DeactivateActCtx(0, This->actctx_cookie);
+        This->actctx_cookie = 0;
+    }
+
     hitem = GetDlgItem(This->dlg_hwnd, pshHelp);
     if(hitem) ShowWindow(hitem, SW_HIDE);
 
@@ -2108,9 +2121,16 @@ static HRESULT create_dialog(FileDialogImpl *This, HWND parent)
     INT_PTR res;
 
     SetLastError(0);
+    ActivateActCtx(COMDLG32_ActCtx, &This->actctx_cookie);
     res = DialogBoxParamW(COMDLG32_hInstance,
                           MAKEINTRESOURCEW(NEWFILEOPENV3ORD),
                           parent, itemdlg_dlgproc, (LPARAM)This);
+    if (This->actctx_cookie)
+    {
+        /* Normally our context will be deactivated in WM_INITDIALOG. */
+        DeactivateActCtx(0, This->actctx_cookie);
+        This->actctx_cookie = 0;
+    }
     This->dlg_hwnd = NULL;
     if(res == -1)
     {
@@ -4408,6 +4428,8 @@ static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **p
 
     fdimpl->client_guid = GUID_NULL;
 
+    fdimpl->actctx_cookie = 0;
+
     fdimpl->hmenu_opendropdown = NULL;
     fdimpl->hfont_opendropdown = NULL;
 
-- 
2.1.4



More information about the wine-devel mailing list