[PATCH 2/3] comctl32: Basic implementation of TaskDialogIndirect (try 2)

Joachim Priesner joachim.priesner at web.de
Thu Oct 2 06:20:19 CDT 2014


This is part of an effort to replace the current TaskDialogIndirect stub
(which uses MessageBoxW) with an own implementation.

In this patch, the actual basic implementation of TaskDialogIndirect is
provided, which for now displays the main instruction and content text,
as well as the icon.

Try 2: Incorporated fixes for the things pointed out by Nikolay Sivov
(thanks!). Also removed the sunken line between task dialog content and
buttons as it is not present in Windows 7 with classic style (which is
basically the style Wine is using).

Tested on openSuse 13.1.
---
 dlls/comctl32/Makefile.in  |   2 +-
 dlls/comctl32/comctl32.h   |   7 +
 dlls/comctl32/comctl32.rc  |  15 ++
 dlls/comctl32/taskdialog.c | 494 ++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 486 insertions(+), 32 deletions(-)

diff --git a/dlls/comctl32/Makefile.in b/dlls/comctl32/Makefile.in
index 9718f4b..728b85c 100644
--- a/dlls/comctl32/Makefile.in
+++ b/dlls/comctl32/Makefile.in
@@ -1,7 +1,7 @@
 EXTRADEFS = -D_COMCTL32_
 MODULE    = comctl32.dll
 IMPORTLIB = comctl32
-IMPORTS   = uuid user32 gdi32 advapi32
+IMPORTS   = uuid user32 gdi32 advapi32 shlwapi
 DELAYIMPORTS = winmm uxtheme
 
 C_SRCS = \
diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h
index b9b0574..2bc3507 100644
--- a/dlls/comctl32/comctl32.h
+++ b/dlls/comctl32/comctl32.h
@@ -65,6 +65,13 @@ extern HBRUSH  COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN;
 
 #define IDS_SEPARATOR      1024
 
+/* Task dialog */
+#define IDD_TASKDIALOG       510
+
+#define IDC_MAIN_ICON        511
+#define IDC_MAIN_INSTRUCTION 512
+#define IDC_CONTENT          513
+
 /* Toolbar imagelist bitmaps */
 #define IDB_STD_SMALL       120
 #define IDB_STD_LARGE       121
diff --git a/dlls/comctl32/comctl32.rc b/dlls/comctl32/comctl32.rc
index 87e2fd1..0450a36 100644
--- a/dlls/comctl32/comctl32.rc
+++ b/dlls/comctl32/comctl32.rc
@@ -93,6 +93,21 @@ BEGIN
   LISTBOX       IDC_TOOLBARBTN_LBOX, 194,17,120,100,LBS_NOTIFY | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
 END
 
+IDD_TASKDIALOG DIALOG 100, 80, 216, 168
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+BEGIN
+  ICON          "",       IDC_MAIN_ICON, 8, 20, 16, 16, WS_CHILD | WS_VISIBLE
+  LTEXT         "",       IDC_MAIN_INSTRUCTION, 32, 4, 176, 48, WS_CHILD | WS_VISIBLE | WS_GROUP | SS_NOPREFIX
+  LTEXT         "",       IDC_CONTENT, 32, 4, 176, 48, WS_CHILD | WS_VISIBLE | WS_GROUP | SS_NOPREFIX
+  PUSHBUTTON    "OK",     IDOK, 16, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+  PUSHBUTTON    "&Yes",   IDYES, 306, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+  PUSHBUTTON    "&No",    IDNO, 364, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+  PUSHBUTTON    "Cancel", IDCANCEL, 74, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+  PUSHBUTTON    "&Retry", IDRETRY, 190, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+  PUSHBUTTON    "&Close", IDCLOSE, 132, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+END
+
+
 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 
 #define WINE_FILEDESCRIPTION_STR "Wine Common Controls"
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index 760a984..38b70e5 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -17,46 +17,478 @@
  */
 
 #include "comctl32.h"
-#include "winuser.h"
+#include "shlwapi.h"
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winternl.h"
+#include "dlgs.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
 
