gdi32/tests: Add D3DKMTCloseAdapter tests.
[wine.git] / dlls / comctl32 / taskdialog.c
blob5645d53f794ded1f3b1986aa0e3b2e5fa1882cc3
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 BOOL has_cancel;
88 WCHAR *expanded_text;
89 WCHAR *collapsed_text;
92 struct button_layout_info
94 LONG width;
95 LONG line;
98 static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam);
99 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id);
100 static void taskdialog_layout(struct taskdialog_info *dialog_info);
102 static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
104 if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4);
105 if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8);
108 static void template_write_data(char **ptr, const void *src, unsigned int size)
110 memcpy(*ptr, src, size);
111 *ptr += size;
114 static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret)
116 HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(),
117 MONITOR_DEFAULTTOPRIMARY);
118 MONITORINFO info;
120 info.cbSize = sizeof(info);
121 GetMonitorInfoW(monitor, &info);
123 if ((taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) && taskconfig->hwndParent)
124 GetWindowRect(taskconfig->hwndParent, ret);
125 else
126 *ret = info.rcWork;
128 return info.rcWork.right - info.rcWork.left;
131 static WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length)
133 DWORD len = GetModuleFileNameW(NULL, name, length);
134 if (len && len < length)
136 WCHAR *p;
137 if ((p = strrchrW(name, '/'))) name = p + 1;
138 if ((p = strrchrW(name, '\\'))) name = p + 1;
139 return name;
141 else
142 return NULL;
145 static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
147 unsigned int size, title_size;
148 static const WORD fontsize = 0x7fff;
149 static const WCHAR emptyW[] = { 0 };
150 const WCHAR *titleW = NULL;
151 DLGTEMPLATE *template;
152 WCHAR pathW[MAX_PATH];
153 char *ptr;
155 /* Window title */
156 if (!taskconfig->pszWindowTitle)
157 titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
158 else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
160 if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
161 titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
163 else
164 titleW = taskconfig->pszWindowTitle;
165 if (!titleW)
166 titleW = emptyW;
167 title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
169 size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
170 size += title_size;
171 size += 2; /* font size */
173 template = Alloc(size);
174 if (!template) return NULL;
176 template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
177 if (taskconfig->dwFlags & TDF_CAN_BE_MINIMIZED) template->style |= WS_MINIMIZEBOX;
178 if (!(taskconfig->dwFlags & TDF_NO_SET_FOREGROUND)) template->style |= DS_SETFOREGROUND;
179 if (taskconfig->dwFlags & TDF_RTL_LAYOUT) template->dwExtendedStyle = WS_EX_LAYOUTRTL | WS_EX_RIGHT | WS_EX_RTLREADING;
181 ptr = (char *)(template + 1);
182 ptr += 2; /* menu */
183 ptr += 2; /* class */
184 template_write_data(&ptr, titleW, title_size);
185 template_write_data(&ptr, &fontsize, sizeof(fontsize));
187 return template;
190 static HWND taskdialog_find_button(HWND *buttons, INT count, INT id)
192 INT button_id;
193 INT i;
195 for (i = 0; i < count; i++)
197 button_id = GetWindowLongW(buttons[i], GWLP_ID);
198 if (button_id == id) return buttons[i];
201 return NULL;
204 static void taskdialog_enable_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
206 HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
207 if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
208 if (hwnd) EnableWindow(hwnd, enable);
211 static void taskdialog_click_button(struct taskdialog_info *dialog_info, INT id)
213 if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, id, 0) == S_OK) EndDialog(dialog_info->hwnd, id);
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, WORD id)
307 INT command_id;
308 HWND button, radio_button;
310 /* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
311 command_id = hwnd ? GetWindowLongW(hwnd, GWLP_ID) : id;
313 if (hwnd && hwnd == dialog_info->expando_button)
315 taskdialog_toggle_expando_control(dialog_info);
316 taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
317 return;
320 if (hwnd && hwnd == dialog_info->verification_box)
322 dialog_info->verification_checked = !dialog_info->verification_checked;
323 taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
324 return;
327 radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
328 if (radio_button)
330 dialog_info->selected_radio_id = command_id;
331 taskdialog_notify(dialog_info, TDN_RADIO_BUTTON_CLICKED, command_id, 0);
332 return;
335 button = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, command_id);
336 if (!button) button = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, command_id);
337 if (!button && command_id == IDOK)
339 button = dialog_info->command_link_count > 0 ? dialog_info->command_links[0] : dialog_info->buttons[0];
340 command_id = GetWindowLongW(button, GWLP_ID);
343 if (button && taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
344 EndDialog(dialog_info->hwnd, command_id);
347 static WCHAR *taskdialog_gettext(struct taskdialog_info *dialog_info, BOOL user_resource, const WCHAR *text)
349 const WCHAR *textW = NULL;
350 INT length;
351 WCHAR *ret;
353 if (IS_INTRESOURCE(text))
355 if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule,
356 (UINT_PTR)text, (WCHAR *)&textW, 0)))
357 return NULL;
359 else
361 textW = text;
362 length = strlenW(textW);
365 ret = Alloc((length + 1) * sizeof(WCHAR));
366 if (ret) memcpy(ret, textW, length * sizeof(WCHAR));
368 return ret;
371 static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info)
373 return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS;
376 static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info)
378 return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON);
381 static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size,
382 BOOL syslink)
384 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
385 HFONT hfont, old_hfont;
386 HDC hdc;
387 RECT rect = {0};
388 WCHAR *text;
389 INT text_length;
391 if (syslink)
393 SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size);
394 return;
397 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
398 style |= DT_RIGHT | DT_RTLREADING;
399 else
400 style |= DT_LEFT;
402 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
403 text_length = GetWindowTextLengthW(hwnd);
404 text = Alloc((text_length + 1) * sizeof(WCHAR));
405 if (!text)
407 size->cx = 0;
408 size->cy = 0;
409 return;
411 GetWindowTextW(hwnd, text, text_length + 1);
412 hdc = GetDC(hwnd);
413 old_hfont = SelectObject(hdc, hfont);
414 rect.right = max_width;
415 size->cy = DrawTextW(hdc, text, text_length, &rect, style);
416 size->cx = min(max_width, rect.right - rect.left);
417 if (old_hfont) SelectObject(hdc, old_hfont);
418 ReleaseDC(hwnd, hdc);
419 Free(text);
422 static void taskdialog_get_radio_button_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size)
424 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
425 HFONT hfont, old_hfont;
426 HDC hdc;
427 RECT rect = {0};
428 INT text_length;
429 WCHAR *text;
430 INT text_offset, radio_box_width, radio_box_height;
432 hdc = GetDC(hwnd);
433 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
434 old_hfont = SelectObject(hdc, hfont);
436 radio_box_width = 12 * GetDpiForWindow(hwnd) / 96 + 1;
437 radio_box_height = 12 * GetDpiForWindow(hwnd) / 96 + 1;
438 GetCharWidthW(hdc, '0', '0', &text_offset);
439 text_offset /= 2;
441 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
442 style |= DT_RIGHT | DT_RTLREADING;
443 else
444 style |= DT_LEFT;
446 rect.right = max_width - radio_box_width - text_offset;
447 text_length = GetWindowTextLengthW(hwnd);
448 text = Alloc((text_length + 1) * sizeof(WCHAR));
449 if (!text)
451 size->cx = 0;
452 size->cy = 0;
453 return;
455 GetWindowTextW(hwnd, text, text_length + 1);
456 size->cy = DrawTextW(hdc, text, text_length, &rect, style);
457 size->cx = min(max_width - radio_box_width - text_offset, rect.right - rect.left);
458 size->cx += radio_box_width + text_offset;
459 size->cy = max(size->cy, radio_box_height);
460 if (old_hfont) SelectObject(hdc, old_hfont);
461 Free(text);
462 ReleaseDC(hwnd, hdc);
465 static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
467 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
468 HFONT hfont, old_hfont;
469 HDC hdc;
470 RECT rect = {0};
471 LONG icon_width, icon_height, text_offset;
472 LONG max_width, max_text_height;
474 hdc = GetDC(hwnd);
475 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
476 old_hfont = SelectObject(hdc, hfont);
478 icon_width = DIALOG_EXPANDO_ICON_WIDTH;
479 icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
480 taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
482 GetCharWidthW(hdc, '0', '0', &text_offset);
483 text_offset /= 2;
485 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
486 style |= DT_RIGHT | DT_RTLREADING;
487 else
488 style |= DT_LEFT;
490 max_width = DIALOG_MIN_WIDTH / 2;
491 taskdialog_du_to_px(dialog_info, &max_width, NULL);
493 rect.right = max_width - icon_width - text_offset;
494 max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
495 size->cy = max(max_text_height, icon_height);
496 size->cx = rect.right - rect.left;
498 rect.right = max_width - icon_width - text_offset;
499 max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
500 size->cy = max(size->cy, max_text_height);
501 size->cx = max(size->cx, rect.right - rect.left);
502 size->cx = min(size->cx, max_width);
504 if (old_hfont) SelectObject(hdc, old_hfont);
505 ReleaseDC(hwnd, hdc);
508 static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
510 if (icon == TD_WARNING_ICON)
511 return IDI_WARNING;
512 else if (icon == TD_ERROR_ICON)
513 return IDI_ERROR;
514 else if (icon == TD_INFORMATION_ICON)
515 return IDI_INFORMATION;
516 else if (icon == TD_SHIELD_ICON)
517 return IDI_SHIELD;
518 else
519 return (ULONG_PTR)icon;
522 static void taskdialog_set_icon(struct taskdialog_info *dialog_info, INT element, HICON icon)
524 DWORD flags = dialog_info->taskconfig->dwFlags;
525 INT cx = 0, cy = 0;
526 HICON hicon;
528 if (!icon) return;
530 if (((flags & TDF_USE_HICON_MAIN) && element == TDIE_ICON_MAIN)
531 || ((flags & TDF_USE_HICON_FOOTER) && element == TDIE_ICON_FOOTER))
532 hicon = icon;
533 else
535 if (element == TDIE_ICON_FOOTER)
537 cx = GetSystemMetrics(SM_CXSMICON);
538 cy = GetSystemMetrics(SM_CYSMICON);
540 hicon = LoadImageW(dialog_info->taskconfig->hInstance, (LPCWSTR)icon, IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTSIZE);
541 if (!hicon)
542 hicon = LoadImageW(NULL, (LPCWSTR)taskdialog_get_standard_icon((LPCWSTR)icon), IMAGE_ICON, cx, cy,
543 LR_SHARED | LR_DEFAULTSIZE);
546 if (!hicon) return;
548 if (element == TDIE_ICON_MAIN)
550 SendMessageW(dialog_info->hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hicon);
551 SendMessageW(dialog_info->main_icon, STM_SETICON, (WPARAM)hicon, 0);
553 else if (element == TDIE_ICON_FOOTER)
554 SendMessageW(dialog_info->footer_icon, STM_SETICON, (WPARAM)hicon, 0);
557 static void taskdialog_set_element_text(struct taskdialog_info *dialog_info, TASKDIALOG_ELEMENTS element,
558 const WCHAR *text)
560 HWND hwnd = NULL;
561 WCHAR *textW;
563 if (element == TDE_CONTENT)
564 hwnd = dialog_info->content;
565 else if (element == TDE_EXPANDED_INFORMATION)
566 hwnd = dialog_info->expanded_info;
567 else if (element == TDE_FOOTER)
568 hwnd = dialog_info->footer_text;
569 else if (element == TDE_MAIN_INSTRUCTION)
570 hwnd = dialog_info->main_instruction;
572 if (!hwnd) return;
574 textW = taskdialog_gettext(dialog_info, TRUE, text);
575 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
576 Free(textW);
579 static void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info)
581 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
582 HWND default_button;
584 if (!dialog_info->radio_button_count) return;
586 default_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count,
587 taskconfig->nDefaultRadioButton);
589 if (!default_button && !(taskconfig->dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON))
590 default_button = dialog_info->radio_buttons[0];
592 if (default_button)
594 SendMessageW(default_button, BM_SETCHECK, BST_CHECKED, 0);
595 taskdialog_on_button_click(dialog_info, default_button, 0);
599 static void taskdialog_add_main_icon(struct taskdialog_info *dialog_info)
601 if (!dialog_info->taskconfig->u.hMainIcon) return;
603 dialog_info->main_icon =
604 CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
605 taskdialog_set_icon(dialog_info, TDIE_ICON_MAIN, dialog_info->taskconfig->u.hMainIcon);
608 static HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink)
610 WCHAR *textW;
611 HWND hwnd;
612 const WCHAR *class;
613 DWORD style = WS_CHILD | WS_VISIBLE;
615 if (!text) return NULL;
617 class = syslink ? WC_LINK : WC_STATICW;
618 if (syslink) style |= WS_TABSTOP;
619 textW = taskdialog_gettext(dialog_info, TRUE, text);
620 hwnd = CreateWindowW(class, textW, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
621 Free(textW);
623 SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0);
624 return hwnd;
627 static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info)
629 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
630 NONCLIENTMETRICSW ncm;
632 if (!taskconfig->pszMainInstruction) return;
634 ncm.cbSize = sizeof(ncm);
635 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
636 /* 1.25 times the height */
637 ncm.lfMessageFont.lfHeight = ncm.lfMessageFont.lfHeight * 5 / 4;
638 ncm.lfMessageFont.lfWeight = FW_BOLD;
639 dialog_info->main_instruction_font = CreateFontIndirectW(&ncm.lfMessageFont);
641 dialog_info->main_instruction =
642 taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE);
645 static void taskdialog_add_content(struct taskdialog_info *dialog_info)
647 dialog_info->content = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszContent, dialog_info->font,
648 taskdialog_hyperlink_enabled(dialog_info));
651 static void taskdialog_add_progress_bar(struct taskdialog_info *dialog_info)
653 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
654 DWORD style = PBS_SMOOTH | PBS_SMOOTHREVERSE | WS_CHILD | WS_VISIBLE;
656 if (!(taskconfig->dwFlags & (TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR))) return;
657 if (taskconfig->dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) style |= PBS_MARQUEE;
658 dialog_info->progress_bar =
659 CreateWindowW(PROGRESS_CLASSW, NULL, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
662 static void taskdialog_add_radio_buttons(struct taskdialog_info *dialog_info)
664 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
665 static const DWORD style = BS_AUTORADIOBUTTON | BS_MULTILINE | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
666 WCHAR *textW;
667 INT i;
669 if (!taskconfig->cRadioButtons || !taskconfig->pRadioButtons) return;
671 dialog_info->radio_buttons = Alloc(taskconfig->cRadioButtons * sizeof(*dialog_info->radio_buttons));
672 if (!dialog_info->radio_buttons) return;
674 dialog_info->radio_button_count = taskconfig->cRadioButtons;
675 for (i = 0; i < dialog_info->radio_button_count; i++)
677 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pRadioButtons[i].pszButtonText);
678 dialog_info->radio_buttons[i] =
679 CreateWindowW(WC_BUTTONW, textW, i == 0 ? style | WS_GROUP : style, 0, 0, 0, 0, dialog_info->hwnd,
680 LongToHandle(taskconfig->pRadioButtons[i].nButtonID), 0, NULL);
681 SendMessageW(dialog_info->radio_buttons[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
682 Free(textW);
686 static void taskdialog_add_command_links(struct taskdialog_info *dialog_info)
688 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
689 DWORD default_style = BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP, style;
690 BOOL is_default;
691 WCHAR *textW;
692 INT i;
694 if (!taskconfig->cButtons || !taskconfig->pButtons || !taskdialog_use_command_link(dialog_info)) return;
696 dialog_info->command_links = Alloc(taskconfig->cButtons * sizeof(*dialog_info->command_links));
697 if (!dialog_info->command_links) return;
699 dialog_info->command_link_count = taskconfig->cButtons;
700 for (i = 0; i < dialog_info->command_link_count; i++)
702 is_default = taskconfig->pButtons[i].nButtonID == taskconfig->nDefaultButton;
703 style = is_default ? default_style | BS_DEFCOMMANDLINK : default_style | BS_COMMANDLINK;
704 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pButtons[i].pszButtonText);
705 dialog_info->command_links[i] = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd,
706 LongToHandle(taskconfig->pButtons[i].nButtonID), 0, NULL);
707 SendMessageW(dialog_info->command_links[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
708 Free(textW);
710 if (is_default && !dialog_info->default_button) dialog_info->default_button = dialog_info->command_links[i];
714 static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
716 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
718 if (!taskconfig->pszExpandedInformation) return;
720 dialog_info->expanded = taskconfig->dwFlags & TDF_EXPANDED_BY_DEFAULT;
721 dialog_info->expanded_info = taskdialog_create_label(dialog_info, taskconfig->pszExpandedInformation,
722 dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
723 ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
726 static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
728 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
729 const WCHAR *textW;
731 if (!taskconfig->pszExpandedInformation) return;
733 if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
735 dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
736 dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
738 else
740 textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
741 : taskconfig->pszCollapsedControlText;
742 dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
743 textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
744 : taskconfig->pszExpandedControlText;
745 dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
748 textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
750 dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
751 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
752 SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
755 static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
757 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
758 static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
759 WCHAR *textW;
761 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
762 if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
764 if (!taskconfig->pszVerificationText) return;
766 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
767 dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
768 SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
769 Free(textW);
771 if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
772 SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
775 static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
776 BOOL custom_button)
778 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
779 WCHAR *textW;
781 textW = taskdialog_gettext(dialog_info, custom_button, text);
782 *button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, dialog_info->hwnd,
783 (HMENU)id, 0, NULL);
784 Free(textW);
785 SendMessageW(*button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
787 if (id == taskconfig->nDefaultButton && !dialog_info->default_button) dialog_info->default_button = *button;
790 static void taskdialog_add_buttons(struct taskdialog_info *dialog_info)
792 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
793 BOOL use_command_links = taskdialog_use_command_link(dialog_info);
794 DWORD flags = taskconfig->dwCommonButtons;
795 INT count, max_count;
797 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
798 max_count = 6;
799 if (!use_command_links && taskconfig->cButtons && taskconfig->pButtons) max_count += taskconfig->cButtons;
801 dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons));
802 if (!dialog_info->buttons) return;
804 for (count = 0; !use_command_links && count < taskconfig->cButtons; count++)
805 taskdialog_add_button(dialog_info, &dialog_info->buttons[count], taskconfig->pButtons[count].nButtonID,
806 taskconfig->pButtons[count].pszButtonText, TRUE);
808 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
809 do \
811 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
812 FALSE); \
813 } while (0)
815 if (flags & TDCBF_OK_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(OK);
816 if (flags & TDCBF_YES_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(YES);
817 if (flags & TDCBF_NO_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(NO);
818 if (flags & TDCBF_RETRY_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
819 if (flags & TDCBF_CANCEL_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
820 if (flags & TDCBF_CLOSE_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
822 if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK);
823 #undef TASKDIALOG_INIT_COMMON_BUTTON
825 dialog_info->button_count = count;
828 static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info)
830 if (!dialog_info->taskconfig->u2.hFooterIcon) return;
832 dialog_info->footer_icon =
833 CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, 0);
834 taskdialog_set_icon(dialog_info, TDIE_ICON_FOOTER, dialog_info->taskconfig->u2.hFooterIcon);
837 static void taskdialog_add_footer_text(struct taskdialog_info *dialog_info)
839 dialog_info->footer_text = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszFooter,
840 dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
843 static LONG taskdialog_get_dialog_width(struct taskdialog_info *dialog_info)
845 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
846 BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
847 LONG max_width, main_icon_width, screen_width;
848 RECT rect;
849 SIZE size;
851 screen_width = taskdialog_get_reference_rect(taskconfig, &rect);
852 if ((taskconfig->dwFlags & TDF_SIZE_TO_CONTENT) && !taskconfig->cxWidth)
854 max_width = DIALOG_MIN_WIDTH;
855 taskdialog_du_to_px(dialog_info, &max_width, NULL);
856 main_icon_width = dialog_info->m.h_spacing;
857 if (dialog_info->main_icon) main_icon_width += GetSystemMetrics(SM_CXICON);
858 if (dialog_info->content)
860 taskdialog_get_label_size(dialog_info, dialog_info->content, 0, &size, syslink);
861 max_width = max(max_width, size.cx + main_icon_width + dialog_info->m.h_spacing * 2);
864 else
866 max_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
867 taskdialog_du_to_px(dialog_info, &max_width, NULL);
869 max_width = min(max_width, screen_width);
870 return max_width;
873 static void taskdialog_label_layout(struct taskdialog_info *dialog_info, HWND hwnd, INT start_x, LONG dialog_width,
874 LONG *dialog_height, BOOL syslink)
876 LONG x, y, max_width;
877 SIZE size;
879 if (!hwnd) return;
881 x = start_x + dialog_info->m.h_spacing;
882 y = *dialog_height + dialog_info->m.v_spacing;
883 max_width = dialog_width - x - dialog_info->m.h_spacing;
884 taskdialog_get_label_size(dialog_info, hwnd, max_width, &size, syslink);
885 SetWindowPos(hwnd, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
886 *dialog_height = y + size.cy;
889 static void taskdialog_layout(struct taskdialog_info *dialog_info)
891 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
892 BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
893 static BOOL first_time = TRUE;
894 RECT ref_rect;
895 LONG dialog_width, dialog_height = 0;
896 LONG h_spacing, v_spacing;
897 LONG main_icon_right, main_icon_bottom;
898 LONG expando_right, expando_bottom;
899 struct button_layout_info *button_layout_infos;
900 LONG button_min_width, button_height;
901 LONG *line_widths, line_count, align;
902 LONG footer_icon_right, footer_icon_bottom;
903 LONG x, y;
904 SIZE size;
905 INT i;
907 taskdialog_get_reference_rect(dialog_info->taskconfig, &ref_rect);
908 dialog_width = taskdialog_get_dialog_width(dialog_info);
910 h_spacing = dialog_info->m.h_spacing;
911 v_spacing = dialog_info->m.v_spacing;
913 /* Main icon */
914 main_icon_right = 0;
915 main_icon_bottom = 0;
916 if (dialog_info->main_icon)
918 x = h_spacing;
919 y = dialog_height + v_spacing;
920 size.cx = GetSystemMetrics(SM_CXICON);
921 size.cy = GetSystemMetrics(SM_CYICON);
922 SetWindowPos(dialog_info->main_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
923 main_icon_right = x + size.cx;
924 main_icon_bottom = y + size.cy;
927 /* Main instruction */
928 taskdialog_label_layout(dialog_info, dialog_info->main_instruction, main_icon_right, dialog_width, &dialog_height,
929 FALSE);
931 /* Content */
932 taskdialog_label_layout(dialog_info, dialog_info->content, main_icon_right, dialog_width, &dialog_height, syslink);
934 /* Expanded information */
935 if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
936 taskdialog_label_layout(dialog_info, dialog_info->expanded_info, main_icon_right, dialog_width, &dialog_height,
937 syslink);
939 /* Progress bar */
940 if (dialog_info->progress_bar)
942 x = main_icon_right + h_spacing;
943 y = dialog_height + v_spacing;
944 size.cx = dialog_width - x - h_spacing;
945 size.cy = GetSystemMetrics(SM_CYVSCROLL);
946 SetWindowPos(dialog_info->progress_bar, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
947 dialog_height = y + size.cy;
950 /* Radio buttons */
951 for (i = 0; i < dialog_info->radio_button_count; i++)
953 x = main_icon_right + h_spacing;
954 y = dialog_height + v_spacing;
955 taskdialog_get_radio_button_size(dialog_info, dialog_info->radio_buttons[i], dialog_width - x - h_spacing, &size);
956 size.cx = dialog_width - x - h_spacing;
957 SetWindowPos(dialog_info->radio_buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
958 dialog_height = y + size.cy;
961 /* Command links */
962 for (i = 0; i < dialog_info->command_link_count; i++)
964 x = main_icon_right + h_spacing;
965 y = dialog_height + v_spacing;
966 taskdialog_get_label_size(dialog_info, dialog_info->command_links[i], dialog_width - x - h_spacing, &size, FALSE);
967 size.cx = dialog_width - x - h_spacing;
968 /* Add spacing */
969 size.cy += 4;
970 SetWindowPos(dialog_info->command_links[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
971 dialog_height = y + size.cy;
974 dialog_height = max(dialog_height, main_icon_bottom);
976 expando_right = 0;
977 expando_bottom = dialog_height;
978 /* Expando control */
979 if (dialog_info->expando_button)
981 x = h_spacing;
982 y = dialog_height + v_spacing;
983 taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
984 SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
985 expando_right = x + size.cx;
986 expando_bottom = y + size.cy;
989 /* Verification box */
990 if (dialog_info->verification_box)
992 x = h_spacing;
993 y = expando_bottom + v_spacing;
994 size.cx = DIALOG_MIN_WIDTH / 2;
995 taskdialog_du_to_px(dialog_info, &size.cx, NULL);
996 taskdialog_get_radio_button_size(dialog_info, dialog_info->verification_box, size.cx, &size);
997 SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
998 expando_right = max(expando_right, x + size.cx);
999 expando_bottom = y + size.cy;
1002 /* Common and custom buttons */
1003 button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
1004 line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
1006 button_min_width = DIALOG_BUTTON_WIDTH;
1007 button_height = DIALOG_BUTTON_HEIGHT;
1008 taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
1009 for (i = 0; i < dialog_info->button_count; i++)
1011 taskdialog_get_label_size(dialog_info, dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2,
1012 &size, FALSE);
1013 button_layout_infos[i].width = max(size.cx, button_min_width);
1016 /* Separate buttons into lines */
1017 x = expando_right + h_spacing;
1018 for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
1020 button_layout_infos[i].line = line_count;
1021 x += button_layout_infos[i].width + h_spacing;
1022 line_widths[line_count] += button_layout_infos[i].width + h_spacing;
1024 if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
1026 x = expando_right + h_spacing;
1027 line_count++;
1030 line_count++;
1032 /* Try to balance lines so they are about the same size */
1033 for (i = 1; i < line_count - 1; i++)
1035 int diff_now = abs(line_widths[i] - line_widths[i - 1]);
1036 unsigned int j, last_button = 0;
1037 int diff_changed;
1039 for (j = 0; j < dialog_info->button_count; j++)
1040 if (button_layout_infos[j].line == i - 1) last_button = j;
1042 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1043 diff_changed = abs(2 * button_layout_infos[last_button].width + line_widths[i] - line_widths[i - 1]);
1045 if (diff_changed < diff_now)
1047 button_layout_infos[last_button].line = i;
1048 line_widths[i] += button_layout_infos[last_button].width;
1049 line_widths[i - 1] -= button_layout_infos[last_button].width;
1053 /* Calculate left alignment so all lines are as far right as possible. */
1054 align = dialog_width - h_spacing;
1055 for (i = 0; i < line_count; i++)
1057 int new_alignment = dialog_width - line_widths[i];
1058 if (new_alignment < align) align = new_alignment;
1061 /* Now that we got them all positioned, move all buttons */
1062 x = align;
1063 size.cy = button_height;
1064 for (i = 0; i < dialog_info->button_count; i++)
1066 /* New line */
1067 if (i > 0 && button_layout_infos[i].line != button_layout_infos[i - 1].line)
1069 x = align;
1070 dialog_height += size.cy + v_spacing;
1073 y = dialog_height + v_spacing;
1074 size.cx = button_layout_infos[i].width;
1075 SetWindowPos(dialog_info->buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1076 x += button_layout_infos[i].width + h_spacing;
1079 /* Add height for last row button and spacing */
1080 dialog_height += size.cy + v_spacing;
1081 dialog_height = max(dialog_height, expando_bottom);
1083 Free(button_layout_infos);
1084 Free(line_widths);
1086 /* Footer icon */
1087 footer_icon_right = 0;
1088 footer_icon_bottom = dialog_height;
1089 if (dialog_info->footer_icon)
1091 x = h_spacing;
1092 y = dialog_height + v_spacing;
1093 size.cx = GetSystemMetrics(SM_CXSMICON);
1094 size.cy = GetSystemMetrics(SM_CYSMICON);
1095 SetWindowPos(dialog_info->footer_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1096 footer_icon_right = x + size.cx;
1097 footer_icon_bottom = y + size.cy;
1100 /* Footer text */
1101 taskdialog_label_layout(dialog_info, dialog_info->footer_text, footer_icon_right, dialog_width, &dialog_height,
1102 syslink);
1103 dialog_height = max(dialog_height, footer_icon_bottom);
1105 /* Expanded information */
1106 if ((taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
1107 taskdialog_label_layout(dialog_info, dialog_info->expanded_info, 0, dialog_width, &dialog_height, syslink);
1109 /* Add height for spacing, title height and frame height */
1110 dialog_height += v_spacing;
1111 dialog_height += GetSystemMetrics(SM_CYCAPTION);
1112 dialog_height += GetSystemMetrics(SM_CXDLGFRAME);
1114 if (first_time)
1116 x = (ref_rect.left + ref_rect.right - dialog_width) / 2;
1117 y = (ref_rect.top + ref_rect.bottom - dialog_height) / 2;
1118 SetWindowPos(dialog_info->hwnd, 0, x, y, dialog_width, dialog_height, SWP_NOZORDER);
1119 first_time = FALSE;
1121 else
1122 SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
1125 static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
1127 HWND hwnd;
1128 HDC hdc;
1129 RECT rect = {0};
1130 WCHAR *text;
1131 LONG icon_width, icon_height, text_offset;
1132 UINT style = DFCS_FLAT;
1133 BOOL draw_focus;
1135 hdc = dis->hDC;
1136 hwnd = dis->hwndItem;
1138 SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
1140 icon_width = DIALOG_EXPANDO_ICON_WIDTH;
1141 icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
1142 taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
1143 rect.right = icon_width;
1144 rect.bottom = icon_height;
1145 style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
1146 DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
1148 GetCharWidthW(hdc, '0', '0', &text_offset);
1149 text_offset /= 2;
1151 rect = dis->rcItem;
1152 rect.left += icon_width + text_offset;
1153 text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
1154 DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
1156 draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
1157 if(draw_focus) DrawFocusRect(hdc, &rect);
1160 static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
1162 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
1163 NONCLIENTMETRICSW ncm;
1164 HDC hdc;
1165 INT id;
1167 ncm.cbSize = sizeof(ncm);
1168 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
1170 memset(dialog_info, 0, sizeof(*dialog_info));
1171 dialog_info->taskconfig = taskconfig;
1172 dialog_info->hwnd = hwnd;
1173 dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont);
1175 hdc = GetDC(dialog_info->hwnd);
1176 SelectObject(hdc, dialog_info->font);
1177 dialog_info->m.x_baseunit = GdiGetCharDimensions(hdc, NULL, &dialog_info->m.y_baseunit);
1178 ReleaseDC(dialog_info->hwnd, hdc);
1180 dialog_info->m.h_spacing = DIALOG_SPACING;
1181 dialog_info->m.v_spacing = DIALOG_SPACING;
1182 taskdialog_du_to_px(dialog_info, &dialog_info->m.h_spacing, &dialog_info->m.v_spacing);
1184 if (taskconfig->dwFlags & TDF_CALLBACK_TIMER)
1186 SetTimer(hwnd, ID_TIMER, DIALOG_TIMER_MS, NULL);
1187 dialog_info->last_timer_tick = GetTickCount();
1190 taskdialog_add_main_icon(dialog_info);
1191 taskdialog_add_main_instruction(dialog_info);
1192 taskdialog_add_content(dialog_info);
1193 taskdialog_add_expanded_info(dialog_info);
1194 taskdialog_add_progress_bar(dialog_info);
1195 taskdialog_add_radio_buttons(dialog_info);
1196 taskdialog_add_command_links(dialog_info);
1197 taskdialog_add_expando_button(dialog_info);
1198 taskdialog_add_verification_box(dialog_info);
1199 taskdialog_add_buttons(dialog_info);
1200 taskdialog_add_footer_icon(dialog_info);
1201 taskdialog_add_footer_text(dialog_info);
1203 /* Set default button */
1204 if (!dialog_info->default_button && dialog_info->command_links)
1205 dialog_info->default_button = dialog_info->command_links[0];
1206 if (!dialog_info->default_button) dialog_info->default_button = dialog_info->buttons[0];
1207 SendMessageW(dialog_info->hwnd, WM_NEXTDLGCTL, (WPARAM)dialog_info->default_button, TRUE);
1208 id = GetWindowLongW(dialog_info->default_button, GWLP_ID);
1209 SendMessageW(dialog_info->hwnd, DM_SETDEFID, id, 0);
1211 dialog_info->has_cancel =
1212 (taskconfig->dwFlags & TDF_ALLOW_DIALOG_CANCELLATION)
1213 || taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, IDCANCEL)
1214 || taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, IDCANCEL);
1216 if (!dialog_info->has_cancel) DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
1218 taskdialog_layout(dialog_info);
1221 static BOOL CALLBACK takdialog_destroy_control(HWND hwnd, LPARAM lParam)
1223 DestroyWindow(hwnd);
1224 return TRUE;
1227 static void taskdialog_destroy(struct taskdialog_info *dialog_info)
1229 EnumChildWindows(dialog_info->hwnd, takdialog_destroy_control, 0);
1231 if (dialog_info->taskconfig->dwFlags & TDF_CALLBACK_TIMER) KillTimer(dialog_info->hwnd, ID_TIMER);
1232 if (dialog_info->font) DeleteObject(dialog_info->font);
1233 if (dialog_info->main_instruction_font) DeleteObject(dialog_info->main_instruction_font);
1234 Free(dialog_info->buttons);
1235 Free(dialog_info->radio_buttons);
1236 Free(dialog_info->command_links);
1237 Free(dialog_info->expanded_text);
1238 Free(dialog_info->collapsed_text);
1241 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1243 static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
1244 struct taskdialog_info *dialog_info;
1245 LRESULT result;
1247 TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
1249 if (msg != WM_INITDIALOG)
1250 dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
1252 switch (msg)
1254 case TDM_NAVIGATE_PAGE:
1255 dialog_info->taskconfig = (const TASKDIALOGCONFIG *)lParam;
1256 taskdialog_destroy(dialog_info);
1257 taskdialog_init(dialog_info, hwnd);
1258 taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1259 /* Default radio button click notification is sent before TDN_NAVIGATED */
1260 taskdialog_check_default_radio_buttons(dialog_info);
1261 taskdialog_notify(dialog_info, TDN_NAVIGATED, 0, 0);
1262 break;
1263 case TDM_CLICK_BUTTON:
1264 taskdialog_click_button(dialog_info, wParam);
1265 break;
1266 case TDM_ENABLE_BUTTON:
1267 taskdialog_enable_button(dialog_info, wParam, lParam);
1268 break;
1269 case TDM_SET_MARQUEE_PROGRESS_BAR:
1271 BOOL marquee = wParam;
1272 LONG style;
1273 if(!dialog_info->progress_bar) break;
1274 style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1275 style = marquee ? style | PBS_MARQUEE : style & (~PBS_MARQUEE);
1276 SetWindowLongW(dialog_info->progress_bar, GWL_STYLE, style);
1277 break;
1279 case TDM_SET_PROGRESS_BAR_STATE:
1280 result = SendMessageW(dialog_info->progress_bar, PBM_SETSTATE, wParam, 0);
1281 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1282 break;
1283 case TDM_SET_PROGRESS_BAR_RANGE:
1284 result = SendMessageW(dialog_info->progress_bar, PBM_SETRANGE, 0, lParam);
1285 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1286 break;
1287 case TDM_SET_PROGRESS_BAR_POS:
1288 result = 0;
1289 if (dialog_info->progress_bar)
1291 LONG style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1292 if (!(style & PBS_MARQUEE)) result = SendMessageW(dialog_info->progress_bar, PBM_SETPOS, wParam, 0);
1294 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1295 break;
1296 case TDM_SET_PROGRESS_BAR_MARQUEE:
1297 SendMessageW(dialog_info->progress_bar, PBM_SETMARQUEE, wParam, lParam);
1298 break;
1299 case TDM_SET_ELEMENT_TEXT:
1300 taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1301 taskdialog_layout(dialog_info);
1302 break;
1303 case TDM_UPDATE_ELEMENT_TEXT:
1304 taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1305 break;
1306 case TDM_CLICK_RADIO_BUTTON:
1307 taskdialog_click_radio_button(dialog_info, wParam);
1308 break;
1309 case TDM_ENABLE_RADIO_BUTTON:
1310 taskdialog_enable_radio_button(dialog_info, wParam, lParam);
1311 break;
1312 case TDM_CLICK_VERIFICATION:
1314 BOOL checked = (BOOL)wParam;
1315 BOOL focused = (BOOL)lParam;
1316 dialog_info->verification_checked = checked;
1317 if (dialog_info->verification_box)
1319 SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
1320 taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
1321 if (focused) SetFocus(dialog_info->verification_box);
1323 break;
1325 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE:
1326 taskdialog_button_set_shield(dialog_info, wParam, lParam);
1327 break;
1328 case TDM_UPDATE_ICON:
1329 taskdialog_set_icon(dialog_info, wParam, (HICON)lParam);
1330 break;
1331 case WM_INITDIALOG:
1332 dialog_info = (struct taskdialog_info *)lParam;
1334 taskdialog_init(dialog_info, hwnd);
1336 SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
1337 taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1338 taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
1339 /* Default radio button click notification sent after TDN_CREATED */
1340 taskdialog_check_default_radio_buttons(dialog_info);
1341 return FALSE;
1342 case WM_COMMAND:
1343 if (HIWORD(wParam) == BN_CLICKED)
1345 taskdialog_on_button_click(dialog_info, (HWND)lParam, LOWORD(wParam));
1346 break;
1348 return FALSE;
1349 case WM_HELP:
1350 taskdialog_notify(dialog_info, TDN_HELP, 0, 0);
1351 break;
1352 case WM_TIMER:
1353 if (ID_TIMER == wParam)
1355 DWORD elapsed = GetTickCount() - dialog_info->last_timer_tick;
1356 if (taskdialog_notify(dialog_info, TDN_TIMER, elapsed, 0) == S_FALSE)
1357 dialog_info->last_timer_tick = GetTickCount();
1359 break;
1360 case WM_NOTIFY:
1362 PNMLINK pnmLink = (PNMLINK)lParam;
1363 HWND hwndFrom = pnmLink->hdr.hwndFrom;
1364 if ((taskdialog_hyperlink_enabled(dialog_info))
1365 && (hwndFrom == dialog_info->content || hwndFrom == dialog_info->expanded_info
1366 || hwndFrom == dialog_info->footer_text)
1367 && (pnmLink->hdr.code == NM_CLICK || pnmLink->hdr.code == NM_RETURN))
1369 taskdialog_notify(dialog_info, TDN_HYPERLINK_CLICKED, 0, (LPARAM)pnmLink->item.szUrl);
1370 break;
1372 return FALSE;
1374 case WM_DRAWITEM:
1376 LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
1377 if (dis->hwndItem == dialog_info->expando_button)
1379 taskdialog_draw_expando_control(dialog_info, dis);
1380 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE);
1381 break;
1383 return FALSE;
1385 case WM_DESTROY:
1386 taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
1387 RemovePropW(hwnd, taskdialog_info_propnameW);
1388 taskdialog_destroy(dialog_info);
1389 break;
1390 case WM_CLOSE:
1391 if (dialog_info->has_cancel)
1393 if(taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, IDCANCEL, 0) == S_OK)
1394 EndDialog(hwnd, IDCANCEL);
1395 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 0);
1396 break;
1398 return FALSE;
1399 default:
1400 return FALSE;
1402 return TRUE;
1405 /***********************************************************************
1406 * TaskDialogIndirect [COMCTL32.@]
1408 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
1409 int *radio_button, BOOL *verification_flag_checked)
1411 struct taskdialog_info dialog_info;
1412 DLGTEMPLATE *template;
1413 INT ret;
1415 TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
1417 if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
1418 return E_INVALIDARG;
1420 dialog_info.taskconfig = taskconfig;
1422 template = create_taskdialog_template(taskconfig);
1423 ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
1424 taskdialog_proc, (LPARAM)&dialog_info);
1425 Free(template);
1427 if (button) *button = ret;
1428 if (radio_button) *radio_button = dialog_info.selected_radio_id;
1429 if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
1431 return S_OK;
1434 /***********************************************************************
1435 * TaskDialog [COMCTL32.@]
1437 HRESULT WINAPI TaskDialog(HWND owner, HINSTANCE hinst, const WCHAR *title, const WCHAR *main_instruction,
1438 const WCHAR *content, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons, const WCHAR *icon, int *button)
1440 TASKDIALOGCONFIG taskconfig;
1442 TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner, hinst, debugstr_w(title), debugstr_w(main_instruction),
1443 debugstr_w(content), common_buttons, debugstr_w(icon), button);
1445 memset(&taskconfig, 0, sizeof(taskconfig));
1446 taskconfig.cbSize = sizeof(taskconfig);
1447 taskconfig.hwndParent = owner;
1448 taskconfig.hInstance = hinst;
1449 taskconfig.dwCommonButtons = common_buttons;
1450 taskconfig.pszWindowTitle = title;
1451 taskconfig.u.pszMainIcon = icon;
1452 taskconfig.pszMainInstruction = main_instruction;
1453 taskconfig.pszContent = content;
1454 return TaskDialogIndirect(&taskconfig, button, NULL, NULL);