[v3 12/12] comctl32: TaskDialog - Add ability to cancel dialog
Fabian Maurer
dark.shadow4 at web.de
Fri Mar 10 12:22:04 CST 2017
v3: Rewrite to implement Nikolay Sivov's suggestions
Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
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
More information about the wine-patches
mailing list