[03/18] comctl32: Added basic implementation for task dialogs and add tests
Fabian Maurer
dark.shadow4 at web.de
Fri Feb 24 14:03:57 CST 2017
The dialog doesn't show text and can basically do nothing,
but it sets the foundation.
Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
dlls/comctl32/taskdialog.c | 212 +++++++++++++++++++++++++++++++++------
dlls/comctl32/tests/taskdialog.c | 40 +++++++-
2 files changed, 223 insertions(+), 29 deletions(-)
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index c26d53bac9..d31e7d0b95 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -28,45 +28,201 @@
#include "commctrl.h"
#include "winerror.h"
#include "comctl32.h"
+
+#include "wine/list.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
+typedef struct
+{
+ DLGITEMTEMPLATE template;
+ const WCHAR *class; /* For simplicity, we don't use ordinals but only the class name */
+ const WCHAR *text;
+ WORD creation_data;
+
+ /* not part of actual template */
+ UINT text_size; /* Length in bytes including null-terminator */
+ UINT class_size; /* Length in bytes including null-terminator */
+ struct list entry;
+}control_info;
+
+typedef struct
+{
+ DLGTEMPLATE template;
+ WORD menu;
+ WORD class;
+ const WCHAR *title;
+
+ /* Not part of actual template */
+ UINT titleSize; /* Length in bytes including null-terminator */
+}dialog_header;
+
+#define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target += size;
+#define STR_SIZE(str) ((lstrlenW(str) + 1) * sizeof(WCHAR))
+
+static void* align_word(void *ptr)
+{
+ return (void *)(((UINT_PTR)ptr + 1 ) & ~1);
+}
+
+static void* align_dword(void *ptr)
+{
+ return (void *)(((UINT_PTR)ptr + 3 ) & ~3);
+}
+
+/* Functions for turning our dialog structures into a usable dialog template
+ * We don't load the dialog template from a resource, we instead create it in memory
+ * This way we can easily handle variable control numbers */
+LPDLGTEMPLATEW dialog_template_create(dialog_header header, struct list *controls)
+{
+ control_info *control;
+ void *template_data_start;
+ char *template_data;
+ int data_size = sizeof(WORD); /* Alignment at WORD boundaries is needed for strings, allocate a bit of padding */
+
+ data_size += sizeof(DLGTEMPLATE);
+ data_size += sizeof(WORD)*2 + header.titleSize;
+ LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+ {
+ data_size += sizeof(DWORD); /* Each item is aligned on DWORD boundary, allocate a bit of padding */
+ data_size += sizeof(DLGITEMTEMPLATE);
+ data_size += control->text_size;
+ data_size += control->class_size;
+ data_size += sizeof(WORD);
+ }
+
+ template_data_start = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data_size);
+ template_data = template_data_start;
+
+ /* Align on WORD boundary for the strings */
+ template_data = align_word(template_data);
+
+ /* Copy header data */
+ MEMCPY_MOVEPTR(template_data, &header.template, sizeof(DLGTEMPLATE));
+ MEMCPY_MOVEPTR(template_data, &header.menu, sizeof(WORD));
+ MEMCPY_MOVEPTR(template_data, &header.class, sizeof(WORD));
+ MEMCPY_MOVEPTR(template_data, header.title, header.titleSize);
+
+ /* Copy dialog member data */
+ LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+ {
+ /* Align on DWORD boundary for each new control */
+ template_data = align_dword(template_data);
+
+ MEMCPY_MOVEPTR(template_data, &control->template, sizeof(DLGITEMTEMPLATE));
+ MEMCPY_MOVEPTR(template_data, control->class, control->class_size);
+ MEMCPY_MOVEPTR(template_data, control->text, control->text_size);
+ MEMCPY_MOVEPTR(template_data, &control->creation_data, sizeof(WORD));
+ }
+
+ return template_data_start;
+}
+
+static void dialog_template_destroy(LPDLGTEMPLATEW template_data)
+{
+ HeapFree(GetProcessHeap(), 0, template_data);
+}
+
+/* Functions to handle the dialog control list */
+
+static void controls_init(struct list **controls)
+{
+ *controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct list));
+ list_init(*controls);
+}
+
+static void controls_destroy(struct list *controls)
+{
+ control_info *control;
+ LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+ {
+ HeapFree(GetProcessHeap(), 0, control);
+ }
+ HeapFree(GetProcessHeap(), 0, controls);
+}
+
+/* Adds a control for the TaskDialog into our list */
+static void controls_add(struct list *controls, WORD id, const WCHAR *class, const WCHAR *text,
+ DWORD style, short x, short y, short cx, short cy)
+{
+ control_info *data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(control_info));
+
+ data->template.x = x;
+ data->template.y = y;
+ data->template.cx = cx;
+ data->template.cy = cy;
+ data->template.id = id;
+ data->template.style = style;
+
+ data->class = class;
+ data->class_size = STR_SIZE(class);
+ data->text = text;
+ data->text_size = STR_SIZE(text);
+
+ list_add_tail(controls, &data->entry);
+}
+
+static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_COMMAND:
+ if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
+ {
+ EndDialog(hwndDlg, 0);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
/***********************************************************************
* TaskDialogIndirect [COMCTL32.@]
*/
HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton,
int *pnRadioButton, BOOL *pfVerificationFlagChecked)
{
- UINT uType = 0;
- INT ret;
+ static const WCHAR empty_string[] = {0};
+ static const WCHAR class_button[] = {'B','u','t','t','o','n',0};
+ static const WCHAR text_ok[] = {'O','K',0};
+ LPDLGTEMPLATEW template_data;
+ dialog_header header = {0};
+ struct list *controls;
+
TRACE("%p, %p, %p, %p\n", pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
- if (pTaskConfig->dwCommonButtons & TDCBF_YES_BUTTON &&
- pTaskConfig->dwCommonButtons & TDCBF_NO_BUTTON &&
- pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
- uType |= MB_YESNOCANCEL;
- else
- if (pTaskConfig->dwCommonButtons & TDCBF_YES_BUTTON &&
- pTaskConfig->dwCommonButtons & TDCBF_NO_BUTTON)
- uType |= MB_YESNO;
- else
- if (pTaskConfig->dwCommonButtons & TDCBF_RETRY_BUTTON &&
- pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
- uType |= MB_RETRYCANCEL;
- else
- if (pTaskConfig->dwCommonButtons & TDCBF_OK_BUTTON &&
- pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
- uType |= MB_OKCANCEL;
- else
- if (pTaskConfig->dwCommonButtons & TDCBF_OK_BUTTON)
- uType |= MB_OK;
- ret = MessageBoxW(pTaskConfig->hwndParent, pTaskConfig->pszMainInstruction,
- pTaskConfig->pszWindowTitle, uType);
- FIXME("dwCommonButtons=%x uType=%x ret=%x\n", pTaskConfig->dwCommonButtons, uType, ret);
-
- if (pnButton) *pnButton = ret;
- if (pnRadioButton) *pnRadioButton = pTaskConfig->nDefaultButton;
- if (pfVerificationFlagChecked) *pfVerificationFlagChecked = TRUE;
+ if (!pTaskConfig || pTaskConfig->cbSize != sizeof(TASKDIALOGCONFIG))
+ return E_INVALIDARG;
+
+ controls_init(&controls);
+
+ /* Start creating controls */
+
+ controls_add(controls, IDOK, class_button, text_ok, WS_CHILD | WS_VISIBLE, 105, 85, 40, 10);
+
+ header.title = pTaskConfig->pszWindowTitle;
+ if(!header.title)
+ header.title = empty_string; /* FIXME: Set to exe path instead */
+ header.titleSize = STR_SIZE(header.title);
+
+ header.template.style = DS_MODALFRAME | WS_CAPTION | WS_VISIBLE;
+ header.template.cdit = list_count(controls);
+ header.template.x = 10;
+ header.template.y = 10;
+ header.template.cx = 150;
+ header.template.cy = 100;
+
+ /* Turn template information into a dialog template to display it */
+ template_data = dialog_template_create(header, controls);
+
+ DialogBoxIndirectParamW(pTaskConfig->hInstance, template_data, pTaskConfig->hwndParent, DialogProc, 0);
+
+ /* Cleanup */
+
+ dialog_template_destroy(template_data);
+ controls_destroy(controls);
+
return S_OK;
}
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index 332fa23db0..54c75ed061 100644
--- a/dlls/comctl32/tests/taskdialog.c
+++ b/dlls/comctl32/tests/taskdialog.c
@@ -29,6 +29,40 @@
static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *);
+static HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd, UINT uNotification, WPARAM wParam,
+ LPARAM lParam, LONG_PTR dwRefData)
+{
+ if(uNotification == TDN_CREATED)
+ {
+ PostMessageW(hwnd, WM_KEYDOWN, VK_RETURN, 0);
+ }
+ return S_OK;
+}
+
+static void test_TaskDialogIndirect(void)
+{
+ TASKDIALOGCONFIG info = {0};
+ HRESULT ret;
+
+ ret = pTaskDialogIndirect(NULL, NULL, NULL, NULL);
+ ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+ ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+ ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+ info.cbSize = sizeof(TASKDIALOGCONFIG);
+ info.pfCallback = TaskDialogCallbackProc;
+
+ /* Skip this test on wine, because it doesn't really fail,
+ * it would displays a dialog that doesn't automatically close */
+ if (strcmp(winetest_platform, "wine"))
+ {
+ ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+ ok(ret == S_OK, "Expected S_OK, got %x\n", ret);
+ }
+
+}
+
START_TEST(taskdialog)
{
ULONG_PTR ctx_cookie;
@@ -46,7 +80,11 @@ START_TEST(taskdialog)
ok(pTaskDialogIndirect != NULL, "TaskDialogIndirect not exported by name.\n");
ptr_ordinal = GetProcAddress(hinst, (const CHAR*)345);
- ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n", ptr_ordinal, pTaskDialogIndirect);
+ ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n",
+ ptr_ordinal, pTaskDialogIndirect);
+
+ if(pTaskDialogIndirect)
+ test_TaskDialogIndirect();
unload_v6_module(ctx_cookie, hCtx);
}
--
2.11.1
More information about the wine-patches
mailing list