From: Fabian Maurer Subject: [v3 12/12] comctl32: TaskDialog - Add ability to cancel dialog Message-Id: <20170310182204.20127-12-dark.shadow4@web.de> Date: Fri, 10 Mar 2017 19:22:04 +0100 In-Reply-To: <20170310182204.20127-1-dark.shadow4@web.de> References: <20170310182204.20127-1-dark.shadow4@web.de> v3: Rewrite to implement Nikolay Sivov's suggestions Signed-off-by: Fabian Maurer --- dlls/comctl32/taskdialog.c | 18 +++++++- dlls/comctl32/tests/taskdialog.c | 95 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c index 47ade8f2f6..a167abfa10 100644 --- a/dlls/comctl32/taskdialog.c +++ b/dlls/comctl32/taskdialog.c @@ -95,6 +95,7 @@ typedef struct const TASKDIALOGCONFIG *task_config; HWND hwnd; HFONT font_default, font_main; + BOOL has_cancel; }taskdialog_info; #define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target += size; @@ -260,6 +261,9 @@ static HRESULT callback(taskdialog_info *dialog_info, UINT uNotification, WPARAM { HRESULT ret_callback; + if(command_id == IDCANCEL && !dialog_info->has_cancel) + return; + ret_callback = callback(dialog_info, TDN_BUTTON_CLICKED, command_id, 0); if(ret_callback == S_OK) { @@ -314,6 +318,9 @@ static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARA callback(dialog_info, TDN_DESTROYED, 0, 0); RemovePropW(hwndDlg, taskdialog_info_propnameW); break; + case WM_CLOSE: + click_button(dialog_info, IDCANCEL); + return FALSE; /* Custom messages*/ @@ -342,6 +349,10 @@ static void taskdialog_info_init(taskdialog_info *dialog_info, const TASKDIALOGC 0, 0, CLEARTYPE_QUALITY, FF_DONTCARE, ncm.lfMessageFont.lfFaceName); dialog_info->font_main = CreateFontW (font_size_main, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, FF_DONTCARE, ncm.lfMessageFont.lfFaceName); + + dialog_info->has_cancel = (task_config->dwFlags & TDF_ALLOW_DIALOG_CANCELLATION) + || (task_config->dwFlags & TDF_CAN_BE_MINIMIZED) + || (task_config->dwCommonButtons & TDCBF_CANCEL_BUTTON); } static void taskdialog_info_destroy(taskdialog_info *dialog_info) @@ -560,7 +571,12 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu 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.style = DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE; + if(dialog_info.has_cancel) + header.template.style |= WS_SYSMENU; + if(pTaskConfig->dwFlags & TDF_CAN_BE_MINIMIZED) + header.template.style |= WS_SYSMENU | WS_MINIMIZEBOX; + header.template.cdit = list_count(&controls); /* TaskDialogs are always desktop centered */ diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c index c29ac89792..a3060837fb 100644 --- a/dlls/comctl32/tests/taskdialog.c +++ b/dlls/comctl32/tests/taskdialog.c @@ -114,6 +114,62 @@ static const struct message_info mes_return_press_retry[] = { { 0 } }; +static const struct message_info mes_cancel_esc_button_ok[] = { + { TDN_CREATED, 0, 0, S_OK, { + { WM_KEYDOWN, VK_ESCAPE, 0, TRUE }, + { WM_KEYDOWN, VK_RETURN, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, {{ 0 }}}, + { 0 } +}; + +static const struct message_info mes_cancel_close_button_ok[] = { + { TDN_CREATED, 0, 0, S_OK, { + { WM_CLOSE, 0, 0, TRUE }, + { WM_KEYDOWN, VK_RETURN, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, {{ 0 }}}, + { 0 } +}; + +static const struct message_info mes_cancel_esc_button_cancel[] = { + { TDN_CREATED, 0, 0, S_OK, { + { WM_KEYDOWN, VK_ESCAPE, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, {{ 0 }}}, + { 0 } +}; + +static const struct message_info mes_cancel_close_button_cancel[] = { + { TDN_CREATED, 0, 0, S_OK, { + { WM_CLOSE, 0, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, {{ 0 }}}, + { 0 } +}; + +static const struct message_info mes_cancel_esc_callback[] = { + { TDN_CREATED, 0, 0, S_OK, { + { WM_KEYDOWN, VK_ESCAPE, 0, TRUE}, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, { + { TDM_CLICK_BUTTON, IDOK, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, {{ 0 }}}, + { 0 } +}; + +static const struct message_info mes_cancel_close_callback[] = { + { TDN_CREATED, 0, 0, S_OK, { + { WM_CLOSE, 0, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, { + { TDM_CLICK_BUTTON, IDOK, 0, TRUE }, + { 0 }}}, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, {{ 0 }}}, + { 0 } +}; + /* Create a message to test against */ static struct message create_test_message(UINT message, WPARAM wParam, LPARAM lParam) @@ -304,6 +360,45 @@ static void test_TaskDialogIndirect(void) /* Test with common and custom buttons and valid default ID */ info.nDefaultButton = ID_START_BUTTON + 3; run_test(&info, ID_START_BUTTON + 3, 0, FALSE, mes_return_press_custom4, "nDefaultButton: all buttons, valid default 2"); + + /* Test ability to cancel dialog */ + info.nDefaultButton = IDOK; + + /* Test with TDF_ALLOW_DIALOG_CANCELLATION and without cancel button */ + info.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; + info.dwCommonButtons = TDCBF_OK_BUTTON; + run_test(&info, IDCANCEL, 0, 0, mes_cancel_esc_button_cancel, "Cancellation: cancel flag but no cancel button 1"); + run_test(&info, IDCANCEL, 0, 0, mes_cancel_close_button_cancel, "Cancellation: cancel flag but no cancel button 2"); + + /* Test with TDF_ALLOW_DIALOG_CANCELLATION and without cancel button */ + info.dwFlags = TDF_CAN_BE_MINIMIZED; + info.dwCommonButtons = TDCBF_OK_BUTTON; + run_test(&info, IDCANCEL, 0, 0, mes_cancel_esc_button_cancel, "Cancellation: cancel flag but no cancel button 3"); + run_test(&info, IDCANCEL, 0, 0, mes_cancel_close_button_cancel, "Cancellation: cancel flag but no cancel button 4"); + + /* Test without allow-cancel flag and with cancel button */ + info.dwFlags = 0; + info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON; + run_test(&info, IDCANCEL, 0, 0, mes_cancel_esc_button_cancel, "Cancellation: no cancel flag but cancel button 1"); + run_test(&info, IDCANCEL, 0, 0, mes_cancel_close_button_cancel, "Cancellation: no cancel flag but cancel button 2"); + + /* Test without allow-cancel flag and without cancel button */ + info.dwFlags = 0; + info.dwCommonButtons = TDCBF_OK_BUTTON; + run_test(&info, IDOK, 0, 0, mes_cancel_esc_button_ok, "Cancellation: no cancel flag and no cancel button 1"); + run_test(&info, IDOK, 0, 0, mes_cancel_close_button_ok, "Cancellation: no cancel flag and no cancel button 2"); + + /* Test if the callback can disable the cancellation with cancel button */ + info.dwFlags = 0; + info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON; + run_test(&info, IDOK, 0, 0, mes_cancel_esc_callback, "Cancellation: stopped by callback 1"); + run_test(&info, IDOK, 0, 0, mes_cancel_close_callback, "Cancellation: stopped by callback 2"); + + /* Test if the callback can disable the cancellation without cancel button */ + info.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; + info.dwCommonButtons = TDCBF_OK_BUTTON; + run_test(&info, IDOK, 0, 0, mes_cancel_esc_callback, "Cancellation: stopped by callback 3"); + run_test(&info, IDOK, 0, 0, mes_cancel_close_callback, "Cancellation: stopped by callback 4"); } START_TEST(taskdialog) -- 2.12.0