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
;
88 WCHAR
*collapsed_text
;
91 struct button_layout_info
97 static void taskdialog_on_button_click(struct taskdialog_info
*dialog_info
, HWND hwnd
);
98 static void taskdialog_layout(struct taskdialog_info
*dialog_info
);
100 static void taskdialog_du_to_px(struct taskdialog_info
*dialog_info
, LONG
*width
, LONG
*height
)
102 if (width
) *width
= MulDiv(*width
, dialog_info
->m
.x_baseunit
, 4);
103 if (height
) *height
= MulDiv(*height
, dialog_info
->m
.y_baseunit
, 8);
106 static void template_write_data(char **ptr
, const void *src
, unsigned int size
)
108 memcpy(*ptr
, src
, size
);
112 static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG
*taskconfig
, RECT
*ret
)
114 HMONITOR monitor
= MonitorFromWindow(taskconfig
->hwndParent
? taskconfig
->hwndParent
: GetActiveWindow(),
115 MONITOR_DEFAULTTOPRIMARY
);
118 info
.cbSize
= sizeof(info
);
119 GetMonitorInfoW(monitor
, &info
);
121 if (taskconfig
->dwFlags
& TDF_POSITION_RELATIVE_TO_WINDOW
&& taskconfig
->hwndParent
)
122 GetWindowRect(taskconfig
->hwndParent
, ret
);
126 return info
.rcWork
.right
- info
.rcWork
.left
;
129 static WCHAR
*taskdialog_get_exe_name(WCHAR
*name
, DWORD length
)
131 DWORD len
= GetModuleFileNameW(NULL
, name
, length
);
132 if (len
&& len
< length
)
135 if ((p
= strrchrW(name
, '/'))) name
= p
+ 1;
136 if ((p
= strrchrW(name
, '\\'))) name
= p
+ 1;
143 static DLGTEMPLATE
*create_taskdialog_template(const TASKDIALOGCONFIG
*taskconfig
)
145 unsigned int size
, title_size
;
146 static const WORD fontsize
= 0x7fff;
147 static const WCHAR emptyW
[] = { 0 };
148 const WCHAR
*titleW
= NULL
;
149 DLGTEMPLATE
*template;
150 WCHAR pathW
[MAX_PATH
];
154 if (!taskconfig
->pszWindowTitle
)
155 titleW
= taskdialog_get_exe_name(pathW
, ARRAY_SIZE(pathW
));
156 else if (IS_INTRESOURCE(taskconfig
->pszWindowTitle
))
158 if (!LoadStringW(taskconfig
->hInstance
, LOWORD(taskconfig
->pszWindowTitle
), (WCHAR
*)&titleW
, 0))
159 titleW
= taskdialog_get_exe_name(pathW
, ARRAY_SIZE(pathW
));
162 titleW
= taskconfig
->pszWindowTitle
;
165 title_size
= (strlenW(titleW
) + 1) * sizeof(WCHAR
);
167 size
= sizeof(DLGTEMPLATE
) + 2 * sizeof(WORD
);
169 size
+= 2; /* font size */
171 template = Alloc(size
);
172 if (!template) return NULL
;
174 template->style
= DS_MODALFRAME
| DS_SETFONT
| WS_CAPTION
| WS_VISIBLE
| WS_SYSMENU
;
175 if (taskconfig
->dwFlags
& TDF_CAN_BE_MINIMIZED
) template->style
|= WS_MINIMIZEBOX
;
176 if (!(taskconfig
->dwFlags
& TDF_NO_SET_FOREGROUND
)) template->style
|= DS_SETFOREGROUND
;
177 if (taskconfig
->dwFlags
& TDF_RTL_LAYOUT
) template->dwExtendedStyle
= WS_EX_LAYOUTRTL
| WS_EX_RIGHT
| WS_EX_RTLREADING
;
179 ptr
= (char *)(template + 1);
181 ptr
+= 2; /* class */
182 template_write_data(&ptr
, titleW
, title_size
);
183 template_write_data(&ptr
, &fontsize
, sizeof(fontsize
));
188 static HWND
taskdialog_find_button(HWND
*buttons
, INT count
, INT id
)
193 for (i
= 0; i
< count
; i
++)
195 button_id
= GetWindowLongW(buttons
[i
], GWLP_ID
);
196 if (button_id
== id
) return buttons
[i
];
202 static void taskdialog_enable_button(const struct taskdialog_info
*dialog_info
, INT id
, BOOL enable
)
204 HWND hwnd
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, id
);
205 if (!hwnd
) hwnd
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, id
);
206 if (hwnd
) EnableWindow(hwnd
, enable
);
209 static void taskdialog_click_button(struct taskdialog_info
*dialog_info
, INT id
)
211 HWND hwnd
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, id
);
212 if (!hwnd
) hwnd
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, id
);
213 if (hwnd
) taskdialog_on_button_click(dialog_info
, hwnd
);
216 static void taskdialog_button_set_shield(const struct taskdialog_info
*dialog_info
, INT id
, BOOL elevate
)
218 HWND hwnd
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, id
);
219 if (!hwnd
) hwnd
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, id
);
220 if (hwnd
) SendMessageW(hwnd
, BCM_SETSHIELD
, 0, elevate
);
223 static void taskdialog_enable_radio_button(const struct taskdialog_info
*dialog_info
, INT id
, BOOL enable
)
225 HWND hwnd
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, id
);
226 if (hwnd
) EnableWindow(hwnd
, enable
);
229 static void taskdialog_click_radio_button(const struct taskdialog_info
*dialog_info
, INT id
)
231 HWND hwnd
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, id
);
232 if (hwnd
) SendMessageW(hwnd
, BM_CLICK
, 0, 0);
235 static HRESULT
taskdialog_notify(struct taskdialog_info
*dialog_info
, UINT notification
, WPARAM wparam
, LPARAM lparam
)
237 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
238 return taskconfig
->pfCallback
239 ? taskconfig
->pfCallback(dialog_info
->hwnd
, notification
, wparam
, lparam
, taskconfig
->lpCallbackData
)
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
)
307 INT command_id
= GetWindowLongW(hwnd
, GWLP_ID
);
310 if (hwnd
== dialog_info
->expando_button
)
312 taskdialog_toggle_expando_control(dialog_info
);
313 taskdialog_notify(dialog_info
, TDN_EXPANDO_BUTTON_CLICKED
, dialog_info
->expanded
, 0);
317 if (hwnd
== dialog_info
->verification_box
)
319 dialog_info
->verification_checked
= !dialog_info
->verification_checked
;
320 taskdialog_notify(dialog_info
, TDN_VERIFICATION_CLICKED
, dialog_info
->verification_checked
, 0);
324 radio_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, command_id
);
327 dialog_info
->selected_radio_id
= command_id
;
328 taskdialog_notify(dialog_info
, TDN_RADIO_BUTTON_CLICKED
, command_id
, 0);
332 if (taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, command_id
, 0) == S_OK
)
333 EndDialog(dialog_info
->hwnd
, command_id
);
336 static WCHAR
*taskdialog_gettext(struct taskdialog_info
*dialog_info
, BOOL user_resource
, const WCHAR
*text
)
338 const WCHAR
*textW
= NULL
;
342 if (IS_INTRESOURCE(text
))
344 if (!(length
= LoadStringW(user_resource
? dialog_info
->taskconfig
->hInstance
: COMCTL32_hModule
,
345 (UINT_PTR
)text
, (WCHAR
*)&textW
, 0)))
351 length
= strlenW(textW
);
354 ret
= Alloc((length
+ 1) * sizeof(WCHAR
));
355 if (ret
) memcpy(ret
, textW
, length
* sizeof(WCHAR
));
360 static BOOL
taskdialog_hyperlink_enabled(struct taskdialog_info
*dialog_info
)
362 return dialog_info
->taskconfig
->dwFlags
& TDF_ENABLE_HYPERLINKS
;
365 static BOOL
taskdialog_use_command_link(struct taskdialog_info
*dialog_info
)
367 return dialog_info
->taskconfig
->dwFlags
& (TDF_USE_COMMAND_LINKS
| TDF_USE_COMMAND_LINKS_NO_ICON
);
370 static void taskdialog_get_label_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, LONG max_width
, SIZE
*size
,
373 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
374 HFONT hfont
, old_hfont
;
382 SendMessageW(hwnd
, LM_GETIDEALSIZE
, max_width
, (LPARAM
)size
);
386 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
387 style
|= DT_RIGHT
| DT_RTLREADING
;
391 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
392 text_length
= GetWindowTextLengthW(hwnd
);
393 text
= Alloc((text_length
+ 1) * sizeof(WCHAR
));
400 GetWindowTextW(hwnd
, text
, text_length
+ 1);
402 old_hfont
= SelectObject(hdc
, hfont
);
403 rect
.right
= max_width
;
404 size
->cy
= DrawTextW(hdc
, text
, text_length
, &rect
, style
);
405 size
->cx
= min(max_width
, rect
.right
- rect
.left
);
406 if (old_hfont
) SelectObject(hdc
, old_hfont
);
407 ReleaseDC(hwnd
, hdc
);
411 static void taskdialog_get_radio_button_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, LONG max_width
, SIZE
*size
)
413 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
414 HFONT hfont
, old_hfont
;
419 INT text_offset
, radio_box_width
, radio_box_height
;
422 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
423 old_hfont
= SelectObject(hdc
, hfont
);
425 radio_box_width
= 12 * GetDpiForWindow(hwnd
) / 96 + 1;
426 radio_box_height
= 12 * GetDpiForWindow(hwnd
) / 96 + 1;
427 GetCharWidthW(hdc
, '0', '0', &text_offset
);
430 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
431 style
|= DT_RIGHT
| DT_RTLREADING
;
435 rect
.right
= max_width
- radio_box_width
- text_offset
;
436 text_length
= GetWindowTextLengthW(hwnd
);
437 text
= Alloc((text_length
+ 1) * sizeof(WCHAR
));
444 GetWindowTextW(hwnd
, text
, text_length
+ 1);
445 size
->cy
= DrawTextW(hdc
, text
, text_length
, &rect
, style
);
446 size
->cx
= min(max_width
- radio_box_width
- text_offset
, rect
.right
- rect
.left
);
447 size
->cx
+= radio_box_width
+ text_offset
;
448 size
->cy
= max(size
->cy
, radio_box_height
);
449 if (old_hfont
) SelectObject(hdc
, old_hfont
);
451 ReleaseDC(hwnd
, hdc
);
454 static void taskdialog_get_expando_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, SIZE
*size
)
456 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
457 HFONT hfont
, old_hfont
;
460 LONG icon_width
, icon_height
, text_offset
;
461 LONG max_width
, max_text_height
;
464 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
465 old_hfont
= SelectObject(hdc
, hfont
);
467 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
468 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
469 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
471 GetCharWidthW(hdc
, '0', '0', &text_offset
);
474 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
475 style
|= DT_RIGHT
| DT_RTLREADING
;
479 max_width
= DIALOG_MIN_WIDTH
/ 2;
480 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
482 rect
.right
= max_width
- icon_width
- text_offset
;
483 max_text_height
= DrawTextW(hdc
, dialog_info
->expanded_text
, -1, &rect
, style
);
484 size
->cy
= max(max_text_height
, icon_height
);
485 size
->cx
= rect
.right
- rect
.left
;
487 rect
.right
= max_width
- icon_width
- text_offset
;
488 max_text_height
= DrawTextW(hdc
, dialog_info
->collapsed_text
, -1, &rect
, style
);
489 size
->cy
= max(size
->cy
, max_text_height
);
490 size
->cx
= max(size
->cx
, rect
.right
- rect
.left
);
491 size
->cx
= min(size
->cx
, max_width
);
493 if (old_hfont
) SelectObject(hdc
, old_hfont
);
494 ReleaseDC(hwnd
, hdc
);
497 static ULONG_PTR
taskdialog_get_standard_icon(LPCWSTR icon
)
499 if (icon
== TD_WARNING_ICON
)
501 else if (icon
== TD_ERROR_ICON
)
503 else if (icon
== TD_INFORMATION_ICON
)
504 return IDI_INFORMATION
;
505 else if (icon
== TD_SHIELD_ICON
)
508 return (ULONG_PTR
)icon
;
511 static void taskdialog_set_icon(struct taskdialog_info
*dialog_info
, INT element
, HICON icon
)
513 DWORD flags
= dialog_info
->taskconfig
->dwFlags
;
519 if (((flags
& TDF_USE_HICON_MAIN
) && element
== TDIE_ICON_MAIN
)
520 || ((flags
& TDF_USE_HICON_FOOTER
) && element
== TDIE_ICON_FOOTER
))
524 if (element
== TDIE_ICON_FOOTER
)
526 cx
= GetSystemMetrics(SM_CXSMICON
);
527 cy
= GetSystemMetrics(SM_CYSMICON
);
529 hicon
= LoadImageW(dialog_info
->taskconfig
->hInstance
, (LPCWSTR
)icon
, IMAGE_ICON
, cx
, cy
, LR_SHARED
| LR_DEFAULTSIZE
);
531 hicon
= LoadImageW(NULL
, (LPCWSTR
)taskdialog_get_standard_icon((LPCWSTR
)icon
), IMAGE_ICON
, cx
, cy
,
532 LR_SHARED
| LR_DEFAULTSIZE
);
537 if (element
== TDIE_ICON_MAIN
)
539 SendMessageW(dialog_info
->hwnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)hicon
);
540 SendMessageW(dialog_info
->main_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
542 else if (element
== TDIE_ICON_FOOTER
)
543 SendMessageW(dialog_info
->footer_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
546 static void taskdialog_set_element_text(struct taskdialog_info
*dialog_info
, TASKDIALOG_ELEMENTS element
,
552 if (element
== TDE_CONTENT
)
553 hwnd
= dialog_info
->content
;
554 else if (element
== TDE_EXPANDED_INFORMATION
)
555 hwnd
= dialog_info
->expanded_info
;
556 else if (element
== TDE_FOOTER
)
557 hwnd
= dialog_info
->footer_text
;
558 else if (element
== TDE_MAIN_INSTRUCTION
)
559 hwnd
= dialog_info
->main_instruction
;
563 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
564 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)textW
);
568 static void taskdialog_check_default_radio_buttons(struct taskdialog_info
*dialog_info
)
570 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
573 if (!dialog_info
->radio_button_count
) return;
575 default_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
,
576 taskconfig
->nDefaultRadioButton
);
578 if (!default_button
&& !(taskconfig
->dwFlags
& TDF_NO_DEFAULT_RADIO_BUTTON
))
579 default_button
= dialog_info
->radio_buttons
[0];
583 SendMessageW(default_button
, BM_SETCHECK
, BST_CHECKED
, 0);
584 taskdialog_on_button_click(dialog_info
, default_button
);
588 static void taskdialog_add_main_icon(struct taskdialog_info
*dialog_info
)
590 if (!dialog_info
->taskconfig
->u
.hMainIcon
) return;
592 dialog_info
->main_icon
=
593 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
594 taskdialog_set_icon(dialog_info
, TDIE_ICON_MAIN
, dialog_info
->taskconfig
->u
.hMainIcon
);
597 static HWND
taskdialog_create_label(struct taskdialog_info
*dialog_info
, const WCHAR
*text
, HFONT font
, BOOL syslink
)
602 DWORD style
= WS_CHILD
| WS_VISIBLE
;
604 if (!text
) return NULL
;
606 class = syslink
? WC_LINK
: WC_STATICW
;
607 if (syslink
) style
|= WS_TABSTOP
;
608 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
609 hwnd
= CreateWindowW(class, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
612 SendMessageW(hwnd
, WM_SETFONT
, (WPARAM
)font
, 0);
616 static void taskdialog_add_main_instruction(struct taskdialog_info
*dialog_info
)
618 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
619 NONCLIENTMETRICSW ncm
;
621 if (!taskconfig
->pszMainInstruction
) return;
623 ncm
.cbSize
= sizeof(ncm
);
624 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
625 /* 1.25 times the height */
626 ncm
.lfMessageFont
.lfHeight
= ncm
.lfMessageFont
.lfHeight
* 5 / 4;
627 ncm
.lfMessageFont
.lfWeight
= FW_BOLD
;
628 dialog_info
->main_instruction_font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
630 dialog_info
->main_instruction
=
631 taskdialog_create_label(dialog_info
, taskconfig
->pszMainInstruction
, dialog_info
->main_instruction_font
, FALSE
);
634 static void taskdialog_add_content(struct taskdialog_info
*dialog_info
)
636 dialog_info
->content
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszContent
, dialog_info
->font
,
637 taskdialog_hyperlink_enabled(dialog_info
));
640 static void taskdialog_add_progress_bar(struct taskdialog_info
*dialog_info
)
642 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
643 DWORD style
= PBS_SMOOTH
| PBS_SMOOTHREVERSE
| WS_CHILD
| WS_VISIBLE
;
645 if (!(taskconfig
->dwFlags
& (TDF_SHOW_PROGRESS_BAR
| TDF_SHOW_MARQUEE_PROGRESS_BAR
))) return;
646 if (taskconfig
->dwFlags
& TDF_SHOW_MARQUEE_PROGRESS_BAR
) style
|= PBS_MARQUEE
;
647 dialog_info
->progress_bar
=
648 CreateWindowW(PROGRESS_CLASSW
, NULL
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
651 static void taskdialog_add_radio_buttons(struct taskdialog_info
*dialog_info
)
653 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
654 static const DWORD style
= BS_AUTORADIOBUTTON
| BS_MULTILINE
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
658 if (!taskconfig
->cRadioButtons
|| !taskconfig
->pRadioButtons
) return;
660 dialog_info
->radio_buttons
= Alloc(taskconfig
->cRadioButtons
* sizeof(*dialog_info
->radio_buttons
));
661 if (!dialog_info
->radio_buttons
) return;
663 dialog_info
->radio_button_count
= taskconfig
->cRadioButtons
;
664 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
666 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pRadioButtons
[i
].pszButtonText
);
667 dialog_info
->radio_buttons
[i
] =
668 CreateWindowW(WC_BUTTONW
, textW
, i
== 0 ? style
| WS_GROUP
: style
, 0, 0, 0, 0, dialog_info
->hwnd
,
669 ULongToHandle(taskconfig
->pRadioButtons
[i
].nButtonID
), 0, NULL
);
670 SendMessageW(dialog_info
->radio_buttons
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
675 static void taskdialog_add_command_links(struct taskdialog_info
*dialog_info
)
677 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
678 DWORD default_style
= BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, style
;
683 if (!taskconfig
->cButtons
|| !taskconfig
->pButtons
|| !taskdialog_use_command_link(dialog_info
)) return;
685 dialog_info
->command_links
= Alloc(taskconfig
->cButtons
* sizeof(*dialog_info
->command_links
));
686 if (!dialog_info
->command_links
) return;
688 dialog_info
->command_link_count
= taskconfig
->cButtons
;
689 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
691 is_default
= taskconfig
->pButtons
[i
].nButtonID
== taskconfig
->nDefaultButton
;
692 style
= is_default
? default_style
| BS_DEFCOMMANDLINK
: default_style
| BS_COMMANDLINK
;
693 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pButtons
[i
].pszButtonText
);
694 dialog_info
->command_links
[i
] = CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
,
695 ULongToHandle(taskconfig
->pButtons
[i
].nButtonID
), 0, NULL
);
696 SendMessageW(dialog_info
->command_links
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
699 if (is_default
&& !dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->command_links
[i
];
703 static void taskdialog_add_expanded_info(struct taskdialog_info
*dialog_info
)
705 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
707 if (!taskconfig
->pszExpandedInformation
) return;
709 dialog_info
->expanded
= taskconfig
->dwFlags
& TDF_EXPANDED_BY_DEFAULT
;
710 dialog_info
->expanded_info
= taskdialog_create_label(dialog_info
, taskconfig
->pszExpandedInformation
,
711 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
712 ShowWindow(dialog_info
->expanded_info
, dialog_info
->expanded
? SW_SHOWDEFAULT
: SW_HIDE
);
715 static void taskdialog_add_expando_button(struct taskdialog_info
*dialog_info
)
717 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
720 if (!taskconfig
->pszExpandedInformation
) return;
722 if (!taskconfig
->pszCollapsedControlText
&& !taskconfig
->pszExpandedControlText
)
724 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_EXPANDED
));
725 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_COLLAPSED
));
729 textW
= taskconfig
->pszExpandedControlText
? taskconfig
->pszExpandedControlText
730 : taskconfig
->pszCollapsedControlText
;
731 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
732 textW
= taskconfig
->pszCollapsedControlText
? taskconfig
->pszCollapsedControlText
733 : taskconfig
->pszExpandedControlText
;
734 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
737 textW
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
739 dialog_info
->expando_button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_OWNERDRAW
, 0,
740 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
741 SendMessageW(dialog_info
->expando_button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
744 static void taskdialog_add_verification_box(struct taskdialog_info
*dialog_info
)
746 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
747 static const DWORD style
= BS_AUTOCHECKBOX
| BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
750 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
751 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
) dialog_info
->verification_checked
= TRUE
;
753 if (!taskconfig
->pszVerificationText
) return;
755 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pszVerificationText
);
756 dialog_info
->verification_box
= CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
757 SendMessageW(dialog_info
->verification_box
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
760 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
)
761 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, BST_CHECKED
, 0);
764 static void taskdialog_add_button(struct taskdialog_info
*dialog_info
, HWND
*button
, INT_PTR id
, const WCHAR
*text
,
767 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
770 textW
= taskdialog_gettext(dialog_info
, custom_button
, text
);
771 *button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0, 0, 0, 0, dialog_info
->hwnd
,
774 SendMessageW(*button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
776 if (id
== taskconfig
->nDefaultButton
&& !dialog_info
->default_button
) dialog_info
->default_button
= *button
;
779 static void taskdialog_add_buttons(struct taskdialog_info
*dialog_info
)
781 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
782 BOOL use_command_links
= taskdialog_use_command_link(dialog_info
);
783 DWORD flags
= taskconfig
->dwCommonButtons
;
784 INT count
, max_count
;
786 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
788 if (!use_command_links
&& taskconfig
->cButtons
&& taskconfig
->pButtons
) max_count
+= taskconfig
->cButtons
;
790 dialog_info
->buttons
= Alloc(max_count
* sizeof(*dialog_info
->buttons
));
791 if (!dialog_info
->buttons
) return;
793 for (count
= 0; !use_command_links
&& count
< taskconfig
->cButtons
; count
++)
794 taskdialog_add_button(dialog_info
, &dialog_info
->buttons
[count
], taskconfig
->pButtons
[count
].nButtonID
,
795 taskconfig
->pButtons
[count
].pszButtonText
, TRUE
);
797 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
800 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
804 if (flags
& TDCBF_OK_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
805 if (flags
& TDCBF_YES_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(YES
);
806 if (flags
& TDCBF_NO_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(NO
);
807 if (flags
& TDCBF_RETRY_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(RETRY
);
808 if (flags
& TDCBF_CANCEL_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL
);
809 if (flags
& TDCBF_CLOSE_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE
);
811 if (!count
&& !dialog_info
->command_link_count
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
812 #undef TASKDIALOG_INIT_COMMON_BUTTON
814 dialog_info
->button_count
= count
;
817 static void taskdialog_add_footer_icon(struct taskdialog_info
*dialog_info
)
819 if (!dialog_info
->taskconfig
->u2
.hFooterIcon
) return;
821 dialog_info
->footer_icon
=
822 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, 0);
823 taskdialog_set_icon(dialog_info
, TDIE_ICON_FOOTER
, dialog_info
->taskconfig
->u2
.hFooterIcon
);
826 static void taskdialog_add_footer_text(struct taskdialog_info
*dialog_info
)
828 dialog_info
->footer_text
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszFooter
,
829 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
832 static LONG
taskdialog_get_dialog_width(struct taskdialog_info
*dialog_info
)
834 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
835 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
836 LONG max_width
, main_icon_width
, screen_width
;
840 screen_width
= taskdialog_get_reference_rect(taskconfig
, &rect
);
841 if ((taskconfig
->dwFlags
& TDF_SIZE_TO_CONTENT
) && !taskconfig
->cxWidth
)
843 max_width
= DIALOG_MIN_WIDTH
;
844 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
845 main_icon_width
= dialog_info
->m
.h_spacing
;
846 if (dialog_info
->main_icon
) main_icon_width
+= GetSystemMetrics(SM_CXICON
);
847 if (dialog_info
->content
)
849 taskdialog_get_label_size(dialog_info
, dialog_info
->content
, 0, &size
, syslink
);
850 max_width
= max(max_width
, size
.cx
+ main_icon_width
+ dialog_info
->m
.h_spacing
* 2);
855 max_width
= max(taskconfig
->cxWidth
, DIALOG_MIN_WIDTH
);
856 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
858 max_width
= min(max_width
, screen_width
);
862 static void taskdialog_label_layout(struct taskdialog_info
*dialog_info
, HWND hwnd
, INT start_x
, LONG dialog_width
,
863 LONG
*dialog_height
, BOOL syslink
)
865 LONG x
, y
, max_width
;
870 x
= start_x
+ dialog_info
->m
.h_spacing
;
871 y
= *dialog_height
+ dialog_info
->m
.v_spacing
;
872 max_width
= dialog_width
- x
- dialog_info
->m
.h_spacing
;
873 taskdialog_get_label_size(dialog_info
, hwnd
, max_width
, &size
, syslink
);
874 SetWindowPos(hwnd
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
875 *dialog_height
= y
+ size
.cy
;
878 static void taskdialog_layout(struct taskdialog_info
*dialog_info
)
880 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
881 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
882 static BOOL first_time
= TRUE
;
884 LONG dialog_width
, dialog_height
= 0;
885 LONG h_spacing
, v_spacing
;
886 LONG main_icon_right
, main_icon_bottom
;
887 LONG expando_right
, expando_bottom
;
888 struct button_layout_info
*button_layout_infos
;
889 LONG button_min_width
, button_height
;
890 LONG
*line_widths
, line_count
, align
;
891 LONG footer_icon_right
, footer_icon_bottom
;
896 taskdialog_get_reference_rect(dialog_info
->taskconfig
, &ref_rect
);
897 dialog_width
= taskdialog_get_dialog_width(dialog_info
);
899 h_spacing
= dialog_info
->m
.h_spacing
;
900 v_spacing
= dialog_info
->m
.v_spacing
;
904 main_icon_bottom
= 0;
905 if (dialog_info
->main_icon
)
908 y
= dialog_height
+ v_spacing
;
909 size
.cx
= GetSystemMetrics(SM_CXICON
);
910 size
.cy
= GetSystemMetrics(SM_CYICON
);
911 SetWindowPos(dialog_info
->main_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
912 main_icon_right
= x
+ size
.cx
;
913 main_icon_bottom
= y
+ size
.cy
;
916 /* Main instruction */
917 taskdialog_label_layout(dialog_info
, dialog_info
->main_instruction
, main_icon_right
, dialog_width
, &dialog_height
,
921 taskdialog_label_layout(dialog_info
, dialog_info
->content
, main_icon_right
, dialog_width
, &dialog_height
, syslink
);
923 /* Expanded information */
924 if (!(taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
925 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, main_icon_right
, dialog_width
, &dialog_height
,
929 if (dialog_info
->progress_bar
)
931 x
= main_icon_right
+ h_spacing
;
932 y
= dialog_height
+ v_spacing
;
933 size
.cx
= dialog_width
- x
- h_spacing
;
934 size
.cy
= GetSystemMetrics(SM_CYVSCROLL
);
935 SetWindowPos(dialog_info
->progress_bar
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
936 dialog_height
= y
+ size
.cy
;
940 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
942 x
= main_icon_right
+ h_spacing
;
943 y
= dialog_height
+ v_spacing
;
944 taskdialog_get_radio_button_size(dialog_info
, dialog_info
->radio_buttons
[i
], dialog_width
- x
- h_spacing
, &size
);
945 size
.cx
= dialog_width
- x
- h_spacing
;
946 SetWindowPos(dialog_info
->radio_buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
947 dialog_height
= y
+ size
.cy
;
951 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
953 x
= main_icon_right
+ h_spacing
;
954 y
= dialog_height
+ v_spacing
;
955 taskdialog_get_label_size(dialog_info
, dialog_info
->command_links
[i
], dialog_width
- x
- h_spacing
, &size
, FALSE
);
956 size
.cx
= dialog_width
- x
- h_spacing
;
959 SetWindowPos(dialog_info
->command_links
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
960 dialog_height
= y
+ size
.cy
;
963 dialog_height
= max(dialog_height
, main_icon_bottom
);
966 expando_bottom
= dialog_height
;
967 /* Expando control */
968 if (dialog_info
->expando_button
)
971 y
= dialog_height
+ v_spacing
;
972 taskdialog_get_expando_size(dialog_info
, dialog_info
->expando_button
, &size
);
973 SetWindowPos(dialog_info
->expando_button
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
974 expando_right
= x
+ size
.cx
;
975 expando_bottom
= y
+ size
.cy
;
978 /* Verification box */
979 if (dialog_info
->verification_box
)
982 y
= expando_bottom
+ v_spacing
;
983 size
.cx
= DIALOG_MIN_WIDTH
/ 2;
984 taskdialog_du_to_px(dialog_info
, &size
.cx
, NULL
);
985 taskdialog_get_radio_button_size(dialog_info
, dialog_info
->verification_box
, size
.cx
, &size
);
986 SetWindowPos(dialog_info
->verification_box
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
987 expando_right
= max(expando_right
, x
+ size
.cx
);
988 expando_bottom
= y
+ size
.cy
;
991 /* Common and custom buttons */
992 button_layout_infos
= Alloc(dialog_info
->button_count
* sizeof(*button_layout_infos
));
993 line_widths
= Alloc(dialog_info
->button_count
* sizeof(*line_widths
));
995 button_min_width
= DIALOG_BUTTON_WIDTH
;
996 button_height
= DIALOG_BUTTON_HEIGHT
;
997 taskdialog_du_to_px(dialog_info
, &button_min_width
, &button_height
);
998 for (i
= 0; i
< dialog_info
->button_count
; i
++)
1000 taskdialog_get_label_size(dialog_info
, dialog_info
->buttons
[i
], dialog_width
- expando_right
- h_spacing
* 2,
1002 button_layout_infos
[i
].width
= max(size
.cx
, button_min_width
);
1005 /* Separate buttons into lines */
1006 x
= expando_right
+ h_spacing
;
1007 for (i
= 0, line_count
= 0; i
< dialog_info
->button_count
; i
++)
1009 button_layout_infos
[i
].line
= line_count
;
1010 x
+= button_layout_infos
[i
].width
+ h_spacing
;
1011 line_widths
[line_count
] += button_layout_infos
[i
].width
+ h_spacing
;
1013 if ((i
+ 1 < dialog_info
->button_count
) && (x
+ button_layout_infos
[i
+ 1].width
+ h_spacing
>= dialog_width
))
1015 x
= expando_right
+ h_spacing
;
1021 /* Try to balance lines so they are about the same size */
1022 for (i
= 1; i
< line_count
- 1; i
++)
1024 int diff_now
= abs(line_widths
[i
] - line_widths
[i
- 1]);
1025 unsigned int j
, last_button
= 0;
1028 for (j
= 0; j
< dialog_info
->button_count
; j
++)
1029 if (button_layout_infos
[j
].line
== i
- 1) last_button
= j
;
1031 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1032 diff_changed
= abs(2 * button_layout_infos
[last_button
].width
+ line_widths
[i
] - line_widths
[i
- 1]);
1034 if (diff_changed
< diff_now
)
1036 button_layout_infos
[last_button
].line
= i
;
1037 line_widths
[i
] += button_layout_infos
[last_button
].width
;
1038 line_widths
[i
- 1] -= button_layout_infos
[last_button
].width
;
1042 /* Calculate left alignment so all lines are as far right as possible. */
1043 align
= dialog_width
- h_spacing
;
1044 for (i
= 0; i
< line_count
; i
++)
1046 int new_alignment
= dialog_width
- line_widths
[i
];
1047 if (new_alignment
< align
) align
= new_alignment
;
1050 /* Now that we got them all positioned, move all buttons */
1052 size
.cy
= button_height
;
1053 for (i
= 0; i
< dialog_info
->button_count
; i
++)
1056 if (i
> 0 && button_layout_infos
[i
].line
!= button_layout_infos
[i
- 1].line
)
1059 dialog_height
+= size
.cy
+ v_spacing
;
1062 y
= dialog_height
+ v_spacing
;
1063 size
.cx
= button_layout_infos
[i
].width
;
1064 SetWindowPos(dialog_info
->buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1065 x
+= button_layout_infos
[i
].width
+ h_spacing
;
1068 /* Add height for last row button and spacing */
1069 dialog_height
+= size
.cy
+ v_spacing
;
1070 dialog_height
= max(dialog_height
, expando_bottom
);
1072 Free(button_layout_infos
);
1076 footer_icon_right
= 0;
1077 footer_icon_bottom
= dialog_height
;
1078 if (dialog_info
->footer_icon
)
1081 y
= dialog_height
+ v_spacing
;
1082 size
.cx
= GetSystemMetrics(SM_CXSMICON
);
1083 size
.cy
= GetSystemMetrics(SM_CYSMICON
);
1084 SetWindowPos(dialog_info
->footer_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1085 footer_icon_right
= x
+ size
.cx
;
1086 footer_icon_bottom
= y
+ size
.cy
;
1090 taskdialog_label_layout(dialog_info
, dialog_info
->footer_text
, footer_icon_right
, dialog_width
, &dialog_height
,
1092 dialog_height
= max(dialog_height
, footer_icon_bottom
);
1094 /* Expanded information */
1095 if ((taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
1096 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, 0, dialog_width
, &dialog_height
, syslink
);
1098 /* Add height for spacing, title height and frame height */
1099 dialog_height
+= v_spacing
;
1100 dialog_height
+= GetSystemMetrics(SM_CYCAPTION
);
1101 dialog_height
+= GetSystemMetrics(SM_CXDLGFRAME
);
1105 x
= (ref_rect
.left
+ ref_rect
.right
- dialog_width
) / 2;
1106 y
= (ref_rect
.top
+ ref_rect
.bottom
- dialog_height
) / 2;
1107 SetWindowPos(dialog_info
->hwnd
, 0, x
, y
, dialog_width
, dialog_height
, SWP_NOZORDER
);
1111 SetWindowPos(dialog_info
->hwnd
, 0, 0, 0, dialog_width
, dialog_height
, SWP_NOMOVE
| SWP_NOZORDER
);
1114 static void taskdialog_draw_expando_control(struct taskdialog_info
*dialog_info
, LPDRAWITEMSTRUCT dis
)
1120 LONG icon_width
, icon_height
, text_offset
;
1121 UINT style
= DFCS_FLAT
;
1125 hwnd
= dis
->hwndItem
;
1127 SendMessageW(hwnd
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
1129 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
1130 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
1131 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
1132 rect
.right
= icon_width
;
1133 rect
.bottom
= icon_height
;
1134 style
|= dialog_info
->expanded
? DFCS_SCROLLUP
: DFCS_SCROLLDOWN
;
1135 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
, style
);
1137 GetCharWidthW(hdc
, '0', '0', &text_offset
);
1141 rect
.left
+= icon_width
+ text_offset
;
1142 text
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
1143 DrawTextW(hdc
, text
, -1, &rect
, DT_WORDBREAK
| DT_END_ELLIPSIS
| DT_EXPANDTABS
);
1145 draw_focus
= (dis
->itemState
& ODS_FOCUS
) && !(dis
->itemState
& ODS_NOFOCUSRECT
);
1146 if(draw_focus
) DrawFocusRect(hdc
, &rect
);
1149 static void taskdialog_init(struct taskdialog_info
*dialog_info
, HWND hwnd
)
1151 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
1152 NONCLIENTMETRICSW ncm
;
1156 ncm
.cbSize
= sizeof(ncm
);
1157 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
1159 memset(dialog_info
, 0, sizeof(*dialog_info
));
1160 dialog_info
->taskconfig
= taskconfig
;
1161 dialog_info
->hwnd
= hwnd
;
1162 dialog_info
->font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
1164 hdc
= GetDC(dialog_info
->hwnd
);
1165 SelectObject(hdc
, dialog_info
->font
);
1166 dialog_info
->m
.x_baseunit
= GdiGetCharDimensions(hdc
, NULL
, &dialog_info
->m
.y_baseunit
);
1167 ReleaseDC(dialog_info
->hwnd
, hdc
);
1169 dialog_info
->m
.h_spacing
= DIALOG_SPACING
;
1170 dialog_info
->m
.v_spacing
= DIALOG_SPACING
;
1171 taskdialog_du_to_px(dialog_info
, &dialog_info
->m
.h_spacing
, &dialog_info
->m
.v_spacing
);
1173 if (taskconfig
->dwFlags
& TDF_CALLBACK_TIMER
)
1175 SetTimer(hwnd
, ID_TIMER
, DIALOG_TIMER_MS
, NULL
);
1176 dialog_info
->last_timer_tick
= GetTickCount();
1179 taskdialog_add_main_icon(dialog_info
);
1180 taskdialog_add_main_instruction(dialog_info
);
1181 taskdialog_add_content(dialog_info
);
1182 taskdialog_add_expanded_info(dialog_info
);
1183 taskdialog_add_progress_bar(dialog_info
);
1184 taskdialog_add_radio_buttons(dialog_info
);
1185 taskdialog_add_command_links(dialog_info
);
1186 taskdialog_add_expando_button(dialog_info
);
1187 taskdialog_add_verification_box(dialog_info
);
1188 taskdialog_add_buttons(dialog_info
);
1189 taskdialog_add_footer_icon(dialog_info
);
1190 taskdialog_add_footer_text(dialog_info
);
1192 /* Set default button */
1193 if (!dialog_info
->default_button
&& dialog_info
->command_links
)
1194 dialog_info
->default_button
= dialog_info
->command_links
[0];
1195 if (!dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->buttons
[0];
1196 SendMessageW(dialog_info
->hwnd
, WM_NEXTDLGCTL
, (WPARAM
)dialog_info
->default_button
, TRUE
);
1197 id
= GetWindowLongW(dialog_info
->default_button
, GWLP_ID
);
1198 SendMessageW(dialog_info
->hwnd
, DM_SETDEFID
, id
, 0);
1200 taskdialog_layout(dialog_info
);
1203 static BOOL CALLBACK
takdialog_destroy_control(HWND hwnd
, LPARAM lParam
)
1205 DestroyWindow(hwnd
);
1209 static void taskdialog_destroy(struct taskdialog_info
*dialog_info
)
1211 EnumChildWindows(dialog_info
->hwnd
, takdialog_destroy_control
, 0);
1213 if (dialog_info
->taskconfig
->dwFlags
& TDF_CALLBACK_TIMER
) KillTimer(dialog_info
->hwnd
, ID_TIMER
);
1214 if (dialog_info
->font
) DeleteObject(dialog_info
->font
);
1215 if (dialog_info
->main_instruction_font
) DeleteObject(dialog_info
->main_instruction_font
);
1216 Free(dialog_info
->buttons
);
1217 Free(dialog_info
->radio_buttons
);
1218 Free(dialog_info
->command_links
);
1219 Free(dialog_info
->expanded_text
);
1220 Free(dialog_info
->collapsed_text
);
1223 static INT_PTR CALLBACK
taskdialog_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1225 static const WCHAR taskdialog_info_propnameW
[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
1226 struct taskdialog_info
*dialog_info
;
1229 TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd
, msg
, wParam
, lParam
);
1231 if (msg
!= WM_INITDIALOG
)
1232 dialog_info
= GetPropW(hwnd
, taskdialog_info_propnameW
);
1236 case TDM_NAVIGATE_PAGE
:
1237 dialog_info
->taskconfig
= (const TASKDIALOGCONFIG
*)lParam
;
1238 taskdialog_destroy(dialog_info
);
1239 taskdialog_init(dialog_info
, hwnd
);
1240 taskdialog_notify(dialog_info
, TDN_DIALOG_CONSTRUCTED
, 0, 0);
1241 /* Default radio button click notification is sent before TDN_NAVIGATED */
1242 taskdialog_check_default_radio_buttons(dialog_info
);
1243 taskdialog_notify(dialog_info
, TDN_NAVIGATED
, 0, 0);
1245 case TDM_CLICK_BUTTON
:
1246 taskdialog_click_button(dialog_info
, wParam
);
1248 case TDM_ENABLE_BUTTON
:
1249 taskdialog_enable_button(dialog_info
, wParam
, lParam
);
1251 case TDM_SET_MARQUEE_PROGRESS_BAR
:
1253 BOOL marquee
= wParam
;
1255 if(!dialog_info
->progress_bar
) break;
1256 style
= GetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
);
1257 style
= marquee
? style
| PBS_MARQUEE
: style
& (~PBS_MARQUEE
);
1258 SetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
, style
);
1261 case TDM_SET_PROGRESS_BAR_STATE
:
1262 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETSTATE
, wParam
, 0);
1263 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1265 case TDM_SET_PROGRESS_BAR_RANGE
:
1266 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETRANGE
, 0, lParam
);
1267 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1269 case TDM_SET_PROGRESS_BAR_POS
:
1271 if (dialog_info
->progress_bar
)
1273 LONG style
= GetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
);
1274 if (!(style
& PBS_MARQUEE
)) result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETPOS
, wParam
, 0);
1276 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1278 case TDM_SET_PROGRESS_BAR_MARQUEE
:
1279 SendMessageW(dialog_info
->progress_bar
, PBM_SETMARQUEE
, wParam
, lParam
);
1281 case TDM_SET_ELEMENT_TEXT
:
1282 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1283 taskdialog_layout(dialog_info
);
1285 case TDM_UPDATE_ELEMENT_TEXT
:
1286 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1288 case TDM_CLICK_RADIO_BUTTON
:
1289 taskdialog_click_radio_button(dialog_info
, wParam
);
1291 case TDM_ENABLE_RADIO_BUTTON
:
1292 taskdialog_enable_radio_button(dialog_info
, wParam
, lParam
);
1294 case TDM_CLICK_VERIFICATION
:
1296 BOOL checked
= (BOOL
)wParam
;
1297 BOOL focused
= (BOOL
)lParam
;
1298 dialog_info
->verification_checked
= checked
;
1299 if (dialog_info
->verification_box
)
1301 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, checked
? BST_CHECKED
: BST_UNCHECKED
, 0);
1302 taskdialog_notify(dialog_info
, TDN_VERIFICATION_CLICKED
, checked
, 0);
1303 if (focused
) SetFocus(dialog_info
->verification_box
);
1307 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE
:
1308 taskdialog_button_set_shield(dialog_info
, wParam
, lParam
);
1310 case TDM_UPDATE_ICON
:
1311 taskdialog_set_icon(dialog_info
, wParam
, (HICON
)lParam
);
1314 dialog_info
= (struct taskdialog_info
*)lParam
;
1316 taskdialog_init(dialog_info
, hwnd
);
1318 SetPropW(hwnd
, taskdialog_info_propnameW
, dialog_info
);
1319 taskdialog_notify(dialog_info
, TDN_DIALOG_CONSTRUCTED
, 0, 0);
1320 taskdialog_notify(dialog_info
, TDN_CREATED
, 0, 0);
1321 /* Default radio button click notification sent after TDN_CREATED */
1322 taskdialog_check_default_radio_buttons(dialog_info
);
1325 if (HIWORD(wParam
) == BN_CLICKED
)
1327 taskdialog_on_button_click(dialog_info
, (HWND
)lParam
);
1332 taskdialog_notify(dialog_info
, TDN_HELP
, 0, 0);
1335 if (ID_TIMER
== wParam
)
1337 DWORD elapsed
= GetTickCount() - dialog_info
->last_timer_tick
;
1338 if (taskdialog_notify(dialog_info
, TDN_TIMER
, elapsed
, 0) == S_FALSE
)
1339 dialog_info
->last_timer_tick
= GetTickCount();
1344 PNMLINK pnmLink
= (PNMLINK
)lParam
;
1345 HWND hwndFrom
= pnmLink
->hdr
.hwndFrom
;
1346 if ((taskdialog_hyperlink_enabled(dialog_info
))
1347 && (hwndFrom
== dialog_info
->content
|| hwndFrom
== dialog_info
->expanded_info
1348 || hwndFrom
== dialog_info
->footer_text
)
1349 && (pnmLink
->hdr
.code
== NM_CLICK
|| pnmLink
->hdr
.code
== NM_RETURN
))
1351 taskdialog_notify(dialog_info
, TDN_HYPERLINK_CLICKED
, 0, (LPARAM
)pnmLink
->item
.szUrl
);
1358 LPDRAWITEMSTRUCT dis
= (LPDRAWITEMSTRUCT
)lParam
;
1359 if (dis
->hwndItem
== dialog_info
->expando_button
)
1361 taskdialog_draw_expando_control(dialog_info
, dis
);
1362 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, TRUE
);
1368 taskdialog_notify(dialog_info
, TDN_DESTROYED
, 0, 0);
1369 RemovePropW(hwnd
, taskdialog_info_propnameW
);
1370 taskdialog_destroy(dialog_info
);
1378 /***********************************************************************
1379 * TaskDialogIndirect [COMCTL32.@]
1381 HRESULT WINAPI
TaskDialogIndirect(const TASKDIALOGCONFIG
*taskconfig
, int *button
,
1382 int *radio_button
, BOOL
*verification_flag_checked
)
1384 struct taskdialog_info dialog_info
;
1385 DLGTEMPLATE
*template;
1388 TRACE("%p, %p, %p, %p\n", taskconfig
, button
, radio_button
, verification_flag_checked
);
1390 if (!taskconfig
|| taskconfig
->cbSize
!= sizeof(TASKDIALOGCONFIG
))
1391 return E_INVALIDARG
;
1393 dialog_info
.taskconfig
= taskconfig
;
1395 template = create_taskdialog_template(taskconfig
);
1396 ret
= (short)DialogBoxIndirectParamW(taskconfig
->hInstance
, template, taskconfig
->hwndParent
,
1397 taskdialog_proc
, (LPARAM
)&dialog_info
);
1400 if (button
) *button
= ret
;
1401 if (radio_button
) *radio_button
= dialog_info
.selected_radio_id
;
1402 if (verification_flag_checked
) *verification_flag_checked
= dialog_info
.verification_checked
;
1407 /***********************************************************************
1408 * TaskDialog [COMCTL32.@]
1410 HRESULT WINAPI
TaskDialog(HWND owner
, HINSTANCE hinst
, const WCHAR
*title
, const WCHAR
*main_instruction
,
1411 const WCHAR
*content
, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons
, const WCHAR
*icon
, int *button
)
1413 TASKDIALOGCONFIG taskconfig
;
1415 TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner
, hinst
, debugstr_w(title
), debugstr_w(main_instruction
),
1416 debugstr_w(content
), common_buttons
, debugstr_w(icon
), button
);
1418 memset(&taskconfig
, 0, sizeof(taskconfig
));
1419 taskconfig
.cbSize
= sizeof(taskconfig
);
1420 taskconfig
.hwndParent
= owner
;
1421 taskconfig
.hInstance
= hinst
;
1422 taskconfig
.dwCommonButtons
= common_buttons
;
1423 taskconfig
.pszWindowTitle
= title
;
1424 taskconfig
.u
.pszMainIcon
= icon
;
1425 taskconfig
.pszMainInstruction
= main_instruction
;
1426 taskconfig
.pszContent
= content
;
1427 return TaskDialogIndirect(&taskconfig
, button
, NULL
, NULL
);