shell32: Use Public instead of AllUsersProfile in the registry.
[wine.git] / dlls / comctl32 / taskdialog.c
blobf69fa6aaa386568f41c69cbe4d521612cb3ee603
1 /*
2 * Task dialog control
4 * Copyright 2017 Fabian Maurer
5 * Copyright 2018 Zhiyi Zhang
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #define NONAMELESSUNION
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "commctrl.h"
34 #include "winerror.h"
35 #include "comctl32.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
42 static const UINT DIALOG_MIN_WIDTH = 240;
43 static const UINT DIALOG_SPACING = 5;
44 static const UINT DIALOG_BUTTON_WIDTH = 50;
45 static const UINT DIALOG_BUTTON_HEIGHT = 14;
46 static const UINT DIALOG_EXPANDO_ICON_WIDTH = 10;
47 static const UINT DIALOG_EXPANDO_ICON_HEIGHT = 10;
48 static const UINT DIALOG_TIMER_MS = 200;
50 static const UINT ID_TIMER = 1;
52 struct taskdialog_info
54 HWND hwnd;
55 const TASKDIALOGCONFIG *taskconfig;
56 DWORD last_timer_tick;
57 HFONT font;
58 HFONT main_instruction_font;
59 /* Control handles */
60 HWND main_icon;
61 HWND main_instruction;
62 HWND content;
63 HWND progress_bar;
64 HWND *radio_buttons;
65 INT radio_button_count;
66 HWND *command_links;
67 INT command_link_count;
68 HWND expanded_info;
69 HWND expando_button;
70 HWND verification_box;
71 HWND footer_icon;
72 HWND footer_text;
73 HWND *buttons;
74 INT button_count;
75 HWND default_button;
76 /* Dialog metrics */
77 struct
79 LONG x_baseunit;
80 LONG y_baseunit;
81 LONG h_spacing;
82 LONG v_spacing;
83 } m;
84 INT selected_radio_id;
85 BOOL verification_checked;
86 BOOL expanded;
87 WCHAR *expanded_text;
88 WCHAR *collapsed_text;
91 struct button_layout_info
93 LONG width;
94 LONG line;
97 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd);
98 static void taskdialog_layout(struct taskdialog_info *dialog_info);
100 static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
102 if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4);
103 if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8);
106 static void template_write_data(char **ptr, const void *src, unsigned int size)
108 memcpy(*ptr, src, size);
109 *ptr += size;
112 static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret)
114 HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(),
115 MONITOR_DEFAULTTOPRIMARY);
116 MONITORINFO info;
118 info.cbSize = sizeof(info);
119 GetMonitorInfoW(monitor, &info);
121 if (taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && taskconfig->hwndParent)
122 GetWindowRect(taskconfig->hwndParent, ret);
123 else
124 *ret = info.rcWork;
126 return info.rcWork.right - info.rcWork.left;
129 static WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length)
131 DWORD len = GetModuleFileNameW(NULL, name, length);
132 if (len && len < length)
134 WCHAR *p;
135 if ((p = strrchrW(name, '/'))) name = p + 1;
136 if ((p = strrchrW(name, '\\'))) name = p + 1;
137 return name;
139 else
140 return NULL;
143 static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
145 unsigned int size, title_size;
146 static const WORD fontsize = 0x7fff;
147 static const WCHAR emptyW[] = { 0 };
148 const WCHAR *titleW = NULL;
149 DLGTEMPLATE *template;
150 WCHAR pathW[MAX_PATH];
151 char *ptr;
153 /* Window title */
154 if (!taskconfig->pszWindowTitle)
155 titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
156 else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
158 if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
159 titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
161 else
162 titleW = taskconfig->pszWindowTitle;
163 if (!titleW)
164 titleW = emptyW;
165 title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
167 size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
168 size += title_size;
169 size += 2; /* font size */
171 template = Alloc(size);
172 if (!template) return NULL;
174 template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
175 if (taskconfig->dwFlags & TDF_CAN_BE_MINIMIZED) template->style |= WS_MINIMIZEBOX;
176 if (!(taskconfig->dwFlags & TDF_NO_SET_FOREGROUND)) template->style |= DS_SETFOREGROUND;
177 if (taskconfig->dwFlags & TDF_RTL_LAYOUT) template->dwExtendedStyle = WS_EX_LAYOUTRTL | WS_EX_RIGHT | WS_EX_RTLREADING;
179 ptr = (char *)(template + 1);
180 ptr += 2; /* menu */
181 ptr += 2; /* class */
182 template_write_data(&ptr, titleW, title_size);
183 template_write_data(&ptr, &fontsize, sizeof(fontsize));
185 return template;
188 static HWND taskdialog_find_button(HWND *buttons, INT count, INT id)
190 INT button_id;
191 INT i;
193 for (i = 0; i < count; i++)
195 button_id = GetWindowLongW(buttons[i], GWLP_ID);
196 if (button_id == id) return buttons[i];
199 return NULL;
202 static void taskdialog_enable_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
204 HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
205 if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
206 if (hwnd) EnableWindow(hwnd, enable);
209 static void taskdialog_click_button(struct taskdialog_info *dialog_info, INT id)
211 HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
212 if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
213 if (hwnd) taskdialog_on_button_click(dialog_info, hwnd);
216 static void taskdialog_button_set_shield(const struct taskdialog_info *dialog_info, INT id, BOOL elevate)
218 HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
219 if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
220 if (hwnd) SendMessageW(hwnd, BCM_SETSHIELD, 0, elevate);
223 static void taskdialog_enable_radio_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
225 HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
226 if (hwnd) EnableWindow(hwnd, enable);
229 static void taskdialog_click_radio_button(const struct taskdialog_info *dialog_info, INT id)
231 HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
232 if (hwnd) SendMessageW(hwnd, BM_CLICK, 0, 0);
235 static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
237 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
238 return taskconfig->pfCallback
239 ? taskconfig->pfCallback(dialog_info->hwnd, notification, wparam, lparam, taskconfig->lpCallbackData)
240 : S_OK;
243 static void taskdialog_move_controls_vertically(HWND parent, HWND *controls, INT count, INT offset)
245 RECT rect;
246 POINT pt;
247 INT i;
249 for (i = 0; i < count; i++)
251 if (!controls[i]) continue;
253 GetWindowRect(controls[i], &rect);
254 pt.x = rect.left;
255 pt.y = rect.top;
256 MapWindowPoints(HWND_DESKTOP, parent, &pt, 1);
257 SetWindowPos(controls[i], 0, pt.x, pt.y + offset, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
261 static void taskdialog_toggle_expando_control(struct taskdialog_info *dialog_info)
263 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
264 const WCHAR *text;
265 RECT info_rect, rect;
266 INT height, offset;
268 dialog_info->expanded = !dialog_info->expanded;
269 text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
270 SendMessageW(dialog_info->expando_button, WM_SETTEXT, 0, (LPARAM)text);
271 ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
273 GetWindowRect(dialog_info->expanded_info, &info_rect);
274 /* If expanded information starts up not expanded, call taskdialog_layout()
275 * to to set size for expanded information control at least once */
276 if (IsRectEmpty(&info_rect))
278 taskdialog_layout(dialog_info);
279 return;
281 height = info_rect.bottom - info_rect.top + dialog_info->m.v_spacing;
282 offset = dialog_info->expanded ? height : -height;
284 /* Update vertical layout, move all controls after expanded information */
285 /* Move dialog */
286 GetWindowRect(dialog_info->hwnd, &rect);
287 SetWindowPos(dialog_info->hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top + offset,
288 SWP_NOMOVE | SWP_NOZORDER);
289 /* Move controls */
290 if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA))
292 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->progress_bar, 1, offset);
293 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->expando_button, 1, offset);
294 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->verification_box, 1, offset);
295 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_icon, 1, offset);
296 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_text, 1, offset);
297 taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->buttons, dialog_info->button_count, offset);
298 taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->radio_buttons,
299 dialog_info->radio_button_count, offset);
300 taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->command_links,
301 dialog_info->command_link_count, offset);
305 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd)
307 INT command_id = GetWindowLongW(hwnd, GWLP_ID);
308 HWND radio_button;
310 if (hwnd == dialog_info->expando_button)
312 taskdialog_toggle_expando_control(dialog_info);
313 taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
314 return;
317 if (hwnd == dialog_info->verification_box)
319 dialog_info->verification_checked = !dialog_info->verification_checked;
320 taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
321 return;
324 radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
325 if (radio_button)
327 dialog_info->selected_radio_id = command_id;
328 taskdialog_notify(dialog_info, TDN_RADIO_BUTTON_CLICKED, command_id, 0);
329 return;
332 if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
333 EndDialog(dialog_info->hwnd, command_id);
336 static WCHAR *taskdialog_gettext(struct taskdialog_info *dialog_info, BOOL user_resource, const WCHAR *text)
338 const WCHAR *textW = NULL;
339 INT length;
340 WCHAR *ret;
342 if (IS_INTRESOURCE(text))
344 if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule,
345 (UINT_PTR)text, (WCHAR *)&textW, 0)))
346 return NULL;
348 else
350 textW = text;
351 length = strlenW(textW);
354 ret = Alloc((length + 1) * sizeof(WCHAR));
355 if (ret) memcpy(ret, textW, length * sizeof(WCHAR));
357 return ret;
360 static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info)
362 return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS;
365 static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info)
367 return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON);
370 static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size,
371 BOOL syslink)
373 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
374 HFONT hfont, old_hfont;
375 HDC hdc;
376 RECT rect = {0};
377 WCHAR *text;
378 INT text_length;
380 if (syslink)
382 SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size);
383 return;
386 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
387 style |= DT_RIGHT | DT_RTLREADING;
388 else
389 style |= DT_LEFT;
391 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
392 text_length = GetWindowTextLengthW(hwnd);
393 text = Alloc((text_length + 1) * sizeof(WCHAR));
394 if (!text)
396 size->cx = 0;
397 size->cy = 0;
398 return;
400 GetWindowTextW(hwnd, text, text_length + 1);
401 hdc = GetDC(hwnd);
402 old_hfont = SelectObject(hdc, hfont);
403 rect.right = max_width;
404 size->cy = DrawTextW(hdc, text, text_length, &rect, style);
405 size->cx = min(max_width, rect.right - rect.left);
406 if (old_hfont) SelectObject(hdc, old_hfont);
407 ReleaseDC(hwnd, hdc);
408 Free(text);
411 static void taskdialog_get_radio_button_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size)
413 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
414 HFONT hfont, old_hfont;
415 HDC hdc;
416 RECT rect = {0};
417 INT text_length;
418 WCHAR *text;
419 INT text_offset, radio_box_width, radio_box_height;
421 hdc = GetDC(hwnd);
422 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
423 old_hfont = SelectObject(hdc, hfont);
425 radio_box_width = 12 * GetDpiForWindow(hwnd) / 96 + 1;
426 radio_box_height = 12 * GetDpiForWindow(hwnd) / 96 + 1;
427 GetCharWidthW(hdc, '0', '0', &text_offset);
428 text_offset /= 2;
430 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
431 style |= DT_RIGHT | DT_RTLREADING;
432 else
433 style |= DT_LEFT;
435 rect.right = max_width - radio_box_width - text_offset;
436 text_length = GetWindowTextLengthW(hwnd);
437 text = Alloc((text_length + 1) * sizeof(WCHAR));
438 if (!text)
440 size->cx = 0;
441 size->cy = 0;
442 return;
444 GetWindowTextW(hwnd, text, text_length + 1);
445 size->cy = DrawTextW(hdc, text, text_length, &rect, style);
446 size->cx = min(max_width - radio_box_width - text_offset, rect.right - rect.left);
447 size->cx += radio_box_width + text_offset;
448 size->cy = max(size->cy, radio_box_height);
449 if (old_hfont) SelectObject(hdc, old_hfont);
450 Free(text);
451 ReleaseDC(hwnd, hdc);
454 static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
456 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
457 HFONT hfont, old_hfont;
458 HDC hdc;
459 RECT rect = {0};
460 LONG icon_width, icon_height, text_offset;
461 LONG max_width, max_text_height;
463 hdc = GetDC(hwnd);
464 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
465 old_hfont = SelectObject(hdc, hfont);
467 icon_width = DIALOG_EXPANDO_ICON_WIDTH;
468 icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
469 taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
471 GetCharWidthW(hdc, '0', '0', &text_offset);
472 text_offset /= 2;
474 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
475 style |= DT_RIGHT | DT_RTLREADING;
476 else
477 style |= DT_LEFT;
479 max_width = DIALOG_MIN_WIDTH / 2;
480 taskdialog_du_to_px(dialog_info, &max_width, NULL);
482 rect.right = max_width - icon_width - text_offset;
483 max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
484 size->cy = max(max_text_height, icon_height);
485 size->cx = rect.right - rect.left;
487 rect.right = max_width - icon_width - text_offset;
488 max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
489 size->cy = max(size->cy, max_text_height);
490 size->cx = max(size->cx, rect.right - rect.left);
491 size->cx = min(size->cx, max_width);
493 if (old_hfont) SelectObject(hdc, old_hfont);
494 ReleaseDC(hwnd, hdc);
497 static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
499 if (icon == TD_WARNING_ICON)
500 return IDI_WARNING;
501 else if (icon == TD_ERROR_ICON)
502 return IDI_ERROR;
503 else if (icon == TD_INFORMATION_ICON)
504 return IDI_INFORMATION;
505 else if (icon == TD_SHIELD_ICON)
506 return IDI_SHIELD;
507 else
508 return (ULONG_PTR)icon;
511 static void taskdialog_set_icon(struct taskdialog_info *dialog_info, INT element, HICON icon)
513 DWORD flags = dialog_info->taskconfig->dwFlags;
514 INT cx = 0, cy = 0;
515 HICON hicon;
517 if (!icon) return;
519 if (((flags & TDF_USE_HICON_MAIN) && element == TDIE_ICON_MAIN)
520 || ((flags & TDF_USE_HICON_FOOTER) && element == TDIE_ICON_FOOTER))
521 hicon = icon;
522 else
524 if (element == TDIE_ICON_FOOTER)
526 cx = GetSystemMetrics(SM_CXSMICON);
527 cy = GetSystemMetrics(SM_CYSMICON);
529 hicon = LoadImageW(dialog_info->taskconfig->hInstance, (LPCWSTR)icon, IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTSIZE);
530 if (!hicon)
531 hicon = LoadImageW(NULL, (LPCWSTR)taskdialog_get_standard_icon((LPCWSTR)icon), IMAGE_ICON, cx, cy,
532 LR_SHARED | LR_DEFAULTSIZE);
535 if (!hicon) return;
537 if (element == TDIE_ICON_MAIN)
539 SendMessageW(dialog_info->hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hicon);
540 SendMessageW(dialog_info->main_icon, STM_SETICON, (WPARAM)hicon, 0);
542 else if (element == TDIE_ICON_FOOTER)
543 SendMessageW(dialog_info->footer_icon, STM_SETICON, (WPARAM)hicon, 0);
546 static void taskdialog_set_element_text(struct taskdialog_info *dialog_info, TASKDIALOG_ELEMENTS element,
547 const WCHAR *text)
549 HWND hwnd = NULL;
550 WCHAR *textW;
552 if (element == TDE_CONTENT)
553 hwnd = dialog_info->content;
554 else if (element == TDE_EXPANDED_INFORMATION)
555 hwnd = dialog_info->expanded_info;
556 else if (element == TDE_FOOTER)
557 hwnd = dialog_info->footer_text;
558 else if (element == TDE_MAIN_INSTRUCTION)
559 hwnd = dialog_info->main_instruction;
561 if (!hwnd) return;
563 textW = taskdialog_gettext(dialog_info, TRUE, text);
564 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
565 Free(textW);
568 static void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info)
570 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
571 HWND default_button;
573 if (!dialog_info->radio_button_count) return;
575 default_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count,
576 taskconfig->nDefaultRadioButton);
578 if (!default_button && !(taskconfig->dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON))
579 default_button = dialog_info->radio_buttons[0];
581 if (default_button)
583 SendMessageW(default_button, BM_SETCHECK, BST_CHECKED, 0);
584 taskdialog_on_button_click(dialog_info, default_button);
588 static void taskdialog_add_main_icon(struct taskdialog_info *dialog_info)
590 if (!dialog_info->taskconfig->u.hMainIcon) return;
592 dialog_info->main_icon =
593 CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
594 taskdialog_set_icon(dialog_info, TDIE_ICON_MAIN, dialog_info->taskconfig->u.hMainIcon);
597 static HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink)
599 WCHAR *textW;
600 HWND hwnd;
601 const WCHAR *class;
602 DWORD style = WS_CHILD | WS_VISIBLE;
604 if (!text) return NULL;
606 class = syslink ? WC_LINK : WC_STATICW;
607 if (syslink) style |= WS_TABSTOP;
608 textW = taskdialog_gettext(dialog_info, TRUE, text);
609 hwnd = CreateWindowW(class, textW, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
610 Free(textW);
612 SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0);
613 return hwnd;
616 static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info)
618 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
619 NONCLIENTMETRICSW ncm;
621 if (!taskconfig->pszMainInstruction) return;
623 ncm.cbSize = sizeof(ncm);
624 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
625 /* 1.25 times the height */
626 ncm.lfMessageFont.lfHeight = ncm.lfMessageFont.lfHeight * 5 / 4;
627 ncm.lfMessageFont.lfWeight = FW_BOLD;
628 dialog_info->main_instruction_font = CreateFontIndirectW(&ncm.lfMessageFont);
630 dialog_info->main_instruction =
631 taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE);
634 static void taskdialog_add_content(struct taskdialog_info *dialog_info)
636 dialog_info->content = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszContent, dialog_info->font,
637 taskdialog_hyperlink_enabled(dialog_info));
640 static void taskdialog_add_progress_bar(struct taskdialog_info *dialog_info)
642 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
643 DWORD style = PBS_SMOOTH | PBS_SMOOTHREVERSE | WS_CHILD | WS_VISIBLE;
645 if (!(taskconfig->dwFlags & (TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR))) return;
646 if (taskconfig->dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) style |= PBS_MARQUEE;
647 dialog_info->progress_bar =
648 CreateWindowW(PROGRESS_CLASSW, NULL, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
651 static void taskdialog_add_radio_buttons(struct taskdialog_info *dialog_info)
653 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
654 static const DWORD style = BS_AUTORADIOBUTTON | BS_MULTILINE | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
655 WCHAR *textW;
656 INT i;
658 if (!taskconfig->cRadioButtons || !taskconfig->pRadioButtons) return;
660 dialog_info->radio_buttons = Alloc(taskconfig->cRadioButtons * sizeof(*dialog_info->radio_buttons));
661 if (!dialog_info->radio_buttons) return;
663 dialog_info->radio_button_count = taskconfig->cRadioButtons;
664 for (i = 0; i < dialog_info->radio_button_count; i++)
666 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pRadioButtons[i].pszButtonText);
667 dialog_info->radio_buttons[i] =
668 CreateWindowW(WC_BUTTONW, textW, i == 0 ? style | WS_GROUP : style, 0, 0, 0, 0, dialog_info->hwnd,
669 ULongToHandle(taskconfig->pRadioButtons[i].nButtonID), 0, NULL);
670 SendMessageW(dialog_info->radio_buttons[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
671 Free(textW);
675 static void taskdialog_add_command_links(struct taskdialog_info *dialog_info)
677 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
678 DWORD default_style = BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP, style;
679 BOOL is_default;
680 WCHAR *textW;
681 INT i;
683 if (!taskconfig->cButtons || !taskconfig->pButtons || !taskdialog_use_command_link(dialog_info)) return;
685 dialog_info->command_links = Alloc(taskconfig->cButtons * sizeof(*dialog_info->command_links));
686 if (!dialog_info->command_links) return;
688 dialog_info->command_link_count = taskconfig->cButtons;
689 for (i = 0; i < dialog_info->command_link_count; i++)
691 is_default = taskconfig->pButtons[i].nButtonID == taskconfig->nDefaultButton;
692 style = is_default ? default_style | BS_DEFCOMMANDLINK : default_style | BS_COMMANDLINK;
693 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pButtons[i].pszButtonText);
694 dialog_info->command_links[i] = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd,
695 ULongToHandle(taskconfig->pButtons[i].nButtonID), 0, NULL);
696 SendMessageW(dialog_info->command_links[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
697 Free(textW);
699 if (is_default && !dialog_info->default_button) dialog_info->default_button = dialog_info->command_links[i];
703 static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
705 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
707 if (!taskconfig->pszExpandedInformation) return;
709 dialog_info->expanded = taskconfig->dwFlags & TDF_EXPANDED_BY_DEFAULT;
710 dialog_info->expanded_info = taskdialog_create_label(dialog_info, taskconfig->pszExpandedInformation,
711 dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
712 ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
715 static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
717 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
718 const WCHAR *textW;
720 if (!taskconfig->pszExpandedInformation) return;
722 if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
724 dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
725 dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
727 else
729 textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
730 : taskconfig->pszCollapsedControlText;
731 dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
732 textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
733 : taskconfig->pszExpandedControlText;
734 dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
737 textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
739 dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
740 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
741 SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
744 static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
746 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
747 static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
748 WCHAR *textW;
750 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
751 if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
753 if (!taskconfig->pszVerificationText) return;
755 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
756 dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
757 SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
758 Free(textW);
760 if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
761 SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
764 static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
765 BOOL custom_button)
767 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
768 WCHAR *textW;
770 textW = taskdialog_gettext(dialog_info, custom_button, text);
771 *button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, dialog_info->hwnd,
772 (HMENU)id, 0, NULL);
773 Free(textW);
774 SendMessageW(*button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
776 if (id == taskconfig->nDefaultButton && !dialog_info->default_button) dialog_info->default_button = *button;
779 static void taskdialog_add_buttons(struct taskdialog_info *dialog_info)
781 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
782 BOOL use_command_links = taskdialog_use_command_link(dialog_info);
783 DWORD flags = taskconfig->dwCommonButtons;
784 INT count, max_count;
786 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
787 max_count = 6;
788 if (!use_command_links && taskconfig->cButtons && taskconfig->pButtons) max_count += taskconfig->cButtons;
790 dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons));
791 if (!dialog_info->buttons) return;
793 for (count = 0; !use_command_links && count < taskconfig->cButtons; count++)
794 taskdialog_add_button(dialog_info, &dialog_info->buttons[count], taskconfig->pButtons[count].nButtonID,
795 taskconfig->pButtons[count].pszButtonText, TRUE);
797 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
798 do \
800 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
801 FALSE); \
802 } while (0)
804 if (flags & TDCBF_OK_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(OK);
805 if (flags & TDCBF_YES_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(YES);
806 if (flags & TDCBF_NO_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(NO);
807 if (flags & TDCBF_RETRY_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
808 if (flags & TDCBF_CANCEL_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
809 if (flags & TDCBF_CLOSE_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
811 if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK);
812 #undef TASKDIALOG_INIT_COMMON_BUTTON
814 dialog_info->button_count = count;
817 static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info)
819 if (!dialog_info->taskconfig->u2.hFooterIcon) return;
821 dialog_info->footer_icon =
822 CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, 0);
823 taskdialog_set_icon(dialog_info, TDIE_ICON_FOOTER, dialog_info->taskconfig->u2.hFooterIcon);
826 static void taskdialog_add_footer_text(struct taskdialog_info *dialog_info)
828 dialog_info->footer_text = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszFooter,
829 dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
832 static LONG taskdialog_get_dialog_width(struct taskdialog_info *dialog_info)
834 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
835 BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
836 LONG max_width, main_icon_width, screen_width;
837 RECT rect;
838 SIZE size;
840 screen_width = taskdialog_get_reference_rect(taskconfig, &rect);
841 if ((taskconfig->dwFlags & TDF_SIZE_TO_CONTENT) && !taskconfig->cxWidth)
843 max_width = DIALOG_MIN_WIDTH;
844 taskdialog_du_to_px(dialog_info, &max_width, NULL);
845 main_icon_width = dialog_info->m.h_spacing;
846 if (dialog_info->main_icon) main_icon_width += GetSystemMetrics(SM_CXICON);
847 if (dialog_info->content)
849 taskdialog_get_label_size(dialog_info, dialog_info->content, 0, &size, syslink);
850 max_width = max(max_width, size.cx + main_icon_width + dialog_info->m.h_spacing * 2);
853 else
855 max_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
856 taskdialog_du_to_px(dialog_info, &max_width, NULL);
858 max_width = min(max_width, screen_width);
859 return max_width;
862 static void taskdialog_label_layout(struct taskdialog_info *dialog_info, HWND hwnd, INT start_x, LONG dialog_width,
863 LONG *dialog_height, BOOL syslink)
865 LONG x, y, max_width;
866 SIZE size;
868 if (!hwnd) return;
870 x = start_x + dialog_info->m.h_spacing;
871 y = *dialog_height + dialog_info->m.v_spacing;
872 max_width = dialog_width - x - dialog_info->m.h_spacing;
873 taskdialog_get_label_size(dialog_info, hwnd, max_width, &size, syslink);
874 SetWindowPos(hwnd, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
875 *dialog_height = y + size.cy;
878 static void taskdialog_layout(struct taskdialog_info *dialog_info)
880 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
881 BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
882 static BOOL first_time = TRUE;
883 RECT ref_rect;
884 LONG dialog_width, dialog_height = 0;
885 LONG h_spacing, v_spacing;
886 LONG main_icon_right, main_icon_bottom;
887 LONG expando_right, expando_bottom;
888 struct button_layout_info *button_layout_infos;
889 LONG button_min_width, button_height;
890 LONG *line_widths, line_count, align;
891 LONG footer_icon_right, footer_icon_bottom;
892 LONG x, y;
893 SIZE size;
894 INT i;
896 taskdialog_get_reference_rect(dialog_info->taskconfig, &ref_rect);
897 dialog_width = taskdialog_get_dialog_width(dialog_info);
899 h_spacing = dialog_info->m.h_spacing;
900 v_spacing = dialog_info->m.v_spacing;
902 /* Main icon */
903 main_icon_right = 0;
904 main_icon_bottom = 0;
905 if (dialog_info->main_icon)
907 x = h_spacing;
908 y = dialog_height + v_spacing;
909 size.cx = GetSystemMetrics(SM_CXICON);
910 size.cy = GetSystemMetrics(SM_CYICON);
911 SetWindowPos(dialog_info->main_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
912 main_icon_right = x + size.cx;
913 main_icon_bottom = y + size.cy;
916 /* Main instruction */
917 taskdialog_label_layout(dialog_info, dialog_info->main_instruction, main_icon_right, dialog_width, &dialog_height,
918 FALSE);
920 /* Content */
921 taskdialog_label_layout(dialog_info, dialog_info->content, main_icon_right, dialog_width, &dialog_height, syslink);
923 /* Expanded information */
924 if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
925 taskdialog_label_layout(dialog_info, dialog_info->expanded_info, main_icon_right, dialog_width, &dialog_height,
926 syslink);
928 /* Progress bar */
929 if (dialog_info->progress_bar)
931 x = main_icon_right + h_spacing;
932 y = dialog_height + v_spacing;
933 size.cx = dialog_width - x - h_spacing;
934 size.cy = GetSystemMetrics(SM_CYVSCROLL);
935 SetWindowPos(dialog_info->progress_bar, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
936 dialog_height = y + size.cy;
939 /* Radio buttons */
940 for (i = 0; i < dialog_info->radio_button_count; i++)
942 x = main_icon_right + h_spacing;
943 y = dialog_height + v_spacing;
944 taskdialog_get_radio_button_size(dialog_info, dialog_info->radio_buttons[i], dialog_width - x - h_spacing, &size);
945 size.cx = dialog_width - x - h_spacing;
946 SetWindowPos(dialog_info->radio_buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
947 dialog_height = y + size.cy;
950 /* Command links */
951 for (i = 0; i < dialog_info->command_link_count; i++)
953 x = main_icon_right + h_spacing;
954 y = dialog_height + v_spacing;
955 taskdialog_get_label_size(dialog_info, dialog_info->command_links[i], dialog_width - x - h_spacing, &size, FALSE);
956 size.cx = dialog_width - x - h_spacing;
957 /* Add spacing */
958 size.cy += 4;
959 SetWindowPos(dialog_info->command_links[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
960 dialog_height = y + size.cy;
963 dialog_height = max(dialog_height, main_icon_bottom);
965 expando_right = 0;
966 expando_bottom = dialog_height;
967 /* Expando control */
968 if (dialog_info->expando_button)
970 x = h_spacing;
971 y = dialog_height + v_spacing;
972 taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
973 SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
974 expando_right = x + size.cx;
975 expando_bottom = y + size.cy;
978 /* Verification box */
979 if (dialog_info->verification_box)
981 x = h_spacing;
982 y = expando_bottom + v_spacing;
983 size.cx = DIALOG_MIN_WIDTH / 2;
984 taskdialog_du_to_px(dialog_info, &size.cx, NULL);
985 taskdialog_get_radio_button_size(dialog_info, dialog_info->verification_box, size.cx, &size);
986 SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
987 expando_right = max(expando_right, x + size.cx);
988 expando_bottom = y + size.cy;
991 /* Common and custom buttons */
992 button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
993 line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
995 button_min_width = DIALOG_BUTTON_WIDTH;
996 button_height = DIALOG_BUTTON_HEIGHT;
997 taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
998 for (i = 0; i < dialog_info->button_count; i++)
1000 taskdialog_get_label_size(dialog_info, dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2,
1001 &size, FALSE);
1002 button_layout_infos[i].width = max(size.cx, button_min_width);
1005 /* Separate buttons into lines */
1006 x = expando_right + h_spacing;
1007 for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
1009 button_layout_infos[i].line = line_count;
1010 x += button_layout_infos[i].width + h_spacing;
1011 line_widths[line_count] += button_layout_infos[i].width + h_spacing;
1013 if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
1015 x = expando_right + h_spacing;
1016 line_count++;
1019 line_count++;
1021 /* Try to balance lines so they are about the same size */
1022 for (i = 1; i < line_count - 1; i++)
1024 int diff_now = abs(line_widths[i] - line_widths[i - 1]);
1025 unsigned int j, last_button = 0;
1026 int diff_changed;
1028 for (j = 0; j < dialog_info->button_count; j++)
1029 if (button_layout_infos[j].line == i - 1) last_button = j;
1031 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1032 diff_changed = abs(2 * button_layout_infos[last_button].width + line_widths[i] - line_widths[i - 1]);
1034 if (diff_changed < diff_now)
1036 button_layout_infos[last_button].line = i;
1037 line_widths[i] += button_layout_infos[last_button].width;
1038 line_widths[i - 1] -= button_layout_infos[last_button].width;
1042 /* Calculate left alignment so all lines are as far right as possible. */
1043 align = dialog_width - h_spacing;
1044 for (i = 0; i < line_count; i++)
1046 int new_alignment = dialog_width - line_widths[i];
1047 if (new_alignment < align) align = new_alignment;
1050 /* Now that we got them all positioned, move all buttons */
1051 x = align;
1052 size.cy = button_height;
1053 for (i = 0; i < dialog_info->button_count; i++)
1055 /* New line */
1056 if (i > 0 && button_layout_infos[i].line != button_layout_infos[i - 1].line)
1058 x = align;
1059 dialog_height += size.cy + v_spacing;
1062 y = dialog_height + v_spacing;
1063 size.cx = button_layout_infos[i].width;
1064 SetWindowPos(dialog_info->buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1065 x += button_layout_infos[i].width + h_spacing;
1068 /* Add height for last row button and spacing */
1069 dialog_height += size.cy + v_spacing;
1070 dialog_height = max(dialog_height, expando_bottom);
1072 Free(button_layout_infos);
1073 Free(line_widths);
1075 /* Footer icon */
1076 footer_icon_right = 0;
1077 footer_icon_bottom = dialog_height;
1078 if (dialog_info->footer_icon)
1080 x = h_spacing;
1081 y = dialog_height + v_spacing;
1082 size.cx = GetSystemMetrics(SM_CXSMICON);
1083 size.cy = GetSystemMetrics(SM_CYSMICON);
1084 SetWindowPos(dialog_info->footer_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1085 footer_icon_right = x + size.cx;
1086 footer_icon_bottom = y + size.cy;
1089 /* Footer text */
1090 taskdialog_label_layout(dialog_info, dialog_info->footer_text, footer_icon_right, dialog_width, &dialog_height,
1091 syslink);
1092 dialog_height = max(dialog_height, footer_icon_bottom);
1094 /* Expanded information */
1095 if ((taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
1096 taskdialog_label_layout(dialog_info, dialog_info->expanded_info, 0, dialog_width, &dialog_height, syslink);
1098 /* Add height for spacing, title height and frame height */
1099 dialog_height += v_spacing;
1100 dialog_height += GetSystemMetrics(SM_CYCAPTION);
1101 dialog_height += GetSystemMetrics(SM_CXDLGFRAME);
1103 if (first_time)
1105 x = (ref_rect.left + ref_rect.right - dialog_width) / 2;
1106 y = (ref_rect.top + ref_rect.bottom - dialog_height) / 2;
1107 SetWindowPos(dialog_info->hwnd, 0, x, y, dialog_width, dialog_height, SWP_NOZORDER);
1108 first_time = FALSE;
1110 else
1111 SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
1114 static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
1116 HWND hwnd;
1117 HDC hdc;
1118 RECT rect = {0};
1119 WCHAR *text;
1120 LONG icon_width, icon_height, text_offset;
1121 UINT style = DFCS_FLAT;
1122 BOOL draw_focus;
1124 hdc = dis->hDC;
1125 hwnd = dis->hwndItem;
1127 SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
1129 icon_width = DIALOG_EXPANDO_ICON_WIDTH;
1130 icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
1131 taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
1132 rect.right = icon_width;
1133 rect.bottom = icon_height;
1134 style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
1135 DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
1137 GetCharWidthW(hdc, '0', '0', &text_offset);
1138 text_offset /= 2;
1140 rect = dis->rcItem;
1141 rect.left += icon_width + text_offset;
1142 text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
1143 DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
1145 draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
1146 if(draw_focus) DrawFocusRect(hdc, &rect);
1149 static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
1151 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
1152 NONCLIENTMETRICSW ncm;
1153 HDC hdc;
1154 INT id;
1156 ncm.cbSize = sizeof(ncm);
1157 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
1159 memset(dialog_info, 0, sizeof(*dialog_info));
1160 dialog_info->taskconfig = taskconfig;
1161 dialog_info->hwnd = hwnd;
1162 dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont);
1164 hdc = GetDC(dialog_info->hwnd);
1165 SelectObject(hdc, dialog_info->font);
1166 dialog_info->m.x_baseunit = GdiGetCharDimensions(hdc, NULL, &dialog_info->m.y_baseunit);
1167 ReleaseDC(dialog_info->hwnd, hdc);
1169 dialog_info->m.h_spacing = DIALOG_SPACING;
1170 dialog_info->m.v_spacing = DIALOG_SPACING;
1171 taskdialog_du_to_px(dialog_info, &dialog_info->m.h_spacing, &dialog_info->m.v_spacing);
1173 if (taskconfig->dwFlags & TDF_CALLBACK_TIMER)
1175 SetTimer(hwnd, ID_TIMER, DIALOG_TIMER_MS, NULL);
1176 dialog_info->last_timer_tick = GetTickCount();
1179 taskdialog_add_main_icon(dialog_info);
1180 taskdialog_add_main_instruction(dialog_info);
1181 taskdialog_add_content(dialog_info);
1182 taskdialog_add_expanded_info(dialog_info);
1183 taskdialog_add_progress_bar(dialog_info);
1184 taskdialog_add_radio_buttons(dialog_info);
1185 taskdialog_add_command_links(dialog_info);
1186 taskdialog_add_expando_button(dialog_info);
1187 taskdialog_add_verification_box(dialog_info);
1188 taskdialog_add_buttons(dialog_info);
1189 taskdialog_add_footer_icon(dialog_info);
1190 taskdialog_add_footer_text(dialog_info);
1192 /* Set default button */
1193 if (!dialog_info->default_button && dialog_info->command_links)
1194 dialog_info->default_button = dialog_info->command_links[0];
1195 if (!dialog_info->default_button) dialog_info->default_button = dialog_info->buttons[0];
1196 SendMessageW(dialog_info->hwnd, WM_NEXTDLGCTL, (WPARAM)dialog_info->default_button, TRUE);
1197 id = GetWindowLongW(dialog_info->default_button, GWLP_ID);
1198 SendMessageW(dialog_info->hwnd, DM_SETDEFID, id, 0);
1200 taskdialog_layout(dialog_info);
1203 static BOOL CALLBACK takdialog_destroy_control(HWND hwnd, LPARAM lParam)
1205 DestroyWindow(hwnd);
1206 return TRUE;
1209 static void taskdialog_destroy(struct taskdialog_info *dialog_info)
1211 EnumChildWindows(dialog_info->hwnd, takdialog_destroy_control, 0);
1213 if (dialog_info->taskconfig->dwFlags & TDF_CALLBACK_TIMER) KillTimer(dialog_info->hwnd, ID_TIMER);
1214 if (dialog_info->font) DeleteObject(dialog_info->font);
1215 if (dialog_info->main_instruction_font) DeleteObject(dialog_info->main_instruction_font);
1216 Free(dialog_info->buttons);
1217 Free(dialog_info->radio_buttons);
1218 Free(dialog_info->command_links);
1219 Free(dialog_info->expanded_text);
1220 Free(dialog_info->collapsed_text);
1223 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1225 static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
1226 struct taskdialog_info *dialog_info;
1227 LRESULT result;
1229 TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
1231 if (msg != WM_INITDIALOG)
1232 dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
1234 switch (msg)
1236 case TDM_NAVIGATE_PAGE:
1237 dialog_info->taskconfig = (const TASKDIALOGCONFIG *)lParam;
1238 taskdialog_destroy(dialog_info);
1239 taskdialog_init(dialog_info, hwnd);
1240 taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1241 /* Default radio button click notification is sent before TDN_NAVIGATED */
1242 taskdialog_check_default_radio_buttons(dialog_info);
1243 taskdialog_notify(dialog_info, TDN_NAVIGATED, 0, 0);
1244 break;
1245 case TDM_CLICK_BUTTON:
1246 taskdialog_click_button(dialog_info, wParam);
1247 break;
1248 case TDM_ENABLE_BUTTON:
1249 taskdialog_enable_button(dialog_info, wParam, lParam);
1250 break;
1251 case TDM_SET_MARQUEE_PROGRESS_BAR:
1253 BOOL marquee = wParam;
1254 LONG style;
1255 if(!dialog_info->progress_bar) break;
1256 style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1257 style = marquee ? style | PBS_MARQUEE : style & (~PBS_MARQUEE);
1258 SetWindowLongW(dialog_info->progress_bar, GWL_STYLE, style);
1259 break;
1261 case TDM_SET_PROGRESS_BAR_STATE:
1262 result = SendMessageW(dialog_info->progress_bar, PBM_SETSTATE, wParam, 0);
1263 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1264 break;
1265 case TDM_SET_PROGRESS_BAR_RANGE:
1266 result = SendMessageW(dialog_info->progress_bar, PBM_SETRANGE, 0, lParam);
1267 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1268 break;
1269 case TDM_SET_PROGRESS_BAR_POS:
1270 result = 0;
1271 if (dialog_info->progress_bar)
1273 LONG style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1274 if (!(style & PBS_MARQUEE)) result = SendMessageW(dialog_info->progress_bar, PBM_SETPOS, wParam, 0);
1276 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1277 break;
1278 case TDM_SET_PROGRESS_BAR_MARQUEE:
1279 SendMessageW(dialog_info->progress_bar, PBM_SETMARQUEE, wParam, lParam);
1280 break;
1281 case TDM_SET_ELEMENT_TEXT:
1282 taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1283 taskdialog_layout(dialog_info);
1284 break;
1285 case TDM_UPDATE_ELEMENT_TEXT:
1286 taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1287 break;
1288 case TDM_CLICK_RADIO_BUTTON:
1289 taskdialog_click_radio_button(dialog_info, wParam);
1290 break;
1291 case TDM_ENABLE_RADIO_BUTTON:
1292 taskdialog_enable_radio_button(dialog_info, wParam, lParam);
1293 break;
1294 case TDM_CLICK_VERIFICATION:
1296 BOOL checked = (BOOL)wParam;
1297 BOOL focused = (BOOL)lParam;
1298 dialog_info->verification_checked = checked;
1299 if (dialog_info->verification_box)
1301 SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
1302 taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
1303 if (focused) SetFocus(dialog_info->verification_box);
1305 break;
1307 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE:
1308 taskdialog_button_set_shield(dialog_info, wParam, lParam);
1309 break;
1310 case TDM_UPDATE_ICON:
1311 taskdialog_set_icon(dialog_info, wParam, (HICON)lParam);
1312 break;
1313 case WM_INITDIALOG:
1314 dialog_info = (struct taskdialog_info *)lParam;
1316 taskdialog_init(dialog_info, hwnd);
1318 SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
1319 taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1320 taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
1321 /* Default radio button click notification sent after TDN_CREATED */
1322 taskdialog_check_default_radio_buttons(dialog_info);
1323 return FALSE;
1324 case WM_COMMAND:
1325 if (HIWORD(wParam) == BN_CLICKED)
1327 taskdialog_on_button_click(dialog_info, (HWND)lParam);
1328 break;
1330 return FALSE;
1331 case WM_HELP:
1332 taskdialog_notify(dialog_info, TDN_HELP, 0, 0);
1333 break;
1334 case WM_TIMER:
1335 if (ID_TIMER == wParam)
1337 DWORD elapsed = GetTickCount() - dialog_info->last_timer_tick;
1338 if (taskdialog_notify(dialog_info, TDN_TIMER, elapsed, 0) == S_FALSE)
1339 dialog_info->last_timer_tick = GetTickCount();
1341 break;
1342 case WM_NOTIFY:
1344 PNMLINK pnmLink = (PNMLINK)lParam;
1345 HWND hwndFrom = pnmLink->hdr.hwndFrom;
1346 if ((taskdialog_hyperlink_enabled(dialog_info))
1347 && (hwndFrom == dialog_info->content || hwndFrom == dialog_info->expanded_info
1348 || hwndFrom == dialog_info->footer_text)
1349 && (pnmLink->hdr.code == NM_CLICK || pnmLink->hdr.code == NM_RETURN))
1351 taskdialog_notify(dialog_info, TDN_HYPERLINK_CLICKED, 0, (LPARAM)pnmLink->item.szUrl);
1352 break;
1354 return FALSE;
1356 case WM_DRAWITEM:
1358 LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
1359 if (dis->hwndItem == dialog_info->expando_button)
1361 taskdialog_draw_expando_control(dialog_info, dis);
1362 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE);
1363 break;
1365 return FALSE;
1367 case WM_DESTROY:
1368 taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
1369 RemovePropW(hwnd, taskdialog_info_propnameW);
1370 taskdialog_destroy(dialog_info);
1371 break;
1372 default:
1373 return FALSE;
1375 return TRUE;
1378 /***********************************************************************
1379 * TaskDialogIndirect [COMCTL32.@]
1381 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
1382 int *radio_button, BOOL *verification_flag_checked)
1384 struct taskdialog_info dialog_info;
1385 DLGTEMPLATE *template;
1386 INT ret;
1388 TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
1390 if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
1391 return E_INVALIDARG;
1393 dialog_info.taskconfig = taskconfig;
1395 template = create_taskdialog_template(taskconfig);
1396 ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
1397 taskdialog_proc, (LPARAM)&dialog_info);
1398 Free(template);
1400 if (button) *button = ret;
1401 if (radio_button) *radio_button = dialog_info.selected_radio_id;
1402 if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
1404 return S_OK;
1407 /***********************************************************************
1408 * TaskDialog [COMCTL32.@]
1410 HRESULT WINAPI TaskDialog(HWND owner, HINSTANCE hinst, const WCHAR *title, const WCHAR *main_instruction,
1411 const WCHAR *content, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons, const WCHAR *icon, int *button)
1413 TASKDIALOGCONFIG taskconfig;
1415 TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner, hinst, debugstr_w(title), debugstr_w(main_instruction),
1416 debugstr_w(content), common_buttons, debugstr_w(icon), button);
1418 memset(&taskconfig, 0, sizeof(taskconfig));
1419 taskconfig.cbSize = sizeof(taskconfig);
1420 taskconfig.hwndParent = owner;
1421 taskconfig.hInstance = hinst;
1422 taskconfig.dwCommonButtons = common_buttons;
1423 taskconfig.pszWindowTitle = title;
1424 taskconfig.u.pszMainIcon = icon;
1425 taskconfig.pszMainInstruction = main_instruction;
1426 taskconfig.pszContent = content;
1427 return TaskDialogIndirect(&taskconfig, button, NULL, NULL);