[PATCH 2/7] comdlg32: Support adding custom controls to the item dialog. (resend)

David Hedberg david.hedberg at gmail.com
Thu May 26 21:05:51 CDT 2011


---
 dlls/comdlg32/itemdlg.c       |  268 ++++++++++++++++++++++++++++++++++++++--
 dlls/comdlg32/tests/itemdlg.c |   71 +++++++++++
 2 files changed, 325 insertions(+), 14 deletions(-)

diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c
index 920353b..92c5d4a 100644
--- a/dlls/comdlg32/itemdlg.c
+++ b/dlls/comdlg32/itemdlg.c
@@ -49,11 +49,33 @@ DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA,
 
 WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
 
+static const WCHAR notifysink_childW[] = {'n','f','s','_','c','h','i','l','d',0};
+static const WCHAR floatnotifysinkW[] = {'F','l','o','a','t','N','o','t','i','f','y','S','i','n','k',0};
+
 enum ITEMDLG_TYPE {
     ITEMDLG_TYPE_OPEN,
     ITEMDLG_TYPE_SAVE
 };
 
+enum ITEMDLG_CCTRL_TYPE {
+    IDLG_CCTRL_MENU,
+    IDLG_CCTRL_PUSHBUTTON,
+    IDLG_CCTRL_COMBOBOX,
+    IDLG_CCTRL_RADIOBUTTONLIST,
+    IDLG_CCTRL_CHECKBUTTON,
+    IDLG_CCTRL_EDITBOX,
+    IDLG_CCTRL_SEPARATOR,
+    IDLG_CCTRL_TEXT
+};
+
+typedef struct {
+    HWND hwnd, wrapper_hwnd;
+    UINT id, dlgid;
+    enum ITEMDLG_CCTRL_TYPE type;
+    CDCONTROLSTATEF cdcstate;
+    struct list entry;
+} customctrl;
+
 typedef struct {
     struct list entry;
     IFileDialogEvents *pfde;
@@ -99,7 +121,10 @@ typedef struct FileDialogImpl {
     LPWSTR custom_cancelbutton;
     LPWSTR custom_filenamelabel;
 
+    UINT cctrl_width, cctrl_def_height, cctrls_cols;
     HWND cctrls_hwnd;
+    struct list cctrls;
+    UINT cctrl_next_dlgid;
 } FileDialogImpl;
 
 /**************************************************************************
@@ -460,6 +485,30 @@ static HRESULT on_default_action(FileDialogImpl *This)
 /**************************************************************************
  * Control functions.
  */
+static inline customctrl *get_cctrl_from_dlgid(FileDialogImpl *This, DWORD dlgid)
+{
+    customctrl *ctrl;
+
+    LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
+        if(ctrl->dlgid == dlgid)
+            return ctrl;
+
+    ERR("Failed to find control with dialog id %d\n", dlgid);
+    return NULL;
+}
+
+static inline customctrl *get_cctrl(FileDialogImpl *This, DWORD ctlid)
+{
+    customctrl *ctrl;
+
+    LIST_FOR_EACH_ENTRY(ctrl, &This->cctrls, customctrl, entry)
+        if(ctrl->id == ctlid)
+            return ctrl;
+
+    ERR("Failed to find control with control id %d\n", ctlid);
+    return NULL;
+}
+
 static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width)
 {
     LPWSTR text;
@@ -487,6 +536,78 @@ static void ctrl_resize(HWND hctrl, UINT min_width, UINT max_width)
     HeapFree(GetProcessHeap(), 0, text);
 }
 
