[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