forked from Mirrors/wine-wine
comctl32/taskdialog: Added support for custom buttons.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>oldstable
parent
df07533a80
commit
5b06498b93
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
if (flags & TDCBF_OK_BUTTON)
|
||||
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 - 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)
|
||||
|
|
Loading…
Reference in New Issue