[v2] comctl32/taskdialog: Implement nDefaultButton and add tests

Fabian Maurer dark.shadow4 at web.de
Sun Nov 5 09:41:43 CST 2017


v2:
-Don't test radiobutton since it's not implemented yet
-moved buttons_make code into test_buttons
-whitespace fix
-Add test for button ID -1 and make it work

Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
 dlls/comctl32/taskdialog.c       |  39 +++++++----
 dlls/comctl32/tests/taskdialog.c | 135 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 155 insertions(+), 19 deletions(-)

diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index 5c4af2ab03..568e8344d4 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -151,7 +151,7 @@ static void taskdialog_get_text_extent(const struct taskdialog_template_desc *de
 }
 
 static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc, WORD id, const WCHAR *class,
-        HINSTANCE hInstance, const WCHAR *text, short x, short y, short cx, short cy)
+        HINSTANCE hInstance, const WCHAR *text, DWORD style, short x, short y, short cx, short cy)
 {
     struct taskdialog_control *control = Alloc(sizeof(*control));
     unsigned int size, class_size, text_size;
@@ -178,7 +178,7 @@ static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc
     control->template = template = Alloc(size);
     control->template_size = size;
 
-    template->style = WS_VISIBLE;
+    template->style = WS_VISIBLE | style;
     template->dwExtendedStyle = 0;
     template->x = x;
     template->y = y;
@@ -206,7 +206,7 @@ static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc
     taskdialog_get_text_extent(desc, str, TRUE, &sz);
 
     desc->dialog_height += DIALOG_SPACING;
-    size = taskdialog_add_control(desc, id, WC_STATICW, desc->taskconfig->hInstance, str, DIALOG_SPACING,
+    size = taskdialog_add_control(desc, id, WC_STATICW, desc->taskconfig->hInstance, str, 0, DIALOG_SPACING,
             desc->dialog_height, sz.cx, sz.cy);
     desc->dialog_height += sz.cy + DIALOG_SPACING;
     return size;
@@ -223,7 +223,7 @@ static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc
 }
 
 static void taskdialog_init_button(struct taskdialog_button_desc *button, struct taskdialog_template_desc *desc,
-        int id, const WCHAR *text, BOOL custom_button)
+        int id, const WCHAR *text, BOOL custom_button, int *default_button, BOOL *found_default_button)
 {
     SIZE sz;
 
@@ -234,16 +234,23 @@ static void taskdialog_init_button(struct taskdialog_button_desc *button, struct
     button->width = max(DIALOG_BUTTON_WIDTH, sz.cx + DIALOG_SPACING * 2);
     button->line = 0;
     button->hinst = custom_button ? desc->taskconfig->hInstance : COMCTL32_hModule;
+
+    if(id == desc->taskconfig->nDefaultButton)
+    {
+        *default_button = id;
+        *found_default_button = TRUE;
+    }
 }
 
 static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons,
-    unsigned int *button_count)
+    unsigned int *button_count, int *default_button, BOOL *found_default_button)
 {
     DWORD flags = desc->taskconfig->dwCommonButtons;
 
 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
     do { \
-        taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \
+        taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE, \
+                default_button , found_default_button); \
     } while(0)
 
     if (flags & TDCBF_OK_BUTTON)
@@ -268,6 +275,8 @@ static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc
     unsigned int location_x, *line_widths, alignment = ~0u;
     const TASKDIALOGCONFIG *taskconfig = desc->taskconfig;
     struct taskdialog_button_desc *buttons;
+    int default_button;
+    BOOL found_default_button = FALSE;
 
     /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
     buttons_size = 6;
@@ -281,14 +290,18 @@ static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc
     if (taskconfig->cButtons && taskconfig->pButtons)
         for (i = 0; i < taskconfig->cButtons; i++)
             taskdialog_init_button(&buttons[count++], desc, taskconfig->pButtons[i].nButtonID,
-                    taskconfig->pButtons[i].pszButtonText, TRUE);
+                    taskconfig->pButtons[i].pszButtonText, TRUE, &default_button, &found_default_button);
 
     /* Common buttons */
-    taskdialog_init_common_buttons(desc, buttons, &count);
+    taskdialog_init_common_buttons(desc, buttons, &count, &default_button, &found_default_button);
 
     /* There must be at least one button */
     if (count == 0)
-        taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE);
+        taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE,
+                &default_button, &found_default_button);
+
+    if(!found_default_button)
+        default_button = buttons[0].id;
 
     /* For easy handling just allocate as many lines as buttons, the worst case. */
     line_widths = Alloc(count * sizeof(*line_widths));
@@ -344,14 +357,16 @@ static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc
     location_x = alignment;
     for (i = 0; i < count; i++)
     {
+        DWORD style = (buttons[i].id == default_button) ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;
+
         if (i > 0 && buttons[i].line != buttons[i - 1].line) /* New line */
         {
             location_x = alignment;
             desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
         }
 
-        size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, buttons[i].hinst, buttons[i].text, location_x,
-                desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
+        size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, buttons[i].hinst, buttons[i].text, style,
+                location_x, desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
 
         location_x += buttons[i].width + DIALOG_SPACING;
     }
@@ -578,7 +593,7 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *butto
     dialog_info.callback_data = taskconfig->lpCallbackData;
 
     template = create_taskdialog_template(taskconfig);