+static LRESULT notifysink_on_create(HWND hwnd, CREATESTRUCTW *crs)
+{
+    FileDialogImpl *This = crs->lpCreateParams;
+    TRACE("%p\n", This);
+
+    SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)This);
+    return TRUE;
+}
+
+static LRESULT CALLBACK notifysink_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+    HWND hwnd_child;
+    RECT rc;
+
+    switch(message)
+    {
+    case WM_NCCREATE:         return notifysink_on_create(hwnd, (CREATESTRUCTW*)lparam);
+    case WM_SIZE:
+        hwnd_child = GetPropW(hwnd, notifysink_childW);
+        GetClientRect(hwnd, &rc);
+        SetWindowPos(hwnd_child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+        return TRUE;
+    }
+
+    return DefWindowProcW(hwnd, message, wparam, lparam);
+}
+
+static HRESULT cctrl_create_new(FileDialogImpl *This, DWORD id,
+                                LPCWSTR text, LPCWSTR wndclass, DWORD ctrl_wsflags,
+                                DWORD ctrl_exflags, UINT height, customctrl **ppctrl)
+{
+    HWND ns_hwnd, control_hwnd;
+    DWORD wsflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
+    customctrl *ctrl;
+
+    if(get_cctrl(This, id))
+        return E_UNEXPECTED; /* Duplicate id */
+
+    ns_hwnd = CreateWindowExW(0, floatnotifysinkW, NULL, wsflags,
+                              0, 0, This->cctrl_width, height, This->cctrls_hwnd,
+                              (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, This);
+    control_hwnd = CreateWindowExW(ctrl_exflags, wndclass, text, wsflags | ctrl_wsflags,
+                                   0, 0, This->cctrl_width, height, ns_hwnd,
+                                   (HMENU)This->cctrl_next_dlgid, COMDLG32_hInstance, 0);
+
+    if(!ns_hwnd || !control_hwnd)
+    {
+        ERR("Failed to create wrapper (%p) or control (%p)\n", ns_hwnd, control_hwnd);
+        DestroyWindow(ns_hwnd);
+        DestroyWindow(control_hwnd);
+
+        return E_FAIL;
+    }
+
+    SetPropW(ns_hwnd, notifysink_childW, control_hwnd);
+
+    ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(customctrl));
+    if(!ctrl)
+        return E_OUTOFMEMORY;
+
+    ctrl->hwnd = control_hwnd;
+    ctrl->wrapper_hwnd = ns_hwnd;
+    ctrl->id = id;
+    ctrl->dlgid = This->cctrl_next_dlgid;
+    ctrl->cdcstate = CDCS_ENABLED | CDCS_VISIBLE;
+    list_add_tail(&This->cctrls, &ctrl->entry);
+    if(ppctrl) *ppctrl = ctrl;
+
+    This->cctrl_next_dlgid++;
+    return S_OK;
+}
+
 /**************************************************************************
  * Container functions.
  */
@@ -533,7 +654,25 @@ static LRESULT ctrl_container_on_create(HWND hwnd, CREATESTRUCTW *crs)
 
 static LRESULT ctrl_container_on_wm_destroy(FileDialogImpl *This)
 {
+    customctrl *cur1, *cur2;
     TRACE("%p", This);
+
+    LIST_FOR_EACH_ENTRY_SAFE(cur1, cur2, &This->cctrls, customctrl, entry)
+    {
+        TRACE("Freeing control %p\n", cur1);
+        list_remove(&cur1->entry);
+
+        if(cur1->type == IDLG_CCTRL_MENU)
+        {
+            TBBUTTON tbb;
+            SendMessageW(cur1->hwnd, TB_GETBUTTON, 0, (LPARAM)&tbb);
+            DestroyMenu((HMENU)tbb.dwData);
+        }
+
+        DestroyWindow(cur1->hwnd);
+        HeapFree(GetProcessHeap(), 0, cur1);
+    }
+
     return TRUE;
 }
 
@@ -557,6 +696,15 @@ static HRESULT init_custom_controls(FileDialogImpl *This)
     static const WCHAR ctrl_container_classname[] =
         {'i','d','l','g','_','c','o','n','t','a','i','n','e','r','_','p','a','n','e',0};
 
+    InitCommonControlsEx(NULL);
+
+    This->cctrl_width = 160;      /* Controls have a fixed width */
+    This->cctrl_def_height = 23;
+    This->cctrls_cols = 0;
+
+    This->cctrl_next_dlgid = 0x2000;
+    list_init(&This->cctrls);
+
     if( !GetClassInfoW(COMDLG32_hInstance, ctrl_container_classname, &wc) )
     {
         wc.style            = CS_HREDRAW | CS_VREDRAW;
@@ -582,6 +730,25 @@ static HRESULT init_custom_controls(FileDialogImpl *This)
 
     SetWindowLongW(This->cctrls_hwnd, GWL_STYLE, WS_TABSTOP);
 
+    /* Register class for  */
+    if( !GetClassInfoW(COMDLG32_hInstance, floatnotifysinkW, &wc) ||
+        wc.hInstance != COMDLG32_hInstance)
+    {
+        wc.style            = CS_HREDRAW | CS_VREDRAW;
+        wc.lpfnWndProc      = notifysink_proc;
+        wc.cbClsExtra       = 0;
+        wc.cbWndExtra       = 0;
+        wc.hInstance        = COMDLG32_hInstance;
+        wc.hIcon            = 0;
+        wc.hCursor          = LoadCursorW(0, (LPWSTR)IDC_ARROW);
+        wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
+        wc.lpszMenuName     = NULL;
+        wc.lpszClassName    = floatnotifysinkW;
+
+        if (!RegisterClassW(&wc))
+            ERR("Failed to register FloatNotifySink window class.\n");
+    }
+
     return S_OK;
 }
 
