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
, text_offset
;
434 LONG max_width
, max_text_height
;
437 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
438 old_hfont
= SelectObject(hdc
, hfont
);
440 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
441 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
442 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
444 GetCharWidthW(hdc
, '0', '0', &text_offset
);
447 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
448 style
|= DT_RIGHT
| DT_RTLREADING
;
452 max_width
= DIALOG_MIN_WIDTH
/ 2;
453 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
455 rect
.right
= max_width
- icon_width
- text_offset
;
456 max_text_height
= DrawTextW(hdc
, dialog_info
->expanded_text
, -1, &rect
, style
);
457 size
->cy
= max(max_text_height
, icon_height
);
458 size
->cx
= rect
.right
- rect
.left
;
460 rect
.right
= max_width
- icon_width
- text_offset
;
461 max_text_height
= DrawTextW(hdc
, dialog_info
->collapsed_text
, -1, &rect
, style
);
462 size
->cy
= max(size
->cy
, max_text_height
);
463 size
->cx
= max(size
->cx
, rect
.right
- rect
.left
);
464 size
->cx
= min(size
->cx
, max_width
);
466 if (old_hfont
) SelectObject(hdc
, old_hfont
);
467 ReleaseDC(hwnd
, hdc
);
470 static ULONG_PTR
taskdialog_get_standard_icon(LPCWSTR icon
)
472 if (icon
== TD_WARNING_ICON
)
474 else if (icon
== TD_ERROR_ICON
)
476 else if (icon
== TD_INFORMATION_ICON
)
477 return IDI_INFORMATION
;
478 else if (icon
== TD_SHIELD_ICON
)
481 return (ULONG_PTR
)icon
;
484 static void taskdialog_set_icon(struct taskdialog_info
*dialog_info
, INT element
, HICON icon
)
486 DWORD flags
= dialog_info
->taskconfig
->dwFlags
;
492 if (((flags
& TDF_USE_HICON_MAIN
) && element
== TDIE_ICON_MAIN
)
493 || ((flags
& TDF_USE_HICON_FOOTER
) && element
== TDIE_ICON_FOOTER
))
497 if (element
== TDIE_ICON_FOOTER
)
499 cx
= GetSystemMetrics(SM_CXSMICON
);
500 cy
= GetSystemMetrics(SM_CYSMICON
);
502 hicon
= LoadImageW(dialog_info
->taskconfig
->hInstance
, (LPCWSTR
)icon
, IMAGE_ICON
, cx
, cy
, LR_SHARED
| LR_DEFAULTSIZE
);
504 hicon
= LoadImageW(NULL
, (LPCWSTR
)taskdialog_get_standard_icon((LPCWSTR
)icon
), IMAGE_ICON
, cx
, cy
,
505 LR_SHARED
| LR_DEFAULTSIZE
);
510 if (element
== TDIE_ICON_MAIN
)
512 SendMessageW(dialog_info
->hwnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)hicon
);
513 SendMessageW(dialog_info
->main_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
515 else if (element
== TDIE_ICON_FOOTER
)
516 SendMessageW(dialog_info
->footer_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
519 static void taskdialog_set_element_text(struct taskdialog_info
*dialog_info
, TASKDIALOG_ELEMENTS element
,
525 if (element
== TDE_CONTENT
)
526 hwnd
= dialog_info
->content
;
527 else if (element
== TDE_EXPANDED_INFORMATION
)
528 hwnd
= dialog_info
->expanded_info
;
529 else if (element
== TDE_FOOTER
)
530 hwnd
= dialog_info
->footer_text
;
531 else if (element
== TDE_MAIN_INSTRUCTION
)
532 hwnd
= dialog_info
->main_instruction
;
536 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
537 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)textW
);
541 static void taskdialog_check_default_radio_buttons(struct taskdialog_info
*dialog_info
)
543 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
546 if (!dialog_info
->radio_button_count
) return;
548 default_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
,
549 taskconfig
->nDefaultRadioButton
);
551 if (!default_button
&& !(taskconfig
->dwFlags
& TDF_NO_DEFAULT_RADIO_BUTTON
))
552 default_button
= dialog_info
->radio_buttons
[0];
556 SendMessageW(default_button
, BM_SETCHECK
, BST_CHECKED
, 0);
557 taskdialog_on_button_click(dialog_info
, default_button
, 0);
561 static void taskdialog_add_main_icon(struct taskdialog_info
*dialog_info
)
563 if (!dialog_info
->taskconfig
->u
.hMainIcon
) return;
565 dialog_info
->main_icon
=
566 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
567 taskdialog_set_icon(dialog_info
, TDIE_ICON_MAIN
, dialog_info
->taskconfig
->u
.hMainIcon
);
570 static HWND
taskdialog_create_label(struct taskdialog_info
*dialog_info
, const WCHAR
*text
, HFONT font
, BOOL syslink
)
575 DWORD style
= WS_CHILD
| WS_VISIBLE
;
577 if (!text
) return NULL
;
579 class = syslink
? WC_LINK
: WC_STATICW
;
580 if (syslink
) style
|= WS_TABSTOP
;
581 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
582 hwnd
= CreateWindowW(class, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
585 SendMessageW(hwnd
, WM_SETFONT
, (WPARAM
)font
, 0);
589 static void taskdialog_add_main_instruction(struct taskdialog_info
*dialog_info
)
591 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
592 NONCLIENTMETRICSW ncm
;
594 if (!taskconfig
->pszMainInstruction
) return;
596 ncm
.cbSize
= sizeof(ncm
);
597 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
598 /* 1.25 times the height */
599 ncm
.lfMessageFont
.lfHeight
= ncm
.lfMessageFont
.lfHeight
* 5 / 4;
600 ncm
.lfMessageFont
.lfWeight
= FW_BOLD
;
601 dialog_info
->main_instruction_font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
603 dialog_info
->main_instruction
=
604 taskdialog_create_label(dialog_info
, taskconfig
->pszMainInstruction
, dialog_info
->main_instruction_font
, FALSE
);
607 static void taskdialog_add_content(struct taskdialog_info
*dialog_info
)
609 dialog_info
->content
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszContent
, dialog_info
->font
,
610 taskdialog_hyperlink_enabled(dialog_info
));
613 static void taskdialog_add_progress_bar(struct taskdialog_info
*dialog_info
)
615 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
616 DWORD style
= PBS_SMOOTH
| PBS_SMOOTHREVERSE
| WS_CHILD
| WS_VISIBLE
;
618 if (!(taskconfig
->dwFlags
& (TDF_SHOW_PROGRESS_BAR
| TDF_SHOW_MARQUEE_PROGRESS_BAR
))) return;
619 if (taskconfig
->dwFlags
& TDF_SHOW_MARQUEE_PROGRESS_BAR
) style
|= PBS_MARQUEE
;
620 dialog_info
->progress_bar
=
621 CreateWindowW(PROGRESS_CLASSW
, NULL
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
624 static void taskdialog_add_radio_buttons(struct taskdialog_info
*dialog_info
)
626 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
627 static const DWORD style
= BS_AUTORADIOBUTTON
| BS_MULTILINE
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
631 if (!taskconfig
->cRadioButtons
|| !taskconfig
->pRadioButtons
) return;
633 dialog_info
->radio_buttons
= Alloc(taskconfig
->cRadioButtons
* sizeof(*dialog_info
->radio_buttons
));
634 if (!dialog_info
->radio_buttons
) return;
636 dialog_info
->radio_button_count
= taskconfig
->cRadioButtons
;
637 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
639 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pRadioButtons
[i
].pszButtonText
);
640 dialog_info
->radio_buttons
[i
] =
641 CreateWindowW(WC_BUTTONW
, textW
, i
== 0 ? style
| WS_GROUP
: style
, 0, 0, 0, 0, dialog_info
->hwnd
,
642 LongToHandle(taskconfig
->pRadioButtons
[i
].nButtonID
), 0, NULL
);
643 SendMessageW(dialog_info
->radio_buttons
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
648 static void taskdialog_add_command_links(struct taskdialog_info
*dialog_info
)
650 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
651 DWORD default_style
= BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, style
;
656 if (!taskconfig
->cButtons
|| !taskconfig
->pButtons
|| !taskdialog_use_command_link(dialog_info
)) return;
658 dialog_info
->command_links
= Alloc(taskconfig
->cButtons
* sizeof(*dialog_info
->command_links
));
659 if (!dialog_info
->command_links
) return;
661 dialog_info
->command_link_count
= taskconfig
->cButtons
;
662 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
664 is_default
= taskconfig
->pButtons
[i
].nButtonID
== taskconfig
->nDefaultButton
;
665 style
= is_default
? default_style
| BS_DEFCOMMANDLINK
: default_style
| BS_COMMANDLINK
;
666 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pButtons
[i
].pszButtonText
);
667 dialog_info
->command_links
[i
] = CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
,
668 LongToHandle(taskconfig
->pButtons
[i
].nButtonID
), 0, NULL
);
669 SendMessageW(dialog_info
->command_links
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
672 if (is_default
&& !dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->command_links
[i
];
676 static void taskdialog_add_expanded_info(struct taskdialog_info
*dialog_info
)
678 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
680 if (!taskconfig
->pszExpandedInformation
) return;
682 dialog_info
->expanded
= taskconfig
->dwFlags
& TDF_EXPANDED_BY_DEFAULT
;
683 dialog_info
->expanded_info
= taskdialog_create_label(dialog_info
, taskconfig
->pszExpandedInformation
,
684 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
685 ShowWindow(dialog_info
->expanded_info
, dialog_info
->expanded
? SW_SHOWDEFAULT
: SW_HIDE
);
688 static void taskdialog_add_expando_button(struct taskdialog_info
*dialog_info
)
690 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
693 if (!taskconfig
->pszExpandedInformation
) return;
695 if (!taskconfig
->pszCollapsedControlText
&& !taskconfig
->pszExpandedControlText
)
697 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_EXPANDED
));
698 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_COLLAPSED
));
702 textW
= taskconfig
->pszExpandedControlText
? taskconfig
->pszExpandedControlText
703 : taskconfig
->pszCollapsedControlText
;
704 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
705 textW
= taskconfig
->pszCollapsedControlText
? taskconfig
->pszCollapsedControlText
706 : taskconfig
->pszExpandedControlText
;
707 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
710 textW
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
712 dialog_info
->expando_button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_OWNERDRAW
, 0,
713 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
714 SendMessageW(dialog_info
->expando_button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
717 static void taskdialog_add_verification_box(struct taskdialog_info
*dialog_info
)
719 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
720 static const DWORD style
= BS_AUTOCHECKBOX
| BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
723 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
724 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
) dialog_info
->verification_checked
= TRUE
;
726 if (!taskconfig
->pszVerificationText
) return;
728 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pszVerificationText
);
729 dialog_info
->verification_box
= CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
730 SendMessageW(dialog_info
->verification_box
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
733 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
)
734 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, BST_CHECKED
, 0);
737 static void taskdialog_add_button(struct taskdialog_info
*dialog_info
, HWND
*button
, INT_PTR id
, const WCHAR
*text
,
740 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
743 textW
= taskdialog_gettext(dialog_info
, custom_button
, text
);
744 *button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0, 0, 0, 0, dialog_info
->hwnd
,
747 SendMessageW(*button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
749 if (id
== taskconfig
->nDefaultButton
&& !dialog_info
->default_button
) dialog_info
->default_button
= *button
;
752 static void taskdialog_add_buttons(struct taskdialog_info
*dialog_info
)
754 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
755 BOOL use_command_links
= taskdialog_use_command_link(dialog_info
);
756 DWORD flags
= taskconfig
->dwCommonButtons
;
757 INT count
, max_count
;
759 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
761 if (!use_command_links
&& taskconfig
->cButtons
&& taskconfig
->pButtons
) max_count
+= taskconfig
->cButtons
;
763 dialog_info
->buttons
= Alloc(max_count
* sizeof(*dialog_info
->buttons
));
764 if (!dialog_info
->buttons
) return;
766 for (count
= 0; !use_command_links
&& count
< taskconfig
->cButtons
; count
++)
767 taskdialog_add_button(dialog_info
, &dialog_info
->buttons
[count
], taskconfig
->pButtons
[count
].nButtonID
,
768 taskconfig
->pButtons
[count
].pszButtonText
, TRUE
);
770 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
773 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
777 if (flags
& TDCBF_OK_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
778 if (flags
& TDCBF_YES_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(YES
);
779 if (flags
& TDCBF_NO_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(NO
);
780 if (flags
& TDCBF_RETRY_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(RETRY
);
781 if (flags
& TDCBF_CANCEL_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL
);
782 if (flags
& TDCBF_CLOSE_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE
);
784 if (!count
&& !dialog_info
->command_link_count
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
785 #undef TASKDIALOG_INIT_COMMON_BUTTON
787 dialog_info
->button_count
= count
;
790 static void taskdialog_add_footer_icon(struct taskdialog_info
*dialog_info
)
792 if (!dialog_info
->taskconfig
->u2
.hFooterIcon
) return;
794 dialog_info
->footer_icon
=
795 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, 0);
796 taskdialog_set_icon(dialog_info
, TDIE_ICON_FOOTER
, dialog_info
->taskconfig
->u2
.hFooterIcon
);
799 static void taskdialog_add_footer_text(struct taskdialog_info
*dialog_info
)
801 dialog_info
->footer_text
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszFooter
,
802 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
805 static LONG
taskdialog_get_dialog_width(struct taskdialog_info
*dialog_info
)
807 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
808 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
809 LONG max_width
, main_icon_width
, screen_width
;
813 screen_width
= taskdialog_get_reference_rect(taskconfig
, &rect
);
814 if ((taskconfig
->dwFlags
& TDF_SIZE_TO_CONTENT
) && !taskconfig
->cxWidth
)
816 max_width
= DIALOG_MIN_WIDTH
;
817 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
818 main_icon_width
= dialog_info
->m
.h_spacing
;
819 if (dialog_info
->main_icon
) main_icon_width
+= GetSystemMetrics(SM_CXICON
);
820 if (dialog_info
->content
)
822 taskdialog_get_label_size(dialog_info
, dialog_info
->content
, 0, &size
, syslink
);
823 max_width
= max(max_width
, size
.cx
+ main_icon_width
+ dialog_info
->m
.h_spacing
* 2);
828 max_width
= max(taskconfig
->cxWidth
, DIALOG_MIN_WIDTH
);
829 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
831 max_width
= min(max_width
, screen_width
);
835 static void taskdialog_label_layout(struct taskdialog_info
*dialog_info
, HWND hwnd
, INT start_x
, LONG dialog_width
,
836 LONG
*dialog_height
, BOOL syslink
)
838 LONG x
, y
, max_width
;
843 x
= start_x
+ dialog_info
->m
.h_spacing
;
844 y
= *dialog_height
+ dialog_info
->m
.v_spacing
;
845 max_width
= dialog_width
- x
- dialog_info
->m
.h_spacing
;
846 taskdialog_get_label_size(dialog_info
, hwnd
, max_width
, &size
, syslink
);
847 SetWindowPos(hwnd
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
848 *dialog_height
= y
+ size
.cy
;
851 static void taskdialog_layout(struct taskdialog_info
*dialog_info
)
853 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
854 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
855 static BOOL first_time
= TRUE
;
857 LONG dialog_width
, dialog_height
= 0;
858 LONG h_spacing
, v_spacing
;
859 LONG main_icon_right
, main_icon_bottom
;
860 LONG expando_right
, expando_bottom
;
861 struct button_layout_info
*button_layout_infos
;
862 LONG button_min_width
, button_height
;
863 LONG
*line_widths
, line_count
, align
;
864 LONG footer_icon_right
, footer_icon_bottom
;
869 taskdialog_get_reference_rect(dialog_info
->taskconfig
, &ref_rect
);
870 dialog_width
= taskdialog_get_dialog_width(dialog_info
);
872 h_spacing
= dialog_info
->m
.h_spacing
;
873 v_spacing
= dialog_info
->m
.v_spacing
;
877 main_icon_bottom
= 0;
878 if (dialog_info
->main_icon
)
881 y
= dialog_height
+ v_spacing
;
882 size
.cx
= GetSystemMetrics(SM_CXICON
);
883 size
.cy
= GetSystemMetrics(SM_CYICON
);
884 SetWindowPos(dialog_info
->main_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
885 main_icon_right
= x
+ size
.cx
;
886 main_icon_bottom
= y
+ size
.cy
;
889 /* Main instruction */
890 taskdialog_label_layout(dialog_info
, dialog_info
->main_instruction
, main_icon_right
, dialog_width
, &dialog_height
,
894 taskdialog_label_layout(dialog_info
, dialog_info
->content
, main_icon_right
, dialog_width
, &dialog_height
, syslink
);
896 /* Expanded information */
897 if (!(taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
898 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, main_icon_right
, dialog_width
, &dialog_height
,
902 if (dialog_info
->progress_bar
)
904 x
= main_icon_right
+ h_spacing
;
905 y
= dialog_height
+ v_spacing
;
906 size
.cx
= dialog_width
- x
- h_spacing
;
907 size
.cy
= GetSystemMetrics(SM_CYVSCROLL
);
908 SetWindowPos(dialog_info
->progress_bar
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
909 dialog_height
= y
+ size
.cy
;
913 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
915 x
= main_icon_right
+ h_spacing
;
916 y
= dialog_height
+ v_spacing
;
917 taskdialog_get_button_size(dialog_info
->radio_buttons
[i
], dialog_width
- x
- h_spacing
, &size
);
918 size
.cx
= dialog_width
- x
- h_spacing
;
919 SetWindowPos(dialog_info
->radio_buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
920 dialog_height
= y
+ size
.cy
;
924 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
926 x
= main_icon_right
+ h_spacing
;
928 /* Only add spacing for the first command links. There is no vertical spacing between command links */
931 taskdialog_get_button_size(dialog_info
->command_links
[i
], dialog_width
- x
- h_spacing
, &size
);
932 size
.cx
= dialog_width
- x
- h_spacing
;
935 SetWindowPos(dialog_info
->command_links
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
936 dialog_height
= y
+ size
.cy
;
939 dialog_height
= max(dialog_height
, main_icon_bottom
);
942 expando_bottom
= dialog_height
;
943 /* Expando control */
944 if (dialog_info
->expando_button
)
947 y
= dialog_height
+ v_spacing
;
948 taskdialog_get_expando_size(dialog_info
, dialog_info
->expando_button
, &size
);
949 SetWindowPos(dialog_info
->expando_button
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
950 expando_right
= x
+ size
.cx
;
951 expando_bottom
= y
+ size
.cy
;
954 /* Verification box */
955 if (dialog_info
->verification_box
)
958 y
= expando_bottom
+ v_spacing
;
959 size
.cx
= DIALOG_MIN_WIDTH
/ 2;
960 taskdialog_du_to_px(dialog_info
, &size
.cx
, NULL
);
961 taskdialog_get_button_size(dialog_info
->verification_box
, size
.cx
, &size
);
962 SetWindowPos(dialog_info
->verification_box
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
963 expando_right
= max(expando_right
, x
+ size
.cx
);
964 expando_bottom
= y
+ size
.cy
;
967 /* Common and custom buttons */
968 button_layout_infos
= Alloc(dialog_info
->button_count
* sizeof(*button_layout_infos
));
969 line_widths
= Alloc(dialog_info
->button_count
* sizeof(*line_widths
));
971 button_min_width
= DIALOG_BUTTON_WIDTH
;
972 button_height
= DIALOG_BUTTON_HEIGHT
;
973 taskdialog_du_to_px(dialog_info
, &button_min_width
, &button_height
);
974 for (i
= 0; i
< dialog_info
->button_count
; i
++)
976 taskdialog_get_button_size(dialog_info
->buttons
[i
], dialog_width
- expando_right
- h_spacing
* 2, &size
);
977 button_layout_infos
[i
].width
= max(size
.cx
, button_min_width
);
980 /* Separate buttons into lines */
981 x
= expando_right
+ h_spacing
;
982 for (i
= 0, line_count
= 0; i
< dialog_info
->button_count
; i
++)
984 button_layout_infos
[i
].line
= line_count
;
985 x
+= button_layout_infos
[i
].width
+ h_spacing
;
986 line_widths
[line_count
] += button_layout_infos
[i
].width
+ h_spacing
;
988 if ((i
+ 1 < dialog_info
->button_count
) && (x
+ button_layout_infos
[i
+ 1].width
+ h_spacing
>= dialog_width
))
990 x
= expando_right
+ h_spacing
;
996 /* Try to balance lines so they are about the same size */
997 for (i
= 1; i
< line_count
- 1; i
++)
999 int diff_now
= abs(line_widths
[i
] - line_widths
[i
- 1]);
1000 unsigned int j
, last_button
= 0;
1003 for (j
= 0; j
< dialog_info
->button_count
; j
++)
1004 if (button_layout_infos
[j
].line
== i
- 1) last_button
= j
;
1006 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1007 diff_changed
= abs(2 * button_layout_infos
[last_button
].width
+ line_widths
[i
] - line_widths
[i
- 1]);
1009 if (diff_changed
< diff_now
)
1011 button_layout_infos
[last_button
].line
= i
;
1012 line_widths
[i
] += button_layout_infos
[last_button
].width
;
1013 line_widths
[i
- 1] -= button_layout_infos
[last_button
].width
;
1017 /* Calculate left alignment so all lines are as far right as possible. */
1018 align
= dialog_width
- h_spacing
;
1019 for (i
= 0; i
< line_count
; i
++)
1021 int new_alignment
= dialog_width
- line_widths
[i
];
1022 if (new_alignment
< align
) align
= new_alignment
;
1025 /* Now that we got them all positioned, move all buttons */
1027 size
.cy
= button_height
;
1028 for (i
= 0; i
< dialog_info
->button_count
; i
++)
1031 if (i
> 0 && button_layout_infos
[i
].line
!= button_layout_infos
[i
- 1].line
)
1034 dialog_height
+= size
.cy
+ v_spacing
;
1037 y
= dialog_height
+ v_spacing
;
1038 size
.cx
= button_layout_infos
[i
].width
;
1039 SetWindowPos(dialog_info
->buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1040 x
+= button_layout_infos
[i
].width
+ h_spacing
;
1043 /* Add height for last row button and spacing */
1044 dialog_height
+= size
.cy
+ v_spacing
;
1045 dialog_height
= max(dialog_height
, expando_bottom
);
1047 Free(button_layout_infos
);
1051 footer_icon_right
= 0;
1052 footer_icon_bottom
= dialog_height
;
1053 if (dialog_info
->footer_icon
)
1056 y
= dialog_height
+ v_spacing
;
1057 size
.cx
= GetSystemMetrics(SM_CXSMICON
);
1058 size
.cy
= GetSystemMetrics(SM_CYSMICON
);
1059 SetWindowPos(dialog_info
->footer_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1060 footer_icon_right
= x
+ size
.cx
;
1061 footer_icon_bottom
= y
+ size
.cy
;
1065 taskdialog_label_layout(dialog_info
, dialog_info
->footer_text
, footer_icon_right
, dialog_width
, &dialog_height
,
1067 dialog_height
= max(dialog_height
, footer_icon_bottom
);
1069 /* Expanded information */
1070 if ((taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
1071 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, 0, dialog_width
, &dialog_height
, syslink
);
1073 /* Add height for spacing, title height and frame height */
1074 dialog_height
+= v_spacing
;
1075 dialog_height
+= GetSystemMetrics(SM_CYCAPTION
);
1076 dialog_height
+= GetSystemMetrics(SM_CXDLGFRAME
);
1080 x
= (ref_rect
.left
+ ref_rect
.right
- dialog_width
) / 2;
1081 y
= (ref_rect
.top
+ ref_rect
.bottom
- dialog_height
) / 2;
1082 SetWindowPos(dialog_info
->hwnd
, 0, x
, y
, dialog_width
, dialog_height
, SWP_NOZORDER
);
1086 SetWindowPos(dialog_info
->hwnd
, 0, 0, 0, dialog_width
, dialog_height
, SWP_NOMOVE
| SWP_NOZORDER
);
1089 static void taskdialog_draw_expando_control(struct taskdialog_info
*dialog_info
, LPDRAWITEMSTRUCT dis
)
1095 LONG icon_width
, icon_height
, text_offset
;
1096 UINT style
= DFCS_FLAT
;
1100 hwnd
= dis
->hwndItem
;
1102 SendMessageW(hwnd
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
1104 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
1105 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
1106 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
1107 rect
.right
= icon_width
;
1108 rect
.bottom
= icon_height
;
1109 style
|= dialog_info
->expanded
? DFCS_SCROLLUP
: DFCS_SCROLLDOWN
;
1110 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
, style
);
1112 GetCharWidthW(hdc
, '0', '0', &text_offset
);
1116 rect
.left
+= icon_width
+ text_offset
;
1117 text
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
1118 DrawTextW(hdc
, text
, -1, &rect
, DT_WORDBREAK
| DT_END_ELLIPSIS
| DT_EXPANDTABS
);
1120 draw_focus
= (dis
->itemState
& ODS_FOCUS
) && !(dis
->itemState
& ODS_NOFOCUSRECT
);
1121 if(draw_focus
) DrawFocusRect(hdc
, &rect
);
1124 static void taskdialog_init(struct taskdialog_info
*dialog_info
, HWND hwnd
)
1126 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
1127 NONCLIENTMETRICSW ncm
;
1131 ncm
.cbSize
= sizeof(ncm
);
1132 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
1134 memset(dialog_info
, 0, sizeof(*dialog_info
));
1135 dialog_info
->taskconfig
= taskconfig
;
1136 dialog_info
->hwnd
= hwnd
;
1137 dialog_info
->font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
1139 hdc
= GetDC(dialog_info
->hwnd
);
1140 SelectObject(hdc
, dialog_info
->font
);
1141 dialog_info
->m
.x_baseunit
= GdiGetCharDimensions(hdc
, NULL
, &dialog_info
->m
.y_baseunit
);
1142 ReleaseDC(dialog_info
->hwnd
, hdc
);
1144 dialog_info
->m
.h_spacing
= DIALOG_SPACING
;
1145 dialog_info
->m
.v_spacing
= DIALOG_SPACING
;
1146 taskdialog_du_to_px(dialog_info
, &dialog_info
->m
.h_spacing
, &dialog_info
->m
.v_spacing
);
1148 if (taskconfig
->dwFlags
& TDF_CALLBACK_TIMER
)
1150 SetTimer(hwnd
, ID_TIMER
, DIALOG_TIMER_MS
, NULL
);
1151 dialog_info
->last_timer_tick
= GetTickCount();
1154 taskdialog_add_main_icon(dialog_info
);
1155 taskdialog_add_main_instruction(dialog_info
);
1156 taskdialog_add_content(dialog_info
);
1157 taskdialog_add_expanded_info(dialog_info
);
1158 taskdialog_add_progress_bar(dialog_info
);
1159 taskdialog_add_radio_buttons(dialog_info
);
1160 taskdialog_add_command_links(dialog_info
);
1161 taskdialog_add_expando_button(dialog_info
);
1162 taskdialog_add_verification_box(dialog_info
);
1163 taskdialog_add_buttons(dialog_info
);
1164 taskdialog_add_footer_icon(dialog_info
);
1165 taskdialog_add_footer_text(dialog_info
);
1167 /* Set default button */
1168 if (!dialog_info
->default_button
&& dialog_info
->command_links
)
1169 dialog_info
->default_button
= dialog_info
->command_links
[0];
1170 if (!dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->buttons
[0];
1171 SendMessageW(dialog_info
->hwnd
, WM_NEXTDLGCTL
, (WPARAM
)dialog_info
->default_button
, TRUE
);
1172 id
= GetWindowLongW(dialog_info
->default_button
, GWLP_ID
);
1173 SendMessageW(dialog_info
->hwnd
, DM_SETDEFID
, id
, 0);
1175 dialog_info
->has_cancel
=
1176 (taskconfig
->dwFlags
& TDF_ALLOW_DIALOG_CANCELLATION
)
1177 || taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, IDCANCEL
)
1178 || taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, IDCANCEL
);
1180 if (!dialog_info
->has_cancel
) DeleteMenu(GetSystemMenu(hwnd
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
1182 taskdialog_layout(dialog_info
);
1185 static BOOL CALLBACK
takdialog_destroy_control(HWND hwnd
, LPARAM lParam
)
1187 DestroyWindow(hwnd
);
1191 static void taskdialog_destroy(struct taskdialog_info
*dialog_info
)
1193 EnumChildWindows(dialog_info
->hwnd
, takdialog_destroy_control
, 0);
1195 if (dialog_info
->taskconfig
->dwFlags
& TDF_CALLBACK_TIMER
) KillTimer(dialog_info
->hwnd
, ID_TIMER
);
1196 if (dialog_info
->font
) DeleteObject(dialog_info
->font
);
1197 if (dialog_info
->main_instruction_font
) DeleteObject(dialog_info
->main_instruction_font
);
1198 Free(dialog_info
->buttons
);
1199 Free(dialog_info
->radio_buttons
);
1200 Free(dialog_info
->command_links
);
1201 Free(dialog_info
->expanded_text
);
1202 Free(dialog_info
->collapsed_text
);
1205 static INT_PTR CALLBACK
taskdialog_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1207 static const WCHAR taskdialog_info_propnameW
[] = L
"TaskDialogInfo";
1208 struct taskdialog_info
*dialog_info
;
1211 TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd
, msg
, wParam
, lParam
);
1213 if (msg
!= WM_INITDIALOG
)
1214 dialog_info
= GetPropW(hwnd
, taskdialog_info_propnameW
);
1218 case TDM_NAVIGATE_PAGE
:
1219 dialog_info
->taskconfig
= (const TASKDIALOGCONFIG
*)lParam
;
1220 taskdialog_destroy(dialog_info
);
1221 taskdialog_init(dialog_info
, hwnd
);
1222 taskdialog_notify(dialog_info
, TDN_DIALOG_CONSTRUCTED
, 0, 0);
1223 /* Default radio button click notification is sent before TDN_NAVIGATED */
1224 taskdialog_check_default_radio_buttons(dialog_info
);
1225 taskdialog_notify(dialog_info
, TDN_NAVIGATED
, 0, 0);
1227 case TDM_CLICK_BUTTON
:
1228 taskdialog_click_button(dialog_info
, wParam
);
1230 case TDM_ENABLE_BUTTON
:
1231 taskdialog_enable_button(dialog_info
, wParam
, lParam
);
1233 case TDM_SET_MARQUEE_PROGRESS_BAR
:
1235 BOOL marquee
= wParam
;
1237 if(!dialog_info
->progress_bar
) break;
1238 style
= GetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
);
1239 style
= marquee
? style
| PBS_MARQUEE
: style
& (~PBS_MARQUEE
);
1240 SetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
, style
);
1243 case TDM_SET_PROGRESS_BAR_STATE
:
1244 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETSTATE
, wParam
, 0);
1245 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1247 case TDM_SET_PROGRESS_BAR_RANGE
:
1248 result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETRANGE
, 0, lParam
);
1249 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1251 case TDM_SET_PROGRESS_BAR_POS
:
1253 if (dialog_info
->progress_bar
)
1255 LONG style
= GetWindowLongW(dialog_info
->progress_bar
, GWL_STYLE
);
1256 if (!(style
& PBS_MARQUEE
)) result
= SendMessageW(dialog_info
->progress_bar
, PBM_SETPOS
, wParam
, 0);
1258 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, result
);
1260 case TDM_SET_PROGRESS_BAR_MARQUEE
:
1261 SendMessageW(dialog_info
->progress_bar
, PBM_SETMARQUEE
, wParam
, lParam
);
1263 case TDM_SET_ELEMENT_TEXT
:
1264 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1265 taskdialog_layout(dialog_info
);
1267 case TDM_UPDATE_ELEMENT_TEXT
:
1268 taskdialog_set_element_text(dialog_info
, wParam
, (const WCHAR
*)lParam
);
1270 case TDM_CLICK_RADIO_BUTTON
:
1271 taskdialog_click_radio_button(dialog_info
, wParam
);
1273 case TDM_ENABLE_RADIO_BUTTON
:
1274 taskdialog_enable_radio_button(dialog_info
, wParam
, lParam
);
1276 case TDM_CLICK_VERIFICATION
:
1278 BOOL checked
= (BOOL
)wParam
;
1279 BOOL focused
= (BOOL
)lParam
;
1280 dialog_info
->verification_checked
= checked
;
1281 if (dialog_info
->verification_box
)
1283 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, checked
? BST_CHECKED
: BST_UNCHECKED
, 0);
1284 taskdialog_notify(dialog_info
, TDN_VERIFICATION_CLICKED
, checked
, 0);
1285 if (focused
) SetFocus(dialog_info
->verification_box
);
1289 case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE
:
1290 taskdialog_button_set_shield(dialog_info
, wParam
, lParam
);
1292 case TDM_UPDATE_ICON
:
1293 taskdialog_set_icon(dialog_info
, wParam
, (HICON
)lParam
);
1296 dialog_info
= (struct taskdialog_info
*)lParam
;
1298 taskdialog_init(dialog_info
, hwnd
);
1300 SetPropW(hwnd
, taskdialog_info_propnameW
, dialog_info
);
1301 taskdialog_notify(dialog_info
, TDN_DIALOG_CONSTRUCTED
, 0, 0);
1302 taskdialog_notify(dialog_info
, TDN_CREATED
, 0, 0);
1303 /* Default radio button click notification sent after TDN_CREATED */
1304 taskdialog_check_default_radio_buttons(dialog_info
);
1307 if (HIWORD(wParam
) == BN_CLICKED
)
1309 taskdialog_on_button_click(dialog_info
, (HWND
)lParam
, LOWORD(wParam
));
1314 taskdialog_notify(dialog_info
, TDN_HELP
, 0, 0);
1317 if (ID_TIMER
== wParam
)
1319 DWORD elapsed
= GetTickCount() - dialog_info
->last_timer_tick
;
1320 if (taskdialog_notify(dialog_info
, TDN_TIMER
, elapsed
, 0) == S_FALSE
)
1321 dialog_info
->last_timer_tick
= GetTickCount();
1326 PNMLINK pnmLink
= (PNMLINK
)lParam
;
1327 HWND hwndFrom
= pnmLink
->hdr
.hwndFrom
;
1328 if ((taskdialog_hyperlink_enabled(dialog_info
))
1329 && (hwndFrom
== dialog_info
->content
|| hwndFrom
== dialog_info
->expanded_info
1330 || hwndFrom
== dialog_info
->footer_text
)
1331 && (pnmLink
->hdr
.code
== NM_CLICK
|| pnmLink
->hdr
.code
== NM_RETURN
))
1333 taskdialog_notify(dialog_info
, TDN_HYPERLINK_CLICKED
, 0, (LPARAM
)pnmLink
->item
.szUrl
);
1340 LPDRAWITEMSTRUCT dis
= (LPDRAWITEMSTRUCT
)lParam
;
1341 if (dis
->hwndItem
== dialog_info
->expando_button
)
1343 taskdialog_draw_expando_control(dialog_info
, dis
);
1344 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, TRUE
);
1350 taskdialog_notify(dialog_info
, TDN_DESTROYED
, 0, 0);
1351 RemovePropW(hwnd
, taskdialog_info_propnameW
);
1352 taskdialog_destroy(dialog_info
);
1355 if (dialog_info
->has_cancel
)
1357 if(taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, IDCANCEL
, 0) == S_OK
)
1358 EndDialog(hwnd
, IDCANCEL
);
1359 SetWindowLongPtrW(hwnd
, DWLP_MSGRESULT
, 0);
1369 /***********************************************************************
1370 * TaskDialogIndirect [COMCTL32.@]
1372 HRESULT WINAPI
TaskDialogIndirect(const TASKDIALOGCONFIG
*taskconfig
, int *button
,
1373 int *radio_button
, BOOL
*verification_flag_checked
)
1375 struct taskdialog_info dialog_info
;
1376 DLGTEMPLATE
*template;
1379 TRACE("%p, %p, %p, %p\n", taskconfig
, button
, radio_button
, verification_flag_checked
);
1381 if (!taskconfig
|| taskconfig
->cbSize
!= sizeof(TASKDIALOGCONFIG
))
1382 return E_INVALIDARG
;
1384 dialog_info
.taskconfig
= taskconfig
;
1386 template = create_taskdialog_template(taskconfig
);
1387 ret
= (short)DialogBoxIndirectParamW(taskconfig
->hInstance
, template, taskconfig
->hwndParent
,
1388 taskdialog_proc
, (LPARAM
)&dialog_info
);
1391 if (button
) *button
= ret
;
1392 if (radio_button
) *radio_button
= dialog_info
.selected_radio_id
;
1393 if (verification_flag_checked
) *verification_flag_checked
= dialog_info
.verification_checked
;
1398 /***********************************************************************
1399 * TaskDialog [COMCTL32.@]
1401 HRESULT WINAPI
TaskDialog(HWND owner
, HINSTANCE hinst
, const WCHAR
*title
, const WCHAR
*main_instruction
,
1402 const WCHAR
*content
, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons
, const WCHAR
*icon
, int *button
)
1404 TASKDIALOGCONFIG taskconfig
;
1406 TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner
, hinst
, debugstr_w(title
), debugstr_w(main_instruction
),
1407 debugstr_w(content
), common_buttons
, debugstr_w(icon
), button
);
1409 memset(&taskconfig
, 0, sizeof(taskconfig
));
1410 taskconfig
.cbSize
= sizeof(taskconfig
);
1411 taskconfig
.hwndParent
= owner
;
1412 taskconfig
.hInstance
= hinst
;
1413 taskconfig
.dwCommonButtons
= common_buttons
;
1414 taskconfig
.pszWindowTitle
= title
;
1415 taskconfig
.u
.pszMainIcon
= icon
;
1416 taskconfig
.pszMainInstruction
= main_instruction
;
1417 taskconfig
.pszContent
= content
;
1418 return TaskDialogIndirect(&taskconfig
, button
, NULL
, NULL
);