[PATCH] comctl32/taskdialog: Implement buttons.

Fabian Maurer dark.shadow4 at web.de
Thu Mar 23 13:22:26 CDT 2017


Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
 dlls/comctl32/taskdialog.c | 224 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 161 insertions(+), 63 deletions(-)

diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index 6b8bcfcae6..037b85e082 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -41,10 +41,13 @@ 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_BUTTON_WIDTH_MIN = 45;
+static const UINT DIALOG_BUTTON_HEIGHT = 13;
+static const UINT DIALOG_SPACING_BUTTONS_LEFT = 30; /* minimum distance from the left dialog border */
+static const UINT DIALOG_SPACING_BUTTON_H = 1; /* Distance between buttons */
+static const UINT DIALOG_SPACING_BUTTON_W = 4; /* Distance between buttons */
 
 static const UINT ID_MAIN_INSTRUCTION = 0xf000;
 
@@ -67,6 +70,15 @@ struct taskdialog_template_desc
     HFONT font;
 };
 
+typedef struct
+{
+    int id;
+    const WCHAR *text;
+    UINT width;
+    UINT x;
+    UINT y;
+} button_info;
+
 static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
 {
     if (width)
@@ -83,6 +95,44 @@ static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, L
         *height = MulDiv(*height, desc->y_baseunit, 8);
 }
 
+/* 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 void template_write_data(char **ptr, const void *src, unsigned int size)
 {
     memcpy(*ptr, src, size);
@@ -90,7 +140,7 @@ static void template_write_data(char **ptr, const void *src, unsigned int size)
 }
 
 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)
+        DWORD style, HINSTANCE hInstance, const WCHAR *text, short x, short y, short cx, short cy)
 {
     struct taskdialog_control *control = Alloc(sizeof(*control));
     unsigned int size, class_size, text_size;
@@ -117,7 +167,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 | WS_CHILD | style;
     template->dwExtendedStyle = 0;
     template->x = x;
     template->y = y;
@@ -136,78 +186,126 @@ static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc
 
 static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc)
 {
-    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;
+    RECT rect;
 
     if (!desc->taskconfig->pszMainInstruction)
         return 0;
 
-    if (IS_INTRESOURCE(desc->taskconfig->pszMainInstruction))
-    {
-        if (!(length = LoadStringW(desc->taskconfig->hInstance, (UINT_PTR)desc->taskconfig->pszMainInstruction,
-                (WCHAR *)&textW, 0)))
-        {
-            WARN("Failed to load main instruction text\n");
-            return 0;
-        }
-    }
-    else
-    {
-        textW = desc->taskconfig->pszMainInstruction;
-        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, desc->taskconfig->pszMainInstruction);
 
-    size = taskdialog_add_control(desc, ID_MAIN_INSTRUCTION, WC_STATICW, desc->taskconfig->hInstance,
+    size = taskdialog_add_control(desc, ID_MAIN_INSTRUCTION, WC_STATICW, 0, desc->taskconfig->hInstance,
             desc->taskconfig->pszMainInstruction, DIALOG_SPACING, desc->dialog_height,
             rect.right, rect.bottom);
-    desc->dialog_height += rect.bottom;
+    desc->dialog_height += rect.bottom + DIALOG_SPACING;
     return size;
 }
 
-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 + 10;
+    if (button.width < DIALOG_BUTTON_WIDTH_MIN)
+        button.width = DIALOG_BUTTON_WIDTH_MIN;
+
+    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 alignment = DIALOG_SPACING_BUTTONS_LEFT; /* minimum distance from the left dialog border */
+    UINT location_x;
+    BOOL first_row = TRUE;
+    button_info *buttons;
+    int count = 0;
+    int i;
     unsigned int size = 0;
 
-#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);
-    if (flags & TDCBF_YES_BUTTON)
-        TASKDIALOG_ADD_COMMON_BUTTON(YES);
+    /* 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)
-        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, IDOK,     MAKEINTRESOURCEW(IDS_BUTTON_OK));
+    if (flags & TDCBF_YES_BUTTON)
+        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));
+
+    /* Position buttons */
+    location_x = alignment;
+    for (i = 0; i < count; i++)
+    {
+        /* When beginning new row, align the first */
+        if (location_x + buttons[i].width + DIALOG_SPACING_BUTTON_W > desc->dialog_width)
+        {
+            if (first_row)
+            {
+                int diff = desc->dialog_width - location_x;
+
+                first_row = FALSE;
+                for (int j = 0; j < i; j++) /* Align first row to the right */
+                    buttons[j].x += diff;
+                alignment = buttons[0].x; /* left-align all coming rows to the first row */
+            }
+            location_x = alignment;
+            desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING_BUTTON_H;
+        }
+
+        buttons[i].x = location_x;
+        buttons[i].y = desc->dialog_height;
+
+        location_x += buttons[i].width + DIALOG_SPACING_BUTTON_W;
+    }
+    if (first_row) /* Always align first row to the right */
+    {
+        int diff = desc->dialog_width - (buttons[count - 1].x + buttons[count - 1].width + DIALOG_SPACING_BUTTON_W);
+        for (int i = 0; i < count; i++)
+            buttons[i].x += diff;
+    }
+
+     /* Now that we got them all positioned, create all buttons */
+    for (i = 0; i < count; i++)
+    {
+        size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, BS_PUSHBUTTON, COMCTL32_hModule,
+                    buttons[i].text, buttons[i].x, buttons[i].y, buttons[i].width, DIALOG_BUTTON_HEIGHT);
+    }
+
+    /* Add height for last row and spacing */
+    desc->dialog_height +=  DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
+
+    HeapFree(GetProcessHeap(), 0, buttons);
     return size;
 }
 
@@ -295,7 +393,7 @@ static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfi
     desc.dialog_width = min(desc.dialog_width, screen_width);
 
     size += taskdialog_add_main_instruction(&desc);
-    size += taskdialog_add_common_buttons(&desc);
+    size += taskdialog_add_buttons(&desc);
 
     template = Alloc(size);
     if (!template)
-- 
2.12.1




More information about the wine-patches mailing list