comctl32/taskdialog: Initial implementation of a minimal task dialog.
Based on patches by Fabian Maurer. Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
134c83203a
commit
995b6900a7
|
@ -99,6 +99,14 @@ extern HBRUSH COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN;
|
|||
#define IDI_TT_WARN_SM 25
|
||||
#define IDI_TT_ERROR_SM 28
|
||||
|
||||
/* Taskdialog strings */
|
||||
#define IDS_BUTTON_YES 3000
|
||||
#define IDS_BUTTON_NO 3001
|
||||
#define IDS_BUTTON_RETRY 3002
|
||||
#define IDS_BUTTON_OK 3003
|
||||
#define IDS_BUTTON_CANCEL 3004
|
||||
#define IDS_BUTTON_CLOSE 3005
|
||||
|
||||
typedef struct
|
||||
{
|
||||
COLORREF clrBtnHighlight; /* COLOR_BTNHIGHLIGHT */
|
||||
|
|
|
@ -46,6 +46,16 @@ STRINGTABLE
|
|||
HKY_NONE "#msgctxt#hotkey#None"
|
||||
}
|
||||
|
||||
STRINGTABLE
|
||||
{
|
||||
IDS_BUTTON_YES "&Yes"
|
||||
IDS_BUTTON_NO "&No"
|
||||
IDS_BUTTON_RETRY "&Retry"
|
||||
IDS_BUTTON_OK "OK"
|
||||
IDS_BUTTON_CANCEL "Cancel"
|
||||
IDS_BUTTON_CLOSE "&Close"
|
||||
}
|
||||
|
||||
IDD_PROPSHEET DIALOG 0, 0, 220, 140
|
||||
STYLE DS_CONTEXTHELP | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
|
||||
CAPTION "Properties for %s"
|
||||
|
|
|
@ -24,49 +24,349 @@
|
|||
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "wingdi.h"
|
||||
#include "winuser.h"
|
||||
#include "commctrl.h"
|
||||
#include "winerror.h"
|
||||
#include "comctl32.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
#include "wine/list.h"
|
||||
#include "wine/unicode.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
|
||||
|
||||
#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
|
||||
#define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
|
||||
#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_SPACING = 5;
|
||||
static const UINT DIALOG_BUTTON_WIDTH = 50;
|
||||
static const UINT DIALOG_BUTTON_HEIGHT = 14;
|
||||
|
||||
static const UINT ID_MAIN_INSTRUCTION = 0xf000;
|
||||
|
||||
struct taskdialog_control
|
||||
{
|
||||
struct list entry;
|
||||
DLGITEMTEMPLATE *template;
|
||||
unsigned int template_size;
|
||||
};
|
||||
|
||||
struct taskdialog_template_desc
|
||||
{
|
||||
const TASKDIALOGCONFIG *taskconfig;
|
||||
unsigned int dialog_height;
|
||||
unsigned int dialog_width;
|
||||
struct list controls;
|
||||
WORD control_count;
|
||||
LONG x_baseunit;
|
||||
LONG y_baseunit;
|
||||
HFONT font;
|
||||
};
|
||||
|
||||
static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
|
||||
{
|
||||
if (width)
|
||||
*width = MulDiv(*width, 4, desc->x_baseunit);
|
||||
if (height)
|
||||
*height = MulDiv(*height, 8, desc->y_baseunit);
|
||||
}
|
||||
|
||||
static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
|
||||
{
|
||||
if (width)
|
||||
*width = MulDiv(*width, desc->x_baseunit, 4);
|
||||
if (height)
|
||||
*height = MulDiv(*height, desc->y_baseunit, 8);
|
||||
}
|
||||
|
||||
static void template_write_data(char **ptr, const void *src, unsigned int size)
|
||||
{
|
||||
memcpy(*ptr, src, size);
|
||||
*ptr += 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)
|
||||
{
|
||||
struct taskdialog_control *control = Alloc(sizeof(*control));
|
||||
unsigned int size, class_size, text_size;
|
||||
DLGITEMTEMPLATE *template;
|
||||
static const WCHAR nulW;
|
||||
const WCHAR *textW;
|
||||
char *ptr;
|
||||
|
||||
class_size = (strlenW(class) + 1) * sizeof(WCHAR);
|
||||
|
||||
if (IS_INTRESOURCE(text))
|
||||
text_size = LoadStringW(hInstance, (UINT_PTR)text, (WCHAR *)&textW, 0) * sizeof(WCHAR);
|
||||
else
|
||||
{
|
||||
textW = text;
|
||||
text_size = strlenW(textW) * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
size = sizeof(DLGITEMTEMPLATE);
|
||||
size += class_size;
|
||||
size += text_size + sizeof(WCHAR);
|
||||
size += sizeof(WORD); /* creation data */
|
||||
|
||||
control->template = template = Alloc(size);
|
||||
control->template_size = size;
|
||||
|
||||
template->style = WS_VISIBLE;
|
||||
template->dwExtendedStyle = 0;
|
||||
template->x = x;
|
||||
template->y = y;
|
||||
template->cx = cx;
|
||||
template->cy = cy;
|
||||
template->id = id;
|
||||
ptr = (char *)(template + 1);
|
||||
template_write_data(&ptr, class, class_size);
|
||||
template_write_data(&ptr, textW, text_size);
|
||||
template_write_data(&ptr, &nulW, sizeof(nulW));
|
||||
|
||||
list_add_tail(&desc->controls, &control->entry);
|
||||
desc->control_count++;
|
||||
return ALIGNED_LENGTH(size, 3);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
size = taskdialog_add_control(desc, ID_MAIN_INSTRUCTION, WC_STATICW, desc->taskconfig->hInstance,
|
||||
desc->taskconfig->pszMainInstruction, DIALOG_SPACING, desc->dialog_height,
|
||||
rect.right, rect.bottom);
|
||||
desc->dialog_height += rect.bottom;
|
||||
return size;
|
||||
}
|
||||
|
||||
static unsigned int taskdialog_add_common_buttons(struct taskdialog_template_desc *desc)
|
||||
{
|
||||
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) \
|
||||
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);
|
||||
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;
|
||||
return size;
|
||||
}
|
||||
|
||||
static void taskdialog_clear_controls(struct list *controls)
|
||||
{
|
||||
struct taskdialog_control *control, *control2;
|
||||
|
||||
LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, struct taskdialog_control, entry)
|
||||
{
|
||||
list_remove(&control->entry);
|
||||
Free(control->template);
|
||||
Free(control);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int taskdialog_get_reference_rect(const struct taskdialog_template_desc *desc, RECT *ret)
|
||||
{
|
||||
HMONITOR monitor = MonitorFromWindow(desc->taskconfig->hwndParent ? desc->taskconfig->hwndParent : GetActiveWindow(),
|
||||
MONITOR_DEFAULTTOPRIMARY);
|
||||
MONITORINFO info;
|
||||
|
||||
info.cbSize = sizeof(info);
|
||||
GetMonitorInfoW(monitor, &info);
|
||||
|
||||
if (desc->taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && desc->taskconfig->hwndParent)
|
||||
GetWindowRect(desc->taskconfig->hwndParent, ret);
|
||||
else
|
||||
*ret = info.rcWork;
|
||||
|
||||
pixels_to_dialogunits(desc, &ret->left, &ret->top);
|
||||
pixels_to_dialogunits(desc, &ret->right, &ret->bottom);
|
||||
|
||||
pixels_to_dialogunits(desc, &info.rcWork.left, &info.rcWork.top);
|
||||
pixels_to_dialogunits(desc, &info.rcWork.right, &info.rcWork.bottom);
|
||||
return info.rcWork.right - info.rcWork.left;
|
||||
}
|
||||
|
||||
static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
|
||||
{
|
||||
struct taskdialog_control *control, *control2;
|
||||
unsigned int size, title_size, screen_width;
|
||||
struct taskdialog_template_desc desc;
|
||||
static const WORD fontsize = 0x7fff;
|
||||
static const WCHAR emptyW[] = { 0 };
|
||||
const WCHAR *titleW = NULL;
|
||||
DLGTEMPLATE *template;
|
||||
NONCLIENTMETRICSW ncm;
|
||||
RECT ref_rect;
|
||||
char *ptr;
|
||||
HDC hdc;
|
||||
|
||||
/* Window title */
|
||||
if (!taskconfig->pszWindowTitle)
|
||||
FIXME("use executable name for window title\n");
|
||||
else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
|
||||
FIXME("load window title from resources\n");
|
||||
else
|
||||
titleW = taskconfig->pszWindowTitle;
|
||||
if (!titleW)
|
||||
titleW = emptyW;
|
||||
title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
|
||||
|
||||
size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
|
||||
if (titleW)
|
||||
size += title_size;
|
||||
size += 2; /* font size */
|
||||
|
||||
list_init(&desc.controls);
|
||||
desc.taskconfig = taskconfig;
|
||||
desc.control_count = 0;
|
||||
|
||||
ncm.cbSize = sizeof(ncm);
|
||||
SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
|
||||
desc.font = CreateFontIndirectW(&ncm.lfMessageFont);
|
||||
|
||||
hdc = GetDC(0);
|
||||
SelectObject(hdc, desc.font);
|
||||
desc.x_baseunit = GdiGetCharDimensions(hdc, NULL, &desc.y_baseunit);
|
||||
ReleaseDC(0, hdc);
|
||||
|
||||
screen_width = taskdialog_get_reference_rect(&desc, &ref_rect);
|
||||
|
||||
desc.dialog_height = DIALOG_SPACING;
|
||||
desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
|
||||
desc.dialog_width = min(desc.dialog_width, screen_width);
|
||||
|
||||
size += taskdialog_add_main_instruction(&desc);
|
||||
size += taskdialog_add_common_buttons(&desc);
|
||||
|
||||
template = Alloc(size);
|
||||
if (!template)
|
||||
{
|
||||
taskdialog_clear_controls(&desc.controls);
|
||||
DeleteObject(desc.font);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
|
||||
template->cdit = desc.control_count;
|
||||
template->x = (ref_rect.left + ref_rect.right + desc.dialog_width) / 2;
|
||||
template->y = (ref_rect.top + ref_rect.bottom + desc.dialog_height) / 2;
|
||||
template->cx = desc.dialog_width;
|
||||
template->cy = desc.dialog_height;
|
||||
|
||||
ptr = (char *)(template + 1);
|
||||
ptr += 2; /* menu */
|
||||
ptr += 2; /* class */
|
||||
template_write_data(&ptr, titleW, title_size);
|
||||
template_write_data(&ptr, &fontsize, sizeof(fontsize));
|
||||
|
||||
/* write control entries */
|
||||
LIST_FOR_EACH_ENTRY_SAFE(control, control2, &desc.controls, struct taskdialog_control, entry)
|
||||
{
|
||||
ALIGN_POINTER(ptr, 3);
|
||||
|
||||
template_write_data(&ptr, control->template, control->template_size);
|
||||
|
||||
/* list item won't be needed later */
|
||||
list_remove(&control->entry);
|
||||
Free(control->template);
|
||||
Free(control);
|
||||
}
|
||||
|
||||
DeleteObject(desc.font);
|
||||
return template;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
if (HIWORD(wParam) == BN_CLICKED)
|
||||
{
|
||||
WORD command_id = LOWORD(wParam);
|
||||
EndDialog(hwnd, command_id);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* TaskDialogIndirect [COMCTL32.@]
|
||||
*/
|
||||
HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
|
||||
int *radio_button, BOOL *verification_flag_checked)
|
||||
{
|
||||
UINT type = 0;
|
||||
DLGTEMPLATE *template;
|
||||
INT ret;
|
||||
|
||||
TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
|
||||
|
||||
if (taskconfig->dwCommonButtons & TDCBF_YES_BUTTON &&
|
||||
taskconfig->dwCommonButtons & TDCBF_NO_BUTTON &&
|
||||
taskconfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
|
||||
type |= MB_YESNOCANCEL;
|
||||
else
|
||||
if (taskconfig->dwCommonButtons & TDCBF_YES_BUTTON &&
|
||||
taskconfig->dwCommonButtons & TDCBF_NO_BUTTON)
|
||||
type |= MB_YESNO;
|
||||
else
|
||||
if (taskconfig->dwCommonButtons & TDCBF_RETRY_BUTTON &&
|
||||
taskconfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
|
||||
type |= MB_RETRYCANCEL;
|
||||
else
|
||||
if (taskconfig->dwCommonButtons & TDCBF_OK_BUTTON &&
|
||||
taskconfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
|
||||
type |= MB_OKCANCEL;
|
||||
else
|
||||
if (taskconfig->dwCommonButtons & TDCBF_OK_BUTTON)
|
||||
type |= MB_OK;
|
||||
|
||||
ret = MessageBoxW(taskconfig->hwndParent, taskconfig->pszMainInstruction,
|
||||
taskconfig->pszWindowTitle, type);
|
||||
|
||||
FIXME("dwCommonButtons=%x type=%x ret=%x\n", taskconfig->dwCommonButtons, type, ret);
|
||||
template = create_taskdialog_template(taskconfig);
|
||||
ret = DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent, taskdialog_proc, 0);
|
||||
Free(template);
|
||||
|
||||
if (button) *button = ret;
|
||||
if (radio_button) *radio_button = taskconfig->nDefaultButton;
|
||||
|
|
Loading…
Reference in New Issue