-    ret = DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
+    ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
             taskdialog_proc, (LPARAM)&dialog_info);
     Free(template);
 
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index dfb6808c38..1db9cda953 100644
--- a/dlls/comctl32/tests/taskdialog.c
+++ b/dlls/comctl32/tests/taskdialog.c
@@ -33,6 +33,11 @@
 #define NUM_MSG_SEQUENCES     1
 #define TASKDIALOG_SEQ_INDEX  0
 
+#define TEST_NUM_BUTTONS 10 /* Number of custom buttons to test with */
+
+#define ID_START 20 /* Lower IDs might be used by the system */
+#define ID_START_BUTTON (ID_START + 0)
+
 static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *);
 static HRESULT (WINAPI *pTaskDialog)(HWND, HINSTANCE, const WCHAR *, const WCHAR *, const WCHAR *,
         TASKDIALOG_COMMON_BUTTON_FLAGS, const WCHAR *, int *);
@@ -66,6 +71,55 @@ static const struct message_info msg_return_press_ok[] =
     { 0 }
 };
 
+static const struct message_info msg_return_press_yes[] =
+{
+    { TDN_CREATED,        0,    0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, IDYES, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_press_no[] =
+{
+    { TDN_CREATED,        0,    0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, IDNO, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_press_cancel[] =
+{
+    { TDN_CREATED,        0,    0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_press_retry[] =
+{
+    { TDN_CREATED,        0,       0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, IDRETRY, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_press_custom1[] =
+{
+    { TDN_CREATED,        0,               0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_press_custom4[] =
+{
+    { TDN_CREATED,        0,                   0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, ID_START_BUTTON + 3, 0, S_OK, NULL },
+    { 0 }
+};
+
+static const struct message_info msg_return_press_custom10[] =
+{
+    { TDN_CREATED,        0,                   0, S_OK, msg_send_return },
+    { TDN_BUTTON_CLICKED, -1, 0, S_OK, NULL },
+    { 0 }
+};
+
 static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg)
 {
     msg->message = WM_TD_CALLBACK;
@@ -76,11 +130,11 @@ static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct
     msg->stage = 0;
 }
 
-#define run_test(info, expect_button, expect_radio, seq, context) \
-        run_test_(info, expect_button, expect_radio, seq, context, \
+#define run_test(info, expect_button, seq, context) \
+        run_test_(info, expect_button, seq, context, \
                   sizeof(seq)/sizeof(seq[0]) - 1, __FILE__, __LINE__)
 
-void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio, const struct message_info *test_messages,
+void run_test_(TASKDIALOGCONFIG *info, int expect_button, const struct message_info *test_messages,
     const char *context, int test_messages_len, const char *file, int line)
 {
     struct message *msg, *msg_start;
@@ -108,8 +162,6 @@ void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio, cons
     ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line);
     ok_(file, line)(ret_button == expect_button,
                      "Wrong button. Expected %d, got %d\n", expect_button, ret_button);
-    ok_(file, line)(ret_radio == expect_radio,
-                     "Wrong radio button. Expected %d, got %d\n", expect_radio, ret_radio);
 
     HeapFree(GetProcessHeap(), 0, msg_start);
 }
@@ -125,7 +177,7 @@ static HRESULT CALLBACK taskdialog_callback_proc(HWND hwnd, UINT notification,
 
     ok(test_ref_data == ref_data, "Unexpected ref data %lu.\n", ref_data);
 
-    init_test_message(notification, wParam, lParam, &msg);
+    init_test_message(notification, (short)wParam, lParam, &msg);
     add_message(sequences, TASKDIALOG_SEQ_INDEX, &msg);
 
     if (notification == TDN_DIALOG_CONSTRUCTED || notification == TDN_DESTROYED) /* Skip implicit messages */
@@ -167,7 +219,75 @@ 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, msg_return_press_ok, "Press VK_RETURN.");
+}
+
+static void test_buttons(void)
+{
+    TASKDIALOGCONFIG info = {0};
+
+    TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS];
+    const WCHAR button_format[] = {'%','0','2','d',0};
+    WCHAR button_titles[TEST_NUM_BUTTONS * 3]; /* Each button has two digits as title, plus null-terminator */
+    int i;
+
+    info.cbSize = sizeof(TASKDIALOGCONFIG);
+    info.pfCallback = taskdialog_callback_proc;
+    info.lpCallbackData = test_ref_data;
+
+    /* Init custom buttons */
+    for (i = 0; i < TEST_NUM_BUTTONS; i++)
+    {
+        WCHAR *text = &button_titles[i * 3];
+        wsprintfW(text, button_format, i);
+
+        custom_buttons[i].pszButtonText = text;
+        custom_buttons[i].nButtonID = ID_START_BUTTON + i;
+    }
+    custom_buttons[TEST_NUM_BUTTONS - 1].nButtonID = -1;
+
+    /* Test nDefaultButton */
+
+    /* Test common buttons with invalid default ID */
+    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, 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, 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, msg_return_press_no, "default button: unset default");
+    info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
+    run_test(&info, IDRETRY, msg_return_press_retry, "default button: unset default");
+    info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON;
+    run_test(&info, IDCANCEL, msg_return_press_cancel, "default button: unset default");
+
+    /* Test with all common and custom buttons and invalid default ID */
+    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, 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, 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, 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, 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, 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, msg_return_press_custom4, "default button: valid default - 2");
 }
 
 START_TEST(taskdialog)
@@ -205,6 +325,7 @@ START_TEST(taskdialog)
 
     test_invalid_parameters();
     test_callback();
+    test_buttons();
 
     unload_v6_module(ctx_cookie, hCtx);
 }
-- 
2.15.0




More information about the wine-patches mailing list