@@ -2525,8 +2692,30 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddMenu(IFileDialogCustomize *iface
                                                      LPCWSTR pszLabel)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    TBBUTTON tbb;
+    HRESULT hr;
+    TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
+
+    hr = cctrl_create_new(This, dwIDCtl, NULL, TOOLBARCLASSNAMEW,
+                          TBSTYLE_FLAT | CCS_NODIVIDER, 0,
+                          This->cctrl_def_height, &ctrl);
+    if(SUCCEEDED(hr))
+    {
+        ctrl->type = IDLG_CCTRL_MENU;
+
+        /* Add the actual button with a popup menu. */
+        tbb.iBitmap = I_IMAGENONE;
+        tbb.dwData = (DWORD)CreatePopupMenu();
+        tbb.iString = (DWORD)pszLabel;
+        tbb.fsState = TBSTATE_ENABLED;
+        tbb.fsStyle = BTNS_WHOLEDROPDOWN;
+        tbb.idCommand = 1;
+
+        SendMessageW(ctrl->hwnd, TB_ADDBUTTONSW, 1, (LPARAM)&tbb);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize *iface,
@@ -2534,16 +2723,32 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddPushButton(IFileDialogCustomize
                                                            LPCWSTR pszLabel)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszLabel));
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d, %p)\n", This, dwIDCtl, pszLabel);
+
+    hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_MULTILINE, 0,
+                          This->cctrl_def_height, &ctrl);
+    if(SUCCEEDED(hr))
+        ctrl->type = IDLG_CCTRL_PUSHBUTTON;
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddComboBox(IFileDialogCustomize *iface,
                                                          DWORD dwIDCtl)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d)\n", This, dwIDCtl);
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d)\n", This, dwIDCtl);
+
+    hr =  cctrl_create_new(This, dwIDCtl, NULL, WC_COMBOBOXW, CBS_DROPDOWNLIST, 0,
+                           This->cctrl_def_height, &ctrl);
+    if(SUCCEEDED(hr))
+        ctrl->type = IDLG_CCTRL_COMBOBOX;
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddRadioButtonList(IFileDialogCustomize *iface,
@@ -2560,8 +2765,19 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddCheckButton(IFileDialogCustomize
                                                             BOOL bChecked)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %s, %d)\n", This, dwIDCtl, debugstr_w(pszLabel), bChecked);
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d, %p, %d)\n", This, dwIDCtl, pszLabel, bChecked);
+
+    hr = cctrl_create_new(This, dwIDCtl, pszLabel, WC_BUTTONW, BS_AUTOCHECKBOX, 0,
+                          This->cctrl_def_height, &ctrl);
+    if(SUCCEEDED(hr))
+    {
+        ctrl->type = IDLG_CCTRL_CHECKBUTTON;
+        SendMessageW(ctrl->hwnd, BM_SETCHECK, bChecked ? BST_CHECKED : BST_UNCHECKED, 0);
+    }
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *iface,
@@ -2569,16 +2785,32 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddEditBox(IFileDialogCustomize *if
                                                         LPCWSTR pszText)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
+
+    hr = cctrl_create_new(This, dwIDCtl, pszText, WC_EDITW, ES_AUTOHSCROLL, WS_EX_CLIENTEDGE,
+                          This->cctrl_def_height, &ctrl);
+    if(SUCCEEDED(hr))
+        ctrl->type = IDLG_CCTRL_EDITBOX;
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddSeparator(IFileDialogCustomize *iface,
                                                           DWORD dwIDCtl)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d)\n", This, dwIDCtl);
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d)\n", This, dwIDCtl);
+
+    hr = cctrl_create_new(This, dwIDCtl, NULL, WC_STATICW, SS_ETCHEDHORZ, 0,
+                          GetSystemMetrics(SM_CYEDGE), &ctrl);
+    if(SUCCEEDED(hr))
+        ctrl->type = IDLG_CCTRL_SEPARATOR;
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface,
@@ -2586,8 +2818,16 @@ static HRESULT WINAPI IFileDialogCustomize_fnAddText(IFileDialogCustomize *iface
                                                      LPCWSTR pszText)
 {
     FileDialogImpl *This = impl_from_IFileDialogCustomize(iface);
-    FIXME("stub - %p (%d, %s)\n", This, dwIDCtl, debugstr_w(pszText));
-    return E_NOTIMPL;
+    customctrl *ctrl;
+    HRESULT hr;
+    TRACE("%p (%d, %p)\n", This, dwIDCtl, pszText);
+
+    hr = cctrl_create_new(This, dwIDCtl, pszText, WC_STATICW, 0, 0,
+                          This->cctrl_def_height, &ctrl);
+    if(SUCCEEDED(hr))
+        ctrl->type = IDLG_CCTRL_TEXT;
+
+    return hr;
 }
 
 static HRESULT WINAPI IFileDialogCustomize_fnSetControlLabel(IFileDialogCustomize *iface,
diff --git a/dlls/comdlg32/tests/itemdlg.c b/dlls/comdlg32/tests/itemdlg.c
index 9e4d571..a815dbf 100644
--- a/dlls/comdlg32/tests/itemdlg.c
+++ b/dlls/comdlg32/tests/itemdlg.c
@@ -1200,11 +1200,21 @@ static void test_filename(void)
     DeleteFileW(filename_ext2W);
 }
 
