From e445361b0a05e13c1ed15d20325564dd6b024cd5 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Sun, 11 Jun 2017 15:04:26 +0300 Subject: [PATCH] comctl32/taskdialog: Added support for custom buttons. Signed-off-by: Nikolay Sivov --- dlls/comctl32/taskdialog.c | 249 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 192 insertions(+), 57 deletions(-) diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c index dc90160e44..f1d055f507 100644 --- a/dlls/comctl32/taskdialog.c +++ b/dlls/comctl32/taskdialog.c @@ -20,6 +20,7 @@ */ #include +#include #include #define NONAMELESSUNION @@ -43,7 +44,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(taskdialog); #define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align) #define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align) -static const UINT DIALOG_MIN_WIDTH = 180; +static const UINT DIALOG_MIN_WIDTH = 240; static const UINT DIALOG_SPACING = 5; static const UINT DIALOG_BUTTON_WIDTH = 50; static const UINT DIALOG_BUTTON_HEIGHT = 14; @@ -70,6 +71,15 @@ struct taskdialog_template_desc HFONT font; }; +struct taskdialog_button_desc +{ + int id; + const WCHAR *text; + unsigned int width; + unsigned int line; + HINSTANCE hinst; +}; + static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height) { if (width) @@ -92,6 +102,47 @@ static void template_write_data(char **ptr, const void *src, unsigned int size) *ptr += size; } +/* used to calculate size for the controls */ +static void taskdialog_get_text_extent(const struct taskdialog_template_desc *desc, const WCHAR *text, + BOOL user_resource, SIZE *sz) +{ + RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */ + const WCHAR *textW = NULL; + static const WCHAR nulW; + unsigned int length; + HFONT oldfont; + HDC hdc; + + if (IS_INTRESOURCE(text)) + { + if (!(length = LoadStringW(user_resource ? desc->taskconfig->hInstance : COMCTL32_hModule, + (UINT_PTR)text, (WCHAR *)&textW, 0))) + { + WARN("Failed to load text\n"); + textW = &nulW; + length = 0; + } + } + else + { + textW = text; + length = strlenW(textW); + } + + hdc = GetDC(0); + oldfont = SelectObject(hdc, desc->font); + + dialogunits_to_pixels(desc, &rect.right, NULL); + DrawTextW(hdc, textW, length, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK); + pixels_to_dialogunits(desc, &rect.right, &rect.bottom); + + SelectObject(hdc, oldfont); + ReleaseDC(0, hdc); + + sz->cx = rect.right - rect.left; + sz->cy = rect.bottom - rect.top; +} + 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) { @@ -139,43 +190,18 @@ static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc *desc, WORD id, const WCHAR *str) { - RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */ - const WCHAR *textW = NULL; - unsigned int size, length; - HFONT oldfont; - HDC hdc; + unsigned int size; + SIZE sz; if (!str) return 0; - if (IS_INTRESOURCE(str)) - { - if (!(length = LoadStringW(desc->taskconfig->hInstance, (UINT_PTR)str, (WCHAR *)&textW, 0))) - { - WARN("Failed to load static text %s, id %#x\n", debugstr_w(str), id); - return 0; - } - } - else - { - textW = str; - length = strlenW(textW); - } - - hdc = GetDC(0); - oldfont = SelectObject(hdc, desc->font); - - dialogunits_to_pixels(desc, &rect.right, NULL); - DrawTextW(hdc, textW, length, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK); - pixels_to_dialogunits(desc, &rect.right, &rect.bottom); - - SelectObject(hdc, oldfont); - ReleaseDC(0, hdc); + 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, - desc->dialog_height, rect.right, rect.bottom); - desc->dialog_height += rect.bottom; + desc->dialog_height, sz.cx, sz.cy); + desc->dialog_height += sz.cy + DIALOG_SPACING; return size; } @@ -189,37 +215,146 @@ static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc return taskdialog_add_static_label(desc, ID_CONTENT, desc->taskconfig->pszContent); } -static unsigned int taskdialog_add_common_buttons(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) +{ + SIZE sz; + + taskdialog_get_text_extent(desc, text, custom_button, &sz); + + button->id = id; + button->text = text; + button->width = max(DIALOG_BUTTON_WIDTH, sz.cx + DIALOG_SPACING * 2); + button->line = 0; + button->hinst = custom_button ? desc->taskconfig->hInstance : COMCTL32_hModule; +} + +static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons, + unsigned int *button_count) { - short button_x = desc->dialog_width - DIALOG_BUTTON_WIDTH - DIALOG_SPACING; DWORD flags = desc->taskconfig->dwCommonButtons; - unsigned int size = 0; -#define TASKDIALOG_ADD_COMMON_BUTTON(id) \ +#define TASKDIALOG_INIT_COMMON_BUTTON(id) \ do { \ - size += taskdialog_add_control(desc, ID##id, WC_BUTTONW, COMCTL32_hModule, MAKEINTRESOURCEW(IDS_BUTTON_##id), \ - button_x, desc->dialog_height + DIALOG_SPACING, DIALOG_BUTTON_WIDTH, DIALOG_BUTTON_HEIGHT); \ - button_x -= DIALOG_BUTTON_WIDTH + DIALOG_SPACING; \ + taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \ } while(0) - if (flags & TDCBF_CLOSE_BUTTON) - TASKDIALOG_ADD_COMMON_BUTTON(CLOSE); - if (flags & TDCBF_CANCEL_BUTTON) - TASKDIALOG_ADD_COMMON_BUTTON(CANCEL); - if (flags & TDCBF_RETRY_BUTTON) - TASKDIALOG_ADD_COMMON_BUTTON(RETRY); - if (flags & TDCBF_NO_BUTTON) - TASKDIALOG_ADD_COMMON_BUTTON(NO); - if (flags & TDCBF_YES_BUTTON) - TASKDIALOG_ADD_COMMON_BUTTON(YES); + if (flags & TDCBF_OK_BUTTON) - TASKDIALOG_ADD_COMMON_BUTTON(OK); - /* Always add OK button */ - if (list_empty(&desc->controls)) - TASKDIALOG_ADD_COMMON_BUTTON(OK); -#undef TASKDIALOG_ADD_COMMON_BUTTON - - /* make room for common buttons row */ - desc->dialog_height += DIALOG_BUTTON_HEIGHT + 2 * DIALOG_SPACING; + TASKDIALOG_INIT_COMMON_BUTTON(OK); + if (flags & TDCBF_YES_BUTTON) + TASKDIALOG_INIT_COMMON_BUTTON(YES); + if (flags & TDCBF_NO_BUTTON) + TASKDIALOG_INIT_COMMON_BUTTON(NO); + if (flags & TDCBF_RETRY_BUTTON) + TASKDIALOG_INIT_COMMON_BUTTON(RETRY); + if (flags & TDCBF_CANCEL_BUTTON) + TASKDIALOG_INIT_COMMON_BUTTON(CANCEL); + if (flags & TDCBF_CLOSE_BUTTON) + TASKDIALOG_INIT_COMMON_BUTTON(CLOSE); + +#undef TASKDIALOG_INIT_COMMON_BUTTON +} + +static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc) +{ + unsigned int count = 0, buttons_size, i, line_count, size = 0; + unsigned int location_x, *line_widths, alignment = ~0u; + const TASKDIALOGCONFIG *taskconfig = desc->taskconfig; + struct taskdialog_button_desc *buttons; + + /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */ + buttons_size = 6; + if (taskconfig->cButtons && taskconfig->pButtons) + buttons_size += taskconfig->cButtons; + + if (!(buttons = Alloc(buttons_size * sizeof(*buttons)))) + return 0; + + /* Custom buttons */ + 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); + + /* Common buttons */ + taskdialog_init_common_buttons(desc, buttons, &count); + + /* There must be at least one button */ + if (count == 0) + taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE); + + /* For easy handling just allocate as many lines as buttons, the worst case. */ + line_widths = Alloc(count * sizeof(*line_widths)); + + /* Separate buttons into lines */ + location_x = DIALOG_SPACING; + for (i = 0, line_count = 0; i < count; i++) + { + if (location_x + buttons[i].width + DIALOG_SPACING > desc->dialog_width) + { + location_x = DIALOG_SPACING; + line_count++; + } + + buttons[i].line = line_count; + + location_x += buttons[i].width + DIALOG_SPACING; + line_widths[line_count] += buttons[i].width + DIALOG_SPACING; + } + line_count++; + + /* Try to balance lines so they are about the same size */ + for (i = 1; i < line_count - 1; i++) + { + int diff_now = abs(line_widths[i] - line_widths[i - 1]); + unsigned int j, last_button = 0; + int diff_changed; + + for (j = 0; j < count; j++) + if (buttons[j].line == i - 1) + last_button = j; + + /* Difference in length of both lines if we wrapped the last button from the last line into this one */ + diff_changed = abs(2 * buttons[last_button].width + line_widths[i] - line_widths[i - 1]); + + if (diff_changed < diff_now) + { + buttons[last_button].line = i; + line_widths[i] += buttons[last_button].width; + line_widths[i - 1] -= buttons[last_button].width; + } + } + + /* Calculate left alignment so all lines are as far right as possible. */ + for (i = 0; i < line_count; i++) + { + int new_alignment = desc->dialog_width - (DIALOG_SPACING + line_widths[i]); + if (new_alignment < alignment) + alignment = new_alignment; + } + + /* Now that we got them all positioned, create all buttons */ + location_x = alignment; + for (i = 0; i < count; i++) + { + 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); + + location_x += buttons[i].width + DIALOG_SPACING; + } + + /* Add height for last row and spacing */ + desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING; + + Free(line_widths); + Free(buttons); + return size; } @@ -307,7 +442,7 @@ static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfi size += taskdialog_add_main_instruction(&desc); size += taskdialog_add_content(&desc); - size += taskdialog_add_common_buttons(&desc); + size += taskdialog_add_buttons(&desc); template = Alloc(size); if (!template) -- 2.11.0