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
27 #define NONAMELESSUNION
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
55 const TASKDIALOGCONFIG
*taskconfig
;
56 DWORD last_timer_tick
;
58 HFONT main_instruction_font
;
61 HWND main_instruction
;
65 INT radio_button_count
;
67 INT command_link_count
;
70 HWND verification_box
;
84 INT selected_radio_id
;
85 BOOL verification_checked
;
89 WCHAR
*collapsed_text
;
92 struct button_layout_info
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
);
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
);
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
);
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
)
137 if ((p
= strrchrW(name
, '/'))) name
= p
+ 1;
138 if ((p
= strrchrW(name
, '\\'))) name
= p
+ 1;
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
];
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
));
164 titleW
= taskconfig
->pszWindowTitle
;
167 title_size
= (strlenW(titleW
) + 1) * sizeof(WCHAR
);
169 size
= sizeof(DLGTEMPLATE
) + 2 * sizeof(WORD
);
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);
183 ptr
+= 2; /* class */
184 template_write_data(&ptr
, titleW
, title_size
);
185 template_write_data(&ptr
, &fontsize
, sizeof(fontsize
));
190 static HWND
taskdialog_find_button(HWND
*buttons
, INT count
, INT id
)
195 for (i
= 0; i
< count
; i
++)
197 button_id
= GetWindowLongW(buttons
[i
], GWLP_ID
);
198 if (button_id
== id
) return buttons
[i
];
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
)
243 static void taskdialog_move_controls_vertically(HWND parent
, HWND
*controls
, INT count
, INT offset
)
249 for (i
= 0; i
< count
; i
++)
251 if (!controls
[i
]) continue;
253 GetWindowRect(controls
[i
], &rect
);
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
;
265 RECT info_rect
, rect
;
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
);
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 */
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
);
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
)
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);
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);
327 radio_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, command_id
);
330 dialog_info
->selected_radio_id
= command_id
;
331 taskdialog_notify(dialog_info
, TDN_RADIO_BUTTON_CLICKED
, command_id
, 0);
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
;
353 if (IS_INTRESOURCE(text
))
355 if (!(length
= LoadStringW(user_resource
? dialog_info
->taskconfig
->hInstance
: COMCTL32_hModule
,
356 (UINT_PTR
)text
, (WCHAR
*)&textW
, 0)))
362 length
= strlenW(textW
);
365 ret
= Alloc((length
+ 1) * sizeof(WCHAR
));
366 if (ret
) memcpy(ret
, textW
, length
* sizeof(WCHAR
));
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
,
384 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
385 HFONT hfont
, old_hfont
;
393 SendMessageW(hwnd
, LM_GETIDEALSIZE
, max_width
, (LPARAM
)size
);
397 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
398 style
|= DT_RIGHT
| DT_RTLREADING
;
402 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
403 text_length
= GetWindowTextLengthW(hwnd
);
404 text
= Alloc((text_length
+ 1) * sizeof(WCHAR
));
411 GetWindowTextW(hwnd
, text
, text_length
+ 1);
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
);
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
;
430 INT text_offset
, radio_box_width
, radio_box_height
;
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
);
441 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
442 style
|= DT_RIGHT
| DT_RTLREADING
;
446 rect
.right
= max_width
- radio_box_width
- text_offset
;
447 text_length
= GetWindowTextLengthW(hwnd
);
448 text
= Alloc((text_length
+ 1) * sizeof(WCHAR
));
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
);
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
;
471 LONG icon_width
, icon_height
, text_offset
;
472 LONG max_width
, max_text_height
;
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
);
485 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
486 style
|= DT_RIGHT
| DT_RTLREADING
;
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
)
512 else if (icon
== TD_ERROR_ICON
)
514 else if (icon
== TD_INFORMATION_ICON
)
515 return IDI_INFORMATION
;
516 else if (icon
== TD_SHIELD_ICON
)
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
;
530 if (((flags
& TDF_USE_HICON_MAIN
) && element
== TDIE_ICON_MAIN
)
531 || ((flags
& TDF_USE_HICON_FOOTER
) && element
== TDIE_ICON_FOOTER
))
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
);
542 hicon
= LoadImageW(NULL
, (LPCWSTR
)taskdialog_get_standard_icon((LPCWSTR
)icon
), IMAGE_ICON
, cx
, cy
,
543 LR_SHARED
| LR_DEFAULTSIZE
);
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
,
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
;
574 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
575 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)textW
);
579 static void taskdialog_check_default_radio_buttons(struct taskdialog_info
*dialog_info
)
581 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
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];
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
)
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
);
623 SendMessageW(hwnd
, WM_SETFONT
, (WPARAM
)font
, 0);
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
;
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);
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
;
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);
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
;
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
));
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
;
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);
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
,
778 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
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
,
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. */
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) \
811 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
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
;
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);
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
);
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
;
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
;
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
;
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
;
915 main_icon_bottom
= 0;
916 if (dialog_info
->main_icon
)
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
,
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
,
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
;
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
;
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
;
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
);
977 expando_bottom
= dialog_height
;
978 /* Expando control */
979 if (dialog_info
->expando_button
)
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
)
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,
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
;
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;
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 */
1063 size
.cy
= button_height
;
1064 for (i
= 0; i
< dialog_info
->button_count
; i
++)
1067 if (i
> 0 && button_layout_infos
[i
].line
!= button_layout_infos
[i
- 1].line
)
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
);
1087 footer_icon_right
= 0;
1088 footer_icon_bottom
= dialog_height
;
1089 if (dialog_info
->footer_icon
)
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
;
1101 taskdialog_label_layout(dialog_info
, dialog_info
->footer_text
, footer_icon_right
, dialog_width
, &dialog_height
,
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
);
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
);
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
)
1131 LONG icon_width
, icon_height
, text_offset
;
1132 UINT style
= DFCS_FLAT
;
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
);
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
;
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
);
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
;
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
);
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);
1263 case TDM_CLICK_BUTTON
:
1264 taskdialog_click_button(dialog_info
, wParam
);
1266 case TDM_ENABLE_BUTTON
:
1267 taskdialog_enable_button(dialog_info
, wParam
, lParam
);
1269 case TDM_SET_MARQUEE_PROGRESS_BAR
:
1271 BOOL marquee
= wParam
;
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
);
1279 case TDM_SET_PROGRESS_BAR_STATE
:
1280 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETSTATE
, wParam
, 0);
1281 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1283 case TDM_SET_PROGRESS_BAR_RANGE
:
1284 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETRANGE
, 0, lParam
);
1285 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1287 case TDM_SET_PROGRESS_BAR_POS
:
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
);
1296 case TDM_SET_PROGRESS_BAR_MARQUEE
:
1297 SendMessageW(dialog_info
->progress_bar
, PBM_SETMARQUEE
, wParam
, lParam
);
1299 case TDM_SET_ELEMENT_TEXT
:
1300 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1301 taskdialog_layout(dialog_info
);
1303 case TDM_UPDATE_ELEMENT_TEXT
:
1304 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1306 case TDM_CLICK_RADIO_BUTTON
:
1307 taskdialog_click_radio_button(dialog_info
, wParam
);
1309 case TDM_ENABLE_RADIO_BUTTON
:
1310 taskdialog_enable_radio_button(dialog_info
, wParam
, lParam
);
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
);
1325 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE
:
1326 taskdialog_button_set_shield(dialog_info
, wParam
, lParam
);
1328 case TDM_UPDATE_ICON
:
1329 taskdialog_set_icon(dialog_info
, wParam
, (HICON
)lParam
);
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
);
1343 if (HIWORD(wParam
) == BN_CLICKED
)
1345 taskdialog_on_button_click(dialog_info
, (HWND
)lParam
, LOWORD(wParam
));
1350 taskdialog_notify(dialog_info
, TDN_HELP
, 0, 0);
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();
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
);
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
);
1386 taskdialog_notify(dialog_info
, TDN_DESTROYED
, 0, 0);
1387 RemovePropW(hwnd
, taskdialog_info_propnameW
);
1388 taskdialog_destroy(dialog_info
);
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);
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;
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
);
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
;
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
);