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"
39 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog
);
41 static const UINT DIALOG_MIN_WIDTH
= 240;
42 static const UINT DIALOG_SPACING
= 5;
43 static const UINT DIALOG_BUTTON_WIDTH
= 50;
44 static const UINT DIALOG_BUTTON_HEIGHT
= 14;
45 static const UINT DIALOG_EXPANDO_ICON_WIDTH
= 10;
46 static const UINT DIALOG_EXPANDO_ICON_HEIGHT
= 10;
47 static const UINT DIALOG_TIMER_MS
= 200;
49 static const UINT ID_TIMER
= 1;
51 struct taskdialog_info
54 const TASKDIALOGCONFIG
*taskconfig
;
55 DWORD last_timer_tick
;
57 HFONT main_instruction_font
;
60 HWND main_instruction
;
64 INT radio_button_count
;
66 INT command_link_count
;
69 HWND verification_box
;
83 INT selected_radio_id
;
84 BOOL verification_checked
;
88 WCHAR
*collapsed_text
;
91 struct button_layout_info
97 static HRESULT
taskdialog_notify(struct taskdialog_info
*dialog_info
, UINT notification
, WPARAM wparam
, LPARAM lparam
);
98 static void taskdialog_on_button_click(struct taskdialog_info
*dialog_info
, HWND hwnd
, WORD id
);
99 static void taskdialog_layout(struct taskdialog_info
*dialog_info
);
101 static void taskdialog_du_to_px(struct taskdialog_info
*dialog_info
, LONG
*width
, LONG
*height
)
103 if (width
) *width
= MulDiv(*width
, dialog_info
->m
.x_baseunit
, 4);
104 if (height
) *height
= MulDiv(*height
, dialog_info
->m
.y_baseunit
, 8);
107 static void template_write_data(char **ptr
, const void *src
, unsigned int size
)
109 memcpy(*ptr
, src
, size
);
113 static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG
*taskconfig
, RECT
*ret
)
115 HMONITOR monitor
= MonitorFromWindow(taskconfig
->hwndParent
? taskconfig
->hwndParent
: GetActiveWindow(),
116 MONITOR_DEFAULTTOPRIMARY
);
119 info
.cbSize
= sizeof(info
);
120 GetMonitorInfoW(monitor
, &info
);
122 if ((taskconfig
->dwFlags
& TDF_POSITION_RELATIVE_TO_WINDOW
) && taskconfig
->hwndParent
)
123 GetWindowRect(taskconfig
->hwndParent
, ret
);
127 return info
.rcWork
.right
- info
.rcWork
.left
;
130 static WCHAR
*taskdialog_get_exe_name(WCHAR
*name
, DWORD length
)
132 DWORD len
= GetModuleFileNameW(NULL
, name
, length
);
133 if (len
&& len
< length
)
136 if ((p
= wcsrchr(name
, '/'))) name
= p
+ 1;
137 if ((p
= wcsrchr(name
, '\\'))) name
= p
+ 1;
144 static DLGTEMPLATE
*create_taskdialog_template(const TASKDIALOGCONFIG
*taskconfig
)
146 unsigned int size
, title_size
;
147 static const WORD fontsize
= 0x7fff;
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
= (lstrlenW(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 if (taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, id
, 0) == S_OK
) EndDialog(dialog_info
->hwnd
, id
);
214 static void taskdialog_button_set_shield(const struct taskdialog_info
*dialog_info
, INT id
, BOOL elevate
)
216 HWND hwnd
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, id
);
217 if (!hwnd
) hwnd
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, id
);
218 if (hwnd
) SendMessageW(hwnd
, BCM_SETSHIELD
, 0, elevate
);
221 static void taskdialog_enable_radio_button(const struct taskdialog_info
*dialog_info
, INT id
, BOOL enable
)
223 HWND hwnd
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, id
);
224 if (hwnd
) EnableWindow(hwnd
, enable
);
227 static void taskdialog_click_radio_button(const struct taskdialog_info
*dialog_info
, INT id
)
229 HWND hwnd
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, id
);
230 if (hwnd
) SendMessageW(hwnd
, BM_CLICK
, 0, 0);
233 static HRESULT
taskdialog_notify(struct taskdialog_info
*dialog_info
, UINT notification
, WPARAM wparam
, LPARAM lparam
)
235 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
236 return taskconfig
->pfCallback
237 ? taskconfig
->pfCallback(dialog_info
->hwnd
, notification
, wparam
, lparam
, taskconfig
->lpCallbackData
)
241 static void taskdialog_move_controls_vertically(HWND parent
, HWND
*controls
, INT count
, INT offset
)
247 for (i
= 0; i
< count
; i
++)
249 if (!controls
[i
]) continue;
251 GetWindowRect(controls
[i
], &rect
);
254 MapWindowPoints(HWND_DESKTOP
, parent
, &pt
, 1);
255 SetWindowPos(controls
[i
], 0, pt
.x
, pt
.y
+ offset
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
259 static void taskdialog_toggle_expando_control(struct taskdialog_info
*dialog_info
)
261 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
263 RECT info_rect
, rect
;
266 dialog_info
->expanded
= !dialog_info
->expanded
;
267 text
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
268 SendMessageW(dialog_info
->expando_button
, WM_SETTEXT
, 0, (LPARAM
)text
);
269 ShowWindow(dialog_info
->expanded_info
, dialog_info
->expanded
? SW_SHOWDEFAULT
: SW_HIDE
);
271 GetWindowRect(dialog_info
->expanded_info
, &info_rect
);
272 /* If expanded information starts up not expanded, call taskdialog_layout()
273 * to to set size for expanded information control at least once */
274 if (IsRectEmpty(&info_rect
))
276 taskdialog_layout(dialog_info
);
279 height
= info_rect
.bottom
- info_rect
.top
+ dialog_info
->m
.v_spacing
;
280 offset
= dialog_info
->expanded
? height
: -height
;
282 /* Update vertical layout, move all controls after expanded information */
284 GetWindowRect(dialog_info
->hwnd
, &rect
);
285 SetWindowPos(dialog_info
->hwnd
, 0, 0, 0, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
+ offset
,
286 SWP_NOMOVE
| SWP_NOZORDER
);
288 if (!(taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
))
290 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->progress_bar
, 1, offset
);
291 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->expando_button
, 1, offset
);
292 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->verification_box
, 1, offset
);
293 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->footer_icon
, 1, offset
);
294 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->footer_text
, 1, offset
);
295 taskdialog_move_controls_vertically(dialog_info
->hwnd
, dialog_info
->buttons
, dialog_info
->button_count
, offset
);
296 taskdialog_move_controls_vertically(dialog_info
->hwnd
, dialog_info
->radio_buttons
,
297 dialog_info
->radio_button_count
, offset
);
298 taskdialog_move_controls_vertically(dialog_info
->hwnd
, dialog_info
->command_links
,
299 dialog_info
->command_link_count
, offset
);
303 static void taskdialog_on_button_click(struct taskdialog_info
*dialog_info
, HWND hwnd
, WORD id
)
306 HWND button
, radio_button
;
308 /* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
309 command_id
= hwnd
? GetWindowLongW(hwnd
, GWLP_ID
) : id
;
311 if (hwnd
&& hwnd
== dialog_info
->expando_button
)
313 taskdialog_toggle_expando_control(dialog_info
);
314 taskdialog_notify(dialog_info
, TDN_EXPANDO_BUTTON_CLICKED
, dialog_info
->expanded
, 0);
318 if (hwnd
&& hwnd
== dialog_info
->verification_box
)
320 dialog_info
->verification_checked
= !dialog_info
->verification_checked
;
321 taskdialog_notify(dialog_info
, TDN_VERIFICATION_CLICKED
, dialog_info
->verification_checked
, 0);
325 radio_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, command_id
);
328 dialog_info
->selected_radio_id
= command_id
;
329 taskdialog_notify(dialog_info
, TDN_RADIO_BUTTON_CLICKED
, command_id
, 0);
333 button
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, command_id
);
334 if (!button
) button
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, command_id
);
335 if (!button
&& command_id
== IDOK
)
337 button
= dialog_info
->command_link_count
> 0 ? dialog_info
->command_links
[0] : dialog_info
->buttons
[0];
338 command_id
= GetWindowLongW(button
, GWLP_ID
);
341 if (button
&& taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, command_id
, 0) == S_OK
)
342 EndDialog(dialog_info
->hwnd
, command_id
);
345 static WCHAR
*taskdialog_gettext(struct taskdialog_info
*dialog_info
, BOOL user_resource
, const WCHAR
*text
)
347 const WCHAR
*textW
= NULL
;
351 if (IS_INTRESOURCE(text
))
353 if (!(length
= LoadStringW(user_resource
? dialog_info
->taskconfig
->hInstance
: COMCTL32_hModule
,
354 (UINT_PTR
)text
, (WCHAR
*)&textW
, 0)))
360 length
= lstrlenW(textW
);
363 ret
= Alloc((length
+ 1) * sizeof(WCHAR
));
364 if (ret
) memcpy(ret
, textW
, length
* sizeof(WCHAR
));
369 static BOOL
taskdialog_hyperlink_enabled(struct taskdialog_info
*dialog_info
)
371 return dialog_info
->taskconfig
->dwFlags
& TDF_ENABLE_HYPERLINKS
;
374 static BOOL
taskdialog_use_command_link(struct taskdialog_info
*dialog_info
)
376 return dialog_info
->taskconfig
->dwFlags
& (TDF_USE_COMMAND_LINKS
| TDF_USE_COMMAND_LINKS_NO_ICON
);
379 static void taskdialog_get_label_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, LONG max_width
, SIZE
*size
,
382 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
383 HFONT hfont
, old_hfont
;
391 SendMessageW(hwnd
, LM_GETIDEALSIZE
, max_width
, (LPARAM
)size
);
395 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
396 style
|= DT_RIGHT
| DT_RTLREADING
;
400 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
401 text_length
= GetWindowTextLengthW(hwnd
);
402 text
= Alloc((text_length
+ 1) * sizeof(WCHAR
));
409 GetWindowTextW(hwnd
, text
, text_length
+ 1);
411 old_hfont
= SelectObject(hdc
, hfont
);
412 rect
.right
= max_width
;
413 size
->cy
= DrawTextW(hdc
, text
, text_length
, &rect
, style
);
414 size
->cx
= min(max_width
, rect
.right
- rect
.left
);
415 if (old_hfont
) SelectObject(hdc
, old_hfont
);
416 ReleaseDC(hwnd
, hdc
);
420 static void taskdialog_get_button_size(HWND hwnd
, LONG max_width
, SIZE
*size
)
422 size
->cx
= max_width
;
424 SendMessageW(hwnd
, BCM_GETIDEALSIZE
, 0, (LPARAM
)size
);
427 static void taskdialog_get_expando_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, SIZE
*size
)
429 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
430 HFONT hfont
, old_hfont
;
433 LONG icon_width
, icon_height
;
435 LONG max_width
, max_text_height
;
438 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
439 old_hfont
= SelectObject(hdc
, hfont
);
441 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
442 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
443 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
445 GetCharWidthW(hdc
, '0', '0', &text_offset
);
448 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
449 style
|= DT_RIGHT
| DT_RTLREADING
;
453 max_width
= DIALOG_MIN_WIDTH
/ 2;
454 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
456 rect
.right
= max_width
- icon_width
- text_offset
;
457 max_text_height
= DrawTextW(hdc
, dialog_info
->expanded_text
, -1, &rect
, style
);
458 size
->cy
= max(max_text_height
, icon_height
);
459 size
->cx
= rect
.right
- rect
.left
;
461 rect
.right
= max_width
- icon_width
- text_offset
;
462 max_text_height
= DrawTextW(hdc
, dialog_info
->collapsed_text
, -1, &rect
, style
);
463 size
->cy
= max(size
->cy
, max_text_height
);
464 size
->cx
= max(size
->cx
, rect
.right
- rect
.left
);
465 size
->cx
= min(size
->cx
, max_width
);
467 if (old_hfont
) SelectObject(hdc
, old_hfont
);
468 ReleaseDC(hwnd
, hdc
);
471 static ULONG_PTR
taskdialog_get_standard_icon(LPCWSTR icon
)
473 if (icon
== TD_WARNING_ICON
)
475 else if (icon
== TD_ERROR_ICON
)
477 else if (icon
== TD_INFORMATION_ICON
)
478 return IDI_INFORMATION
;
479 else if (icon
== TD_SHIELD_ICON
)
482 return (ULONG_PTR
)icon
;
485 static void taskdialog_set_icon(struct taskdialog_info
*dialog_info
, INT element
, HICON icon
)
487 DWORD flags
= dialog_info
->taskconfig
->dwFlags
;
493 if (((flags
& TDF_USE_HICON_MAIN
) && element
== TDIE_ICON_MAIN
)
494 || ((flags
& TDF_USE_HICON_FOOTER
) && element
== TDIE_ICON_FOOTER
))
498 if (element
== TDIE_ICON_FOOTER
)
500 cx
= GetSystemMetrics(SM_CXSMICON
);
501 cy
= GetSystemMetrics(SM_CYSMICON
);
503 hicon
= LoadImageW(dialog_info
->taskconfig
->hInstance
, (LPCWSTR
)icon
, IMAGE_ICON
, cx
, cy
, LR_SHARED
| LR_DEFAULTSIZE
);
505 hicon
= LoadImageW(NULL
, (LPCWSTR
)taskdialog_get_standard_icon((LPCWSTR
)icon
), IMAGE_ICON
, cx
, cy
,
506 LR_SHARED
| LR_DEFAULTSIZE
);
511 if (element
== TDIE_ICON_MAIN
)
513 SendMessageW(dialog_info
->hwnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)hicon
);
514 SendMessageW(dialog_info
->main_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
516 else if (element
== TDIE_ICON_FOOTER
)
517 SendMessageW(dialog_info
->footer_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
520 static void taskdialog_set_element_text(struct taskdialog_info
*dialog_info
, TASKDIALOG_ELEMENTS element
,
526 if (element
== TDE_CONTENT
)
527 hwnd
= dialog_info
->content
;
528 else if (element
== TDE_EXPANDED_INFORMATION
)
529 hwnd
= dialog_info
->expanded_info
;
530 else if (element
== TDE_FOOTER
)
531 hwnd
= dialog_info
->footer_text
;
532 else if (element
== TDE_MAIN_INSTRUCTION
)
533 hwnd
= dialog_info
->main_instruction
;
537 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
538 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)textW
);
542 static void taskdialog_check_default_radio_buttons(struct taskdialog_info
*dialog_info
)
544 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
547 if (!dialog_info
->radio_button_count
) return;
549 default_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
,
550 taskconfig
->nDefaultRadioButton
);
552 if (!default_button
&& !(taskconfig
->dwFlags
& TDF_NO_DEFAULT_RADIO_BUTTON
))
553 default_button
= dialog_info
->radio_buttons
[0];
557 SendMessageW(default_button
, BM_SETCHECK
, BST_CHECKED
, 0);
558 taskdialog_on_button_click(dialog_info
, default_button
, 0);
562 static void taskdialog_add_main_icon(struct taskdialog_info
*dialog_info
)
564 if (!dialog_info
->taskconfig
->u
.hMainIcon
) return;
566 dialog_info
->main_icon
=
567 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
568 taskdialog_set_icon(dialog_info
, TDIE_ICON_MAIN
, dialog_info
->taskconfig
->u
.hMainIcon
);
571 static HWND
taskdialog_create_label(struct taskdialog_info
*dialog_info
, const WCHAR
*text
, HFONT font
, BOOL syslink
)
576 DWORD style
= WS_CHILD
| WS_VISIBLE
;
578 if (!text
) return NULL
;
580 class = syslink
? WC_LINK
: WC_STATICW
;
581 if (syslink
) style
|= WS_TABSTOP
;
582 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
583 hwnd
= CreateWindowW(class, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
586 SendMessageW(hwnd
, WM_SETFONT
, (WPARAM
)font
, 0);
590 static void taskdialog_add_main_instruction(struct taskdialog_info
*dialog_info
)
592 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
593 NONCLIENTMETRICSW ncm
;
595 if (!taskconfig
->pszMainInstruction
) return;
597 ncm
.cbSize
= sizeof(ncm
);
598 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
599 /* 1.25 times the height */
600 ncm
.lfMessageFont
.lfHeight
= ncm
.lfMessageFont
.lfHeight
* 5 / 4;
601 ncm
.lfMessageFont
.lfWeight
= FW_BOLD
;
602 dialog_info
->main_instruction_font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
604 dialog_info
->main_instruction
=
605 taskdialog_create_label(dialog_info
, taskconfig
->pszMainInstruction
, dialog_info
->main_instruction_font
, FALSE
);
608 static void taskdialog_add_content(struct taskdialog_info
*dialog_info
)
610 dialog_info
->content
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszContent
, dialog_info
->font
,
611 taskdialog_hyperlink_enabled(dialog_info
));
614 static void taskdialog_add_progress_bar(struct taskdialog_info
*dialog_info
)
616 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
617 DWORD style
= PBS_SMOOTH
| PBS_SMOOTHREVERSE
| WS_CHILD
| WS_VISIBLE
;
619 if (!(taskconfig
->dwFlags
& (TDF_SHOW_PROGRESS_BAR
| TDF_SHOW_MARQUEE_PROGRESS_BAR
))) return;
620 if (taskconfig
->dwFlags
& TDF_SHOW_MARQUEE_PROGRESS_BAR
) style
|= PBS_MARQUEE
;
621 dialog_info
->progress_bar
=
622 CreateWindowW(PROGRESS_CLASSW
, NULL
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
625 static void taskdialog_add_radio_buttons(struct taskdialog_info
*dialog_info
)
627 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
628 static const DWORD style
= BS_AUTORADIOBUTTON
| BS_MULTILINE
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
632 if (!taskconfig
->cRadioButtons
|| !taskconfig
->pRadioButtons
) return;
634 dialog_info
->radio_buttons
= Alloc(taskconfig
->cRadioButtons
* sizeof(*dialog_info
->radio_buttons
));
635 if (!dialog_info
->radio_buttons
) return;
637 dialog_info
->radio_button_count
= taskconfig
->cRadioButtons
;
638 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
640 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pRadioButtons
[i
].pszButtonText
);
641 dialog_info
->radio_buttons
[i
] =
642 CreateWindowW(WC_BUTTONW
, textW
, i
== 0 ? style
| WS_GROUP
: style
, 0, 0, 0, 0, dialog_info
->hwnd
,
643 LongToHandle(taskconfig
->pRadioButtons
[i
].nButtonID
), 0, NULL
);
644 SendMessageW(dialog_info
->radio_buttons
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
649 static void taskdialog_add_command_links(struct taskdialog_info
*dialog_info
)
651 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
652 DWORD default_style
= BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, style
;
657 if (!taskconfig
->cButtons
|| !taskconfig
->pButtons
|| !taskdialog_use_command_link(dialog_info
)) return;
659 dialog_info
->command_links
= Alloc(taskconfig
->cButtons
* sizeof(*dialog_info
->command_links
));
660 if (!dialog_info
->command_links
) return;
662 dialog_info
->command_link_count
= taskconfig
->cButtons
;
663 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
665 is_default
= taskconfig
->pButtons
[i
].nButtonID
== taskconfig
->nDefaultButton
;
666 style
= is_default
? default_style
| BS_DEFCOMMANDLINK
: default_style
| BS_COMMANDLINK
;
667 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pButtons
[i
].pszButtonText
);
668 dialog_info
->command_links
[i
] = CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
,
669 LongToHandle(taskconfig
->pButtons
[i
].nButtonID
), 0, NULL
);
670 SendMessageW(dialog_info
->command_links
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
673 if (is_default
&& !dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->command_links
[i
];
677 static void taskdialog_add_expanded_info(struct taskdialog_info
*dialog_info
)
679 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
681 if (!taskconfig
->pszExpandedInformation
) return;
683 dialog_info
->expanded
= taskconfig
->dwFlags
& TDF_EXPANDED_BY_DEFAULT
;
684 dialog_info
->expanded_info
= taskdialog_create_label(dialog_info
, taskconfig
->pszExpandedInformation
,
685 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
686 ShowWindow(dialog_info
->expanded_info
, dialog_info
->expanded
? SW_SHOWDEFAULT
: SW_HIDE
);
689 static void taskdialog_add_expando_button(struct taskdialog_info
*dialog_info
)
691 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
694 if (!taskconfig
->pszExpandedInformation
) return;
696 if (!taskconfig
->pszCollapsedControlText
&& !taskconfig
->pszExpandedControlText
)
698 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_EXPANDED
));
699 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_COLLAPSED
));
703 textW
= taskconfig
->pszExpandedControlText
? taskconfig
->pszExpandedControlText
704 : taskconfig
->pszCollapsedControlText
;
705 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
706 textW
= taskconfig
->pszCollapsedControlText
? taskconfig
->pszCollapsedControlText
707 : taskconfig
->pszExpandedControlText
;
708 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
711 textW
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
713 dialog_info
->expando_button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_OWNERDRAW
, 0,
714 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
715 SendMessageW(dialog_info
->expando_button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
718 static void taskdialog_add_verification_box(struct taskdialog_info
*dialog_info
)
720 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
721 static const DWORD style
= BS_AUTOCHECKBOX
| BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
724 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
725 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
) dialog_info
->verification_checked
= TRUE
;
727 if (!taskconfig
->pszVerificationText
) return;
729 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pszVerificationText
);
730 dialog_info
->verification_box
= CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
731 SendMessageW(dialog_info
->verification_box
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
734 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
)
735 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, BST_CHECKED
, 0);
738 static void taskdialog_add_button(struct taskdialog_info
*dialog_info
, HWND
*button
, INT_PTR id
, const WCHAR
*text
,
741 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
744 textW
= taskdialog_gettext(dialog_info
, custom_button
, text
);
745 *button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0, 0, 0, 0, dialog_info
->hwnd
,
748 SendMessageW(*button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
750 if (id
== taskconfig
->nDefaultButton
&& !dialog_info
->default_button
) dialog_info
->default_button
= *button
;
753 static void taskdialog_add_buttons(struct taskdialog_info
*dialog_info
)
755 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
756 BOOL use_command_links
= taskdialog_use_command_link(dialog_info
);
757 DWORD flags
= taskconfig
->dwCommonButtons
;
758 INT count
, max_count
;
760 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
762 if (!use_command_links
&& taskconfig
->cButtons
&& taskconfig
->pButtons
) max_count
+= taskconfig
->cButtons
;
764 dialog_info
->buttons
= Alloc(max_count
* sizeof(*dialog_info
->buttons
));
765 if (!dialog_info
->buttons
) return;
767 for (count
= 0; !use_command_links
&& count
< taskconfig
->cButtons
; count
++)
768 taskdialog_add_button(dialog_info
, &dialog_info
->buttons
[count
], taskconfig
->pButtons
[count
].nButtonID
,
769 taskconfig
->pButtons
[count
].pszButtonText
, TRUE
);
771 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
774 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
778 if (flags
& TDCBF_OK_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
779 if (flags
& TDCBF_YES_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(YES
);
780 if (flags
& TDCBF_NO_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(NO
);
781 if (flags
& TDCBF_RETRY_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(RETRY
);
782 if (flags
& TDCBF_CANCEL_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL
);
783 if (flags
& TDCBF_CLOSE_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE
);
785 if (!count
&& !dialog_info
->command_link_count
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
786 #undef TASKDIALOG_INIT_COMMON_BUTTON
788 dialog_info
->button_count
= count
;
791 static void taskdialog_add_footer_icon(struct taskdialog_info
*dialog_info
)
793 if (!dialog_info
->taskconfig
->u2
.hFooterIcon
) return;
795 dialog_info
->footer_icon
=
796 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, 0);
797 taskdialog_set_icon(dialog_info
, TDIE_ICON_FOOTER
, dialog_info
->taskconfig
->u2
.hFooterIcon
);
800 static void taskdialog_add_footer_text(struct taskdialog_info
*dialog_info
)
802 dialog_info
->footer_text
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszFooter
,
803 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
806 static LONG
taskdialog_get_dialog_width(struct taskdialog_info
*dialog_info
)
808 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
809 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
810 LONG max_width
, main_icon_width
, screen_width
;
814 screen_width
= taskdialog_get_reference_rect(taskconfig
, &rect
);
815 if ((taskconfig
->dwFlags
& TDF_SIZE_TO_CONTENT
) && !taskconfig
->cxWidth
)
817 max_width
= DIALOG_MIN_WIDTH
;
818 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
819 main_icon_width
= dialog_info
->m
.h_spacing
;
820 if (dialog_info
->main_icon
) main_icon_width
+= GetSystemMetrics(SM_CXICON
);
821 if (dialog_info
->content
)
823 taskdialog_get_label_size(dialog_info
, dialog_info
->content
, 0, &size
, syslink
);
824 max_width
= max(max_width
, size
.cx
+ main_icon_width
+ dialog_info
->m
.h_spacing
* 2);
829 max_width
= max(taskconfig
->cxWidth
, DIALOG_MIN_WIDTH
);
830 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
832 max_width
= min(max_width
, screen_width
);
836 static void taskdialog_label_layout(struct taskdialog_info
*dialog_info
, HWND hwnd
, INT start_x
, LONG dialog_width
,
837 LONG
*dialog_height
, BOOL syslink
)
839 LONG x
, y
, max_width
;
844 x
= start_x
+ dialog_info
->m
.h_spacing
;
845 y
= *dialog_height
+ dialog_info
->m
.v_spacing
;
846 max_width
= dialog_width
- x
- dialog_info
->m
.h_spacing
;
847 taskdialog_get_label_size(dialog_info
, hwnd
, max_width
, &size
, syslink
);
848 SetWindowPos(hwnd
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
849 *dialog_height
= y
+ size
.cy
;
852 static void taskdialog_layout(struct taskdialog_info
*dialog_info
)
854 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
855 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
856 static BOOL first_time
= TRUE
;
858 LONG dialog_width
, dialog_height
= 0;
859 LONG h_spacing
, v_spacing
;
860 LONG main_icon_right
, main_icon_bottom
;
861 LONG expando_right
, expando_bottom
;
862 struct button_layout_info
*button_layout_infos
;
863 LONG button_min_width
, button_height
;
864 LONG
*line_widths
, line_count
, align
;
865 LONG footer_icon_right
, footer_icon_bottom
;
870 taskdialog_get_reference_rect(dialog_info
->taskconfig
, &ref_rect
);
871 dialog_width
= taskdialog_get_dialog_width(dialog_info
);
873 h_spacing
= dialog_info
->m
.h_spacing
;
874 v_spacing
= dialog_info
->m
.v_spacing
;
878 main_icon_bottom
= 0;
879 if (dialog_info
->main_icon
)
882 y
= dialog_height
+ v_spacing
;
883 size
.cx
= GetSystemMetrics(SM_CXICON
);
884 size
.cy
= GetSystemMetrics(SM_CYICON
);
885 SetWindowPos(dialog_info
->main_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
886 main_icon_right
= x
+ size
.cx
;
887 main_icon_bottom
= y
+ size
.cy
;
890 /* Main instruction */
891 taskdialog_label_layout(dialog_info
, dialog_info
->main_instruction
, main_icon_right
, dialog_width
, &dialog_height
,
895 taskdialog_label_layout(dialog_info
, dialog_info
->content
, main_icon_right
, dialog_width
, &dialog_height
, syslink
);
897 /* Expanded information */
898 if (!(taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
899 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, main_icon_right
, dialog_width
, &dialog_height
,
903 if (dialog_info
->progress_bar
)
905 x
= main_icon_right
+ h_spacing
;
906 y
= dialog_height
+ v_spacing
;
907 size
.cx
= dialog_width
- x
- h_spacing
;
908 size
.cy
= GetSystemMetrics(SM_CYVSCROLL
);
909 SetWindowPos(dialog_info
->progress_bar
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
910 dialog_height
= y
+ size
.cy
;
914 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
916 x
= main_icon_right
+ h_spacing
;
917 y
= dialog_height
+ v_spacing
;
918 taskdialog_get_button_size(dialog_info
->radio_buttons
[i
], dialog_width
- x
- h_spacing
, &size
);
919 size
.cx
= dialog_width
- x
- h_spacing
;
920 SetWindowPos(dialog_info
->radio_buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
921 dialog_height
= y
+ size
.cy
;
925 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
927 x
= main_icon_right
+ h_spacing
;
929 /* Only add spacing for the first command links. There is no vertical spacing between command links */
932 taskdialog_get_button_size(dialog_info
->command_links
[i
], dialog_width
- x
- h_spacing
, &size
);
933 size
.cx
= dialog_width
- x
- h_spacing
;
936 SetWindowPos(dialog_info
->command_links
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
937 dialog_height
= y
+ size
.cy
;
940 dialog_height
= max(dialog_height
, main_icon_bottom
);
943 expando_bottom
= dialog_height
;
944 /* Expando control */
945 if (dialog_info
->expando_button
)
948 y
= dialog_height
+ v_spacing
;
949 taskdialog_get_expando_size(dialog_info
, dialog_info
->expando_button
, &size
);
950 SetWindowPos(dialog_info
->expando_button
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
951 expando_right
= x
+ size
.cx
;
952 expando_bottom
= y
+ size
.cy
;
955 /* Verification box */
956 if (dialog_info
->verification_box
)
959 y
= expando_bottom
+ v_spacing
;
960 size
.cx
= DIALOG_MIN_WIDTH
/ 2;
961 taskdialog_du_to_px(dialog_info
, &size
.cx
, NULL
);
962 taskdialog_get_button_size(dialog_info
->verification_box
, size
.cx
, &size
);
963 SetWindowPos(dialog_info
->verification_box
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
964 expando_right
= max(expando_right
, x
+ size
.cx
);
965 expando_bottom
= y
+ size
.cy
;
968 /* Common and custom buttons */
969 button_layout_infos
= Alloc(dialog_info
->button_count
* sizeof(*button_layout_infos
));
970 line_widths
= Alloc(dialog_info
->button_count
* sizeof(*line_widths
));
972 button_min_width
= DIALOG_BUTTON_WIDTH
;
973 button_height
= DIALOG_BUTTON_HEIGHT
;
974 taskdialog_du_to_px(dialog_info
, &button_min_width
, &button_height
);
975 for (i
= 0; i
< dialog_info
->button_count
; i
++)
977 taskdialog_get_button_size(dialog_info
->buttons
[i
], dialog_width
- expando_right
- h_spacing
* 2, &size
);
978 button_layout_infos
[i
].width
= max(size
.cx
, button_min_width
);
981 /* Separate buttons into lines */
982 x
= expando_right
+ h_spacing
;
983 for (i
= 0, line_count
= 0; i
< dialog_info
->button_count
; i
++)
985 button_layout_infos
[i
].line
= line_count
;
986 x
+= button_layout_infos
[i
].width
+ h_spacing
;
987 line_widths
[line_count
] += button_layout_infos
[i
].width
+ h_spacing
;
989 if ((i
+ 1 < dialog_info
->button_count
) && (x
+ button_layout_infos
[i
+ 1].width
+ h_spacing
>= dialog_width
))
991 x
= expando_right
+ h_spacing
;
997 /* Try to balance lines so they are about the same size */
998 for (i
= 1; i
< line_count
- 1; i
++)
1000 int diff_now
= abs(line_widths
[i
] - line_widths
[i
- 1]);
1001 unsigned int j
, last_button
= 0;
1004 for (j
= 0; j
< dialog_info
->button_count
; j
++)
1005 if (button_layout_infos
[j
].line
== i
- 1) last_button
= j
;
1007 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1008 diff_changed
= abs(2 * button_layout_infos
[last_button
].width
+ line_widths
[i
] - line_widths
[i
- 1]);
1010 if (diff_changed
< diff_now
)
1012 button_layout_infos
[last_button
].line
= i
;
1013 line_widths
[i
] += button_layout_infos
[last_button
].width
;
1014 line_widths
[i
- 1] -= button_layout_infos
[last_button
].width
;
1018 /* Calculate left alignment so all lines are as far right as possible. */
1019 align
= dialog_width
- h_spacing
;
1020 for (i
= 0; i
< line_count
; i
++)
1022 int new_alignment
= dialog_width
- line_widths
[i
];
1023 if (new_alignment
< align
) align
= new_alignment
;
1026 /* Now that we got them all positioned, move all buttons */
1028 size
.cy
= button_height
;
1029 for (i
= 0; i
< dialog_info
->button_count
; i
++)
1032 if (i
> 0 && button_layout_infos
[i
].line
!= button_layout_infos
[i
- 1].line
)
1035 dialog_height
+= size
.cy
+ v_spacing
;
1038 y
= dialog_height
+ v_spacing
;
1039 size
.cx
= button_layout_infos
[i
].width
;
1040 SetWindowPos(dialog_info
->buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1041 x
+= button_layout_infos
[i
].width
+ h_spacing
;
1044 /* Add height for last row button and spacing */
1045 dialog_height
+= size
.cy
+ v_spacing
;
1046 dialog_height
= max(dialog_height
, expando_bottom
);
1048 Free(button_layout_infos
);
1052 footer_icon_right
= 0;
1053 footer_icon_bottom
= dialog_height
;
1054 if (dialog_info
->footer_icon
)
1057 y
= dialog_height
+ v_spacing
;
1058 size
.cx
= GetSystemMetrics(SM_CXSMICON
);
1059 size
.cy
= GetSystemMetrics(SM_CYSMICON
);
1060 SetWindowPos(dialog_info
->footer_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1061 footer_icon_right
= x
+ size
.cx
;
1062 footer_icon_bottom
= y
+ size
.cy
;
1066 taskdialog_label_layout(dialog_info
, dialog_info
->footer_text
, footer_icon_right
, dialog_width
, &dialog_height
,
1068 dialog_height
= max(dialog_height
, footer_icon_bottom
);
1070 /* Expanded information */
1071 if ((taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
1072 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, 0, dialog_width
, &dialog_height
, syslink
);
1074 /* Add height for spacing, title height and frame height */
1075 dialog_height
+= v_spacing
;
1076 dialog_height
+= GetSystemMetrics(SM_CYCAPTION
);
1077 dialog_height
+= GetSystemMetrics(SM_CXDLGFRAME
);
1081 x
= (ref_rect
.left
+ ref_rect
.right
- dialog_width
) / 2;
1082 y
= (ref_rect
.top
+ ref_rect
.bottom
- dialog_height
) / 2;
1083 SetWindowPos(dialog_info
->hwnd
, 0, x
, y
, dialog_width
, dialog_height
, SWP_NOZORDER
);
1087 SetWindowPos(dialog_info
->hwnd
, 0, 0, 0, dialog_width
, dialog_height
, SWP_NOMOVE
| SWP_NOZORDER
);
1090 static void taskdialog_draw_expando_control(struct taskdialog_info
*dialog_info
, LPDRAWITEMSTRUCT dis
)
1096 LONG icon_width
, icon_height
;
1098 UINT style
= DFCS_FLAT
;
1102 hwnd
= dis
->hwndItem
;
1104 SendMessageW(hwnd
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
1106 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
1107 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
1108 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
1109 rect
.right
= icon_width
;
1110 rect
.bottom
= icon_height
;
1111 style
|= dialog_info
->expanded
? DFCS_SCROLLUP
: DFCS_SCROLLDOWN
;
1112 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
, style
);
1114 GetCharWidthW(hdc
, '0', '0', &text_offset
);
1118 rect
.left
+= icon_width
+ text_offset
;
1119 text
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
1120 DrawTextW(hdc
, text
, -1, &rect
, DT_WORDBREAK
| DT_END_ELLIPSIS
| DT_EXPANDTABS
);
1122 draw_focus
= (dis
->itemState
& ODS_FOCUS
) && !(dis
->itemState
& ODS_NOFOCUSRECT
);
1123 if(draw_focus
) DrawFocusRect(hdc
, &rect
);
1126 static void taskdialog_init(struct taskdialog_info
*dialog_info
, HWND hwnd
)
1128 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
1129 NONCLIENTMETRICSW ncm
;
1133 ncm
.cbSize
= sizeof(ncm
);
1134 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
1136 memset(dialog_info
, 0, sizeof(*dialog_info
));
1137 dialog_info
->taskconfig
= taskconfig
;
1138 dialog_info
->hwnd
= hwnd
;
1139 dialog_info
->font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
1141 hdc
= GetDC(dialog_info
->hwnd
);
1142 SelectObject(hdc
, dialog_info
->font
);
1143 dialog_info
->m
.x_baseunit
= GdiGetCharDimensions(hdc
, NULL
, &dialog_info
->m
.y_baseunit
);
1144 ReleaseDC(dialog_info
->hwnd
, hdc
);
1146 dialog_info
->m
.h_spacing
= DIALOG_SPACING
;
1147 dialog_info
->m
.v_spacing
= DIALOG_SPACING
;
1148 taskdialog_du_to_px(dialog_info
, &dialog_info
->m
.h_spacing
, &dialog_info
->m
.v_spacing
);
1150 if (taskconfig
->dwFlags
& TDF_CALLBACK_TIMER
)
1152 SetTimer(hwnd
, ID_TIMER
, DIALOG_TIMER_MS
, NULL
);
1153 dialog_info
->last_timer_tick
= GetTickCount();
1156 taskdialog_add_main_icon(dialog_info
);
1157 taskdialog_add_main_instruction(dialog_info
);
1158 taskdialog_add_content(dialog_info
);
1159 taskdialog_add_expanded_info(dialog_info
);
1160 taskdialog_add_progress_bar(dialog_info
);
1161 taskdialog_add_radio_buttons(dialog_info
);
1162 taskdialog_add_command_links(dialog_info
);
1163 taskdialog_add_expando_button(dialog_info
);
1164 taskdialog_add_verification_box(dialog_info
);
1165 taskdialog_add_buttons(dialog_info
);
1166 taskdialog_add_footer_icon(dialog_info
);
1167 taskdialog_add_footer_text(dialog_info
);
1169 /* Set default button */
1170 if (!dialog_info
->default_button
&& dialog_info
->command_links
)
1171 dialog_info
->default_button
= dialog_info
->command_links
[0];
1172 if (!dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->buttons
[0];
1173 SendMessageW(dialog_info
->hwnd
, WM_NEXTDLGCTL
, (WPARAM
)dialog_info
->default_button
, TRUE
);
1174 id
= GetWindowLongW(dialog_info
->default_button
, GWLP_ID
);
1175 SendMessageW(dialog_info
->hwnd
, DM_SETDEFID
, id
, 0);
1177 dialog_info
->has_cancel
=
1178 (taskconfig
->dwFlags
& TDF_ALLOW_DIALOG_CANCELLATION
)
1179 || taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, IDCANCEL
)
1180 || taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, IDCANCEL
);
1182 if (!dialog_info
->has_cancel
) DeleteMenu(GetSystemMenu(hwnd
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
1184 taskdialog_layout(dialog_info
);
1187 static BOOL CALLBACK
takdialog_destroy_control(HWND hwnd
, LPARAM lParam
)
1189 DestroyWindow(hwnd
);
1193 static void taskdialog_destroy(struct taskdialog_info
*dialog_info
)
1195 EnumChildWindows(dialog_info
->hwnd
, takdialog_destroy_control
, 0);
1197 if (dialog_info
->taskconfig
->dwFlags
& TDF_CALLBACK_TIMER
) KillTimer(dialog_info
->hwnd
, ID_TIMER
);
1198 if (dialog_info
->font
) DeleteObject(dialog_info
->font
);
1199 if (dialog_info
->main_instruction_font
) DeleteObject(dialog_info
->main_instruction_font
);
1200 Free(dialog_info
->buttons
);
1201 Free(dialog_info
->radio_buttons
);
1202 Free(dialog_info
->command_links
);
1203 Free(dialog_info
->expanded_text
);
1204 Free(dialog_info
->collapsed_text
);
1207 static INT_PTR CALLBACK
taskdialog_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1209 static const WCHAR taskdialog_info_propnameW
[] = L
"TaskDialogInfo";
1210 struct taskdialog_info
*dialog_info
;
1213 TRACE("hwnd %p, msg 0x%04x, wparam %Ix, lparam %Ix\n", hwnd
, msg
, wParam
, lParam
);
1215 if (msg
!= WM_INITDIALOG
)
1216 dialog_info
= GetPropW(hwnd
, taskdialog_info_propnameW
);
1220 case TDM_NAVIGATE_PAGE
:
1221 dialog_info
->taskconfig
= (const TASKDIALOGCONFIG
*)lParam
;
1222 taskdialog_destroy(dialog_info
);
1223 taskdialog_init(dialog_info
, hwnd
);
1224 taskdialog_notify(dialog_info
, TDN_DIALOG_CONSTRUCTED
, 0, 0);
1225 /* Default radio button click notification is sent before TDN_NAVIGATED */
1226 taskdialog_check_default_radio_buttons(dialog_info
);
1227 taskdialog_notify(dialog_info
, TDN_NAVIGATED
, 0, 0);
1229 case TDM_CLICK_BUTTON
:
1230 taskdialog_click_button(dialog_info
, wParam
);
1232 case TDM_ENABLE_BUTTON
:
1233 taskdialog_enable_button(dialog_info
, wParam
, lParam
);
1235 case TDM_SET_MARQUEE_PROGRESS_BAR
:
1237 BOOL marquee
= wParam
;
1239 if(!dialog_info
->progress_bar
) break;
1240 style
= GetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
);
1241 style
= marquee
? style
| PBS_MARQUEE
: style
& (~PBS_MARQUEE
);
1242 SetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
, style
);
1245 case TDM_SET_PROGRESS_BAR_STATE
:
1246 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETSTATE
, wParam
, 0);
1247 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1249 case TDM_SET_PROGRESS_BAR_RANGE
:
1250 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETRANGE
, 0, lParam
);
1251 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1253 case TDM_SET_PROGRESS_BAR_POS
:
1255 if (dialog_info
->progress_bar
)
1257 LONG style
= GetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
);
1258 if (!(style
& PBS_MARQUEE
)) result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETPOS
, wParam
, 0);
1260 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1262 case TDM_SET_PROGRESS_BAR_MARQUEE
:
1263 SendMessageW(dialog_info
->progress_bar
, PBM_SETMARQUEE
, wParam
, lParam
);
1265 case TDM_SET_ELEMENT_TEXT
:
1266 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1267 taskdialog_layout(dialog_info
);
1269 case TDM_UPDATE_ELEMENT_TEXT
:
1270 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1272 case TDM_CLICK_RADIO_BUTTON
:
1273 taskdialog_click_radio_button(dialog_info
, wParam
);
1275 case TDM_ENABLE_RADIO_BUTTON
:
1276 taskdialog_enable_radio_button(dialog_info
, wParam
, lParam
);
1278 case TDM_CLICK_VERIFICATION
:
1280 BOOL checked
= (BOOL
)wParam
;
1281 BOOL focused
= (BOOL
)lParam
;
1282 dialog_info
->verification_checked
= checked
;
1283 if (dialog_info
->verification_box
)
1285 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, checked
? BST_CHECKED
: BST_UNCHECKED
, 0);
1286 taskdialog_notify(dialog_info
, TDN_VERIFICATION_CLICKED
, checked
, 0);
1287 if (focused
) SetFocus(dialog_info
->verification_box
);
1291 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE
:
1292 taskdialog_button_set_shield(dialog_info
, wParam
, lParam
);
1294 case TDM_UPDATE_ICON
:
1295 taskdialog_set_icon(dialog_info
, wParam
, (HICON
)lParam
);
1298 dialog_info
= (struct taskdialog_info
*)lParam
;
1300 taskdialog_init(dialog_info
, hwnd
);
1302 SetPropW(hwnd
, taskdialog_info_propnameW
, dialog_info
);
1303 taskdialog_notify(dialog_info
, TDN_DIALOG_CONSTRUCTED
, 0, 0);
1304 taskdialog_notify(dialog_info
, TDN_CREATED
, 0, 0);
1305 /* Default radio button click notification sent after TDN_CREATED */
1306 taskdialog_check_default_radio_buttons(dialog_info
);
1309 if (HIWORD(wParam
) == BN_CLICKED
)
1311 taskdialog_on_button_click(dialog_info
, (HWND
)lParam
, LOWORD(wParam
));
1316 taskdialog_notify(dialog_info
, TDN_HELP
, 0, 0);
1319 if (ID_TIMER
== wParam
)
1321 DWORD elapsed
= GetTickCount() - dialog_info
->last_timer_tick
;
1322 if (taskdialog_notify(dialog_info
, TDN_TIMER
, elapsed
, 0) == S_FALSE
)
1323 dialog_info
->last_timer_tick
= GetTickCount();
1328 PNMLINK pnmLink
= (PNMLINK
)lParam
;
1329 HWND hwndFrom
= pnmLink
->hdr
.hwndFrom
;
1330 if ((taskdialog_hyperlink_enabled(dialog_info
))
1331 && (hwndFrom
== dialog_info
->content
|| hwndFrom
== dialog_info
->expanded_info
1332 || hwndFrom
== dialog_info
->footer_text
)
1333 && (pnmLink
->hdr
.code
== NM_CLICK
|| pnmLink
->hdr
.code
== NM_RETURN
))
1335 taskdialog_notify(dialog_info
, TDN_HYPERLINK_CLICKED
, 0, (LPARAM
)pnmLink
->item
.szUrl
);
1342 LPDRAWITEMSTRUCT dis
= (LPDRAWITEMSTRUCT
)lParam
;
1343 if (dis
->hwndItem
== dialog_info
->expando_button
)
1345 taskdialog_draw_expando_control(dialog_info
, dis
);
1346 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, TRUE
);
1352 taskdialog_notify(dialog_info
, TDN_DESTROYED
, 0, 0);
1353 RemovePropW(hwnd
, taskdialog_info_propnameW
);
1354 taskdialog_destroy(dialog_info
);
1357 if (dialog_info
->has_cancel
)
1359 if(taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, IDCANCEL
, 0) == S_OK
)
1360 EndDialog(hwnd
, IDCANCEL
);
1361 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, 0);
1371 /***********************************************************************
1372 * TaskDialogIndirect [COMCTL32.@]
1374 HRESULT WINAPI
TaskDialogIndirect(const TASKDIALOGCONFIG
*taskconfig
, int *button
,
1375 int *radio_button
, BOOL
*verification_flag_checked
)
1377 struct taskdialog_info dialog_info
;
1378 DLGTEMPLATE
*template;
1381 TRACE("%p, %p, %p, %p\n", taskconfig
, button
, radio_button
, verification_flag_checked
);
1383 if (!taskconfig
|| taskconfig
->cbSize
!= sizeof(TASKDIALOGCONFIG
))
1384 return E_INVALIDARG
;
1386 dialog_info
.taskconfig
= taskconfig
;
1388 template = create_taskdialog_template(taskconfig
);
1389 ret
= (short)DialogBoxIndirectParamW(taskconfig
->hInstance
, template, taskconfig
->hwndParent
,
1390 taskdialog_proc
, (LPARAM
)&dialog_info
);
1393 if (button
) *button
= ret
;
1394 if (radio_button
) *radio_button
= dialog_info
.selected_radio_id
;
1395 if (verification_flag_checked
) *verification_flag_checked
= dialog_info
.verification_checked
;
1400 /***********************************************************************
1401 * TaskDialog [COMCTL32.@]
1403 HRESULT WINAPI
TaskDialog(HWND owner
, HINSTANCE hinst
, const WCHAR
*title
, const WCHAR
*main_instruction
,
1404 const WCHAR
*content
, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons
, const WCHAR
*icon
, int *button
)
1406 TASKDIALOGCONFIG taskconfig
;
1408 TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner
, hinst
, debugstr_w(title
), debugstr_w(main_instruction
),
1409 debugstr_w(content
), common_buttons
, debugstr_w(icon
), button
);
1411 memset(&taskconfig
, 0, sizeof(taskconfig
));
1412 taskconfig
.cbSize
= sizeof(taskconfig
);
1413 taskconfig
.hwndParent
= owner
;
1414 taskconfig
.hInstance
= hinst
;
1415 taskconfig
.dwCommonButtons
= common_buttons
;
1416 taskconfig
.pszWindowTitle
= title
;
1417 taskconfig
.u
.pszMainIcon
= icon
;
1418 taskconfig
.pszMainInstruction
= main_instruction
;
1419 taskconfig
.pszContent
= content
;
1420 return TaskDialogIndirect(&taskconfig
, button
, NULL
, NULL
);