2017-03-17 15:38:05 +01:00
|
|
|
/*
|
|
|
|
* Task dialog control
|
|
|
|
*
|
|
|
|
* Copyright 2017 Fabian Maurer
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
2017-06-11 21:11:46 +02:00
|
|
|
#include <stdlib.h>
|
2017-03-17 15:38:05 +01:00
|
|
|
#include <string.h>
|
|
|
|
|
2017-06-01 08:38:14 +02:00
|
|
|
#define NONAMELESSUNION
|
|
|
|
|
2017-03-17 15:38:05 +01:00
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
2017-03-21 15:31:20 +01:00
|
|
|
#include "wingdi.h"
|
2017-03-17 15:38:05 +01:00
|
|
|
#include "winuser.h"
|
|
|
|
#include "commctrl.h"
|
|
|
|
#include "winerror.h"
|
|
|
|
#include "comctl32.h"
|
2017-03-21 15:31:20 +01:00
|
|
|
|
2017-03-17 15:38:05 +01:00
|
|
|
#include "wine/debug.h"
|
2017-03-21 15:31:20 +01:00
|
|
|
#include "wine/list.h"
|
|
|
|
#include "wine/unicode.h"
|
2017-03-17 15:38:05 +01:00
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
#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)
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
static const UINT DIALOG_MIN_WIDTH = 240;
|
2017-03-21 15:31:20 +01:00
|
|
|
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;
|
2017-05-24 19:43:25 +02:00
|
|
|
static const UINT ID_CONTENT = 0xf001;
|
2017-03-21 15:31:20 +01:00
|
|
|
|
|
|
|
struct taskdialog_control
|
|
|
|
{
|
|
|
|
struct list entry;
|
|
|
|
DLGITEMTEMPLATE *template;
|
|
|
|
unsigned int template_size;
|
|
|
|
};
|
|
|
|
|
2017-11-06 12:40:38 +01:00
|
|
|
struct taskdialog_button_desc
|
|
|
|
{
|
|
|
|
int id;
|
|
|
|
const WCHAR *text;
|
|
|
|
unsigned int width;
|
|
|
|
unsigned int line;
|
|
|
|
HINSTANCE hinst;
|
|
|
|
};
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
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;
|
2017-11-06 12:40:38 +01:00
|
|
|
struct taskdialog_button_desc *default_button;
|
2017-06-11 21:11:46 +02:00
|
|
|
};
|
|
|
|
|
2017-09-20 13:50:01 +02:00
|
|
|
struct taskdialog_info
|
|
|
|
{
|
|
|
|
HWND hwnd;
|
|
|
|
PFTASKDIALOGCALLBACK callback;
|
|
|
|
LONG_PTR callback_data;
|
|
|
|
};
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
/* used to calculate size for the controls */
|
|
|
|
static void taskdialog_get_text_extent(const struct taskdialog_template_desc *desc, const WCHAR *text,
|
|
|
|
BOOL user_resource, SIZE *sz)
|
|
|
|
{
|
|
|
|
RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */
|
|
|
|
const WCHAR *textW = NULL;
|
|
|
|
static const WCHAR nulW;
|
|
|
|
unsigned int length;
|
|
|
|
HFONT oldfont;
|
|
|
|
HDC hdc;
|
|
|
|
|
|
|
|
if (IS_INTRESOURCE(text))
|
|
|
|
{
|
|
|
|
if (!(length = LoadStringW(user_resource ? desc->taskconfig->hInstance : COMCTL32_hModule,
|
|
|
|
(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);
|
|
|
|
|
|
|
|
sz->cx = rect.right - rect.left;
|
|
|
|
sz->cy = rect.bottom - rect.top;
|
|
|
|
}
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc, WORD id, const WCHAR *class,
|
2017-11-06 12:40:38 +01:00
|
|
|
HINSTANCE hInstance, const WCHAR *text, DWORD style, short x, short y, short cx, short cy)
|
2017-03-21 15:31:20 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2017-11-06 12:40:38 +01:00
|
|
|
template->style = WS_VISIBLE | style;
|
2017-03-21 15:31:20 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-05-24 19:43:25 +02:00
|
|
|
static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc *desc, WORD id, const WCHAR *str)
|
2017-03-21 15:31:20 +01:00
|
|
|
{
|
2017-06-11 21:11:46 +02:00
|
|
|
unsigned int size;
|
|
|
|
SIZE sz;
|
2017-03-21 15:31:20 +01:00
|
|
|
|
2017-05-24 19:43:25 +02:00
|
|
|
if (!str)
|
2017-03-21 15:31:20 +01:00
|
|
|
return 0;
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
taskdialog_get_text_extent(desc, str, TRUE, &sz);
|
2017-03-21 15:31:20 +01:00
|
|
|
|
2017-05-24 19:43:25 +02:00
|
|
|
desc->dialog_height += DIALOG_SPACING;
|
2017-11-06 12:40:38 +01:00
|
|
|
size = taskdialog_add_control(desc, id, WC_STATICW, desc->taskconfig->hInstance, str, 0, DIALOG_SPACING,
|
2017-06-11 21:11:46 +02:00
|
|
|
desc->dialog_height, sz.cx, sz.cy);
|
|
|
|
desc->dialog_height += sz.cy + DIALOG_SPACING;
|
2017-03-21 15:31:20 +01:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2017-05-24 19:43:25 +02:00
|
|
|
static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc)
|
|
|
|
{
|
|
|
|
return taskdialog_add_static_label(desc, ID_MAIN_INSTRUCTION, desc->taskconfig->pszMainInstruction);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc)
|
|
|
|
{
|
|
|
|
return taskdialog_add_static_label(desc, ID_CONTENT, desc->taskconfig->pszContent);
|
|
|
|
}
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
static void taskdialog_init_button(struct taskdialog_button_desc *button, struct taskdialog_template_desc *desc,
|
|
|
|
int id, const WCHAR *text, BOOL custom_button)
|
|
|
|
{
|
|
|
|
SIZE sz;
|
|
|
|
|
|
|
|
taskdialog_get_text_extent(desc, text, custom_button, &sz);
|
|
|
|
|
|
|
|
button->id = id;
|
|
|
|
button->text = text;
|
|
|
|
button->width = max(DIALOG_BUTTON_WIDTH, sz.cx + DIALOG_SPACING * 2);
|
|
|
|
button->line = 0;
|
|
|
|
button->hinst = custom_button ? desc->taskconfig->hInstance : COMCTL32_hModule;
|
2017-11-06 12:40:38 +01:00
|
|
|
|
|
|
|
if (id == desc->taskconfig->nDefaultButton)
|
|
|
|
desc->default_button = button;
|
2017-06-11 21:11:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons,
|
|
|
|
unsigned int *button_count)
|
2017-03-21 15:31:20 +01:00
|
|
|
{
|
|
|
|
DWORD flags = desc->taskconfig->dwCommonButtons;
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
#define TASKDIALOG_INIT_COMMON_BUTTON(id) \
|
2017-03-21 15:31:20 +01:00
|
|
|
do { \
|
2017-06-11 21:11:46 +02:00
|
|
|
taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \
|
2017-03-21 15:31:20 +01:00
|
|
|
} while(0)
|
2017-06-11 21:11:46 +02:00
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
if (flags & TDCBF_OK_BUTTON)
|
2017-06-11 21:11:46 +02:00
|
|
|
TASKDIALOG_INIT_COMMON_BUTTON(OK);
|
|
|
|
if (flags & TDCBF_YES_BUTTON)
|
|
|
|
TASKDIALOG_INIT_COMMON_BUTTON(YES);
|
|
|
|
if (flags & TDCBF_NO_BUTTON)
|
|
|
|
TASKDIALOG_INIT_COMMON_BUTTON(NO);
|
|
|
|
if (flags & TDCBF_RETRY_BUTTON)
|
|
|
|
TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
|
|
|
|
if (flags & TDCBF_CANCEL_BUTTON)
|
|
|
|
TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
|
|
|
|
if (flags & TDCBF_CLOSE_BUTTON)
|
|
|
|
TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
|
|
|
|
|
|
|
|
#undef TASKDIALOG_INIT_COMMON_BUTTON
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc)
|
|
|
|
{
|
|
|
|
unsigned int count = 0, buttons_size, i, line_count, size = 0;
|
|
|
|
unsigned int location_x, *line_widths, alignment = ~0u;
|
|
|
|
const TASKDIALOGCONFIG *taskconfig = desc->taskconfig;
|
|
|
|
struct taskdialog_button_desc *buttons;
|
|
|
|
|
|
|
|
/* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
|
|
|
|
buttons_size = 6;
|
|
|
|
if (taskconfig->cButtons && taskconfig->pButtons)
|
|
|
|
buttons_size += taskconfig->cButtons;
|
|
|
|
|
|
|
|
if (!(buttons = Alloc(buttons_size * sizeof(*buttons))))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Custom buttons */
|
|
|
|
if (taskconfig->cButtons && taskconfig->pButtons)
|
|
|
|
for (i = 0; i < taskconfig->cButtons; i++)
|
|
|
|
taskdialog_init_button(&buttons[count++], desc, taskconfig->pButtons[i].nButtonID,
|
|
|
|
taskconfig->pButtons[i].pszButtonText, TRUE);
|
|
|
|
|
|
|
|
/* Common buttons */
|
|
|
|
taskdialog_init_common_buttons(desc, buttons, &count);
|
|
|
|
|
|
|
|
/* There must be at least one button */
|
|
|
|
if (count == 0)
|
|
|
|
taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE);
|
|
|
|
|
2017-11-06 12:40:38 +01:00
|
|
|
if (!desc->default_button)
|
|
|
|
desc->default_button = &buttons[0];
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
/* For easy handling just allocate as many lines as buttons, the worst case. */
|
|
|
|
line_widths = Alloc(count * sizeof(*line_widths));
|
|
|
|
|
|
|
|
/* Separate buttons into lines */
|
|
|
|
location_x = DIALOG_SPACING;
|
|
|
|
for (i = 0, line_count = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
if (location_x + buttons[i].width + DIALOG_SPACING > desc->dialog_width)
|
|
|
|
{
|
|
|
|
location_x = DIALOG_SPACING;
|
|
|
|
line_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
buttons[i].line = line_count;
|
|
|
|
|
|
|
|
location_x += buttons[i].width + DIALOG_SPACING;
|
|
|
|
line_widths[line_count] += buttons[i].width + DIALOG_SPACING;
|
|
|
|
}
|
|
|
|
line_count++;
|
|
|
|
|
|
|
|
/* Try to balance lines so they are about the same size */
|
|
|
|
for (i = 1; i < line_count - 1; i++)
|
|
|
|
{
|
|
|
|
int diff_now = abs(line_widths[i] - line_widths[i - 1]);
|
|
|
|
unsigned int j, last_button = 0;
|
|
|
|
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 + line_widths[i] - line_widths[i - 1]);
|
|
|
|
|
|
|
|
if (diff_changed < diff_now)
|
|
|
|
{
|
|
|
|
buttons[last_button].line = i;
|
|
|
|
line_widths[i] += buttons[last_button].width;
|
|
|
|
line_widths[i - 1] -= buttons[last_button].width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate left alignment so all lines are as far right as possible. */
|
|
|
|
for (i = 0; i < line_count; i++)
|
|
|
|
{
|
|
|
|
int new_alignment = desc->dialog_width - line_widths[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++)
|
|
|
|
{
|
2017-11-06 12:40:38 +01:00
|
|
|
DWORD style = &buttons[i] == desc->default_button ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;
|
|
|
|
|
2017-06-11 21:11:46 +02:00
|
|
|
if (i > 0 && buttons[i].line != buttons[i - 1].line) /* New line */
|
|
|
|
{
|
|
|
|
location_x = alignment;
|
|
|
|
desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
|
|
|
|
}
|
|
|
|
|
2017-11-06 12:40:38 +01:00
|
|
|
size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, buttons[i].hinst, buttons[i].text, style,
|
|
|
|
location_x, desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
|
2017-06-11 21:11:46 +02:00
|
|
|
|
|
|
|
location_x += buttons[i].width + DIALOG_SPACING;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add height for last row and spacing */
|
|
|
|
desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
|
|
|
|
|
|
|
|
Free(line_widths);
|
|
|
|
Free(buttons);
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-10-02 15:56:57 +02:00
|
|
|
static WCHAR *taskdialog_get_exe_name(const TASKDIALOGCONFIG *taskconfig, WCHAR *name, DWORD length)
|
|
|
|
{
|
|
|
|
DWORD len = GetModuleFileNameW(NULL, name, length);
|
|
|
|
if (len && len < length)
|
|
|
|
{
|
|
|
|
WCHAR *p;
|
|
|
|
if ((p = strrchrW(name, '/'))) name = p + 1;
|
|
|
|
if ((p = strrchrW(name, '\\'))) name = p + 1;
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
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;
|
2017-10-02 15:56:57 +02:00
|
|
|
WCHAR pathW[MAX_PATH];
|
2017-03-21 15:31:20 +01:00
|
|
|
RECT ref_rect;
|
|
|
|
char *ptr;
|
|
|
|
HDC hdc;
|
|
|
|
|
|
|
|
/* Window title */
|
|
|
|
if (!taskconfig->pszWindowTitle)
|
2018-02-26 23:29:13 +01:00
|
|
|
titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
|
2017-03-21 15:31:20 +01:00
|
|
|
else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
|
2017-10-02 15:56:57 +02:00
|
|
|
{
|
|
|
|
if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
|
2018-02-26 23:29:13 +01:00
|
|
|
titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
|
2017-10-02 15:56:57 +02:00
|
|
|
}
|
2017-03-21 15:31:20 +01:00
|
|
|
else
|
|
|
|
titleW = taskconfig->pszWindowTitle;
|
|
|
|
if (!titleW)
|
|
|
|
titleW = emptyW;
|
|
|
|
title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
|
|
|
|
|
|
|
|
size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
|
2017-04-30 21:16:22 +02:00
|
|
|
size += title_size;
|
2017-03-21 15:31:20 +01:00
|
|
|
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);
|
|
|
|
|
2017-05-24 19:43:25 +02:00
|
|
|
desc.dialog_height = 0;
|
2017-03-21 15:31:20 +01:00
|
|
|
desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
|
|
|
|
desc.dialog_width = min(desc.dialog_width, screen_width);
|
2017-11-06 12:40:38 +01:00
|
|
|
desc.default_button = NULL;
|
2017-03-21 15:31:20 +01:00
|
|
|
|
|
|
|
size += taskdialog_add_main_instruction(&desc);
|
2017-05-24 19:43:25 +02:00
|
|
|
size += taskdialog_add_content(&desc);
|
2017-06-11 21:11:46 +02:00
|
|
|
size += taskdialog_add_buttons(&desc);
|
2017-03-21 15:31:20 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-09-20 13:50:01 +02:00
|
|
|
static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
|
|
|
|
{
|
|
|
|
return dialog_info->callback ? dialog_info->callback(dialog_info->hwnd, notification, wparam, lparam,
|
|
|
|
dialog_info->callback_data) : S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, WORD command_id)
|
|
|
|
{
|
|
|
|
if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
|
|
|
|
EndDialog(dialog_info->hwnd, command_id);
|
|
|
|
}
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
2017-09-20 13:50:01 +02:00
|
|
|
static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
|
|
|
|
struct taskdialog_info *dialog_info;
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
|
|
|
|
|
2017-09-20 13:50:01 +02:00
|
|
|
if (msg != WM_INITDIALOG)
|
|
|
|
dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
switch (msg)
|
|
|
|
{
|
2017-09-20 13:50:01 +02:00
|
|
|
case TDM_CLICK_BUTTON:
|
|
|
|
taskdialog_on_button_click(dialog_info, LOWORD(wParam));
|
|
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
dialog_info = (struct taskdialog_info *)lParam;
|
|
|
|
dialog_info->hwnd = hwnd;
|
|
|
|
SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
|
|
|
|
|
|
|
|
taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
|
|
|
|
break;
|
|
|
|
case WM_SHOWWINDOW:
|
|
|
|
taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
|
|
|
|
break;
|
2017-03-21 15:31:20 +01:00
|
|
|
case WM_COMMAND:
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED)
|
|
|
|
{
|
2017-09-20 13:50:01 +02:00
|
|
|
taskdialog_on_button_click(dialog_info, LOWORD(wParam));
|
2017-03-21 15:31:20 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
break;
|
2017-09-20 13:50:01 +02:00
|
|
|
case WM_DESTROY:
|
|
|
|
taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
|
|
|
|
RemovePropW(hwnd, taskdialog_info_propnameW);
|
|
|
|
break;
|
2017-03-21 15:31:20 +01:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2017-03-17 15:38:05 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* TaskDialogIndirect [COMCTL32.@]
|
|
|
|
*/
|
|
|
|
HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
|
|
|
|
int *radio_button, BOOL *verification_flag_checked)
|
|
|
|
{
|
2017-09-20 13:50:01 +02:00
|
|
|
struct taskdialog_info dialog_info;
|
2017-03-21 15:31:20 +01:00
|
|
|
DLGTEMPLATE *template;
|
2017-03-17 15:38:05 +01:00
|
|
|
INT ret;
|
|
|
|
|
|
|
|
TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
|
|
|
|
|
2017-09-20 13:50:01 +02:00
|
|
|
if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
dialog_info.callback = taskconfig->pfCallback;
|
|
|
|
dialog_info.callback_data = taskconfig->lpCallbackData;
|
|
|
|
|
2017-03-21 15:31:20 +01:00
|
|
|
template = create_taskdialog_template(taskconfig);
|
2017-11-06 12:40:38 +01:00
|
|
|
ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
|
2017-09-20 13:50:01 +02:00
|
|
|
taskdialog_proc, (LPARAM)&dialog_info);
|
2017-03-21 15:31:20 +01:00
|
|
|
Free(template);
|
2017-03-17 15:38:05 +01:00
|
|
|
|
|
|
|
if (button) *button = ret;
|
|
|
|
if (radio_button) *radio_button = taskconfig->nDefaultButton;
|
|
|
|
if (verification_flag_checked) *verification_flag_checked = TRUE;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
2017-06-01 08:38:14 +02:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* TaskDialog [COMCTL32.@]
|
|
|
|
*/
|
|
|
|
HRESULT WINAPI TaskDialog(HWND owner, HINSTANCE hinst, const WCHAR *title, const WCHAR *main_instruction,
|
|
|
|
const WCHAR *content, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons, const WCHAR *icon, int *button)
|
|
|
|
{
|
|
|
|
TASKDIALOGCONFIG taskconfig;
|
|
|
|
|
|
|
|
TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner, hinst, debugstr_w(title), debugstr_w(main_instruction),
|
|
|
|
debugstr_w(content), common_buttons, debugstr_w(icon), button);
|
|
|
|
|
|
|
|
memset(&taskconfig, 0, sizeof(taskconfig));
|
|
|
|
taskconfig.cbSize = sizeof(taskconfig);
|
|
|
|
taskconfig.hwndParent = owner;
|
|
|
|
taskconfig.hInstance = hinst;
|
|
|
|
taskconfig.dwCommonButtons = common_buttons;
|
|
|
|
taskconfig.pszWindowTitle = title;
|
|
|
|
taskconfig.u.pszMainIcon = icon;
|
|
|
|
taskconfig.pszMainInstruction = main_instruction;
|
|
|
|
taskconfig.pszContent = content;
|
|
|
|
return TaskDialogIndirect(&taskconfig, button, NULL, NULL);
|
|
|
|
}
|