From 32c42cc109e0cf8826a2e0b791ee2a2d32f932e5 Mon Sep 17 00:00:00 2001 From: Fabian Maurer Date: Mon, 6 Nov 2017 14:40:38 +0300 Subject: [PATCH] comctl32/taskdialog: Added support for nDefaultButton. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/comctl32/taskdialog.c | 40 +++++---- dlls/comctl32/tests/taskdialog.c | 135 +++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 22 deletions(-) diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c index 5c4af2ab03..eb9d5b460c 100644 --- a/dlls/comctl32/taskdialog.c +++ b/dlls/comctl32/taskdialog.c @@ -59,6 +59,15 @@ struct taskdialog_control unsigned int template_size; }; +struct taskdialog_button_desc +{ + int id; + const WCHAR *text; + unsigned int width; + unsigned int line; + HINSTANCE hinst; +}; + struct taskdialog_template_desc { const TASKDIALOGCONFIG *taskconfig; @@ -69,15 +78,7 @@ struct taskdialog_template_desc LONG x_baseunit; LONG y_baseunit; HFONT font; -}; - -struct taskdialog_button_desc -{ - int id; - const WCHAR *text; - unsigned int width; - unsigned int line; - HINSTANCE hinst; + struct taskdialog_button_desc *default_button; }; struct taskdialog_info @@ -151,7 +152,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 +179,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 +207,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; @@ -234,6 +235,9 @@ 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) + desc->default_button = button; } static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons, @@ -290,6 +294,9 @@ static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc if (count == 0) taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE); + if (!desc->default_button) + desc->default_button = &buttons[0]; + /* For easy handling just allocate as many lines as buttons, the worst case. */ line_widths = Alloc(count * sizeof(*line_widths)); @@ -344,14 +351,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] == desc->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; } @@ -464,6 +473,7 @@ static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfi desc.dialog_height = 0; desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH); desc.dialog_width = min(desc.dialog_width, screen_width); + desc.default_button = NULL; size += taskdialog_add_main_instruction(&desc); size += taskdialog_add_content(&desc); @@ -578,7 +588,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); }