+#define TASKDIALOG_SPACING        8
+
+#define IMAGERES_QUESTION_ICON    MAKEINTRESOURCEW(0x63)
+#define IMAGERES_ERROR_ICON       MAKEINTRESOURCEW(0x62)
+#define IMAGERES_WARNING_ICON     MAKEINTRESOURCEW(0x54)
+#define IMAGERES_INFORMATION_ICON MAKEINTRESOURCEW(0x51)
+
+/* Retrieves an icon for the task dialog from the specified instance and icon identifier. */
+HICON TASKDIALOG_GetIcon(HINSTANCE instance, PCWSTR icon)
+{
+    /* If instance is NULL and icon is not one if the TD_*_ICON constants, the icon is taken
+     * from the system's image resources (imageres.dll). This is documented in the MSDN
+     * documentation for the TDM_UPDATE_ICON message. Until imageres.dll is implemented,
+     * hard-code some of the offsets for standard icons in imageres.dll so at least some
+     * of the icons are displayed correctly. */
+    if (icon == TD_ERROR_ICON || (instance == NULL && icon == IMAGERES_ERROR_ICON))
+        return LoadIconW(0, (LPWSTR)IDI_ERROR);
+    else if (icon == TD_WARNING_ICON || (instance == NULL && icon == IMAGERES_WARNING_ICON))
+        return LoadIconW(0, (LPWSTR)IDI_WARNING);
+    else if (icon == TD_INFORMATION_ICON || (instance == NULL && icon == IMAGERES_INFORMATION_ICON))
+        return LoadIconW(0, (LPWSTR)IDI_INFORMATION);
+    else if (icon == TD_SHIELD_ICON)
+        return LoadIconW(0, (LPWSTR)IDI_SHIELD);
+    else if (instance == NULL && icon == IMAGERES_QUESTION_ICON)
+        return LoadIconW(0, (LPWSTR)IDI_QUESTION);
+    else if (instance)
+        return LoadIconW(instance, icon);
+    return 0;
+}
+
+/*
+ * TaskDialog layout (not everything implemented yet):
+ *
+ * +------------------------------------------------------------+
+ * | Window title (fallback: executable file name)            X |
+ * +------------------------------------------------------------+
+ * | _THE   Main instruction                                    |
+ * | MAIN                                                       |
+ * | ICON   Content                                             |
+ * |                                                            |
+ * |        Expanded information if TDF_EXPAND_FOOTER AREA      |
+ * |        is not set.                                         |
+ * |                                                            |
+ * |        [Progress Bar or Marquee Progress Bar_____________] |
+ * |                                                            |
+ * |        (*) Radio button 1                                  |
+ * |        ( ) Radio button 2                                  |
+ * |                                                            |
+ * |        => User buttons if TDF_USE_COMMAND_LINKS or         |
+ * |           TDF_USE_COMMAND_LINKS_NOICON is set              |
+ * |                                                            |
+ * |        => In this case, the user buttons don't appear      |
+ * |           below.                                           |
+ * |                                                            |
+ * +============================================================+
+ * | (V) Expand/collapse        [User buttons] [Common buttons] |
+ * | [ ] Verification checkbox                                  |
+ * +============================================================+
+ * | ICON  Footer text                                          |
+ * |       which can span multiple lines.                       |
+ * +============================================================+
+ * | Expanded information if TDF_EXPAND_FOOTER_AREA is set      |
+ * +------------------------------------------------------------+
+ */
+static void TASKDIALOG_OnInit(HWND hwnd, TASKDIALOGCONFIG *config)
+{
+    HFONT hNormalFont, hMainInstructionFont;
+    LOGFONTW mainInstructionFontAttributes = {0};
+    HWND hItem, hDefaultButton = 0;
+    HICON hIconMain = 0;
+    HDC hdc;
+    BOOL hasButton = 0;
+    int i, nButtons, currentX, currentY;
+    int buttonWidth, buttonAreaWidth, buttonHeight;
+    int windowClientWidth, windowClientHeight, windowLeft, windowTop;
+    int windowBorderHeight, windowBorderWidth;
+    int mainIconHeight, mainIconLeft, mainIconWidth;
+    int mainAreaTextLeft, mainAreaTextWidth;
+    int contentTextWidth, contentTextHeight;
+    int mainInstructionWidth, mainInstructionHeight;
+    HMONITOR monitor = 0;
+    MONITORINFO mon_info;
+    LPCWSTR lpszContent, lpszMainInstruction;
+    WCHAR *windowTitleBuffer = NULL, *contentTextBuffer = NULL, *mainInstructionBuffer = NULL;
+    const WCHAR *ptr;
+    RECT rect;
+    static const int commonButtons[6] = { IDOK, IDYES, IDNO, IDCANCEL, IDRETRY, IDCLOSE };
+
+    /* Get the text to display. */
+    TRACE("pszWindowTitle=%s\n", debugstr_w(config->pszWindowTitle));
+    if (IS_INTRESOURCE(config->pszWindowTitle))
+    {
+        UINT len = LoadStringW(config->hInstance, LOWORD(config->pszWindowTitle), (LPWSTR)&ptr, 0);
+        if (len)
+        {
+            windowTitleBuffer = Alloc((len + 1) * sizeof(WCHAR));
+            if (!windowTitleBuffer)
+            {
+                EndDialog(hwnd, E_OUTOFMEMORY);
+                return;
+            }
+            memcpy(windowTitleBuffer, ptr, len * sizeof(WCHAR));
+            windowTitleBuffer[len] = 0;
+            SetWindowTextW(hwnd, windowTitleBuffer);
+        }
+        /* If the supplied window title is invalid, display the executable file name instead. */
+        else
+        {
+            windowTitleBuffer = Alloc((MAX_PATH + 1) * sizeof(WCHAR));
+            if (!windowTitleBuffer)
+            {
+                EndDialog(hwnd, E_OUTOFMEMORY);
+                return;
+            }
+            GetModuleFileNameW(NULL, windowTitleBuffer, MAX_PATH);
+            windowTitleBuffer[MAX_PATH] = 0;
+            SetWindowTextW(hwnd, PathFindFileNameW(windowTitleBuffer));
+        }
+        Free(windowTitleBuffer);
+        windowTitleBuffer = NULL;
+    }
+    else SetWindowTextW(hwnd, config->pszWindowTitle);
+
+    TRACE("pszMainInstruction=%s\n", debugstr_w(config->pszMainInstruction));
+    if (IS_INTRESOURCE(config->pszMainInstruction))
+    {
+        UINT len = LoadStringW(config->hInstance, LOWORD(config->pszMainInstruction), (LPWSTR)&ptr, 0);
+        mainInstructionBuffer = Alloc((len + 1) * sizeof(WCHAR));
+        if (!mainInstructionBuffer)
+        {
+            EndDialog(hwnd, E_OUTOFMEMORY);
+            return;
+        }
+        memcpy(mainInstructionBuffer, ptr, len * sizeof(WCHAR));
+        mainInstructionBuffer[len] = 0;
+        lpszMainInstruction = mainInstructionBuffer;
+    }
+    else lpszMainInstruction = config->pszMainInstruction;
+    SetWindowTextW(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), lpszMainInstruction);
+
+    TRACE("pszContent=%s\n", debugstr_w(config->pszContent));
+    if (IS_INTRESOURCE(config->pszContent))
+    {
+        UINT len = LoadStringW(config->hInstance, LOWORD(config->pszContent), (LPWSTR)&ptr, 0);
+        contentTextBuffer = Alloc((len + 1) * sizeof(WCHAR));
+        if (!contentTextBuffer)
+        {
+            EndDialog(hwnd, E_OUTOFMEMORY);
+            return;
+        }
+        memcpy(contentTextBuffer, ptr, len * sizeof(WCHAR));
+        contentTextBuffer[len] = 0;
+        lpszContent = contentTextBuffer;
+    }
+    else lpszContent = config->pszContent;
+    SetWindowTextW(GetDlgItem(hwnd, IDC_CONTENT), lpszContent);
+
+    if (config->pszFooter)
+    {
+        FIXME("pszFooter=%s\n", debugstr_w(config->pszFooter));
+    }
+    if (config->pszVerificationText)
+    {
+        FIXME("pszVerificationText=%s\n", debugstr_w(config->pszVerificationText));
+    }
+    if (config->pszExpandedInformation)
+    {
+        FIXME("pszExpandedInformation=%s\n", debugstr_w(config->pszExpandedInformation));
+    }
+    if (config->pszExpandedControlText)
+    {
+        FIXME("pszExpandedControlText=%s\n", debugstr_w(config->pszExpandedControlText));
+    }
+    if (config->pszCollapsedControlText)
+    {
+        FIXME("pszCollapsedControlText=%s\n", debugstr_w(config->pszCollapsedControlText));
+    }
+
+    /* Destroy unused common buttons. */
+    TRACE("dwCommonButtons=%x\n", config->dwCommonButtons);
+    if (!(config->dwCommonButtons & TDCBF_YES_BUTTON))
+        DestroyWindow(GetDlgItem(hwnd, IDYES));
+    else hasButton = 1;
+    if (!(config->dwCommonButtons & TDCBF_NO_BUTTON))
+        DestroyWindow(GetDlgItem(hwnd, IDNO));
+    else hasButton = 1;
+    if (!(config->dwCommonButtons & TDCBF_CANCEL_BUTTON))
+        DestroyWindow(GetDlgItem(hwnd, IDCANCEL));
+    else hasButton = 1;
+    if (!(config->dwCommonButtons & TDCBF_RETRY_BUTTON))
+        DestroyWindow(GetDlgItem(hwnd, IDRETRY));
+    else hasButton = 1;
+    if (!(config->dwCommonButtons & TDCBF_CLOSE_BUTTON))
+        DestroyWindow(GetDlgItem(hwnd, IDCLOSE));
+    else hasButton = 1;
+
+    /* Set user-defined buttons. */
+    if (config->cButtons)
+        FIXME("Custom task dialog buttons not implemented.\n");
+    if (config->cRadioButtons)
+        FIXME("Task dialog radio buttons not implemented.\n");
+
+    /* If no other buttons are specified, the OK button remains. */
+    if (!(config->dwCommonButtons & TDCBF_OK_BUTTON) && hasButton)
+        DestroyWindow(GetDlgItem(hwnd, IDOK));
+
+    /* Get the main icon. */
+    if (config->dwFlags & TDF_USE_HICON_MAIN)
+        hIconMain = config->hMainIcon;
+    else
+        hIconMain = TASKDIALOG_GetIcon(config->hInstance, config->pszMainIcon);
+
+    /* Position everything. */
+    GetWindowRect(hwnd, &rect);
+    windowBorderHeight = rect.bottom - rect.top;
+    windowBorderWidth  = rect.right - rect.left;
+    GetClientRect(hwnd, &rect);
+    windowBorderHeight -= rect.bottom - rect.top;
+    windowBorderWidth  -= rect.right - rect.left;
+
+    /* Get the main icon size. */
+    if (hIconMain) {
+        SendDlgItemMessageW(hwnd, IDC_MAIN_ICON, STM_SETICON, (WPARAM)hIconMain, 0);
+        GetWindowRect(GetDlgItem(hwnd, IDC_MAIN_ICON), &rect);
+        MapWindowPoints(0, hwnd, (LPPOINT)&rect, 2);
+        mainIconHeight = rect.bottom - rect.top;
+        mainIconLeft = TASKDIALOG_SPACING;
+        mainIconWidth = rect.right - rect.left;
+    } else {
+        mainIconHeight = 0;
+        mainIconLeft = 0;
+        mainIconWidth = 0;
+    }
+
+    /* Set a bold font for the main instruction. */
+    hdc = GetDC(hwnd);
+    hNormalFont = SelectObject(hdc, (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0));
+
+    GetObjectW(hNormalFont, sizeof(mainInstructionFontAttributes), &mainInstructionFontAttributes);
+    mainInstructionFontAttributes.lfWeight = FW_BOLD;
+    hMainInstructionFont = CreateFontIndirectW(&mainInstructionFontAttributes);
+    SendMessageW(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), WM_SETFONT, (WPARAM)hMainInstructionFont, 0);
+
+    /* Get the number of visible buttons and their size. */
+    buttonHeight = buttonWidth = 1;
+    nButtons = 0;
+    for (i = 0; i < (sizeof(commonButtons) / sizeof(commonButtons[0])); i++)
+    {
+        hItem = GetDlgItem(hwnd, commonButtons[i]);
+        if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE)
+        {
+            WCHAR buttonText[1024];
+            int w, h;
+            nButtons++;
+            if (GetWindowTextW(hItem, buttonText, 1024))
+            {
+                DrawTextW(hdc, buttonText, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT);
+                h = rect.bottom - rect.top;
+                w = rect.right - rect.left;
+                if (h > buttonHeight) buttonHeight = h;
+                if (w > buttonWidth)  buttonWidth = w ;
+            }
+        }
+    }
+
+    /* Give the buttons some white space. */
+    buttonHeight *= 2;
+    buttonWidth = max(buttonWidth * 2, buttonHeight);
+    buttonAreaWidth = (buttonWidth + TASKDIALOG_SPACING) * nButtons - TASKDIALOG_SPACING;
+
+    mainAreaTextLeft = TASKDIALOG_SPACING;
+    if (mainIconWidth) mainAreaTextLeft += mainIconWidth + TASKDIALOG_SPACING;
+
+    /* Get the size of the texts in the main area.
+     * Minimum text width corresponds to space for the buttons. */
+    GetClientRect(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), &rect);
+    rect.top = rect.left = rect.bottom = 0;
+    rect.right = max(rect.right, buttonAreaWidth);
+    SelectObject(hdc, hMainInstructionFont);
+    DrawTextW(hdc, lpszMainInstruction, -1, &rect,
+              DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT);
+    mainInstructionWidth = rect.right;
+    mainInstructionHeight = rect.bottom;
+
+    GetClientRect(GetDlgItem(hwnd, IDC_CONTENT), &rect);
+    rect.top = rect.left = rect.bottom = 0;
+    rect.right = max(rect.right, buttonAreaWidth);
+    SelectObject(hdc, hNormalFont);
+    DrawTextW(hdc, lpszContent, -1, &rect,
+              DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT);
+    contentTextWidth = rect.right;
+    contentTextHeight = rect.bottom;
+
+    ReleaseDC(hwnd, hdc);
+
+    mainAreaTextWidth = max(mainInstructionWidth, contentTextWidth);
+    windowClientWidth = max(TASKDIALOG_SPACING + buttonAreaWidth,
+                            mainAreaTextLeft + mainAreaTextWidth) + TASKDIALOG_SPACING;
+    if (config->cxWidth)
+        FIXME("Ignoring config->cxWidth.\n");
+
+    /* Position everything from top to bottom. */
+    currentY = TASKDIALOG_SPACING;
+
+    /* Position the main icon. */
+    if (hIconMain)
+        SetWindowPos(GetDlgItem(hwnd, IDC_MAIN_ICON), 0, mainIconLeft, currentY, 0, 0,
+                     SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
+    else DestroyWindow(GetDlgItem(hwnd, IDC_MAIN_ICON));
+
+    /* Position the texts in the main area. */
+    if (mainInstructionHeight)
+    {
+        SetWindowPos(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), 0, mainAreaTextLeft, currentY,
+                     mainInstructionWidth, mainInstructionHeight,
+                     SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
+        currentY += mainInstructionHeight + TASKDIALOG_SPACING;
+    }
+    else DestroyWindow(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION));
+
+    if (contentTextHeight)
+    {
+        SetWindowPos(GetDlgItem(hwnd, IDC_CONTENT), 0, mainAreaTextLeft, currentY,
+                     contentTextWidth, contentTextHeight,
+                     SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
+        currentY += contentTextHeight + TASKDIALOG_SPACING;
+    }
+    else DestroyWindow(GetDlgItem(hwnd, IDC_CONTENT));
+
+    if (mainIconHeight)
+        currentY = max(currentY, TASKDIALOG_SPACING + mainIconHeight + TASKDIALOG_SPACING);
+
+    /* Position the buttons: right-aligned */
+    currentX = windowClientWidth - TASKDIALOG_SPACING - buttonAreaWidth;
+    for (i = 0; i < (sizeof(commonButtons) / sizeof(commonButtons[0])); i++)
+    {
+        hItem = GetDlgItem(hwnd, commonButtons[i]);
+        if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE)
+        {
+            if (hDefaultButton == 0 || config->nDefaultButton == commonButtons[i])
+                hDefaultButton = hItem;
+            SetWindowPos(hItem, 0, currentX, currentY, buttonWidth, buttonHeight,
+                         SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
+            currentX += buttonWidth + TASKDIALOG_SPACING;
+        }
+    }
+
+    if (hDefaultButton)
+    {
+        SetFocus(hDefaultButton);
+        SendMessageW(hDefaultButton, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
+    }
+
+    currentY += buttonHeight + TASKDIALOG_SPACING;
+    windowClientHeight = currentY;
+
+    /* Query parent window/desktop size and center window. */
+    monitor = MonitorFromWindow(config->hwndParent ? config->hwndParent : GetActiveWindow(),
+                                MONITOR_DEFAULTTOPRIMARY);
+    mon_info.cbSize = sizeof(mon_info);
+    GetMonitorInfoW(monitor, &mon_info);
+    windowLeft = (mon_info.rcWork.left + mon_info.rcWork.right
+                  - (windowClientWidth + windowBorderWidth)) / 2;
+    windowTop = (mon_info.rcWork.top + mon_info.rcWork.bottom
+                 - (windowClientHeight + windowBorderHeight)) / 2;
+    SetWindowPos(hwnd, 0, windowLeft, windowTop,
+                 windowClientWidth + windowBorderWidth, windowClientHeight + windowBorderHeight,
+                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
+
+    DeleteObject(hMainInstructionFont);
+    Free(contentTextBuffer);
+    Free(mainInstructionBuffer);
+}
+
+
+/**************************************************************************
+ *           TASKDIALOG_DlgProc
+ *
+ * Dialog procedure for task dialogs.
+ */
+static INT_PTR CALLBACK TASKDIALOG_DlgProc(HWND hwnd, UINT message,
+                                           WPARAM wParam, LPARAM lParam)
+{
+    switch(message) {
+    case WM_INITDIALOG:
+    {
+        TASKDIALOGCONFIG *config = (TASKDIALOGCONFIG*)lParam;
+        TASKDIALOG_OnInit(hwnd, config);
+        break;
+    }
+
+    case WM_COMMAND:
+    {
+        if (LOWORD(wParam) == IDCANCEL)
+            /* IDCANCEL button must be present or TDF_ALLOW_DIALOG_CANCELLATION flag set. */
+            FIXME("Not checking if dialog cancellation is allowed\n");
+        EndDialog(hwnd, wParam);
+        break;
+    }
+
+    default:
+        /* Ignore all the other messages. */
+        TRACE("Message number 0x%04x is being ignored.\n", message);
+        break;
+    }
+    return 0;
+}
+
 /***********************************************************************
  * TaskDialogIndirect [COMCTL32.@]
  */
 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton,
                                   int *pnRadioButton, BOOL *pfVerificationFlagChecked)
 {
-    UINT uType = 0;
-    INT  ret;
-    FIXME("%p, %p, %p, %p\n", pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
-
-    if (pTaskConfig->dwCommonButtons & TDCBF_YES_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_NO_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
-        uType |= MB_YESNOCANCEL;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_YES_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_NO_BUTTON)
-        uType |= MB_YESNO;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_RETRY_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
-        uType |= MB_RETRYCANCEL;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_OK_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
-        uType |= MB_OKCANCEL;
+    LPVOID template;
+    HRSRC hRes;
+    INT_PTR ret;
+
+    if (!pTaskConfig) {
+        TRACE("pTaskConfig = NULL\n");
+        return E_INVALIDARG;
+    }
+
+    TRACE("hWndParent=%p, hInstance=%p, dwCommonButtons=%x, pnButton=%p\n",
+          pTaskConfig->hwndParent, pTaskConfig->hInstance, pTaskConfig->dwCommonButtons,
+          pnButton);
+
+    if (!(hRes = FindResourceExW(COMCTL32_hModule, (LPWSTR)RT_DIALOG,
+                                 MAKEINTRESOURCEW(IDD_TASKDIALOG), LANG_NEUTRAL)))
+    {
+        ERR("Cannot find TASKDIALOG resource\n");
+        return E_FAIL;
+    }
+    if (!(template = LoadResource(COMCTL32_hModule, hRes)))
+    {
+        ERR("Cannot load TASKDIALOG resource\n");
+        return E_FAIL;
+    }
+
+    ret = DialogBoxIndirectParamW(pTaskConfig->hInstance, template,
+                                  pTaskConfig->hwndParent, TASKDIALOG_DlgProc, (LPARAM)pTaskConfig);
+    if (pnRadioButton)
+    {
+        FIXME("Task dialog radio buttons not implemented.\n");
+        *pnRadioButton = pTaskConfig->nDefaultButton;
+    }
+    if (pfVerificationFlagChecked)
+    {
+        FIXME("Task dialog verification check box not implemented.\n");
+        *pfVerificationFlagChecked = TRUE;
+    }
+    if (ret > 0)
+    {
+        if (pnButton) *pnButton = ret;
+        return S_OK;
+    }
     else
-    if (pTaskConfig->dwCommonButtons & TDCBF_OK_BUTTON)
-        uType |= MB_OK;
-    ret = MessageBoxW(pTaskConfig->hwndParent, pTaskConfig->pszMainInstruction,
-                      pTaskConfig->pszWindowTitle, uType);
-    FIXME("dwCommonButtons=%x uType=%x ret=%x\n", pTaskConfig->dwCommonButtons, uType, ret);
-
-    if (pnButton) *pnButton = ret;
-    if (pnRadioButton) *pnRadioButton = pTaskConfig->nDefaultButton;
-    if (pfVerificationFlagChecked) *pfVerificationFlagChecked = TRUE;
-    return S_OK;
+    {
+        if (pnButton) *pnButton = 0;
+        /* DialogBoxIndirect returns -1 on failure. */
+        return ret == -1 ? E_FAIL : ret;
+    }
 }
-- 
1.8.4.5




More information about the wine-patches mailing list