+static const WCHAR label[] = {'l','a','b','e','l',0};
+static const WCHAR menuW[] = {'m','e','n','u','_','i','t','e','m',0};
+static const WCHAR pushbutton1W[] = {'p','u','s','h','b','u','t','t','o','n','_','i','t','e','m',0};
+static const WCHAR pushbutton2W[] = {'p','u','s','h','b','u','t','t','o','n','2','_','i','t','e','m',0};
+static const WCHAR checkbutton1W[] = {'c','h','e','c','k','b','u','t','t','o','n','1','_','i','t','e','m',0};
+static const WCHAR editbox1W[] = {'e','d','i','t','b','o','x','W','1','_','i','t','e','m',0};
+static const WCHAR textW[] = {'t','e','x','t','_','i','t','e','m',0};
+static const WCHAR visualgroup1W[] = {'v','i','s','u','a','l','g','r','o','u','p','1',0};
+
 static void test_customize(void)
 {
     IFileDialog *pfod;
     IFileDialogCustomize *pfdc;
     IOleWindow *pow;
+    UINT i;
     LONG ref;
     HWND dlg_hwnd;
     HRESULT hr;
@@ -1220,6 +1230,12 @@ static void test_customize(void)
         return;
     }
 
+    i = 0;
+    hr = IFileDialogCustomize_AddPushButton(pfdc, i, pushbutton1W);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddPushButton(pfdc, i, pushbutton1W);
+    ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+
     hr = IFileDialog_QueryInterface(pfod, &IID_IOleWindow, (void**)&pow);
     ok(hr == S_OK, "Got 0x%08x\n", hr);
     hr = IOleWindow_GetWindow(pow, &dlg_hwnd);
@@ -1227,6 +1243,61 @@ static void test_customize(void)
     ok(dlg_hwnd == NULL, "NULL\n");
     IOleWindow_Release(pow);
 
+    hr = IFileDialogCustomize_EnableOpenDropDown(pfdc, i);
+    todo_wine ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_EnableOpenDropDown(pfdc, ++i);
+    todo_wine ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddMenu(pfdc, i, menuW);
+    todo_wine ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddMenu(pfdc, ++i, label);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddPushButton(pfdc, i, pushbutton2W);
+    ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddPushButton(pfdc, ++i, pushbutton2W);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddComboBox(pfdc, i);
+    ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddComboBox(pfdc, ++i);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddRadioButtonList(pfdc, i);
+    todo_wine ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddRadioButtonList(pfdc, ++i);
+    todo_wine ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddCheckButton(pfdc, i, label, TRUE);
+    todo_wine ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddCheckButton(pfdc, ++i, checkbutton1W, TRUE);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddEditBox(pfdc, i, label);
+    ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddEditBox(pfdc, ++i, editbox1W);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddSeparator(pfdc, i);
+    ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddSeparator(pfdc, ++i);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_AddText(pfdc, i, label);
+    ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_AddText(pfdc, ++i, textW);
+    ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_StartVisualGroup(pfdc, i, label);
+    todo_wine ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_StartVisualGroup(pfdc, ++i, visualgroup1W);
+    todo_wine ok(hr == S_OK, "got 0x%08x.\n", hr);
+
+    hr = IFileDialogCustomize_StartVisualGroup(pfdc, ++i, label);
+    todo_wine ok(hr == E_UNEXPECTED, "got 0x%08x.\n", hr);
+    hr = IFileDialogCustomize_EndVisualGroup(pfdc);
+    todo_wine ok(hr == S_OK, "got 0x%08x.\n", hr);
+
     IFileDialogCustomize_Release(pfdc);
     ref = IFileOpenDialog_Release(pfod);
     ok(!ref, "Refcount not zero (%d).\n", ref);
-- 
1.7.5.rc3




More information about the wine-patches mailing list