From: Fabian Maurer Subject: [v4 2/5] comctl32: Add basic implementation for task dialogs Message-Id: <20170315201323.29623-2-dark.shadow4@web.de> Date: Wed, 15 Mar 2017 21:13:20 +0100 In-Reply-To: <20170315201323.29623-1-dark.shadow4@web.de> References: <20170315201323.29623-1-dark.shadow4@web.de> 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 v4: Rewrite to fix a bunch of issues and make patchset smaller Signed-off-by: Fabian Maurer --- dlls/comctl32/taskdialog.c | 192 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 163 insertions(+), 29 deletions(-) diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c index c26d53bac9..0d0535ea2b 100644 --- a/dlls/comctl32/taskdialog.c +++ b/dlls/comctl32/taskdialog.c @@ -28,9 +28,134 @@ #include "commctrl.h" #include "winerror.h" #include "comctl32.h" + +#include "wine/list.h" #include "wine/debug.h" -WINE_DEFAULT_DEBUG_CHANNEL(commctrl); +WINE_DEFAULT_DEBUG_CHANNEL(taskdlg); + +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 title_size; /* Length in bytes including null-terminator */ +} dialog_header; + +#define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target = (void *)((char *)target + size); +#define ALIGN_POINTER(ptr, boundary) ptr = (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 */ +DLGTEMPLATE* dialog_template_create(dialog_header header, struct list *controls) +{ + control_info *control; + DLGTEMPLATE *template_data_start; + void *template_data; + int data_size = 0; + + data_size += sizeof(DLGTEMPLATE); + data_size += sizeof(WORD) * 2 + header.title_size; + 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; + + /* 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.title_size); + + /* Copy dialog member data */ + LIST_FOR_EACH_ENTRY(control, controls, control_info, entry) + { + /* Align on DWORD boundary for each new control */ + ALIGN_POINTER(template_data, 3); + + 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) +{ + TRACE("hwndDlg=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwndDlg, uMsg, wParam, lParam); + + switch (uMsg) + { + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + WORD command_id = LOWORD(wParam); + EndDialog(hwndDlg, command_id); + return TRUE; + } + break; + } + return FALSE; +} /*********************************************************************** * TaskDialogIndirect [COMCTL32.@] @@ -38,35 +163,44 @@ WINE_DEFAULT_DEBUG_CHANNEL(commctrl); 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}; + DLGTEMPLATE *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.title_size = (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; } -- 2.12.0