[v4 5/5] comctl32: TaskDialog - Implement buttons

Fabian Maurer dark.shadow4 at web.de
Wed Mar 15 15:13:23 CDT 2017


v3: Rewrite to implement Nikolay Sivov's suggestions
v4: Rewrite to fix a bunch of issues and make patchset smaller

They are properly placed but aren't working yet.

Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
 dlls/comctl32/comctl32.h   |   8 +++
 dlls/comctl32/comctl32.rc  |  10 +++
 dlls/comctl32/taskdialog.c | 163 +++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 175 insertions(+), 6 deletions(-)

diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h
index b9b0574427..d6d347f7e9 100644
--- a/dlls/comctl32/comctl32.h
+++ b/dlls/comctl32/comctl32.h
@@ -52,6 +52,14 @@ extern HBRUSH  COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN;
 
 #define IDS_CLOSE	  4160
 
+/* Taskdialog */
+#define IDS_OK            1000
+#define IDS_AMP_YES       1001
+#define IDS_AMP_NO        1002
+#define IDS_AMP_RETRY     1003
+#define IDS_CANCEL        1004
+#define IDS_AMP_CLOSE     1005
+
 /* Toolbar customization dialog */
 #define IDD_TBCUSTOMIZE     200
 
diff --git a/dlls/comctl32/comctl32.rc b/dlls/comctl32/comctl32.rc
index 87e2fd1f6c..40d6b27d54 100644
--- a/dlls/comctl32/comctl32.rc
+++ b/dlls/comctl32/comctl32.rc
@@ -32,6 +32,16 @@ STRINGTABLE
 
 STRINGTABLE
 {
+    IDS_OK        "OK"
+    IDS_AMP_YES   "&Yes"
+    IDS_AMP_NO    "&No"
+    IDS_AMP_RETRY "&Retry"
+    IDS_CANCEL    "Cancel"
+    IDS_AMP_CLOSE "&Close"
+}
+
+STRINGTABLE
+{
     IDM_TODAY    "Today:"
     IDM_GOTODAY  "Go to today"
 }
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index 72e72d5d34..ad6b2bbfb3 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -45,12 +45,18 @@ WINE_DEFAULT_DEBUG_CHANNEL(taskdlg);
 static const UINT DIALOG_DEFAULT_WIDTH = 180;
 
 static const UINT DIALOG_SPACING = 5;
+static const UINT DIALOG_SPACING_BUTTONS_LEFT = 40; /* 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 DIALOG_BUTTON_HEIGHT = 10;
 
 #define ID_START 0xF000
 
 static const int ID_TEXTMAIN    = ID_START + 1;
 static const int ID_TEXTCONTENT = ID_START + 2;
 
+static const int MIN_SIZE_BUTTON = 30;
+
 typedef struct
 {
     DLGITEMTEMPLATE template;
@@ -81,6 +87,15 @@ typedef struct
     HFONT font_default, font_main;
 } taskdialog_info;
 
+typedef struct
+{
+    int id;
+    const WCHAR *text;
+    UINT width;
+    UINT x;
+    UINT y;
+}button_info;
+
 #define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target = (void *)((char *)target + size);
 #define ALIGN_POINTER(ptr, boundary) ptr = (void *)(((UINT_PTR)(ptr) + (boundary)) & ~(boundary))
 #define STR_EMPTY(str) (str == NULL || str[0] == 0)
@@ -193,7 +208,7 @@ static void controls_destroy(struct list *controls)
 }
 
 /* Adds a control for the TaskDialog into our list */
