[PATCH 7/9] comctl32/taskdialog: Add support for verification box.

Zhiyi Zhang zzhang at codeweavers.com
Mon Jul 16 02:39:42 CDT 2018


Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 dlls/comctl32/taskdialog.c       |  58 +++++++++++++-
 dlls/comctl32/tests/taskdialog.c | 130 ++++++++++++++++++++++++-------
 2 files changed, 160 insertions(+), 28 deletions(-)

diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index 645739fdc2..c38e973fb6 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -66,6 +66,7 @@ struct taskdialog_info
     INT command_link_count;
     HWND expanded_info;
     HWND expando_button;
+    HWND verification_box;
     HWND *buttons;
     INT button_count;
     HWND default_button;
@@ -78,6 +79,7 @@ struct taskdialog_info
         LONG v_spacing;
     } m;
     INT selected_radio_id;
+    BOOL verification_checked;
     BOOL expanded;
     WCHAR *expanded_text;
     WCHAR *collapsed_text;
@@ -247,6 +249,13 @@ static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND
         return;
     }
 
+    if (hwnd == dialog_info->verification_box)
+    {
+        dialog_info->verification_checked = !dialog_info->verification_checked;
+        taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
+        return;
+    }
+
     radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
     if (radio_button)
     {
@@ -636,6 +645,26 @@ static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
     SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
 }
 
+static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
+    WCHAR *textW;
+
+    if (!taskconfig->pszVerificationText) return;
+
+    textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
+    dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
+    SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+    Free(textW);
+
+    if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
+    {
+        dialog_info->verification_checked = TRUE;
+        SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
+    }
+}
+
 static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
                                   BOOL custom_button)
 {
@@ -806,6 +835,19 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
         expando_bottom = y + size.cy;
     }
 
+    /* Verification box */
+    if (dialog_info->verification_box)
+    {
+        x = h_spacing;
+        y = expando_bottom + v_spacing;
+        size.cx = DIALOG_MIN_WIDTH / 2;
+        taskdialog_du_to_px(dialog_info, &size.cx, NULL);
+        taskdialog_get_radio_button_size(dialog_info, dialog_info->verification_box, size.cx, &size);
+        SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        expando_right = max(expando_right, x + size.cx);
+        expando_bottom = y + size.cy;
+    }
+
     /* Common and custom buttons */
     button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
     line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
@@ -983,6 +1025,7 @@ static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
     taskdialog_add_radio_buttons(dialog_info);
     taskdialog_add_command_links(dialog_info);
     taskdialog_add_expando_button(dialog_info);
+    taskdialog_add_verification_box(dialog_info);
     taskdialog_add_buttons(dialog_info);
 
     /* Set default button */
@@ -1063,6 +1106,19 @@ static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
         case TDM_ENABLE_RADIO_BUTTON:
             taskdialog_enable_radio_button(dialog_info, wParam, lParam);
             break;
+        case TDM_CLICK_VERIFICATION:
+        {
+            BOOL checked = (BOOL)wParam;
+            BOOL focused = (BOOL)lParam;
+            dialog_info->verification_checked = checked;
+            if (dialog_info->verification_box)
+            {
+                SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
+                taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
+                if (focused) SetFocus(dialog_info->verification_box);
+            }
+            break;
+        }
         case WM_INITDIALOG:
             dialog_info = (struct taskdialog_info *)lParam;
 
@@ -1151,7 +1207,7 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *butto
 
     if (button) *button = ret;
     if (radio_button) *radio_button = dialog_info.selected_radio_id;
-    if (verification_flag_checked) *verification_flag_checked = TRUE;
+    if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
 
     return S_OK;
 }
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index 7fb8f3dcfc..07a80a2c2e 100644
--- a/dlls/comctl32/tests/taskdialog.c
+++ b/dlls/comctl32/tests/taskdialog.c
@@ -229,6 +229,48 @@ static const struct message_info msg_return_press_negative_id_radio_button[] =
     { 0 }
 };
 
