[v3 03/12] comctl32: Added basic implementation for task dialogs and add tests
Fabian Maurer
dark.shadow4 at web.de
Fri Mar 10 12:21:55 CST 2017
The dialog doesn't show text and can basically do nothing,
but it sets the foundation.
v2: Skip test on windows when taskdialogs aren't available
v3: Rewrite to implement Nikolay Sivov's suggestions
Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
dlls/comctl32/taskdialog.c | 195 +++++++++++++++++++++++++++++++++------
dlls/comctl32/tests/taskdialog.c | 36 ++++++++
2 files changed, 203 insertions(+), 28 deletions(-)
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index c26d53bac9..2166ce1898 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -28,45 +28,184 @@
#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;
+
+static void* align_pointer(void *ptr, unsigned int boundary)
+{
+ boundary--;
+ return (void *)(((UINT_PTR)ptr + boundary) & ~boundary);
+}
+
+/* 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 = Alloc(data_size);
+ template_data = template_data_start;
+
+ /* Align on WORD boundary for the strings */
+ template_data = align_pointer(template_data, 2);
+
+ /* 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_pointer(template_data, 4);
+
+ 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;
+}
+
+/* Functions to handle the dialog control list */
+
+static void controls_destroy(struct list *controls)
+{
+ control_info *control, *control2;
+ LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, control_info, entry)
+ {
+ list_remove(&control->entry);
+ Free(control);
+ }
+}
+
+/* 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 = Alloc(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 = (lstrlenW(class) + 1) * sizeof(WCHAR);
+ data->text = text;
+ data->text_size = (lstrlenW(text) + 1) * sizeof(WCHAR);
+
+ 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 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;
+
+ list_init(&controls);
+
+ /* Start creating controls */
+
+ controls_add(&controls, IDOK, WC_BUTTONW, 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 = (lstrlenW(header.title) + 1) * sizeof(WCHAR);
+
+ 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 */
+
+ Free(template_data);
+ controls_destroy(&controls);
+
return S_OK;
}
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index e5286cab31..c1336bf710 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;
@@ -53,5 +87,7 @@ START_TEST(taskdialog)
ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n",
ptr_ordinal, pTaskDialogIndirect);
+ test_TaskDialogIndirect();
+
unload_v6_module(ctx_cookie, hCtx);
}
--
2.12.0
More information about the wine-patches
mailing list