wined3d: Move the fill mode to wined3d_rasterizer_state.
[wine.git] / dlls / comctl32 / taskdialog.c
blob3d98c644e84102a17bd776db622ad3066b5f12ad
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"
39 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
41 static const UINT DIALOG_MIN_WIDTH = 240;
42 static const UINT DIALOG_SPACING = 5;
43 static const UINT DIALOG_BUTTON_WIDTH = 50;
44 static const UINT DIALOG_BUTTON_HEIGHT = 14;
45 static const UINT DIALOG_EXPANDO_ICON_WIDTH = 10;
46 static const UINT DIALOG_EXPANDO_ICON_HEIGHT = 10;
47 static const UINT DIALOG_TIMER_MS = 200;
49 static const UINT ID_TIMER = 1;
51 struct taskdialog_info
53 HWND hwnd;
54 const TASKDIALOGCONFIG *taskconfig;
55 DWORD last_timer_tick;
56 HFONT font;
57 HFONT main_instruction_font;
58 /* Control handles */
59 HWND main_icon;
60 HWND main_instruction;
61 HWND content;
62 HWND progress_bar;
63 HWND *radio_buttons;
64 INT radio_button_count;
65 HWND *command_links;
66 INT command_link_count;
67 HWND expanded_info;
68 HWND expando_button;
69 HWND verification_box;
70 HWND footer_icon;
71 HWND footer_text;
72 HWND *buttons;
73 INT button_count;
74 HWND default_button;
75 /* Dialog metrics */
76 struct
78 LONG x_baseunit;
79 LONG y_baseunit;
80 LONG h_spacing;
81 LONG v_spacing;
82 } m;
83 INT selected_radio_id;
84 BOOL verification_checked;
85 BOOL expanded;
86 BOOL has_cancel;
87 WCHAR *expanded_text;
88 WCHAR *collapsed_text;
91 struct button_layout_info
93 LONG width;
94 LONG line;
97 static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam);
98 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id);
99 static void taskdialog_layout(struct taskdialog_info *dialog_info);
101 static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
103 if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4);
104 if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8);
107 static void template_write_data(char **ptr, const void *src, unsigned int size)
109 memcpy(*ptr, src, size);
110 *ptr += size;
113 static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret)
115 HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(),
116 MONITOR_DEFAULTTOPRIMARY);
117 MONITORINFO info;
119 info.cbSize = sizeof(info);
120 GetMonitorInfoW(monitor, &info);
122 if ((taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) && taskconfig->hwndParent)
123 GetWindowRect(taskconfig->hwndParent, ret);
124 else
125 *ret = info.rcWork;
127 return info.rcWork.right - info.rcWork.left;
130 static WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length)
132 DWORD len = GetModuleFileNameW(NULL, name, length);
133 if (len && len < length)
135 WCHAR *p;
136 if ((p = wcsrchr(name, '/'))) name = p + 1;
137 if ((p = wcsrchr(name, '\\'))) name = p + 1;
138 return name;
140 else
141 return NULL;
144 static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
146 unsigned int size, title_size;
147 static const WORD fontsize = 0x7fff;
148 static const WCHAR emptyW[] = { 0 };
149 const WCHAR *titleW = NULL;
150 DLGTEMPLATE *template;
151 WCHAR pathW[MAX_PATH];
152 char *ptr;
154 /* Window title */
155 if (!taskconfig->pszWindowTitle)
156 titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
157 else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
159 if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
160 titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
162 else
163 titleW = taskconfig->pszWindowTitle;
164 if (!titleW)
165 titleW = emptyW;
166 title_size = (lstrlenW(titleW) + 1) * sizeof(WCHAR);
168 size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
169 size += title_size;
170 size += 2; /* font size */
172 template = Alloc(size);
173 if (!template) return NULL;
175 template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
176 if (taskconfig->dwFlags & TDF_CAN_BE_MINIMIZED) template->style |= WS_MINIMIZEBOX;
177 if (!(taskconfig->dwFlags & TDF_NO_SET_FOREGROUND)) template->style |= DS_SETFOREGROUND;
178 if (taskconfig->dwFlags & TDF_RTL_LAYOUT) template->dwExtendedStyle = WS_EX_LAYOUTRTL | WS_EX_RIGHT | WS_EX_RTLREADING;
180 ptr = (char *)(template + 1);
181 ptr += 2; /* menu */
182 ptr += 2; /* class */
183 template_write_data(&ptr, titleW, title_size);
184 template_write_data(&ptr, &fontsize, sizeof(fontsize));
186 return template;
189 static HWND taskdialog_find_button(HWND *buttons, INT count, INT id)
191 INT button_id;
192 INT i;
194 for (i = 0; i < count; i++)
196 button_id = GetWindowLongW(buttons[i], GWLP_ID);
197 if (button_id == id) return buttons[i];
200 return NULL;
203 static void taskdialog_enable_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
205 HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
206 if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
207 if (hwnd) EnableWindow(hwnd, enable);
210 static void taskdialog_click_button(struct taskdialog_info *dialog_info, INT id)
212 if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, id, 0) == S_OK) EndDialog(dialog_info->hwnd, id);
215 static void taskdialog_button_set_shield(const struct taskdialog_info *dialog_info, INT id, BOOL elevate)
217 HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
218 if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
219 if (hwnd) SendMessageW(hwnd, BCM_SETSHIELD, 0, elevate);
222 static void taskdialog_enable_radio_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
224 HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
225 if (hwnd) EnableWindow(hwnd, enable);
228 static void taskdialog_click_radio_button(const struct taskdialog_info *dialog_info, INT id)
230 HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
231 if (hwnd) SendMessageW(hwnd, BM_CLICK, 0, 0);
234 static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
236 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
237 return taskconfig->pfCallback
238 ? taskconfig->pfCallback(dialog_info->hwnd, notification, wparam, lparam, taskconfig->lpCallbackData)
239 : S_OK;
242 static void taskdialog_move_controls_vertically(HWND parent, HWND *controls, INT count, INT offset)
244 RECT rect;
245 POINT pt;
246 INT i;
248 for (i = 0; i < count; i++)
250 if (!controls[i]) continue;
252 GetWindowRect(controls[i], &rect);
253 pt.x = rect.left;
254 pt.y = rect.top;
255 MapWindowPoints(HWND_DESKTOP, parent, &pt, 1);
256 SetWindowPos(controls[i], 0, pt.x, pt.y + offset, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
260 static void taskdialog_toggle_expando_control(struct taskdialog_info *dialog_info)
262 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
263 const WCHAR *text;
264 RECT info_rect, rect;
265 INT height, offset;
267 dialog_info->expanded = !dialog_info->expanded;
268 text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
269 SendMessageW(dialog_info->expando_button, WM_SETTEXT, 0, (LPARAM)text);
270 ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
272 GetWindowRect(dialog_info->expanded_info, &info_rect);
273 /* If expanded information starts up not expanded, call taskdialog_layout()
274 * to to set size for expanded information control at least once */
275 if (IsRectEmpty(&info_rect))
277 taskdialog_layout(dialog_info);
278 return;
280 height = info_rect.bottom - info_rect.top + dialog_info->m.v_spacing;
281 offset = dialog_info->expanded ? height : -height;
283 /* Update vertical layout, move all controls after expanded information */
284 /* Move dialog */
285 GetWindowRect(dialog_info->hwnd, &rect);
286 SetWindowPos(dialog_info->hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top + offset,
287 SWP_NOMOVE | SWP_NOZORDER);
288 /* Move controls */
289 if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA))
291 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->progress_bar, 1, offset);
292 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->expando_button, 1, offset);
293 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->verification_box, 1, offset);
294 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_icon, 1, offset);
295 taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_text, 1, offset);
296 taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->buttons, dialog_info->button_count, offset);
297 taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->radio_buttons,
298 dialog_info->radio_button_count, offset);
299 taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->command_links,
300 dialog_info->command_link_count, offset);
304 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id)
306 INT command_id;
307 HWND button, radio_button;
309 /* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
310 command_id = hwnd ? GetWindowLongW(hwnd, GWLP_ID) : id;
312 if (hwnd && hwnd == dialog_info->expando_button)
314 taskdialog_toggle_expando_control(dialog_info);
315 taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
316 return;
319 if (hwnd && hwnd == dialog_info->verification_box)
321 dialog_info->verification_checked = !dialog_info->verification_checked;
322 taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
323 return;
326 radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
327 if (radio_button)
329 dialog_info->selected_radio_id = command_id;
330 taskdialog_notify(dialog_info, TDN_RADIO_BUTTON_CLICKED, command_id, 0);
331 return;
334 button = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, command_id);
335 if (!button) button = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, command_id);
336 if (!button && command_id == IDOK)
338 button = dialog_info->command_link_count > 0 ? dialog_info->command_links[0] : dialog_info->buttons[0];
339 command_id = GetWindowLongW(button, GWLP_ID);
342 if (button && taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
343 EndDialog(dialog_info->hwnd, command_id);
346 static WCHAR *taskdialog_gettext(struct taskdialog_info *dialog_info, BOOL user_resource, const WCHAR *text)
348 const WCHAR *textW = NULL;
349 INT length;
350 WCHAR *ret;
352 if (IS_INTRESOURCE(text))
354 if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule,
355 (UINT_PTR)text, (WCHAR *)&textW, 0)))
356 return NULL;
358 else
360 textW = text;
361 length = lstrlenW(textW);
364 ret = Alloc((length + 1) * sizeof(WCHAR));
365 if (ret) memcpy(ret, textW, length * sizeof(WCHAR));
367 return ret;
370 static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info)
372 return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS;
375 static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info)
377 return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON);
380 static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size,
381 BOOL syslink)
383 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
384 HFONT hfont, old_hfont;
385 HDC hdc;
386 RECT rect = {0};
387 WCHAR *text;
388 INT text_length;
390 if (syslink)
392 SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size);
393 return;
396 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
397 style |= DT_RIGHT | DT_RTLREADING;
398 else
399 style |= DT_LEFT;
401 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
402 text_length = GetWindowTextLengthW(hwnd);
403 text = Alloc((text_length + 1) * sizeof(WCHAR));
404 if (!text)
406 size->cx = 0;
407 size->cy = 0;
408 return;
410 GetWindowTextW(hwnd, text, text_length + 1);
411 hdc = GetDC(hwnd);
412 old_hfont = SelectObject(hdc, hfont);
413 rect.right = max_width;
414 size->cy = DrawTextW(hdc, text, text_length, &rect, style);
415 size->cx = min(max_width, rect.right - rect.left);
416 if (old_hfont) SelectObject(hdc, old_hfont);
417 ReleaseDC(hwnd, hdc);
418 Free(text);
421 static void taskdialog_get_button_size(HWND hwnd, LONG max_width, SIZE *size)
423 size->cx = max_width;
424 size->cy = 0;
425 SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)size);
428 static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
430 DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
431 HFONT hfont, old_hfont;
432 HDC hdc;
433 RECT rect = {0};
434 LONG icon_width, icon_height, text_offset;
435 LONG max_width, max_text_height;
437 hdc = GetDC(hwnd);
438 hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
439 old_hfont = SelectObject(hdc, hfont);
441 icon_width = DIALOG_EXPANDO_ICON_WIDTH;
442 icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
443 taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
445 GetCharWidthW(hdc, '0', '0', &text_offset);
446 text_offset /= 2;
448 if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
449 style |= DT_RIGHT | DT_RTLREADING;
450 else
451 style |= DT_LEFT;
453 max_width = DIALOG_MIN_WIDTH / 2;
454 taskdialog_du_to_px(dialog_info, &max_width, NULL);
456 rect.right = max_width - icon_width - text_offset;
457 max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
458 size->cy = max(max_text_height, icon_height);
459 size->cx = rect.right - rect.left;
461 rect.right = max_width - icon_width - text_offset;
462 max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
463 size->cy = max(size->cy, max_text_height);
464 size->cx = max(size->cx, rect.right - rect.left);
465 size->cx = min(size->cx, max_width);
467 if (old_hfont) SelectObject(hdc, old_hfont);
468 ReleaseDC(hwnd, hdc);
471 static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
473 if (icon == TD_WARNING_ICON)
474 return IDI_WARNING;
475 else if (icon == TD_ERROR_ICON)
476 return IDI_ERROR;
477 else if (icon == TD_INFORMATION_ICON)
478 return IDI_INFORMATION;
479 else if (icon == TD_SHIELD_ICON)
480 return IDI_SHIELD;
481 else
482 return (ULONG_PTR)icon;
485 static void taskdialog_set_icon(struct taskdialog_info *dialog_info, INT element, HICON icon)
487 DWORD flags = dialog_info->taskconfig->dwFlags;
488 INT cx = 0, cy = 0;
489 HICON hicon;
491 if (!icon) return;
493 if (((flags & TDF_USE_HICON_MAIN) && element == TDIE_ICON_MAIN)
494 || ((flags & TDF_USE_HICON_FOOTER) && element == TDIE_ICON_FOOTER))
495 hicon = icon;
496 else
498 if (element == TDIE_ICON_FOOTER)
500 cx = GetSystemMetrics(SM_CXSMICON);
501 cy = GetSystemMetrics(SM_CYSMICON);
503 hicon = LoadImageW(dialog_info->taskconfig->hInstance, (LPCWSTR)icon, IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTSIZE);
504 if (!hicon)
505 hicon = LoadImageW(NULL, (LPCWSTR)taskdialog_get_standard_icon((LPCWSTR)icon), IMAGE_ICON, cx, cy,
506 LR_SHARED | LR_DEFAULTSIZE);
509 if (!hicon) return;
511 if (element == TDIE_ICON_MAIN)
513 SendMessageW(dialog_info->hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hicon);
514 SendMessageW(dialog_info->main_icon, STM_SETICON, (WPARAM)hicon, 0);
516 else if (element == TDIE_ICON_FOOTER)
517 SendMessageW(dialog_info->footer_icon, STM_SETICON, (WPARAM)hicon, 0);
520 static void taskdialog_set_element_text(struct taskdialog_info *dialog_info, TASKDIALOG_ELEMENTS element,
521 const WCHAR *text)
523 HWND hwnd = NULL;
524 WCHAR *textW;
526 if (element == TDE_CONTENT)
527 hwnd = dialog_info->content;
528 else if (element == TDE_EXPANDED_INFORMATION)
529 hwnd = dialog_info->expanded_info;
530 else if (element == TDE_FOOTER)
531 hwnd = dialog_info->footer_text;
532 else if (element == TDE_MAIN_INSTRUCTION)
533 hwnd = dialog_info->main_instruction;
535 if (!hwnd) return;
537 textW = taskdialog_gettext(dialog_info, TRUE, text);
538 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
539 Free(textW);
542 static void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info)
544 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
545 HWND default_button;
547 if (!dialog_info->radio_button_count) return;
549 default_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count,
550 taskconfig->nDefaultRadioButton);
552 if (!default_button && !(taskconfig->dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON))
553 default_button = dialog_info->radio_buttons[0];
555 if (default_button)
557 SendMessageW(default_button, BM_SETCHECK, BST_CHECKED, 0);
558 taskdialog_on_button_click(dialog_info, default_button, 0);
562 static void taskdialog_add_main_icon(struct taskdialog_info *dialog_info)
564 if (!dialog_info->taskconfig->u.hMainIcon) return;
566 dialog_info->main_icon =
567 CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
568 taskdialog_set_icon(dialog_info, TDIE_ICON_MAIN, dialog_info->taskconfig->u.hMainIcon);
571 static HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink)
573 WCHAR *textW;
574 HWND hwnd;
575 const WCHAR *class;
576 DWORD style = WS_CHILD | WS_VISIBLE;
578 if (!text) return NULL;
580 class = syslink ? WC_LINK : WC_STATICW;
581 if (syslink) style |= WS_TABSTOP;
582 textW = taskdialog_gettext(dialog_info, TRUE, text);
583 hwnd = CreateWindowW(class, textW, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
584 Free(textW);
586 SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0);
587 return hwnd;
590 static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info)
592 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
593 NONCLIENTMETRICSW ncm;
595 if (!taskconfig->pszMainInstruction) return;
597 ncm.cbSize = sizeof(ncm);
598 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
599 /* 1.25 times the height */
600 ncm.lfMessageFont.lfHeight = ncm.lfMessageFont.lfHeight * 5 / 4;
601 ncm.lfMessageFont.lfWeight = FW_BOLD;
602 dialog_info->main_instruction_font = CreateFontIndirectW(&ncm.lfMessageFont);
604 dialog_info->main_instruction =
605 taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE);
608 static void taskdialog_add_content(struct taskdialog_info *dialog_info)
610 dialog_info->content = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszContent, dialog_info->font,
611 taskdialog_hyperlink_enabled(dialog_info));
614 static void taskdialog_add_progress_bar(struct taskdialog_info *dialog_info)
616 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
617 DWORD style = PBS_SMOOTH | PBS_SMOOTHREVERSE | WS_CHILD | WS_VISIBLE;
619 if (!(taskconfig->dwFlags & (TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR))) return;
620 if (taskconfig->dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) style |= PBS_MARQUEE;
621 dialog_info->progress_bar =
622 CreateWindowW(PROGRESS_CLASSW, NULL, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
625 static void taskdialog_add_radio_buttons(struct taskdialog_info *dialog_info)
627 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
628 static const DWORD style = BS_AUTORADIOBUTTON | BS_MULTILINE | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
629 WCHAR *textW;
630 INT i;
632 if (!taskconfig->cRadioButtons || !taskconfig->pRadioButtons) return;
634 dialog_info->radio_buttons = Alloc(taskconfig->cRadioButtons * sizeof(*dialog_info->radio_buttons));
635 if (!dialog_info->radio_buttons) return;
637 dialog_info->radio_button_count = taskconfig->cRadioButtons;
638 for (i = 0; i < dialog_info->radio_button_count; i++)
640 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pRadioButtons[i].pszButtonText);
641 dialog_info->radio_buttons[i] =
642 CreateWindowW(WC_BUTTONW, textW, i == 0 ? style | WS_GROUP : style, 0, 0, 0, 0, dialog_info->hwnd,
643 LongToHandle(taskconfig->pRadioButtons[i].nButtonID), 0, NULL);
644 SendMessageW(dialog_info->radio_buttons[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
645 Free(textW);
649 static void taskdialog_add_command_links(struct taskdialog_info *dialog_info)
651 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
652 DWORD default_style = BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP, style;
653 BOOL is_default;
654 WCHAR *textW;
655 INT i;
657 if (!taskconfig->cButtons || !taskconfig->pButtons || !taskdialog_use_command_link(dialog_info)) return;
659 dialog_info->command_links = Alloc(taskconfig->cButtons * sizeof(*dialog_info->command_links));
660 if (!dialog_info->command_links) return;
662 dialog_info->command_link_count = taskconfig->cButtons;
663 for (i = 0; i < dialog_info->command_link_count; i++)
665 is_default = taskconfig->pButtons[i].nButtonID == taskconfig->nDefaultButton;
666 style = is_default ? default_style | BS_DEFCOMMANDLINK : default_style | BS_COMMANDLINK;
667 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pButtons[i].pszButtonText);
668 dialog_info->command_links[i] = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd,
669 LongToHandle(taskconfig->pButtons[i].nButtonID), 0, NULL);
670 SendMessageW(dialog_info->command_links[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
671 Free(textW);
673 if (is_default && !dialog_info->default_button) dialog_info->default_button = dialog_info->command_links[i];
677 static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
679 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
681 if (!taskconfig->pszExpandedInformation) return;
683 dialog_info->expanded = taskconfig->dwFlags & TDF_EXPANDED_BY_DEFAULT;
684 dialog_info->expanded_info = taskdialog_create_label(dialog_info, taskconfig->pszExpandedInformation,
685 dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
686 ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
689 static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
691 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
692 const WCHAR *textW;
694 if (!taskconfig->pszExpandedInformation) return;
696 if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
698 dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
699 dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
701 else
703 textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
704 : taskconfig->pszCollapsedControlText;
705 dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
706 textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
707 : taskconfig->pszExpandedControlText;
708 dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
711 textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
713 dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
714 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
715 SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
718 static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
720 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
721 static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
722 WCHAR *textW;
724 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
725 if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
727 if (!taskconfig->pszVerificationText) return;
729 textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
730 dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
731 SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
732 Free(textW);
734 if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
735 SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
738 static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
739 BOOL custom_button)
741 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
742 WCHAR *textW;
744 textW = taskdialog_gettext(dialog_info, custom_button, text);
745 *button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, dialog_info->hwnd,
746 (HMENU)id, 0, NULL);
747 Free(textW);
748 SendMessageW(*button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
750 if (id == taskconfig->nDefaultButton && !dialog_info->default_button) dialog_info->default_button = *button;
753 static void taskdialog_add_buttons(struct taskdialog_info *dialog_info)
755 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
756 BOOL use_command_links = taskdialog_use_command_link(dialog_info);
757 DWORD flags = taskconfig->dwCommonButtons;
758 INT count, max_count;
760 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
761 max_count = 6;
762 if (!use_command_links && taskconfig->cButtons && taskconfig->pButtons) max_count += taskconfig->cButtons;
764 dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons));
765 if (!dialog_info->buttons) return;
767 for (count = 0; !use_command_links && count < taskconfig->cButtons; count++)
768 taskdialog_add_button(dialog_info, &dialog_info->buttons[count], taskconfig->pButtons[count].nButtonID,
769 taskconfig->pButtons[count].pszButtonText, TRUE);
771 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
772 do \
774 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
775 FALSE); \
776 } while (0)
778 if (flags & TDCBF_OK_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(OK);
779 if (flags & TDCBF_YES_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(YES);
780 if (flags & TDCBF_NO_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(NO);
781 if (flags & TDCBF_RETRY_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
782 if (flags & TDCBF_CANCEL_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
783 if (flags & TDCBF_CLOSE_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
785 if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK);
786 #undef TASKDIALOG_INIT_COMMON_BUTTON
788 dialog_info->button_count = count;
791 static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info)
793 if (!dialog_info->taskconfig->u2.hFooterIcon) return;
795 dialog_info->footer_icon =
796 CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, 0);
797 taskdialog_set_icon(dialog_info, TDIE_ICON_FOOTER, dialog_info->taskconfig->u2.hFooterIcon);
800 static void taskdialog_add_footer_text(struct taskdialog_info *dialog_info)
802 dialog_info->footer_text = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszFooter,
803 dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
806 static LONG taskdialog_get_dialog_width(struct taskdialog_info *dialog_info)
808 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
809 BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
810 LONG max_width, main_icon_width, screen_width;
811 RECT rect;
812 SIZE size;
814 screen_width = taskdialog_get_reference_rect(taskconfig, &rect);
815 if ((taskconfig->dwFlags & TDF_SIZE_TO_CONTENT) && !taskconfig->cxWidth)
817 max_width = DIALOG_MIN_WIDTH;
818 taskdialog_du_to_px(dialog_info, &max_width, NULL);
819 main_icon_width = dialog_info->m.h_spacing;
820 if (dialog_info->main_icon) main_icon_width += GetSystemMetrics(SM_CXICON);
821 if (dialog_info->content)
823 taskdialog_get_label_size(dialog_info, dialog_info->content, 0, &size, syslink);
824 max_width = max(max_width, size.cx + main_icon_width + dialog_info->m.h_spacing * 2);
827 else
829 max_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
830 taskdialog_du_to_px(dialog_info, &max_width, NULL);
832 max_width = min(max_width, screen_width);
833 return max_width;
836 static void taskdialog_label_layout(struct taskdialog_info *dialog_info, HWND hwnd, INT start_x, LONG dialog_width,
837 LONG *dialog_height, BOOL syslink)
839 LONG x, y, max_width;
840 SIZE size;
842 if (!hwnd) return;
844 x = start_x + dialog_info->m.h_spacing;
845 y = *dialog_height + dialog_info->m.v_spacing;
846 max_width = dialog_width - x - dialog_info->m.h_spacing;
847 taskdialog_get_label_size(dialog_info, hwnd, max_width, &size, syslink);
848 SetWindowPos(hwnd, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
849 *dialog_height = y + size.cy;
852 static void taskdialog_layout(struct taskdialog_info *dialog_info)
854 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
855 BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
856 static BOOL first_time = TRUE;
857 RECT ref_rect;
858 LONG dialog_width, dialog_height = 0;
859 LONG h_spacing, v_spacing;
860 LONG main_icon_right, main_icon_bottom;
861 LONG expando_right, expando_bottom;
862 struct button_layout_info *button_layout_infos;
863 LONG button_min_width, button_height;
864 LONG *line_widths, line_count, align;
865 LONG footer_icon_right, footer_icon_bottom;
866 LONG x, y;
867 SIZE size;
868 INT i;
870 taskdialog_get_reference_rect(dialog_info->taskconfig, &ref_rect);
871 dialog_width = taskdialog_get_dialog_width(dialog_info);
873 h_spacing = dialog_info->m.h_spacing;
874 v_spacing = dialog_info->m.v_spacing;
876 /* Main icon */
877 main_icon_right = 0;
878 main_icon_bottom = 0;
879 if (dialog_info->main_icon)
881 x = h_spacing;
882 y = dialog_height + v_spacing;
883 size.cx = GetSystemMetrics(SM_CXICON);
884 size.cy = GetSystemMetrics(SM_CYICON);
885 SetWindowPos(dialog_info->main_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
886 main_icon_right = x + size.cx;
887 main_icon_bottom = y + size.cy;
890 /* Main instruction */
891 taskdialog_label_layout(dialog_info, dialog_info->main_instruction, main_icon_right, dialog_width, &dialog_height,
892 FALSE);
894 /* Content */
895 taskdialog_label_layout(dialog_info, dialog_info->content, main_icon_right, dialog_width, &dialog_height, syslink);
897 /* Expanded information */
898 if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
899 taskdialog_label_layout(dialog_info, dialog_info->expanded_info, main_icon_right, dialog_width, &dialog_height,
900 syslink);
902 /* Progress bar */
903 if (dialog_info->progress_bar)
905 x = main_icon_right + h_spacing;
906 y = dialog_height + v_spacing;
907 size.cx = dialog_width - x - h_spacing;
908 size.cy = GetSystemMetrics(SM_CYVSCROLL);
909 SetWindowPos(dialog_info->progress_bar, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
910 dialog_height = y + size.cy;
913 /* Radio buttons */
914 for (i = 0; i < dialog_info->radio_button_count; i++)
916 x = main_icon_right + h_spacing;
917 y = dialog_height + v_spacing;
918 taskdialog_get_button_size(dialog_info->radio_buttons[i], dialog_width - x - h_spacing, &size);
919 size.cx = dialog_width - x - h_spacing;
920 SetWindowPos(dialog_info->radio_buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
921 dialog_height = y + size.cy;
924 /* Command links */
925 for (i = 0; i < dialog_info->command_link_count; i++)
927 x = main_icon_right + h_spacing;
928 y = dialog_height;
929 /* Only add spacing for the first command links. There is no vertical spacing between command links */
930 if (!i)
931 y += v_spacing;
932 taskdialog_get_button_size(dialog_info->command_links[i], dialog_width - x - h_spacing, &size);
933 size.cx = dialog_width - x - h_spacing;
934 /* Add spacing */
935 size.cy += 4;
936 SetWindowPos(dialog_info->command_links[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
937 dialog_height = y + size.cy;
940 dialog_height = max(dialog_height, main_icon_bottom);
942 expando_right = 0;
943 expando_bottom = dialog_height;
944 /* Expando control */
945 if (dialog_info->expando_button)
947 x = h_spacing;
948 y = dialog_height + v_spacing;
949 taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
950 SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
951 expando_right = x + size.cx;
952 expando_bottom = y + size.cy;
955 /* Verification box */
956 if (dialog_info->verification_box)
958 x = h_spacing;
959 y = expando_bottom + v_spacing;
960 size.cx = DIALOG_MIN_WIDTH / 2;
961 taskdialog_du_to_px(dialog_info, &size.cx, NULL);
962 taskdialog_get_button_size(dialog_info->verification_box, size.cx, &size);
963 SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
964 expando_right = max(expando_right, x + size.cx);
965 expando_bottom = y + size.cy;
968 /* Common and custom buttons */
969 button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
970 line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
972 button_min_width = DIALOG_BUTTON_WIDTH;
973 button_height = DIALOG_BUTTON_HEIGHT;
974 taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
975 for (i = 0; i < dialog_info->button_count; i++)
977 taskdialog_get_button_size(dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2, &size);
978 button_layout_infos[i].width = max(size.cx, button_min_width);
981 /* Separate buttons into lines */
982 x = expando_right + h_spacing;
983 for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
985 button_layout_infos[i].line = line_count;
986 x += button_layout_infos[i].width + h_spacing;
987 line_widths[line_count] += button_layout_infos[i].width + h_spacing;
989 if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
991 x = expando_right + h_spacing;
992 line_count++;
995 line_count++;
997 /* Try to balance lines so they are about the same size */
998 for (i = 1; i < line_count - 1; i++)
1000 int diff_now = abs(line_widths[i] - line_widths[i - 1]);
1001 unsigned int j, last_button = 0;
1002 int diff_changed;
1004 for (j = 0; j < dialog_info->button_count; j++)
1005 if (button_layout_infos[j].line == i - 1) last_button = j;
1007 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1008 diff_changed = abs(2 * button_layout_infos[last_button].width + line_widths[i] - line_widths[i - 1]);
1010 if (diff_changed < diff_now)
1012 button_layout_infos[last_button].line = i;
1013 line_widths[i] += button_layout_infos[last_button].width;
1014 line_widths[i - 1] -= button_layout_infos[last_button].width;
1018 /* Calculate left alignment so all lines are as far right as possible. */
1019 align = dialog_width - h_spacing;
1020 for (i = 0; i < line_count; i++)
1022 int new_alignment = dialog_width - line_widths[i];
1023 if (new_alignment < align) align = new_alignment;
1026 /* Now that we got them all positioned, move all buttons */
1027 x = align;
1028 size.cy = button_height;
1029 for (i = 0; i < dialog_info->button_count; i++)
1031 /* New line */
1032 if (i > 0 && button_layout_infos[i].line != button_layout_infos[i - 1].line)
1034 x = align;
1035 dialog_height += size.cy + v_spacing;
1038 y = dialog_height + v_spacing;
1039 size.cx = button_layout_infos[i].width;
1040 SetWindowPos(dialog_info->buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1041 x += button_layout_infos[i].width + h_spacing;
1044 /* Add height for last row button and spacing */
1045 dialog_height += size.cy + v_spacing;
1046 dialog_height = max(dialog_height, expando_bottom);
1048 Free(button_layout_infos);
1049 Free(line_widths);
1051 /* Footer icon */
1052 footer_icon_right = 0;
1053 footer_icon_bottom = dialog_height;
1054 if (dialog_info->footer_icon)
1056 x = h_spacing;
1057 y = dialog_height + v_spacing;
1058 size.cx = GetSystemMetrics(SM_CXSMICON);
1059 size.cy = GetSystemMetrics(SM_CYSMICON);
1060 SetWindowPos(dialog_info->footer_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1061 footer_icon_right = x + size.cx;
1062 footer_icon_bottom = y + size.cy;
1065 /* Footer text */
1066 taskdialog_label_layout(dialog_info, dialog_info->footer_text, footer_icon_right, dialog_width, &dialog_height,
1067 syslink);
1068 dialog_height = max(dialog_height, footer_icon_bottom);
1070 /* Expanded information */
1071 if ((taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
1072 taskdialog_label_layout(dialog_info, dialog_info->expanded_info, 0, dialog_width, &dialog_height, syslink);
1074 /* Add height for spacing, title height and frame height */
1075 dialog_height += v_spacing;
1076 dialog_height += GetSystemMetrics(SM_CYCAPTION);
1077 dialog_height += GetSystemMetrics(SM_CXDLGFRAME);
1079 if (first_time)
1081 x = (ref_rect.left + ref_rect.right - dialog_width) / 2;
1082 y = (ref_rect.top + ref_rect.bottom - dialog_height) / 2;
1083 SetWindowPos(dialog_info->hwnd, 0, x, y, dialog_width, dialog_height, SWP_NOZORDER);
1084 first_time = FALSE;
1086 else
1087 SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
1090 static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
1092 HWND hwnd;
1093 HDC hdc;
1094 RECT rect = {0};
1095 WCHAR *text;
1096 LONG icon_width, icon_height, text_offset;
1097 UINT style = DFCS_FLAT;
1098 BOOL draw_focus;
1100 hdc = dis->hDC;
1101 hwnd = dis->hwndItem;
1103 SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
1105 icon_width = DIALOG_EXPANDO_ICON_WIDTH;
1106 icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
1107 taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
1108 rect.right = icon_width;
1109 rect.bottom = icon_height;
1110 style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
1111 DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
1113 GetCharWidthW(hdc, '0', '0', &text_offset);
1114 text_offset /= 2;
1116 rect = dis->rcItem;
1117 rect.left += icon_width + text_offset;
1118 text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
1119 DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
1121 draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
1122 if(draw_focus) DrawFocusRect(hdc, &rect);
1125 static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
1127 const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
1128 NONCLIENTMETRICSW ncm;
1129 HDC hdc;
1130 INT id;
1132 ncm.cbSize = sizeof(ncm);
1133 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
1135 memset(dialog_info, 0, sizeof(*dialog_info));
1136 dialog_info->taskconfig = taskconfig;
1137 dialog_info->hwnd = hwnd;
1138 dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont);
1140 hdc = GetDC(dialog_info->hwnd);
1141 SelectObject(hdc, dialog_info->font);
1142 dialog_info->m.x_baseunit = GdiGetCharDimensions(hdc, NULL, &dialog_info->m.y_baseunit);
1143 ReleaseDC(dialog_info->hwnd, hdc);
1145 dialog_info->m.h_spacing = DIALOG_SPACING;
1146 dialog_info->m.v_spacing = DIALOG_SPACING;
1147 taskdialog_du_to_px(dialog_info, &dialog_info->m.h_spacing, &dialog_info->m.v_spacing);
1149 if (taskconfig->dwFlags & TDF_CALLBACK_TIMER)
1151 SetTimer(hwnd, ID_TIMER, DIALOG_TIMER_MS, NULL);
1152 dialog_info->last_timer_tick = GetTickCount();
1155 taskdialog_add_main_icon(dialog_info);
1156 taskdialog_add_main_instruction(dialog_info);
1157 taskdialog_add_content(dialog_info);
1158 taskdialog_add_expanded_info(dialog_info);
1159 taskdialog_add_progress_bar(dialog_info);
1160 taskdialog_add_radio_buttons(dialog_info);
1161 taskdialog_add_command_links(dialog_info);
1162 taskdialog_add_expando_button(dialog_info);
1163 taskdialog_add_verification_box(dialog_info);
1164 taskdialog_add_buttons(dialog_info);
1165 taskdialog_add_footer_icon(dialog_info);
1166 taskdialog_add_footer_text(dialog_info);
1168 /* Set default button */
1169 if (!dialog_info->default_button && dialog_info->command_links)
1170 dialog_info->default_button = dialog_info->command_links[0];
1171 if (!dialog_info->default_button) dialog_info->default_button = dialog_info->buttons[0];
1172 SendMessageW(dialog_info->hwnd, WM_NEXTDLGCTL, (WPARAM)dialog_info->default_button, TRUE);
1173 id = GetWindowLongW(dialog_info->default_button, GWLP_ID);
1174 SendMessageW(dialog_info->hwnd, DM_SETDEFID, id, 0);
1176 dialog_info->has_cancel =
1177 (taskconfig->dwFlags & TDF_ALLOW_DIALOG_CANCELLATION)
1178 || taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, IDCANCEL)
1179 || taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, IDCANCEL);
1181 if (!dialog_info->has_cancel) DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
1183 taskdialog_layout(dialog_info);
1186 static BOOL CALLBACK takdialog_destroy_control(HWND hwnd, LPARAM lParam)
1188 DestroyWindow(hwnd);
1189 return TRUE;
1192 static void taskdialog_destroy(struct taskdialog_info *dialog_info)
1194 EnumChildWindows(dialog_info->hwnd, takdialog_destroy_control, 0);
1196 if (dialog_info->taskconfig->dwFlags & TDF_CALLBACK_TIMER) KillTimer(dialog_info->hwnd, ID_TIMER);
1197 if (dialog_info->font) DeleteObject(dialog_info->font);
1198 if (dialog_info->main_instruction_font) DeleteObject(dialog_info->main_instruction_font);
1199 Free(dialog_info->buttons);
1200 Free(dialog_info->radio_buttons);
1201 Free(dialog_info->command_links);
1202 Free(dialog_info->expanded_text);
1203 Free(dialog_info->collapsed_text);
1206 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1208 static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
1209 struct taskdialog_info *dialog_info;
1210 LRESULT result;
1212 TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
1214 if (msg != WM_INITDIALOG)
1215 dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
1217 switch (msg)
1219 case TDM_NAVIGATE_PAGE:
1220 dialog_info->taskconfig = (const TASKDIALOGCONFIG *)lParam;
1221 taskdialog_destroy(dialog_info);
1222 taskdialog_init(dialog_info, hwnd);
1223 taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1224 /* Default radio button click notification is sent before TDN_NAVIGATED */
1225 taskdialog_check_default_radio_buttons(dialog_info);
1226 taskdialog_notify(dialog_info, TDN_NAVIGATED, 0, 0);
1227 break;
1228 case TDM_CLICK_BUTTON:
1229 taskdialog_click_button(dialog_info, wParam);
1230 break;
1231 case TDM_ENABLE_BUTTON:
1232 taskdialog_enable_button(dialog_info, wParam, lParam);
1233 break;
1234 case TDM_SET_MARQUEE_PROGRESS_BAR:
1236 BOOL marquee = wParam;
1237 LONG style;
1238 if(!dialog_info->progress_bar) break;
1239 style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1240 style = marquee ? style | PBS_MARQUEE : style & (~PBS_MARQUEE);
1241 SetWindowLongW(dialog_info->progress_bar, GWL_STYLE, style);
1242 break;
1244 case TDM_SET_PROGRESS_BAR_STATE:
1245 result = SendMessageW(dialog_info->progress_bar, PBM_SETSTATE, wParam, 0);
1246 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1247 break;
1248 case TDM_SET_PROGRESS_BAR_RANGE:
1249 result = SendMessageW(dialog_info->progress_bar, PBM_SETRANGE, 0, lParam);
1250 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1251 break;
1252 case TDM_SET_PROGRESS_BAR_POS:
1253 result = 0;
1254 if (dialog_info->progress_bar)
1256 LONG style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1257 if (!(style & PBS_MARQUEE)) result = SendMessageW(dialog_info->progress_bar, PBM_SETPOS, wParam, 0);
1259 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1260 break;
1261 case TDM_SET_PROGRESS_BAR_MARQUEE:
1262 SendMessageW(dialog_info->progress_bar, PBM_SETMARQUEE, wParam, lParam);
1263 break;
1264 case TDM_SET_ELEMENT_TEXT:
1265 taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1266 taskdialog_layout(dialog_info);
1267 break;
1268 case TDM_UPDATE_ELEMENT_TEXT:
1269 taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1270 break;
1271 case TDM_CLICK_RADIO_BUTTON:
1272 taskdialog_click_radio_button(dialog_info, wParam);
1273 break;
1274 case TDM_ENABLE_RADIO_BUTTON:
1275 taskdialog_enable_radio_button(dialog_info, wParam, lParam);
1276 break;
1277 case TDM_CLICK_VERIFICATION:
1279 BOOL checked = (BOOL)wParam;
1280 BOOL focused = (BOOL)lParam;
1281 dialog_info->verification_checked = checked;
1282 if (dialog_info->verification_box)
1284 SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
1285 taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
1286 if (focused) SetFocus(dialog_info->verification_box);
1288 break;
1290 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE:
1291 taskdialog_button_set_shield(dialog_info, wParam, lParam);
1292 break;
1293 case TDM_UPDATE_ICON:
1294 taskdialog_set_icon(dialog_info, wParam, (HICON)lParam);
1295 break;
1296 case WM_INITDIALOG:
1297 dialog_info = (struct taskdialog_info *)lParam;
1299 taskdialog_init(dialog_info, hwnd);
1301 SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
1302 taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1303 taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
1304 /* Default radio button click notification sent after TDN_CREATED */
1305 taskdialog_check_default_radio_buttons(dialog_info);
1306 return FALSE;
1307 case WM_COMMAND:
1308 if (HIWORD(wParam) == BN_CLICKED)
1310 taskdialog_on_button_click(dialog_info, (HWND)lParam, LOWORD(wParam));
1311 break;
1313 return FALSE;
1314 case WM_HELP:
1315 taskdialog_notify(dialog_info, TDN_HELP, 0, 0);
1316 break;
1317 case WM_TIMER:
1318 if (ID_TIMER == wParam)
1320 DWORD elapsed = GetTickCount() - dialog_info->last_timer_tick;
1321 if (taskdialog_notify(dialog_info, TDN_TIMER, elapsed, 0) == S_FALSE)
1322 dialog_info->last_timer_tick = GetTickCount();
1324 break;
1325 case WM_NOTIFY:
1327 PNMLINK pnmLink = (PNMLINK)lParam;
1328 HWND hwndFrom = pnmLink->hdr.hwndFrom;
1329 if ((taskdialog_hyperlink_enabled(dialog_info))
1330 && (hwndFrom == dialog_info->content || hwndFrom == dialog_info->expanded_info
1331 || hwndFrom == dialog_info->footer_text)
1332 && (pnmLink->hdr.code == NM_CLICK || pnmLink->hdr.code == NM_RETURN))
1334 taskdialog_notify(dialog_info, TDN_HYPERLINK_CLICKED, 0, (LPARAM)pnmLink->item.szUrl);
1335 break;
1337 return FALSE;
1339 case WM_DRAWITEM:
1341 LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
1342 if (dis->hwndItem == dialog_info->expando_button)
1344 taskdialog_draw_expando_control(dialog_info, dis);
1345 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE);
1346 break;
1348 return FALSE;
1350 case WM_DESTROY:
1351 taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
1352 RemovePropW(hwnd, taskdialog_info_propnameW);
1353 taskdialog_destroy(dialog_info);
1354 break;
1355 case WM_CLOSE:
1356 if (dialog_info->has_cancel)
1358 if(taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, IDCANCEL, 0) == S_OK)
1359 EndDialog(hwnd, IDCANCEL);
1360 SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 0);
1361 break;
1363 return FALSE;
1364 default:
1365 return FALSE;
1367 return TRUE;
1370 /***********************************************************************
1371 * TaskDialogIndirect [COMCTL32.@]
1373 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
1374 int *radio_button, BOOL *verification_flag_checked)
1376 struct taskdialog_info dialog_info;
1377 DLGTEMPLATE *template;
1378 INT ret;
1380 TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
1382 if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
1383 return E_INVALIDARG;
1385 dialog_info.taskconfig = taskconfig;
1387 template = create_taskdialog_template(taskconfig);
1388 ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
1389 taskdialog_proc, (LPARAM)&dialog_info);
1390 Free(template);
1392 if (button) *button = ret;
1393 if (radio_button) *radio_button = dialog_info.selected_radio_id;
1394 if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
1396 return S_OK;
1399 /***********************************************************************
1400 * TaskDialog [COMCTL32.@]
1402 HRESULT WINAPI TaskDialog(HWND owner, HINSTANCE hinst, const WCHAR *title, const WCHAR *main_instruction,
1403 const WCHAR *content, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons, const WCHAR *icon, int *button)
1405 TASKDIALOGCONFIG taskconfig;
1407 TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner, hinst, debugstr_w(title), debugstr_w(main_instruction),
1408 debugstr_w(content), common_buttons, debugstr_w(icon), button);
1410 memset(&taskconfig, 0, sizeof(taskconfig));
1411 taskconfig.cbSize = sizeof(taskconfig);
1412 taskconfig.hwndParent = owner;
1413 taskconfig.hInstance = hinst;
1414 taskconfig.dwCommonButtons = common_buttons;
1415 taskconfig.pszWindowTitle = title;
1416 taskconfig.u.pszMainIcon = icon;
1417 taskconfig.pszMainInstruction = main_instruction;
1418 taskconfig.pszContent = content;
1419 return TaskDialogIndirect(&taskconfig, button, NULL, NULL);