[PATCH v3] comctl32/taskdialog: Implement buttons.
Fabian Maurer
dark.shadow4 at web.de
Sat Jun 10 09:50:44 CDT 2017
v3: Rebased and fixed minor issues
Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
dlls/comctl32/taskdialog.c | 239 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 182 insertions(+), 57 deletions(-)
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index dc90160e44..13df959f7b 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -20,6 +20,7 @@
*/
#include <stdarg.h>
+#include <stdlib.h>
#include <string.h>
#define NONAMELESSUNION
@@ -43,10 +44,12 @@ 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;
+static const UINT DIALOG_SPACING_BUTTON_H = 1; /* Distance between buttons */
+static const UINT DIALOG_SPACING_BUTTON_W = 5; /* Distance between buttons */
static const UINT ID_MAIN_INSTRUCTION = 0xf000;
static const UINT ID_CONTENT = 0xf001;
@@ -70,6 +73,14 @@ struct taskdialog_template_desc
HFONT font;
};
+typedef struct
+{
+ int id;
+ const WCHAR *text;
+ UINT width;
+ UINT line;
+} button_info;
+
static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
{
if (width)
@@ -92,6 +103,44 @@ static void template_write_data(char **ptr, const void *src, unsigned int size)
*ptr += size;
}
+/* used to calculate size for the controls */
+static RECT text_get_rect(const struct taskdialog_template_desc *desc, const WCHAR *text)
+{
+ const WCHAR *textW = NULL;
+ unsigned int length;
+ HFONT oldfont;
+ HDC hdc;
+ static const WCHAR nulW;
+ RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */
+
+ if (IS_INTRESOURCE(text))
+ {
+ if (!(length = LoadStringW(desc->taskconfig->hInstance, (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);
+
+ return rect;
+}
+
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 +188,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;
+ RECT rect;
+ unsigned int size;
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);
+ rect = text_get_rect(desc, str);
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 += rect.bottom + DIALOG_SPACING;
return size;
}
@@ -189,37 +213,138 @@ 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 button_info make_button(struct taskdialog_template_desc *desc, int id, const WCHAR *text)
+{
+ RECT rect;
+ button_info button;
+
+ button.id = id;
+ button.text = text;
+ rect = text_get_rect(desc, text);
+ button.width = rect.right + DIALOG_SPACING * 2;
+ if (button.width < DIALOG_BUTTON_WIDTH)
+ button.width = DIALOG_BUTTON_WIDTH;
+
+ return button;
+}
+
+static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc)
{
- short button_x = desc->dialog_width - DIALOG_BUTTON_WIDTH - DIALOG_SPACING;
DWORD flags = desc->taskconfig->dwCommonButtons;
+ const TASKDIALOGCONFIG *taskconfig = desc->taskconfig;
+ UINT location_x;
+ button_info *buttons;
+ int count = 0;
+ int i, j;
unsigned int size = 0;
+ int *lines_width;
+ int max_lines = 0;
+ UINT alignment = (UINT)-1;
-#define TASKDIALOG_ADD_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; \
- } 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);
+ /* Allocate enough memory for the custom and the default buttons */
+ if (taskconfig->cButtons && taskconfig->pButtons)
+ buttons = HeapAlloc(GetProcessHeap(), 0, (taskconfig->cButtons + 6) * sizeof(button_info));
+ else
+ buttons = HeapAlloc(GetProcessHeap(), 0, 6 * sizeof(button_info));
+
+ /* Custom buttons */
+ if (taskconfig->cButtons && taskconfig->pButtons)
+ {
+ for (i = 0; i < taskconfig->cButtons; i++)
+ {
+ buttons[count++] = make_button(desc, taskconfig->pButtons[i].nButtonID, taskconfig->pButtons[i].pszButtonText);
+ }
+ }
+
+ /* Default buttons */
+ if (flags & TDCBF_OK_BUTTON)
+ buttons[count++] = make_button(desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK));
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;
+ buttons[count++] = make_button(desc, IDYES, MAKEINTRESOURCEW(IDS_BUTTON_YES));
+ if (flags & TDCBF_NO_BUTTON)
+ buttons[count++] = make_button(desc, IDNO, MAKEINTRESOURCEW(IDS_BUTTON_NO));
+ if (flags & TDCBF_RETRY_BUTTON)
+ buttons[count++] = make_button(desc, IDRETRY, MAKEINTRESOURCEW(IDS_BUTTON_RETRY));
+ if (flags & TDCBF_CANCEL_BUTTON)
+ buttons[count++] = make_button(desc, IDCANCEL, MAKEINTRESOURCEW(IDS_BUTTON_CANCEL));
+ if (flags & TDCBF_CLOSE_BUTTON)
+ buttons[count++] = make_button(desc, IDCLOSE, MAKEINTRESOURCEW(IDS_BUTTON_CLOSE));
+
+ /* There must be at least one button */
+ if (count == 0)
+ buttons[count++] = make_button(desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK));
+
+ /* For easy handling just allocate as many lines as buttons, the worst case */
+ lines_width = Alloc(sizeof(int) * count);
+
+ /* Separate buttons into lines */
+ location_x = DIALOG_SPACING;
+ for (i = 0; i < count; i++)
+ {
+ if (location_x + buttons[i].width + DIALOG_SPACING_BUTTON_W > desc->dialog_width)
+ {
+ location_x = DIALOG_SPACING;
+ max_lines++;
+ }
+
+ buttons[i].line = max_lines;
+
+ location_x += buttons[i].width + DIALOG_SPACING_BUTTON_W;
+ lines_width[max_lines] += buttons[i].width + DIALOG_SPACING_BUTTON_W;
+ }
+ max_lines++;
+
+ /* Try to balance lines so they are about the same size */
+ for(int i = 1; i < max_lines - 1; i++)
+ {
+ int diff_now = abs(lines_width[i] - lines_width[i - 1]);
+ int last_button;
+ 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 + lines_width[i] - lines_width[i - 1]);
+
+ if(diff_changed < diff_now)
+ {
+ buttons[last_button].line = i;
+ lines_width[i] += buttons[last_button].width;
+ lines_width[i - 1] -= buttons[last_button].width;
+ }
+ }
+
+ /* Calculate left alignment so all lines as far right as possible */
+ for(int i = 0; i < max_lines; i++)
+ {
+ int new_alignment = desc->dialog_width - (DIALOG_SPACING + lines_width[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_BUTTON_H;
+ }
+
+ size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, COMCTL32_hModule,
+ buttons[i].text, location_x, desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
+
+ location_x += buttons[i].width + DIALOG_SPACING_BUTTON_W;
+ }
+
+ /* Add height for last row and spacing */
+ desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
+
+ Free(lines_width);
+ HeapFree(GetProcessHeap(), 0, buttons);
return size;
}
@@ -307,7 +432,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.13.1
More information about the wine-patches
mailing list