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
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog
);
39 static const UINT DIALOG_MIN_WIDTH
= 240;
40 static const UINT DIALOG_SPACING
= 5;
41 static const UINT DIALOG_BUTTON_WIDTH
= 50;
42 static const UINT DIALOG_BUTTON_HEIGHT
= 14;
43 static const UINT DIALOG_EXPANDO_ICON_WIDTH
= 10;
44 static const UINT DIALOG_EXPANDO_ICON_HEIGHT
= 10;
45 static const UINT DIALOG_TIMER_MS
= 200;
47 static const UINT ID_TIMER
= 1;
49 struct taskdialog_info
52 const TASKDIALOGCONFIG
*taskconfig
;
53 DWORD last_timer_tick
;
55 HFONT main_instruction_font
;
58 HWND main_instruction
;
62 INT radio_button_count
;
64 INT command_link_count
;
67 HWND verification_box
;
81 INT selected_radio_id
;
82 BOOL verification_checked
;
86 WCHAR
*collapsed_text
;
89 struct button_layout_info
95 static HRESULT
taskdialog_notify(struct taskdialog_info
*dialog_info
, UINT notification
, WPARAM wparam
, LPARAM lparam
);
96 static void taskdialog_on_button_click(struct taskdialog_info
*dialog_info
, HWND hwnd
, WORD id
);
97 static void taskdialog_layout(struct taskdialog_info
*dialog_info
);
99 static void taskdialog_du_to_px(struct taskdialog_info
*dialog_info
, LONG
*width
, LONG
*height
)
101 if (width
) *width
= MulDiv(*width
, dialog_info
->m
.x_baseunit
, 4);
102 if (height
) *height
= MulDiv(*height
, dialog_info
->m
.y_baseunit
, 8);
105 static void template_write_data(char **ptr
, const void *src
, unsigned int size
)
107 memcpy(*ptr
, src
, size
);
111 static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG
*taskconfig
, RECT
*ret
)
113 HMONITOR monitor
= MonitorFromWindow(taskconfig
->hwndParent
? taskconfig
->hwndParent
: GetActiveWindow(),
114 MONITOR_DEFAULTTOPRIMARY
);
117 info
.cbSize
= sizeof(info
);
118 GetMonitorInfoW(monitor
, &info
);
120 if ((taskconfig
->dwFlags
& TDF_POSITION_RELATIVE_TO_WINDOW
) && taskconfig
->hwndParent
)
121 GetWindowRect(taskconfig
->hwndParent
, ret
);
125 return info
.rcWork
.right
- info
.rcWork
.left
;
128 static WCHAR
*taskdialog_get_exe_name(WCHAR
*name
, DWORD length
)
130 DWORD len
= GetModuleFileNameW(NULL
, name
, length
);
131 if (len
&& len
< length
)
134 if ((p
= wcsrchr(name
, '/'))) name
= p
+ 1;
135 if ((p
= wcsrchr(name
, '\\'))) name
= p
+ 1;
142 static DLGTEMPLATE
*create_taskdialog_template(const TASKDIALOGCONFIG
*taskconfig
)
144 unsigned int size
, title_size
;
145 static const WORD fontsize
= 0x7fff;
146 const WCHAR
*titleW
= NULL
;
147 DLGTEMPLATE
*template;
148 WCHAR pathW
[MAX_PATH
];
152 if (!taskconfig
->pszWindowTitle
)
153 titleW
= taskdialog_get_exe_name(pathW
, ARRAY_SIZE(pathW
));
154 else if (IS_INTRESOURCE(taskconfig
->pszWindowTitle
))
156 if (!LoadStringW(taskconfig
->hInstance
, LOWORD(taskconfig
->pszWindowTitle
), (WCHAR
*)&titleW
, 0))
157 titleW
= taskdialog_get_exe_name(pathW
, ARRAY_SIZE(pathW
));
160 titleW
= taskconfig
->pszWindowTitle
;
163 title_size
= (lstrlenW(titleW
) + 1) * sizeof(WCHAR
);
165 size
= sizeof(DLGTEMPLATE
) + 2 * sizeof(WORD
);
167 size
+= 2; /* font size */
169 template = Alloc(size
);
170 if (!template) return NULL
;
172 template->style
= DS_MODALFRAME
| DS_SETFONT
| WS_CAPTION
| WS_VISIBLE
| WS_SYSMENU
;
173 if (taskconfig
->dwFlags
& TDF_CAN_BE_MINIMIZED
) template->style
|= WS_MINIMIZEBOX
;
174 if (!(taskconfig
->dwFlags
& TDF_NO_SET_FOREGROUND
)) template->style
|= DS_SETFOREGROUND
;
175 if (taskconfig
->dwFlags
& TDF_RTL_LAYOUT
) template->dwExtendedStyle
= WS_EX_LAYOUTRTL
| WS_EX_RIGHT
| WS_EX_RTLREADING
;
177 ptr
= (char *)(template + 1);
179 ptr
+= 2; /* class */
180 template_write_data(&ptr
, titleW
, title_size
);
181 template_write_data(&ptr
, &fontsize
, sizeof(fontsize
));
186 static HWND
taskdialog_find_button(HWND
*buttons
, INT count
, INT id
)
191 for (i
= 0; i
< count
; i
++)
193 button_id
= GetWindowLongW(buttons
[i
], GWLP_ID
);
194 if (button_id
== id
) return buttons
[i
];
200 static void taskdialog_enable_button(const struct taskdialog_info
*dialog_info
, INT id
, BOOL enable
)
202 HWND hwnd
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, id
);
203 if (!hwnd
) hwnd
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, id
);
204 if (hwnd
) EnableWindow(hwnd
, enable
);
207 static void taskdialog_click_button(struct taskdialog_info
*dialog_info
, INT id
)
209 if (taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, id
, 0) == S_OK
) EndDialog(dialog_info
->hwnd
, id
);
212 static void taskdialog_button_set_shield(const struct taskdialog_info
*dialog_info
, INT id
, BOOL elevate
)
214 HWND hwnd
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, id
);
215 if (!hwnd
) hwnd
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, id
);
216 if (hwnd
) SendMessageW(hwnd
, BCM_SETSHIELD
, 0, elevate
);
219 static void taskdialog_enable_radio_button(const struct taskdialog_info
*dialog_info
, INT id
, BOOL enable
)
221 HWND hwnd
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, id
);
222 if (hwnd
) EnableWindow(hwnd
, enable
);
225 static void taskdialog_click_radio_button(const struct taskdialog_info
*dialog_info
, INT id
)
227 HWND hwnd
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, id
);
228 if (hwnd
) SendMessageW(hwnd
, BM_CLICK
, 0, 0);
231 static HRESULT
taskdialog_notify(struct taskdialog_info
*dialog_info
, UINT notification
, WPARAM wparam
, LPARAM lparam
)
233 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
234 return taskconfig
->pfCallback
235 ? taskconfig
->pfCallback(dialog_info
->hwnd
, notification
, wparam
, lparam
, taskconfig
->lpCallbackData
)
239 static void taskdialog_move_controls_vertically(HWND parent
, HWND
*controls
, INT count
, INT offset
)
245 for (i
= 0; i
< count
; i
++)
247 if (!controls
[i
]) continue;
249 GetWindowRect(controls
[i
], &rect
);
252 MapWindowPoints(HWND_DESKTOP
, parent
, &pt
, 1);
253 SetWindowPos(controls
[i
], 0, pt
.x
, pt
.y
+ offset
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
);
257 static void taskdialog_toggle_expando_control(struct taskdialog_info
*dialog_info
)
259 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
261 RECT info_rect
, rect
;
264 dialog_info
->expanded
= !dialog_info
->expanded
;
265 text
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
266 SendMessageW(dialog_info
->expando_button
, WM_SETTEXT
, 0, (LPARAM
)text
);
267 ShowWindow(dialog_info
->expanded_info
, dialog_info
->expanded
? SW_SHOWDEFAULT
: SW_HIDE
);
269 GetWindowRect(dialog_info
->expanded_info
, &info_rect
);
270 /* If expanded information starts up not expanded, call taskdialog_layout()
271 * to to set size for expanded information control at least once */
272 if (IsRectEmpty(&info_rect
))
274 taskdialog_layout(dialog_info
);
277 height
= info_rect
.bottom
- info_rect
.top
+ dialog_info
->m
.v_spacing
;
278 offset
= dialog_info
->expanded
? height
: -height
;
280 /* Update vertical layout, move all controls after expanded information */
282 GetWindowRect(dialog_info
->hwnd
, &rect
);
283 SetWindowPos(dialog_info
->hwnd
, 0, 0, 0, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
+ offset
,
284 SWP_NOMOVE
| SWP_NOZORDER
);
286 if (!(taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
))
288 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->progress_bar
, 1, offset
);
289 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->expando_button
, 1, offset
);
290 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->verification_box
, 1, offset
);
291 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->footer_icon
, 1, offset
);
292 taskdialog_move_controls_vertically(dialog_info
->hwnd
, &dialog_info
->footer_text
, 1, offset
);
293 taskdialog_move_controls_vertically(dialog_info
->hwnd
, dialog_info
->buttons
, dialog_info
->button_count
, offset
);
294 taskdialog_move_controls_vertically(dialog_info
->hwnd
, dialog_info
->radio_buttons
,
295 dialog_info
->radio_button_count
, offset
);
296 taskdialog_move_controls_vertically(dialog_info
->hwnd
, dialog_info
->command_links
,
297 dialog_info
->command_link_count
, offset
);
301 static void taskdialog_on_button_click(struct taskdialog_info
*dialog_info
, HWND hwnd
, WORD id
)
304 HWND button
, radio_button
;
306 /* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
307 command_id
= hwnd
? GetWindowLongW(hwnd
, GWLP_ID
) : id
;
309 if (hwnd
&& hwnd
== dialog_info
->expando_button
)
311 taskdialog_toggle_expando_control(dialog_info
);
312 taskdialog_notify(dialog_info
, TDN_EXPANDO_BUTTON_CLICKED
, dialog_info
->expanded
, 0);
316 if (hwnd
&& hwnd
== dialog_info
->verification_box
)
318 dialog_info
->verification_checked
= !dialog_info
->verification_checked
;
319 taskdialog_notify(dialog_info
, TDN_VERIFICATION_CLICKED
, dialog_info
->verification_checked
, 0);
323 radio_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
, command_id
);
326 dialog_info
->selected_radio_id
= command_id
;
327 taskdialog_notify(dialog_info
, TDN_RADIO_BUTTON_CLICKED
, command_id
, 0);
331 button
= taskdialog_find_button(dialog_info
->command_links
, dialog_info
->command_link_count
, command_id
);
332 if (!button
) button
= taskdialog_find_button(dialog_info
->buttons
, dialog_info
->button_count
, command_id
);
333 if (!button
&& command_id
== IDOK
)
335 button
= dialog_info
->command_link_count
> 0 ? dialog_info
->command_links
[0] : dialog_info
->buttons
[0];
336 command_id
= GetWindowLongW(button
, GWLP_ID
);
339 if (button
&& taskdialog_notify(dialog_info
, TDN_BUTTON_CLICKED
, command_id
, 0) == S_OK
)
340 EndDialog(dialog_info
->hwnd
, command_id
);
343 static WCHAR
*taskdialog_gettext(struct taskdialog_info
*dialog_info
, BOOL user_resource
, const WCHAR
*text
)
345 const WCHAR
*textW
= NULL
;
349 if (IS_INTRESOURCE(text
))
351 if (!(length
= LoadStringW(user_resource
? dialog_info
->taskconfig
->hInstance
: COMCTL32_hModule
,
352 (UINT_PTR
)text
, (WCHAR
*)&textW
, 0)))
358 length
= lstrlenW(textW
);
361 ret
= Alloc((length
+ 1) * sizeof(WCHAR
));
362 if (ret
) memcpy(ret
, textW
, length
* sizeof(WCHAR
));
367 static BOOL
taskdialog_hyperlink_enabled(struct taskdialog_info
*dialog_info
)
369 return dialog_info
->taskconfig
->dwFlags
& TDF_ENABLE_HYPERLINKS
;
372 static BOOL
taskdialog_use_command_link(struct taskdialog_info
*dialog_info
)
374 return dialog_info
->taskconfig
->dwFlags
& (TDF_USE_COMMAND_LINKS
| TDF_USE_COMMAND_LINKS_NO_ICON
);
377 static void taskdialog_get_label_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, LONG max_width
, SIZE
*size
,
380 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
381 HFONT hfont
, old_hfont
;
389 SendMessageW(hwnd
, LM_GETIDEALSIZE
, max_width
, (LPARAM
)size
);
393 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
394 style
|= DT_RIGHT
| DT_RTLREADING
;
398 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
399 text_length
= GetWindowTextLengthW(hwnd
);
400 text
= Alloc((text_length
+ 1) * sizeof(WCHAR
));
407 GetWindowTextW(hwnd
, text
, text_length
+ 1);
409 old_hfont
= SelectObject(hdc
, hfont
);
410 rect
.right
= max_width
;
411 size
->cy
= DrawTextW(hdc
, text
, text_length
, &rect
, style
);
412 size
->cx
= min(max_width
, rect
.right
- rect
.left
);
413 if (old_hfont
) SelectObject(hdc
, old_hfont
);
414 ReleaseDC(hwnd
, hdc
);
418 static void taskdialog_get_button_size(HWND hwnd
, LONG max_width
, SIZE
*size
)
420 size
->cx
= max_width
;
422 SendMessageW(hwnd
, BCM_GETIDEALSIZE
, 0, (LPARAM
)size
);
425 static void taskdialog_get_expando_size(struct taskdialog_info
*dialog_info
, HWND hwnd
, SIZE
*size
)
427 DWORD style
= DT_EXPANDTABS
| DT_CALCRECT
| DT_WORDBREAK
;
428 HFONT hfont
, old_hfont
;
431 LONG icon_width
, icon_height
;
433 LONG max_width
, max_text_height
;
436 hfont
= (HFONT
)SendMessageW(hwnd
, WM_GETFONT
, 0, 0);
437 old_hfont
= SelectObject(hdc
, hfont
);
439 icon_width
= DIALOG_EXPANDO_ICON_WIDTH
;
440 icon_height
= DIALOG_EXPANDO_ICON_HEIGHT
;
441 taskdialog_du_to_px(dialog_info
, &icon_width
, &icon_height
);
443 GetCharWidthW(hdc
, '0', '0', &text_offset
);
446 if (dialog_info
->taskconfig
->dwFlags
& TDF_RTL_LAYOUT
)
447 style
|= DT_RIGHT
| DT_RTLREADING
;
451 max_width
= DIALOG_MIN_WIDTH
/ 2;
452 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
454 rect
.right
= max_width
- icon_width
- text_offset
;
455 max_text_height
= DrawTextW(hdc
, dialog_info
->expanded_text
, -1, &rect
, style
);
456 size
->cy
= max(max_text_height
, icon_height
);
457 size
->cx
= rect
.right
- rect
.left
;
459 rect
.right
= max_width
- icon_width
- text_offset
;
460 max_text_height
= DrawTextW(hdc
, dialog_info
->collapsed_text
, -1, &rect
, style
);
461 size
->cy
= max(size
->cy
, max_text_height
);
462 size
->cx
= max(size
->cx
, rect
.right
- rect
.left
);
463 size
->cx
= min(size
->cx
, max_width
);
465 if (old_hfont
) SelectObject(hdc
, old_hfont
);
466 ReleaseDC(hwnd
, hdc
);
469 static ULONG_PTR
taskdialog_get_standard_icon(LPCWSTR icon
)
471 if (icon
== TD_WARNING_ICON
)
473 else if (icon
== TD_ERROR_ICON
)
475 else if (icon
== TD_INFORMATION_ICON
)
476 return IDI_INFORMATION
;
477 else if (icon
== TD_SHIELD_ICON
)
480 return (ULONG_PTR
)icon
;
483 static void taskdialog_set_icon(struct taskdialog_info
*dialog_info
, INT element
, HICON icon
)
485 DWORD flags
= dialog_info
->taskconfig
->dwFlags
;
491 if (((flags
& TDF_USE_HICON_MAIN
) && element
== TDIE_ICON_MAIN
)
492 || ((flags
& TDF_USE_HICON_FOOTER
) && element
== TDIE_ICON_FOOTER
))
496 if (element
== TDIE_ICON_FOOTER
)
498 cx
= GetSystemMetrics(SM_CXSMICON
);
499 cy
= GetSystemMetrics(SM_CYSMICON
);
501 hicon
= LoadImageW(dialog_info
->taskconfig
->hInstance
, (LPCWSTR
)icon
, IMAGE_ICON
, cx
, cy
, LR_SHARED
| LR_DEFAULTSIZE
);
503 hicon
= LoadImageW(NULL
, (LPCWSTR
)taskdialog_get_standard_icon((LPCWSTR
)icon
), IMAGE_ICON
, cx
, cy
,
504 LR_SHARED
| LR_DEFAULTSIZE
);
509 if (element
== TDIE_ICON_MAIN
)
511 SendMessageW(dialog_info
->hwnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)hicon
);
512 SendMessageW(dialog_info
->main_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
514 else if (element
== TDIE_ICON_FOOTER
)
515 SendMessageW(dialog_info
->footer_icon
, STM_SETICON
, (WPARAM
)hicon
, 0);
518 static void taskdialog_set_element_text(struct taskdialog_info
*dialog_info
, TASKDIALOG_ELEMENTS element
,
524 if (element
== TDE_CONTENT
)
525 hwnd
= dialog_info
->content
;
526 else if (element
== TDE_EXPANDED_INFORMATION
)
527 hwnd
= dialog_info
->expanded_info
;
528 else if (element
== TDE_FOOTER
)
529 hwnd
= dialog_info
->footer_text
;
530 else if (element
== TDE_MAIN_INSTRUCTION
)
531 hwnd
= dialog_info
->main_instruction
;
535 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
536 SendMessageW(hwnd
, WM_SETTEXT
, 0, (LPARAM
)textW
);
540 static void taskdialog_check_default_radio_buttons(struct taskdialog_info
*dialog_info
)
542 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
545 if (!dialog_info
->radio_button_count
) return;
547 default_button
= taskdialog_find_button(dialog_info
->radio_buttons
, dialog_info
->radio_button_count
,
548 taskconfig
->nDefaultRadioButton
);
550 if (!default_button
&& !(taskconfig
->dwFlags
& TDF_NO_DEFAULT_RADIO_BUTTON
))
551 default_button
= dialog_info
->radio_buttons
[0];
555 SendMessageW(default_button
, BM_SETCHECK
, BST_CHECKED
, 0);
556 taskdialog_on_button_click(dialog_info
, default_button
, 0);
560 static void taskdialog_add_main_icon(struct taskdialog_info
*dialog_info
)
562 if (!dialog_info
->taskconfig
->hMainIcon
) return;
564 dialog_info
->main_icon
=
565 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
566 taskdialog_set_icon(dialog_info
, TDIE_ICON_MAIN
, dialog_info
->taskconfig
->hMainIcon
);
569 static HWND
taskdialog_create_label(struct taskdialog_info
*dialog_info
, const WCHAR
*text
, HFONT font
, BOOL syslink
)
574 DWORD style
= WS_CHILD
| WS_VISIBLE
;
576 if (!text
) return NULL
;
578 class = syslink
? WC_LINK
: WC_STATICW
;
579 if (syslink
) style
|= WS_TABSTOP
;
580 textW
= taskdialog_gettext(dialog_info
, TRUE
, text
);
581 hwnd
= CreateWindowW(class, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
584 SendMessageW(hwnd
, WM_SETFONT
, (WPARAM
)font
, 0);
588 static void taskdialog_add_main_instruction(struct taskdialog_info
*dialog_info
)
590 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
591 NONCLIENTMETRICSW ncm
;
593 if (!taskconfig
->pszMainInstruction
) return;
595 ncm
.cbSize
= sizeof(ncm
);
596 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, ncm
.cbSize
, &ncm
, 0);
597 /* 1.25 times the height */
598 ncm
.lfMessageFont
.lfHeight
= ncm
.lfMessageFont
.lfHeight
* 5 / 4;
599 ncm
.lfMessageFont
.lfWeight
= FW_BOLD
;
600 dialog_info
->main_instruction_font
= CreateFontIndirectW(&ncm
.lfMessageFont
);
602 dialog_info
->main_instruction
=
603 taskdialog_create_label(dialog_info
, taskconfig
->pszMainInstruction
, dialog_info
->main_instruction_font
, FALSE
);
606 static void taskdialog_add_content(struct taskdialog_info
*dialog_info
)
608 dialog_info
->content
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszContent
, dialog_info
->font
,
609 taskdialog_hyperlink_enabled(dialog_info
));
612 static void taskdialog_add_progress_bar(struct taskdialog_info
*dialog_info
)
614 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
615 DWORD style
= PBS_SMOOTH
| PBS_SMOOTHREVERSE
| WS_CHILD
| WS_VISIBLE
;
617 if (!(taskconfig
->dwFlags
& (TDF_SHOW_PROGRESS_BAR
| TDF_SHOW_MARQUEE_PROGRESS_BAR
))) return;
618 if (taskconfig
->dwFlags
& TDF_SHOW_MARQUEE_PROGRESS_BAR
) style
|= PBS_MARQUEE
;
619 dialog_info
->progress_bar
=
620 CreateWindowW(PROGRESS_CLASSW
, NULL
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, NULL
);
623 static void taskdialog_add_radio_buttons(struct taskdialog_info
*dialog_info
)
625 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
626 static const DWORD style
= BS_AUTORADIOBUTTON
| BS_MULTILINE
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
630 if (!taskconfig
->cRadioButtons
|| !taskconfig
->pRadioButtons
) return;
632 dialog_info
->radio_buttons
= Alloc(taskconfig
->cRadioButtons
* sizeof(*dialog_info
->radio_buttons
));
633 if (!dialog_info
->radio_buttons
) return;
635 dialog_info
->radio_button_count
= taskconfig
->cRadioButtons
;
636 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
638 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pRadioButtons
[i
].pszButtonText
);
639 dialog_info
->radio_buttons
[i
] =
640 CreateWindowW(WC_BUTTONW
, textW
, i
== 0 ? style
| WS_GROUP
: style
, 0, 0, 0, 0, dialog_info
->hwnd
,
641 LongToHandle(taskconfig
->pRadioButtons
[i
].nButtonID
), 0, NULL
);
642 SendMessageW(dialog_info
->radio_buttons
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
647 static void taskdialog_add_command_links(struct taskdialog_info
*dialog_info
)
649 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
650 DWORD default_style
= BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, style
;
655 if (!taskconfig
->cButtons
|| !taskconfig
->pButtons
|| !taskdialog_use_command_link(dialog_info
)) return;
657 dialog_info
->command_links
= Alloc(taskconfig
->cButtons
* sizeof(*dialog_info
->command_links
));
658 if (!dialog_info
->command_links
) return;
660 dialog_info
->command_link_count
= taskconfig
->cButtons
;
661 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
663 is_default
= taskconfig
->pButtons
[i
].nButtonID
== taskconfig
->nDefaultButton
;
664 style
= is_default
? default_style
| BS_DEFCOMMANDLINK
: default_style
| BS_COMMANDLINK
;
665 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pButtons
[i
].pszButtonText
);
666 dialog_info
->command_links
[i
] = CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
,
667 LongToHandle(taskconfig
->pButtons
[i
].nButtonID
), 0, NULL
);
668 SendMessageW(dialog_info
->command_links
[i
], WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
671 if (is_default
&& !dialog_info
->default_button
) dialog_info
->default_button
= dialog_info
->command_links
[i
];
675 static void taskdialog_add_expanded_info(struct taskdialog_info
*dialog_info
)
677 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
679 if (!taskconfig
->pszExpandedInformation
) return;
681 dialog_info
->expanded
= taskconfig
->dwFlags
& TDF_EXPANDED_BY_DEFAULT
;
682 dialog_info
->expanded_info
= taskdialog_create_label(dialog_info
, taskconfig
->pszExpandedInformation
,
683 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
684 ShowWindow(dialog_info
->expanded_info
, dialog_info
->expanded
? SW_SHOWDEFAULT
: SW_HIDE
);
687 static void taskdialog_add_expando_button(struct taskdialog_info
*dialog_info
)
689 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
692 if (!taskconfig
->pszExpandedInformation
) return;
694 if (!taskconfig
->pszCollapsedControlText
&& !taskconfig
->pszExpandedControlText
)
696 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_EXPANDED
));
697 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, FALSE
, MAKEINTRESOURCEW(IDS_TD_COLLAPSED
));
701 textW
= taskconfig
->pszExpandedControlText
? taskconfig
->pszExpandedControlText
702 : taskconfig
->pszCollapsedControlText
;
703 dialog_info
->expanded_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
704 textW
= taskconfig
->pszCollapsedControlText
? taskconfig
->pszCollapsedControlText
705 : taskconfig
->pszExpandedControlText
;
706 dialog_info
->collapsed_text
= taskdialog_gettext(dialog_info
, TRUE
, textW
);
709 textW
= dialog_info
->expanded
? dialog_info
->expanded_text
: dialog_info
->collapsed_text
;
711 dialog_info
->expando_button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
| BS_OWNERDRAW
, 0,
712 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
713 SendMessageW(dialog_info
->expando_button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
716 static void taskdialog_add_verification_box(struct taskdialog_info
*dialog_info
)
718 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
719 static const DWORD style
= BS_AUTOCHECKBOX
| BS_MULTILINE
| BS_LEFT
| BS_TOP
| WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
;
722 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
723 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
) dialog_info
->verification_checked
= TRUE
;
725 if (!taskconfig
->pszVerificationText
) return;
727 textW
= taskdialog_gettext(dialog_info
, TRUE
, taskconfig
->pszVerificationText
);
728 dialog_info
->verification_box
= CreateWindowW(WC_BUTTONW
, textW
, style
, 0, 0, 0, 0, dialog_info
->hwnd
, 0, 0, 0);
729 SendMessageW(dialog_info
->verification_box
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
732 if (taskconfig
->dwFlags
& TDF_VERIFICATION_FLAG_CHECKED
)
733 SendMessageW(dialog_info
->verification_box
, BM_SETCHECK
, BST_CHECKED
, 0);
736 static void taskdialog_add_button(struct taskdialog_info
*dialog_info
, HWND
*button
, INT_PTR id
, const WCHAR
*text
,
739 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
742 textW
= taskdialog_gettext(dialog_info
, custom_button
, text
);
743 *button
= CreateWindowW(WC_BUTTONW
, textW
, WS_CHILD
| WS_VISIBLE
| WS_TABSTOP
, 0, 0, 0, 0, dialog_info
->hwnd
,
746 SendMessageW(*button
, WM_SETFONT
, (WPARAM
)dialog_info
->font
, 0);
748 if (id
== taskconfig
->nDefaultButton
&& !dialog_info
->default_button
) dialog_info
->default_button
= *button
;
751 static void taskdialog_add_buttons(struct taskdialog_info
*dialog_info
)
753 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
754 BOOL use_command_links
= taskdialog_use_command_link(dialog_info
);
755 DWORD flags
= taskconfig
->dwCommonButtons
;
756 INT count
, max_count
;
758 /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
760 if (!use_command_links
&& taskconfig
->cButtons
&& taskconfig
->pButtons
) max_count
+= taskconfig
->cButtons
;
762 dialog_info
->buttons
= Alloc(max_count
* sizeof(*dialog_info
->buttons
));
763 if (!dialog_info
->buttons
) return;
765 for (count
= 0; !use_command_links
&& count
< taskconfig
->cButtons
; count
++)
766 taskdialog_add_button(dialog_info
, &dialog_info
->buttons
[count
], taskconfig
->pButtons
[count
].nButtonID
,
767 taskconfig
->pButtons
[count
].pszButtonText
, TRUE
);
769 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
772 taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
776 if (flags
& TDCBF_OK_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
777 if (flags
& TDCBF_YES_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(YES
);
778 if (flags
& TDCBF_NO_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(NO
);
779 if (flags
& TDCBF_RETRY_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(RETRY
);
780 if (flags
& TDCBF_CANCEL_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL
);
781 if (flags
& TDCBF_CLOSE_BUTTON
) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE
);
783 if (!count
&& !dialog_info
->command_link_count
) TASKDIALOG_INIT_COMMON_BUTTON(OK
);
784 #undef TASKDIALOG_INIT_COMMON_BUTTON
786 dialog_info
->button_count
= count
;
789 static void taskdialog_add_footer_icon(struct taskdialog_info
*dialog_info
)
791 if (!dialog_info
->taskconfig
->hFooterIcon
) return;
793 dialog_info
->footer_icon
=
794 CreateWindowW(WC_STATICW
, NULL
, WS_CHILD
| WS_VISIBLE
| SS_ICON
, 0, 0, 0, 0, dialog_info
->hwnd
, NULL
, 0, 0);
795 taskdialog_set_icon(dialog_info
, TDIE_ICON_FOOTER
, dialog_info
->taskconfig
->hFooterIcon
);
798 static void taskdialog_add_footer_text(struct taskdialog_info
*dialog_info
)
800 dialog_info
->footer_text
= taskdialog_create_label(dialog_info
, dialog_info
->taskconfig
->pszFooter
,
801 dialog_info
->font
, taskdialog_hyperlink_enabled(dialog_info
));
804 static LONG
taskdialog_get_dialog_width(struct taskdialog_info
*dialog_info
)
806 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
807 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
808 LONG max_width
, main_icon_width
, screen_width
;
812 screen_width
= taskdialog_get_reference_rect(taskconfig
, &rect
);
813 if ((taskconfig
->dwFlags
& TDF_SIZE_TO_CONTENT
) && !taskconfig
->cxWidth
)
815 max_width
= DIALOG_MIN_WIDTH
;
816 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
817 main_icon_width
= dialog_info
->m
.h_spacing
;
818 if (dialog_info
->main_icon
) main_icon_width
+= GetSystemMetrics(SM_CXICON
);
819 if (dialog_info
->content
)
821 taskdialog_get_label_size(dialog_info
, dialog_info
->content
, 0, &size
, syslink
);
822 max_width
= max(max_width
, size
.cx
+ main_icon_width
+ dialog_info
->m
.h_spacing
* 2);
827 max_width
= max(taskconfig
->cxWidth
, DIALOG_MIN_WIDTH
);
828 taskdialog_du_to_px(dialog_info
, &max_width
, NULL
);
830 max_width
= min(max_width
, screen_width
);
834 static void taskdialog_label_layout(struct taskdialog_info
*dialog_info
, HWND hwnd
, INT start_x
, LONG dialog_width
,
835 LONG
*dialog_height
, BOOL syslink
)
837 LONG x
, y
, max_width
;
842 x
= start_x
+ dialog_info
->m
.h_spacing
;
843 y
= *dialog_height
+ dialog_info
->m
.v_spacing
;
844 max_width
= dialog_width
- x
- dialog_info
->m
.h_spacing
;
845 taskdialog_get_label_size(dialog_info
, hwnd
, max_width
, &size
, syslink
);
846 SetWindowPos(hwnd
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
847 *dialog_height
= y
+ size
.cy
;
850 static void taskdialog_layout(struct taskdialog_info
*dialog_info
)
852 const TASKDIALOGCONFIG
*taskconfig
= dialog_info
->taskconfig
;
853 BOOL syslink
= taskdialog_hyperlink_enabled(dialog_info
);
854 static BOOL first_time
= TRUE
;
856 LONG dialog_width
, dialog_height
= 0;
857 LONG h_spacing
, v_spacing
;
858 LONG main_icon_right
, main_icon_bottom
;
859 LONG expando_right
, expando_bottom
;
860 struct button_layout_info
*button_layout_infos
;
861 LONG button_min_width
, button_height
;
862 LONG
*line_widths
, line_count
, align
;
863 LONG footer_icon_right
, footer_icon_bottom
;
868 taskdialog_get_reference_rect(dialog_info
->taskconfig
, &ref_rect
);
869 dialog_width
= taskdialog_get_dialog_width(dialog_info
);
871 h_spacing
= dialog_info
->m
.h_spacing
;
872 v_spacing
= dialog_info
->m
.v_spacing
;
876 main_icon_bottom
= 0;
877 if (dialog_info
->main_icon
)
880 y
= dialog_height
+ v_spacing
;
881 size
.cx
= GetSystemMetrics(SM_CXICON
);
882 size
.cy
= GetSystemMetrics(SM_CYICON
);
883 SetWindowPos(dialog_info
->main_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
884 main_icon_right
= x
+ size
.cx
;
885 main_icon_bottom
= y
+ size
.cy
;
888 /* Main instruction */
889 taskdialog_label_layout(dialog_info
, dialog_info
->main_instruction
, main_icon_right
, dialog_width
, &dialog_height
,
893 taskdialog_label_layout(dialog_info
, dialog_info
->content
, main_icon_right
, dialog_width
, &dialog_height
, syslink
);
895 /* Expanded information */
896 if (!(taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
897 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, main_icon_right
, dialog_width
, &dialog_height
,
901 if (dialog_info
->progress_bar
)
903 x
= main_icon_right
+ h_spacing
;
904 y
= dialog_height
+ v_spacing
;
905 size
.cx
= dialog_width
- x
- h_spacing
;
906 size
.cy
= GetSystemMetrics(SM_CYVSCROLL
);
907 SetWindowPos(dialog_info
->progress_bar
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
908 dialog_height
= y
+ size
.cy
;
912 for (i
= 0; i
< dialog_info
->radio_button_count
; i
++)
914 x
= main_icon_right
+ h_spacing
;
915 y
= dialog_height
+ v_spacing
;
916 taskdialog_get_button_size(dialog_info
->radio_buttons
[i
], dialog_width
- x
- h_spacing
, &size
);
917 size
.cx
= dialog_width
- x
- h_spacing
;
918 SetWindowPos(dialog_info
->radio_buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
919 dialog_height
= y
+ size
.cy
;
923 for (i
= 0; i
< dialog_info
->command_link_count
; i
++)
925 x
= main_icon_right
+ h_spacing
;
927 /* Only add spacing for the first command links. There is no vertical spacing between command links */
930 taskdialog_get_button_size(dialog_info
->command_links
[i
], dialog_width
- x
- h_spacing
, &size
);
931 size
.cx
= dialog_width
- x
- h_spacing
;
934 SetWindowPos(dialog_info
->command_links
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
935 dialog_height
= y
+ size
.cy
;
938 dialog_height
= max(dialog_height
, main_icon_bottom
);
941 expando_bottom
= dialog_height
;
942 /* Expando control */
943 if (dialog_info
->expando_button
)
946 y
= dialog_height
+ v_spacing
;
947 taskdialog_get_expando_size(dialog_info
, dialog_info
->expando_button
, &size
);
948 SetWindowPos(dialog_info
->expando_button
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
949 expando_right
= x
+ size
.cx
;
950 expando_bottom
= y
+ size
.cy
;
953 /* Verification box */
954 if (dialog_info
->verification_box
)
957 y
= expando_bottom
+ v_spacing
;
958 size
.cx
= DIALOG_MIN_WIDTH
/ 2;
959 taskdialog_du_to_px(dialog_info
, &size
.cx
, NULL
);
960 taskdialog_get_button_size(dialog_info
->verification_box
, size
.cx
, &size
);
961 SetWindowPos(dialog_info
->verification_box
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
962 expando_right
= max(expando_right
, x
+ size
.cx
);
963 expando_bottom
= y
+ size
.cy
;
966 /* Common and custom buttons */
967 button_layout_infos
= Alloc(dialog_info
->button_count
* sizeof(*button_layout_infos
));
968 line_widths
= Alloc(dialog_info
->button_count
* sizeof(*line_widths
));
970 button_min_width
= DIALOG_BUTTON_WIDTH
;
971 button_height
= DIALOG_BUTTON_HEIGHT
;
972 taskdialog_du_to_px(dialog_info
, &button_min_width
, &button_height
);
973 for (i
= 0; i
< dialog_info
->button_count
; i
++)
975 taskdialog_get_button_size(dialog_info
->buttons
[i
], dialog_width
- expando_right
- h_spacing
* 2, &size
);
976 button_layout_infos
[i
].width
= max(size
.cx
, button_min_width
);
979 /* Separate buttons into lines */
980 x
= expando_right
+ h_spacing
;
981 for (i
= 0, line_count
= 0; i
< dialog_info
->button_count
; i
++)
983 button_layout_infos
[i
].line
= line_count
;
984 x
+= button_layout_infos
[i
].width
+ h_spacing
;
985 line_widths
[line_count
] += button_layout_infos
[i
].width
+ h_spacing
;
987 if ((i
+ 1 < dialog_info
->button_count
) && (x
+ button_layout_infos
[i
+ 1].width
+ h_spacing
>= dialog_width
))
989 x
= expando_right
+ h_spacing
;
995 /* Try to balance lines so they are about the same size */
996 for (i
= 1; i
< line_count
- 1; i
++)
998 int diff_now
= abs(line_widths
[i
] - line_widths
[i
- 1]);
999 unsigned int j
, last_button
= 0;
1002 for (j
= 0; j
< dialog_info
->button_count
; j
++)
1003 if (button_layout_infos
[j
].line
== i
- 1) last_button
= j
;
1005 /* Difference in length of both lines if we wrapped the last button from the last line into this one */
1006 diff_changed
= abs(2 * button_layout_infos
[last_button
].width
+ line_widths
[i
] - line_widths
[i
- 1]);
1008 if (diff_changed
< diff_now
)
1010 button_layout_infos
[last_button
].line
= i
;
1011 line_widths
[i
] += button_layout_infos
[last_button
].width
;
1012 line_widths
[i
- 1] -= button_layout_infos
[last_button
].width
;
1016 /* Calculate left alignment so all lines are as far right as possible. */
1017 align
= dialog_width
- h_spacing
;
1018 for (i
= 0; i
< line_count
; i
++)
1020 int new_alignment
= dialog_width
- line_widths
[i
];
1021 if (new_alignment
< align
) align
= new_alignment
;
1024 /* Now that we got them all positioned, move all buttons */
1026 size
.cy
= button_height
;
1027 for (i
= 0; i
< dialog_info
->button_count
; i
++)
1030 if (i
> 0 && button_layout_infos
[i
].line
!= button_layout_infos
[i
- 1].line
)
1033 dialog_height
+= size
.cy
+ v_spacing
;
1036 y
= dialog_height
+ v_spacing
;
1037 size
.cx
= button_layout_infos
[i
].width
;
1038 SetWindowPos(dialog_info
->buttons
[i
], 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1039 x
+= button_layout_infos
[i
].width
+ h_spacing
;
1042 /* Add height for last row button and spacing */
1043 dialog_height
+= size
.cy
+ v_spacing
;
1044 dialog_height
= max(dialog_height
, expando_bottom
);
1046 Free(button_layout_infos
);
1050 footer_icon_right
= 0;
1051 footer_icon_bottom
= dialog_height
;
1052 if (dialog_info
->footer_icon
)
1055 y
= dialog_height
+ v_spacing
;
1056 size
.cx
= GetSystemMetrics(SM_CXSMICON
);
1057 size
.cy
= GetSystemMetrics(SM_CYSMICON
);
1058 SetWindowPos(dialog_info
->footer_icon
, 0, x
, y
, size
.cx
, size
.cy
, SWP_NOZORDER
);
1059 footer_icon_right
= x
+ size
.cx
;
1060 footer_icon_bottom
= y
+ size
.cy
;
1064 taskdialog_label_layout(dialog_info
, dialog_info
->footer_text
, footer_icon_right
, dialog_width
, &dialog_height
,
1066 dialog_height
= max(dialog_height
, footer_icon_bottom
);
1068 /* Expanded information */
1069 if ((taskconfig
->dwFlags
& TDF_EXPAND_FOOTER_AREA
) && dialog_info
->expanded
)
1070 taskdialog_label_layout(dialog_info
, dialog_info
->expanded_info
, 0, dialog_width
, &dialog_height
, syslink
);
1072 /* Add height for spacing, title height and frame height */
1073 dialog_height
+= v_spacing
;
1074 dialog_height
+= GetSystemMetrics(SM_CYCAPTION
);
1075 dialog_height
+= GetSystemMetrics(SM_CXDLGFRAME
);
1079 x
= (ref_rect
.left
+ ref_rect
.right
- dialog_width
) / 2;
1080 y
= (ref_rect
.top
+ ref_rect
.bottom
- dialog_height
) / 2;
1081 SetWindowPos(dialog_info
->hwnd
, 0, x
, y
, dialog_width
, dialog_height
, SWP_NOZORDER
);
1085 SetWindowPos(dialog_info
->hwnd
, 0, 0, 0, dialog_width
, dialog_height
, SWP_NOMOVE
| SWP_NOZORDER
);
1088 static void taskdialog_draw_expando_control(struct taskdialog_info
*dialog_info
, LPDRAWITEMSTRUCT dis
)
1094 LONG icon_width
, icon_height
;
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 %Ix, lparam %Ix\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
.pszMainIcon
= icon
;
1416 taskconfig
.pszMainInstruction
= main_instruction
;
1417 taskconfig
.pszContent
= content
;
1418 return TaskDialogIndirect(&taskconfig
, button
, NULL
, NULL
);