+static const struct message_info msg_return_default_verification_unchecked[] =
+{
+    { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
+    { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_default_verification_checked[] =
+{
+    { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
+    { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_uncheck_verification[] =
+{
+    { TDM_CLICK_VERIFICATION, FALSE, 0 },
+    { 0 }
+};
+
+static const struct message_info msg_return_verification_unchecked[] =
+{
+    { TDN_CREATED, 0, 0, S_OK, msg_uncheck_verification },
+    { TDN_VERIFICATION_CLICKED, FALSE, 0, S_OK, msg_send_click_ok },
+    { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_check_verification[] =
+{
+    { TDM_CLICK_VERIFICATION, TRUE, 0 },
+    { 0 }
+};
+
+static const struct message_info msg_return_verification_checked[] =
+{
+    { TDN_CREATED, 0, 0, S_OK, msg_check_verification },
+    { TDN_VERIFICATION_CLICKED, TRUE, 0, S_OK, msg_send_click_ok },
+    { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
+    { 0 }
+};
+
 static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg)
 {
     msg->message = WM_TD_CALLBACK;
@@ -239,17 +281,18 @@ static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct
     msg->stage = 0;
 }
 
-#define run_test(info, expect_button, expect_radio_button, seq, context) \
-        run_test_(info, expect_button, expect_radio_button, seq, context, \
-                  ARRAY_SIZE(seq) - 1, __FILE__, __LINE__)
+#define run_test(info, expect_button, expect_radio_button, verification_checked, seq, context) \
+        run_test_(info, expect_button, expect_radio_button, verification_checked, seq, context, \
+                   ARRAY_SIZE(seq) - 1, __FILE__, __LINE__)
 
-static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button,
+static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button, BOOL verification_checked,
                       const struct message_info *test_messages, const char *context, int test_messages_len,
                       const char *file, int line)
 {
     struct message *msg, *msg_start;
     int ret_button = 0;
     int ret_radio = 0;
+    BOOL ret_verification = FALSE;
     HRESULT hr;
     int i;
 
@@ -266,7 +309,7 @@ static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radi
     current_message_info = test_messages;
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, NULL);
+    hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, &ret_verification);
     ok_(file, line)(hr == S_OK, "TaskDialogIndirect() failed, got %#x.\n", hr);
 
     ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line);
@@ -331,7 +374,7 @@ static void test_callback(void)
     info.pfCallback = taskdialog_callback_proc;
     info.lpCallbackData = test_ref_data;
 
-    run_test(&info, IDOK, 0, msg_return_press_ok, "Press VK_RETURN.");
+    run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "Press VK_RETURN.");
 }
 
 static void test_buttons(void)
@@ -376,16 +419,16 @@ static void test_buttons(void)
     info.nDefaultButton = 0; /* Should default to first created button */
     info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON
             | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
-    run_test(&info, IDOK, 0, msg_return_press_ok, "default button: unset default");
+    run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "default button: unset default");
     info.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON
             | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
-    run_test(&info, IDYES, 0, msg_return_press_yes, "default button: unset default");
+    run_test(&info, IDYES, 0, FALSE, msg_return_press_yes, "default button: unset default");
     info.dwCommonButtons = TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
-    run_test(&info, IDNO, 0, msg_return_press_no, "default button: unset default");
+    run_test(&info, IDNO, 0, FALSE, msg_return_press_no, "default button: unset default");
     info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
-    run_test(&info, IDRETRY, 0, msg_return_press_retry, "default button: unset default");
+    run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: unset default");
     info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON;
-    run_test(&info, IDCANCEL, 0, msg_return_press_cancel, "default button: unset default");
+    run_test(&info, IDCANCEL, 0, FALSE, msg_return_press_cancel, "default button: unset default");
 
     /* Custom buttons could be command links */
     for (i = 0; i < ARRAY_SIZE(command_link_flags); i++)
@@ -396,30 +439,30 @@ static void test_buttons(void)
         info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */
         info.cButtons = TEST_NUM_BUTTONS;
         info.pButtons = custom_buttons;
-        run_test(&info, ID_START_BUTTON, 0, msg_return_press_custom1,
+        run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
                  "default button: invalid default, with common buttons - 1");
 
         info.nDefaultButton = -1; /* Should work despite button ID -1 */
-        run_test(&info, -1, 0, msg_return_press_custom10, "default button: invalid default, with common buttons - 2");
+        run_test(&info, -1, 0, FALSE, msg_return_press_custom10, "default button: invalid default, with common buttons - 2");
 
         info.nDefaultButton = -2; /* Should also default to first created button */
-        run_test(&info, ID_START_BUTTON, 0, msg_return_press_custom1,
+        run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
                  "default button: invalid default, with common buttons - 3");
 
         /* Test with only custom buttons and invalid default ID */
         info.dwCommonButtons = 0;
-        run_test(&info, ID_START_BUTTON, 0, msg_return_press_custom1,
+        run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
                  "default button: invalid default, no common buttons");
 
         /* Test with common and custom buttons and valid default ID */
         info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON
                                | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
         info.nDefaultButton = IDRETRY;
-        run_test(&info, IDRETRY, 0, msg_return_press_retry, "default button: valid default - 1");
+        run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: valid default - 1");
 
         /* Test with common and custom buttons and valid default ID */
         info.nDefaultButton = ID_START_BUTTON + 3;
-        run_test(&info, ID_START_BUTTON + 3, 0, msg_return_press_custom4, "default button: valid default - 2");
+        run_test(&info, ID_START_BUTTON + 3, 0, FALSE, msg_return_press_custom4, "default button: valid default - 2");
     }
 
     /* Test radio buttons */
@@ -431,44 +474,52 @@ static void test_buttons(void)
     info.pRadioButtons = radio_buttons;
 
     /* Test default first radio button */
-    run_test(&info, IDOK, ID_START_RADIO_BUTTON, msg_return_default_radio_button_1, "default radio button: default first radio button");
+    run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_1,
+             "default radio button: default first radio button");
 
     /* Test default radio button */
     info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