-static void controls_add(struct list *controls, WORD id, const WCHAR *class, const WCHAR *text,
+static DLGITEMTEMPLATE* controls_add(struct list *controls, WORD id, const WCHAR *class, const WCHAR *text,
                        DWORD style, short x, short y, short cx, short cy)
 {
     control_info *data = Alloc(sizeof(control_info));
@@ -211,6 +226,8 @@ static void controls_add(struct list *controls, WORD id, const WCHAR *class, con
     data->text_size = (lstrlenW(text) + 1) * sizeof(WCHAR);
 
     list_add_tail(controls, &data->entry);
+
+    return &data->template;
 }
 
 /* DialogProc and helper functions */
@@ -292,6 +309,130 @@ static void taskdialog_info_destroy(taskdialog_info *dialog_info)
     DeleteObject(dialog_info->font_default);
 }
 
+static button_info make_button(HDC hdc, UINT dialog_width, int id, const WCHAR *text)
+{
+    RECT rect;
+    button_info button;
+
+    button.id = id;
+    button.text = text;
+    rect = text_get_rect(hdc, text, dialog_width);
+    button.width = rect.right + 10;
+    if (button.width < MIN_SIZE_BUTTON)
+        button.width = MIN_SIZE_BUTTON;
+
+    return button;
+}
+
+static UINT add_buttons(HDC hdc, const TASKDIALOGCONFIG *task_config, struct list *controls,
+                        UINT dialog_width, UINT dialog_height)
+{
+    static const WCHAR class_button[] = WC_BUTTONW; /* Can't use WC_BUTTONW directy, need to store it into a static variable since it goes out of scope */
+    static WCHAR text_ok    [20] = {0};
+    static WCHAR text_yes   [20] = {0};
+    static WCHAR text_no    [20] = {0};
+    static WCHAR text_cancel[20] = {0};
+    static WCHAR text_retry [20] = {0};
+    static WCHAR text_close [20] = {0};
+    static BOOL text_initialized = 0;
+    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;
+
+    if (!text_initialized)
+    {
+        text_initialized = TRUE;
+        LoadStringW(COMCTL32_hModule, IDS_OK,        text_ok,     sizeof(text_ok)     / sizeof(WCHAR));
+        LoadStringW(COMCTL32_hModule, IDS_AMP_YES,   text_yes,    sizeof(text_yes)    / sizeof(WCHAR));
+        LoadStringW(COMCTL32_hModule, IDS_AMP_NO,    text_no,     sizeof(text_no)     / sizeof(WCHAR));
+        LoadStringW(COMCTL32_hModule, IDS_CANCEL,    text_cancel, sizeof(text_cancel) / sizeof(WCHAR));
+        LoadStringW(COMCTL32_hModule, IDS_AMP_RETRY, text_retry,  sizeof(text_retry)  / sizeof(WCHAR));
+        LoadStringW(COMCTL32_hModule, IDS_AMP_CLOSE, text_close,  sizeof(text_close)  / sizeof(WCHAR));
+    }
+
+    /* Allocate enough memory for the custom and the default buttons */
+    if (task_config->cButtons && task_config->pButtons)
+        buttons = HeapAlloc(GetProcessHeap(), 0, (task_config->cButtons + 6) * sizeof(button_info));
+    else
+    {
+        buttons = HeapAlloc(GetProcessHeap(), 0, 6 * sizeof(button_info));
+    }
+
+    /* Custom buttons */
+    if (task_config->cButtons && task_config->pButtons)
+    {
+        for (i = 0; i < task_config->cButtons; i++)
+        {
+            buttons[count++] = make_button(hdc, dialog_width, task_config->pButtons[i].nButtonID,
+                                           task_config->pButtons[i].pszButtonText);
+        }
+    }
+
+    /* Default buttons */
+    if (task_config->dwCommonButtons & TDCBF_OK_BUTTON)
+        buttons[count++] = make_button(hdc, dialog_width, IDOK, text_ok);
+    if (task_config->dwCommonButtons & TDCBF_YES_BUTTON)
+        buttons[count++] = make_button(hdc, dialog_width, IDYES, text_yes);
+    if (task_config->dwCommonButtons & TDCBF_NO_BUTTON)
+        buttons[count++] = make_button(hdc, dialog_width, IDNO, text_no);
+    if (task_config->dwCommonButtons & TDCBF_RETRY_BUTTON)
+        buttons[count++] = make_button(hdc, dialog_width, IDRETRY, text_retry);
+    if (task_config->dwCommonButtons & TDCBF_CANCEL_BUTTON)
+        buttons[count++] = make_button(hdc, dialog_width, IDCANCEL, text_cancel);
+    if (task_config->dwCommonButtons & TDCBF_CLOSE_BUTTON)
+        buttons[count++] = make_button(hdc, dialog_width, IDCLOSE, text_close);
+
+    /* There must be at least one button */
+    if (!(task_config->cButtons && task_config->pButtons) && !task_config->dwCommonButtons)
+        buttons[count++] = make_button(hdc, dialog_width, IDOK, text_ok);
+
+    /* Position buttons */
+    location_x = alignment;
+    for (i = 0; i < count; i++)
+    {
+        if (location_x + buttons[i].width + DIALOG_SPACING_BUTTON_W > dialog_width) /* When beginning new row, align the first */
+        {
+            if (first_row)
+            {
+                int diff = 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;
+            dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING_BUTTON_H;
+        }
+
+        buttons[i].x = location_x;
+        buttons[i].y = dialog_height;
+
+        location_x += buttons[i].width + DIALOG_SPACING_BUTTON_W;
+    }
+    if (first_row) /* Always align first row to the right */
+    {
+        int diff = 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++)
+    {
+        controls_add(controls, buttons[i].id, class_button, buttons[i].text, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+                                buttons[i].x, buttons[i].y, buttons[i].width, DIALOG_BUTTON_HEIGHT);
+    }
+
+    dialog_height += DIALOG_BUTTON_HEIGHT * 2;
+
+    HeapFree(GetProcessHeap(), 0, buttons);
+    return dialog_height;
+}
+
 /***********************************************************************
  * TaskDialogIndirect [COMCTL32.@]
  */
@@ -299,7 +440,6 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu
                                   int *pnRadioButton, BOOL *pfVerificationFlagChecked)
 {
     static const WCHAR empty_string[] = {0};
-    static const WCHAR text_ok[] = {'O','K',0};
     RECT desktop;
     UINT dialog_width; /* In dialog units */
     UINT dialog_height; /* In dialog units */
@@ -308,6 +448,7 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu
     struct list controls;
     taskdialog_info dialog_info;
     HDC dc_dummy;
+    int ret;
 
     TRACE("%p, %p, %p, %p\n", pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
 
@@ -357,9 +498,10 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu
         dialog_height += rect.bottom;
     }
 
-    controls_add(&controls, IDOK, WC_BUTTONW, text_ok,
-                 WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, dialog_width - 40 - 10, dialog_height, 40, 10);
-    dialog_height += DIALOG_SPACING * 2;
+    dialog_height += DIALOG_SPACING;
+
+    /* Create buttons */
+    dialog_height = add_buttons(dc_dummy, pTaskConfig, &controls, dialog_width, dialog_height);
 
     header.title = pTaskConfig->pszWindowTitle;
     if (!header.title)
@@ -378,7 +520,16 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu
     /* Turn template information into a dialog template to display it */
     template_data = dialog_template_create(header, &controls);
 
-    DialogBoxIndirectParamW(pTaskConfig->hInstance, template_data, pTaskConfig->hwndParent, DialogProc, (LPARAM)&dialog_info);
+    ret = DialogBoxIndirectParamW(pTaskConfig->hInstance, template_data, pTaskConfig->hwndParent, DialogProc, (LPARAM)&dialog_info);
+
+    if (pnButton)
+        *pnButton = ret;
+
+    if (pnRadioButton)
+        *pnRadioButton = 0;
+
+    if (pfVerificationFlagChecked)
+        *pfVerificationFlagChecked = FALSE;
 
     /* Cleanup */
 
-- 
2.12.0




More information about the wine-patches mailing list