[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