-    run_test(&info, IDOK, info.nDefaultRadioButton, msg_return_default_radio_button_2, "default radio button: default radio button");
+    run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_2,
+             "default radio button: default radio button");
 
     /* Test default radio button with -2 */
     info.nDefaultRadioButton = -2;
-    run_test(&info, IDOK, info.nDefaultRadioButton, msg_return_default_radio_button_3, "default radio button: default radio button with id -2");
+    run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_3,
+             "default radio button: default radio button with id -2");
 
     /* Test default radio button after clicking the first, messages still work even radio button is disabled */
     info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
-    run_test(&info, IDOK, ID_START_RADIO_BUTTON, msg_return_first_radio_button, "default radio button: radio button after clicking");
+    run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_first_radio_button,
+             "default radio button: radio button after clicking");
 
     /* Test radio button after disabling and clicking the first */
     info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
-    run_test(&info, IDOK, ID_START_RADIO_BUTTON, msg_return_default_radio_button_clicking_disabled, "default radio button: disable radio button before clicking");
+    run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_clicking_disabled,
+             "default radio button: disable radio button before clicking");
 
     /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set, TDN_RADIO_BUTTON_CLICKED will still be received, just radio button not selected */
     info.nDefaultRadioButton = ID_START_RADIO_BUTTON;
     info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
-    run_test(&info, IDOK, info.nDefaultRadioButton, msg_return_no_default_radio_button_flag, "default radio button: no default radio flag");
+    run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_no_default_radio_button_flag,
+             "default radio button: no default radio flag");
 
     /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is 0.
      * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */
     info.nDefaultRadioButton = 0;
     info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
-    run_test(&info, IDOK, 0, msg_return_no_default_radio_button_id_and_flag, "default radio button: no default radio id and flag");
+    run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag,
+             "default radio button: no default radio id and flag");
 
     /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is invalid.
      * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */
     info.nDefaultRadioButton = 0xff;
     info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
-    run_test(&info, IDOK, 0, msg_return_no_default_radio_button_id_and_flag, "default radio button: no default flag, invalid id");
+    run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag,
+             "default radio button: no default flag, invalid id");
 
     info.nDefaultRadioButton = 0;
     info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
-    run_test(&info, IDOK, -2, msg_return_press_negative_id_radio_button,
+    run_test(&info, IDOK, -2, FALSE, msg_return_press_negative_id_radio_button,
              "radio button: manually click radio button with negative id");
 }
 
@@ -481,7 +532,7 @@ static void test_help(void)
     info.lpCallbackData = test_ref_data;
     info.dwCommonButtons = TDCBF_OK_BUTTON;
 
-    run_test(&info, IDOK, 0, msg_got_tdn_help, "send f1");
+    run_test(&info, IDOK, 0, FALSE, msg_got_tdn_help, "send f1");
 }
 
 struct timer_notification_data
@@ -601,6 +652,30 @@ static void test_progress_bar(void)
     pTaskDialogIndirect(&info, NULL, NULL, NULL);
 }
 
+static void test_verification_box(void)
+{
+    TASKDIALOGCONFIG info = {0};
+    WCHAR textW[] = {'t', 'e', 'x', 't', 0};
+
+    info.cbSize = sizeof(TASKDIALOGCONFIG);
+    info.pfCallback = taskdialog_callback_proc;
+    info.lpCallbackData = test_ref_data;
+    info.dwCommonButtons = TDCBF_OK_BUTTON;
+    info.pszVerificationText = textW;
+
+    run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked");
+
+    info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED;
+    run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked");
+
+    run_test(&info, IDOK, 0, FALSE, msg_return_verification_unchecked,
+             "default verification box: default checked and then unchecked");
+
+    info.dwFlags = 0;
+    run_test(&info, IDOK, 0, FALSE, msg_return_verification_checked,
+             "default verification box: default unchecked and then checked");
+}
+
 START_TEST(taskdialog)
 {
     ULONG_PTR ctx_cookie;
@@ -640,6 +715,7 @@ START_TEST(taskdialog)
     test_help();
     test_timer();
     test_progress_bar();
+    test_verification_box();
 
     unload_v6_module(ctx_cookie, hCtx);
 }
-- 
2.18.0





More information about the wine-devel mailing list