Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/comctl32_v6/taskdialog.c
8687 views
1
/*
2
* Task dialog control
3
*
4
* Copyright 2017 Fabian Maurer
5
* Copyright 2018 Zhiyi Zhang
6
*
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.
11
*
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.
16
*
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
20
*
21
*/
22
23
#include <stdarg.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "windef.h"
28
#include "winbase.h"
29
#include "wingdi.h"
30
#include "winuser.h"
31
#include "commctrl.h"
32
#include "winerror.h"
33
#include "comctl32.h"
34
35
#include "wine/debug.h"
36
37
WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
38
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;
46
47
static const UINT ID_TIMER = 1;
48
49
struct taskdialog_info
50
{
51
HWND hwnd;
52
const TASKDIALOGCONFIG *taskconfig;
53
DWORD last_timer_tick;
54
HFONT font;
55
HFONT main_instruction_font;
56
/* Control handles */
57
HWND main_icon;
58
HWND main_instruction;
59
HWND content;
60
HWND progress_bar;
61
HWND *radio_buttons;
62
INT radio_button_count;
63
HWND *command_links;
64
INT command_link_count;
65
HWND expanded_info;
66
HWND expando_button;
67
HWND verification_box;
68
HWND footer_icon;
69
HWND footer_text;
70
HWND *buttons;
71
INT button_count;
72
HWND default_button;
73
/* Dialog metrics */
74
struct
75
{
76
LONG x_baseunit;
77
LONG y_baseunit;
78
LONG h_spacing;
79
LONG v_spacing;
80
} m;
81
INT selected_radio_id;
82
BOOL verification_checked;
83
BOOL expanded;
84
BOOL has_cancel;
85
WCHAR *expanded_text;
86
WCHAR *collapsed_text;
87
BOOL had_first_layout;
88
};
89
90
struct button_layout_info
91
{
92
LONG width;
93
LONG line;
94
};
95
96
static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam);
97
static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id);
98
static void taskdialog_layout(struct taskdialog_info *dialog_info);
99
100
static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
101
{
102
if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4);
103
if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8);
104
}
105
106
static void template_write_data(char **ptr, const void *src, unsigned int size)
107
{
108
memcpy(*ptr, src, size);
109
*ptr += size;
110
}
111
112
static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret)
113
{
114
HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(),
115
MONITOR_DEFAULTTOPRIMARY);
116
MONITORINFO info;
117
118
info.cbSize = sizeof(info);
119
GetMonitorInfoW(monitor, &info);
120
121
if ((taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) && taskconfig->hwndParent)
122
GetWindowRect(taskconfig->hwndParent, ret);
123
else
124
*ret = info.rcWork;
125
126
return info.rcWork.right - info.rcWork.left;
127
}
128
129
static WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length)
130
{
131
DWORD len = GetModuleFileNameW(NULL, name, length);
132
if (len && len < length)
133
{
134
WCHAR *p;
135
if ((p = wcsrchr(name, '/'))) name = p + 1;
136
if ((p = wcsrchr(name, '\\'))) name = p + 1;
137
return name;
138
}
139
else
140
return NULL;
141
}
142
143
static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
144
{
145
unsigned int size, title_size;
146
static const WORD fontsize = 0x7fff;
147
const WCHAR *titleW = NULL;
148
DLGTEMPLATE *template;
149
WCHAR pathW[MAX_PATH];
150
char *ptr;
151
152
/* Window title */
153
if (!taskconfig->pszWindowTitle)
154
titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
155
else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
156
{
157
if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
158
titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
159
}
160
else
161
titleW = taskconfig->pszWindowTitle;
162
if (!titleW)
163
titleW = L"";
164
title_size = (lstrlenW(titleW) + 1) * sizeof(WCHAR);
165
166
size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
167
size += title_size;
168
size += 2; /* font size */
169
170
template = Alloc(size);
171
if (!template) return NULL;
172
173
template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
174
if (taskconfig->dwFlags & TDF_CAN_BE_MINIMIZED) template->style |= WS_MINIMIZEBOX;
175
if (!(taskconfig->dwFlags & TDF_NO_SET_FOREGROUND)) template->style |= DS_SETFOREGROUND;
176
if (taskconfig->dwFlags & TDF_RTL_LAYOUT) template->dwExtendedStyle = WS_EX_LAYOUTRTL | WS_EX_RIGHT | WS_EX_RTLREADING;
177
178
ptr = (char *)(template + 1);
179
ptr += 2; /* menu */
180
ptr += 2; /* class */
181
template_write_data(&ptr, titleW, title_size);
182
template_write_data(&ptr, &fontsize, sizeof(fontsize));
183
184
return template;
185
}
186
187
static HWND taskdialog_find_button(HWND *buttons, INT count, INT id)
188
{
189
INT button_id;
190
INT i;
191
192
for (i = 0; i < count; i++)
193
{
194
button_id = GetWindowLongW(buttons[i], GWLP_ID);
195
if (button_id == id) return buttons[i];
196
}
197
198
return NULL;
199
}
200
201
static void taskdialog_enable_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
202
{
203
HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
204
if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
205
if (hwnd) EnableWindow(hwnd, enable);
206
}
207
208
static void taskdialog_click_button(struct taskdialog_info *dialog_info, INT id)
209
{
210
if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, id, 0) == S_OK) EndDialog(dialog_info->hwnd, id);
211
}
212
213
static void taskdialog_button_set_shield(const struct taskdialog_info *dialog_info, INT id, BOOL elevate)
214
{
215
HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
216
if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
217
if (hwnd) SendMessageW(hwnd, BCM_SETSHIELD, 0, elevate);
218
}
219
220
static void taskdialog_enable_radio_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
221
{
222
HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
223
if (hwnd) EnableWindow(hwnd, enable);
224
}
225
226
static void taskdialog_click_radio_button(const struct taskdialog_info *dialog_info, INT id)
227
{
228
HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
229
if (hwnd) SendMessageW(hwnd, BM_CLICK, 0, 0);
230
}
231
232
static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
233
{
234
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
235
return taskconfig->pfCallback
236
? taskconfig->pfCallback(dialog_info->hwnd, notification, wparam, lparam, taskconfig->lpCallbackData)
237
: S_OK;
238
}
239
240
static void taskdialog_move_controls_vertically(HWND parent, HWND *controls, INT count, INT offset)
241
{
242
RECT rect;
243
POINT pt;
244
INT i;
245
246
for (i = 0; i < count; i++)
247
{
248
if (!controls[i]) continue;
249
250
GetWindowRect(controls[i], &rect);
251
pt.x = rect.left;
252
pt.y = rect.top;
253
MapWindowPoints(HWND_DESKTOP, parent, &pt, 1);
254
SetWindowPos(controls[i], 0, pt.x, pt.y + offset, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
255
}
256
}
257
258
static void taskdialog_toggle_expando_control(struct taskdialog_info *dialog_info)
259
{
260
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
261
const WCHAR *text;
262
RECT info_rect, rect;
263
INT height, offset;
264
265
dialog_info->expanded = !dialog_info->expanded;
266
text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
267
SendMessageW(dialog_info->expando_button, WM_SETTEXT, 0, (LPARAM)text);
268
ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
269
270
GetWindowRect(dialog_info->expanded_info, &info_rect);
271
/* If expanded information starts up not expanded, call taskdialog_layout()
272
* to to set size for expanded information control at least once */
273
if (IsRectEmpty(&info_rect))
274
{
275
taskdialog_layout(dialog_info);
276
return;
277
}
278
height = info_rect.bottom - info_rect.top + dialog_info->m.v_spacing;
279
offset = dialog_info->expanded ? height : -height;
280
281
/* Update vertical layout, move all controls after expanded information */
282
/* Move dialog */
283
GetWindowRect(dialog_info->hwnd, &rect);
284
SetWindowPos(dialog_info->hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top + offset,
285
SWP_NOMOVE | SWP_NOZORDER);
286
/* Move controls */
287
if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA))
288
{
289
taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->progress_bar, 1, offset);
290
taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->expando_button, 1, offset);
291
taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->verification_box, 1, offset);
292
taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_icon, 1, offset);
293
taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_text, 1, offset);
294
taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->buttons, dialog_info->button_count, offset);
295
taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->radio_buttons,
296
dialog_info->radio_button_count, offset);
297
taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->command_links,
298
dialog_info->command_link_count, offset);
299
}
300
}
301
302
static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id)
303
{
304
INT command_id;
305
HWND button, radio_button;
306
307
/* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
308
command_id = hwnd ? GetWindowLongW(hwnd, GWLP_ID) : id;
309
310
if (hwnd && hwnd == dialog_info->expando_button)
311
{
312
taskdialog_toggle_expando_control(dialog_info);
313
taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
314
return;
315
}
316
317
if (hwnd && hwnd == dialog_info->verification_box)
318
{
319
dialog_info->verification_checked = !dialog_info->verification_checked;
320
taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
321
return;
322
}
323
324
radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
325
if (radio_button)
326
{
327
dialog_info->selected_radio_id = command_id;
328
taskdialog_notify(dialog_info, TDN_RADIO_BUTTON_CLICKED, command_id, 0);
329
return;
330
}
331
332
button = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, command_id);
333
if (!button) button = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, command_id);
334
if (!button && command_id == IDOK)
335
{
336
button = dialog_info->command_link_count > 0 ? dialog_info->command_links[0] : dialog_info->buttons[0];
337
command_id = GetWindowLongW(button, GWLP_ID);
338
}
339
340
if (button && taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
341
EndDialog(dialog_info->hwnd, command_id);
342
}
343
344
static WCHAR *taskdialog_gettext(struct taskdialog_info *dialog_info, BOOL user_resource, const WCHAR *text)
345
{
346
const WCHAR *textW = NULL;
347
INT length;
348
WCHAR *ret;
349
350
if (IS_INTRESOURCE(text))
351
{
352
if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule,
353
(UINT_PTR)text, (WCHAR *)&textW, 0)))
354
return NULL;
355
}
356
else
357
{
358
textW = text;
359
length = lstrlenW(textW);
360
}
361
362
ret = Alloc((length + 1) * sizeof(WCHAR));
363
if (ret) memcpy(ret, textW, length * sizeof(WCHAR));
364
365
return ret;
366
}
367
368
static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info)
369
{
370
return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS;
371
}
372
373
static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info)
374
{
375
return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON);
376
}
377
378
static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size,
379
BOOL syslink)
380
{
381
DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
382
HFONT hfont, old_hfont;
383
HDC hdc;
384
RECT rect = {0};
385
WCHAR *text;
386
INT text_length;
387
388
if (syslink)
389
{
390
SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size);
391
return;
392
}
393
394
if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
395
style |= DT_RIGHT | DT_RTLREADING;
396
else
397
style |= DT_LEFT;
398
399
hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
400
text_length = GetWindowTextLengthW(hwnd);
401
text = Alloc((text_length + 1) * sizeof(WCHAR));
402
if (!text)
403
{
404
size->cx = 0;
405
size->cy = 0;
406
return;
407
}
408
GetWindowTextW(hwnd, text, text_length + 1);
409
hdc = GetDC(hwnd);
410
old_hfont = SelectObject(hdc, hfont);
411
rect.right = max_width;
412
size->cy = DrawTextW(hdc, text, text_length, &rect, style);
413
size->cx = min(max_width, rect.right - rect.left);
414
if (old_hfont) SelectObject(hdc, old_hfont);
415
ReleaseDC(hwnd, hdc);
416
Free(text);
417
}
418
419
static void taskdialog_get_button_size(const struct taskdialog_info *dialog_info, HWND hwnd,
420
LONG max_width, SIZE *size)
421
{
422
size->cx = max_width;
423
size->cy = 0;
424
SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)size);
425
size->cx = min(max_width, size->cx + dialog_info->m.h_spacing * 4);
426
}
427
428
static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
429
{
430
DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
431
HFONT hfont, old_hfont;
432
HDC hdc;
433
RECT rect = {0};
434
LONG icon_width, icon_height;
435
INT text_offset;
436
LONG max_width, max_text_height;
437
438
hdc = GetDC(hwnd);
439
hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
440
old_hfont = SelectObject(hdc, hfont);
441
442
icon_width = DIALOG_EXPANDO_ICON_WIDTH;
443
icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
444
taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
445
446
GetCharWidthW(hdc, '0', '0', &text_offset);
447
text_offset /= 2;
448
449
if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
450
style |= DT_RIGHT | DT_RTLREADING;
451
else
452
style |= DT_LEFT;
453
454
max_width = DIALOG_MIN_WIDTH / 2;
455
taskdialog_du_to_px(dialog_info, &max_width, NULL);
456
457
rect.right = max_width - icon_width - text_offset;
458
max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
459
size->cy = max(max_text_height, icon_height);
460
size->cx = rect.right - rect.left;
461
462
rect.right = max_width - icon_width - text_offset;
463
max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
464
size->cy = max(size->cy, max_text_height);
465
size->cx = max(size->cx, rect.right - rect.left);
466
467
size->cx += icon_width + text_offset;
468
size->cx = min(size->cx, max_width);
469
470
if (old_hfont) SelectObject(hdc, old_hfont);
471
ReleaseDC(hwnd, hdc);
472
}
473
474
static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
475
{
476
if (icon == TD_WARNING_ICON)
477
return IDI_WARNING;
478
else if (icon == TD_ERROR_ICON)
479
return IDI_ERROR;
480
else if (icon == TD_INFORMATION_ICON)
481
return IDI_INFORMATION;
482
else if (icon == TD_SHIELD_ICON)
483
return IDI_SHIELD;
484
else
485
return (ULONG_PTR)icon;
486
}
487
488
static void taskdialog_set_icon(struct taskdialog_info *dialog_info, INT element, HICON icon)
489
{
490
DWORD flags = dialog_info->taskconfig->dwFlags;
491
INT cx = 0, cy = 0;
492
HICON hicon;
493
494
if (!icon) return;
495
496
if (((flags & TDF_USE_HICON_MAIN) && element == TDIE_ICON_MAIN)
497
|| ((flags & TDF_USE_HICON_FOOTER) && element == TDIE_ICON_FOOTER))
498
hicon = icon;
499
else
500
{
501
if (element == TDIE_ICON_FOOTER)
502
{
503
cx = GetSystemMetrics(SM_CXSMICON);
504
cy = GetSystemMetrics(SM_CYSMICON);
505
}
506
hicon = LoadImageW(dialog_info->taskconfig->hInstance, (LPCWSTR)icon, IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTSIZE);
507
if (!hicon)
508
hicon = LoadImageW(NULL, (LPCWSTR)taskdialog_get_standard_icon((LPCWSTR)icon), IMAGE_ICON, cx, cy,
509
LR_SHARED | LR_DEFAULTSIZE);
510
}
511
512
if (!hicon) return;
513
514
if (element == TDIE_ICON_MAIN)
515
{
516
SendMessageW(dialog_info->hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hicon);
517
SendMessageW(dialog_info->main_icon, STM_SETICON, (WPARAM)hicon, 0);
518
}
519
else if (element == TDIE_ICON_FOOTER)
520
SendMessageW(dialog_info->footer_icon, STM_SETICON, (WPARAM)hicon, 0);
521
}
522
523
static void taskdialog_set_element_text(struct taskdialog_info *dialog_info, TASKDIALOG_ELEMENTS element,
524
const WCHAR *text)
525
{
526
HWND hwnd = NULL;
527
WCHAR *textW;
528
529
if (element == TDE_CONTENT)
530
hwnd = dialog_info->content;
531
else if (element == TDE_EXPANDED_INFORMATION)
532
hwnd = dialog_info->expanded_info;
533
else if (element == TDE_FOOTER)
534
hwnd = dialog_info->footer_text;
535
else if (element == TDE_MAIN_INSTRUCTION)
536
hwnd = dialog_info->main_instruction;
537
538
if (!hwnd) return;
539
540
textW = taskdialog_gettext(dialog_info, TRUE, text);
541
SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
542
Free(textW);
543
}
544
545
static void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info)
546
{
547
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
548
HWND default_button;
549
550
if (!dialog_info->radio_button_count) return;
551
552
default_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count,
553
taskconfig->nDefaultRadioButton);
554
555
if (!default_button && !(taskconfig->dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON))
556
default_button = dialog_info->radio_buttons[0];
557
558
if (default_button)
559
{
560
SendMessageW(default_button, BM_SETCHECK, BST_CHECKED, 0);
561
taskdialog_on_button_click(dialog_info, default_button, 0);
562
}
563
}
564
565
static void taskdialog_add_main_icon(struct taskdialog_info *dialog_info)
566
{
567
if (!dialog_info->taskconfig->hMainIcon) return;
568
569
dialog_info->main_icon =
570
CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
571
taskdialog_set_icon(dialog_info, TDIE_ICON_MAIN, dialog_info->taskconfig->hMainIcon);
572
}
573
574
static HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink)
575
{
576
WCHAR *textW;
577
HWND hwnd;
578
const WCHAR *class;
579
DWORD style = WS_CHILD | WS_VISIBLE;
580
581
if (!text) return NULL;
582
583
class = syslink ? WC_LINK : WC_STATICW;
584
if (syslink) style |= WS_TABSTOP;
585
textW = taskdialog_gettext(dialog_info, TRUE, text);
586
hwnd = CreateWindowW(class, textW, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
587
Free(textW);
588
589
SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0);
590
return hwnd;
591
}
592
593
static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info)
594
{
595
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
596
NONCLIENTMETRICSW ncm;
597
598
if (!taskconfig->pszMainInstruction) return;
599
600
ncm.cbSize = sizeof(ncm);
601
SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
602
/* 1.25 times the height */
603
ncm.lfMessageFont.lfHeight = ncm.lfMessageFont.lfHeight * 5 / 4;
604
ncm.lfMessageFont.lfWeight = FW_BOLD;
605
dialog_info->main_instruction_font = CreateFontIndirectW(&ncm.lfMessageFont);
606
607
dialog_info->main_instruction =
608
taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE);
609
}
610
611
static void taskdialog_add_content(struct taskdialog_info *dialog_info)
612
{
613
dialog_info->content = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszContent, dialog_info->font,
614
taskdialog_hyperlink_enabled(dialog_info));
615
}
616
617
static void taskdialog_add_progress_bar(struct taskdialog_info *dialog_info)
618
{
619
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
620
DWORD style = PBS_SMOOTH | PBS_SMOOTHREVERSE | WS_CHILD | WS_VISIBLE;
621
622
if (!(taskconfig->dwFlags & (TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR))) return;
623
if (taskconfig->dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) style |= PBS_MARQUEE;
624
dialog_info->progress_bar =
625
CreateWindowW(PROGRESS_CLASSW, NULL, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
626
}
627
628
static void taskdialog_add_radio_buttons(struct taskdialog_info *dialog_info)
629
{
630
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
631
static const DWORD style = BS_AUTORADIOBUTTON | BS_MULTILINE | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
632
WCHAR *textW;
633
INT i;
634
635
if (!taskconfig->cRadioButtons || !taskconfig->pRadioButtons) return;
636
637
dialog_info->radio_buttons = Alloc(taskconfig->cRadioButtons * sizeof(*dialog_info->radio_buttons));
638
if (!dialog_info->radio_buttons) return;
639
640
dialog_info->radio_button_count = taskconfig->cRadioButtons;
641
for (i = 0; i < dialog_info->radio_button_count; i++)
642
{
643
textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pRadioButtons[i].pszButtonText);
644
dialog_info->radio_buttons[i] =
645
CreateWindowW(WC_BUTTONW, textW, i == 0 ? style | WS_GROUP : style, 0, 0, 0, 0, dialog_info->hwnd,
646
LongToHandle(taskconfig->pRadioButtons[i].nButtonID), 0, NULL);
647
SendMessageW(dialog_info->radio_buttons[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
648
Free(textW);
649
}
650
}
651
652
static void taskdialog_add_command_links(struct taskdialog_info *dialog_info)
653
{
654
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
655
DWORD default_style = BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP, style;
656
BOOL is_default;
657
WCHAR *textW;
658
INT i;
659
660
if (!taskconfig->cButtons || !taskconfig->pButtons || !taskdialog_use_command_link(dialog_info)) return;
661
662
dialog_info->command_links = Alloc(taskconfig->cButtons * sizeof(*dialog_info->command_links));
663
if (!dialog_info->command_links) return;
664
665
dialog_info->command_link_count = taskconfig->cButtons;
666
for (i = 0; i < dialog_info->command_link_count; i++)
667
{
668
is_default = taskconfig->pButtons[i].nButtonID == taskconfig->nDefaultButton;
669
style = is_default ? default_style | BS_DEFCOMMANDLINK : default_style | BS_COMMANDLINK;
670
textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pButtons[i].pszButtonText);
671
dialog_info->command_links[i] = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd,
672
LongToHandle(taskconfig->pButtons[i].nButtonID), 0, NULL);
673
SendMessageW(dialog_info->command_links[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
674
Free(textW);
675
676
if (is_default && !dialog_info->default_button) dialog_info->default_button = dialog_info->command_links[i];
677
}
678
}
679
680
static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
681
{
682
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
683
684
if (!taskconfig->pszExpandedInformation) return;
685
686
dialog_info->expanded = taskconfig->dwFlags & TDF_EXPANDED_BY_DEFAULT;
687
dialog_info->expanded_info = taskdialog_create_label(dialog_info, taskconfig->pszExpandedInformation,
688
dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
689
ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
690
}
691
692
static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
693
{
694
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
695
const WCHAR *textW;
696
697
if (!taskconfig->pszExpandedInformation) return;
698
699
if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
700
{
701
dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
702
dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
703
}
704
else
705
{
706
textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
707
: taskconfig->pszCollapsedControlText;
708
dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
709
textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
710
: taskconfig->pszExpandedControlText;
711
dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
712
}
713
714
textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
715
716
dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
717
0, 0, 0, dialog_info->hwnd, 0, 0, 0);
718
SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
719
}
720
721
static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
722
{
723
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
724
static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
725
WCHAR *textW;
726
727
/* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
728
if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
729
730
if (!taskconfig->pszVerificationText) return;
731
732
textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
733
dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
734
SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
735
Free(textW);
736
737
if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
738
SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
739
}
740
741
static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
742
BOOL custom_button)
743
{
744
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
745
WCHAR *textW;
746
747
textW = taskdialog_gettext(dialog_info, custom_button, text);
748
*button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, dialog_info->hwnd,
749
(HMENU)id, 0, NULL);
750
Free(textW);
751
SendMessageW(*button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
752
753
if (id == taskconfig->nDefaultButton && !dialog_info->default_button) dialog_info->default_button = *button;
754
}
755
756
static void taskdialog_add_buttons(struct taskdialog_info *dialog_info)
757
{
758
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
759
BOOL use_command_links = taskdialog_use_command_link(dialog_info);
760
DWORD flags = taskconfig->dwCommonButtons;
761
INT count, max_count;
762
763
/* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
764
max_count = 6;
765
if (!use_command_links && taskconfig->cButtons && taskconfig->pButtons) max_count += taskconfig->cButtons;
766
767
dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons));
768
if (!dialog_info->buttons) return;
769
770
for (count = 0; !use_command_links && count < taskconfig->cButtons; count++)
771
taskdialog_add_button(dialog_info, &dialog_info->buttons[count], taskconfig->pButtons[count].nButtonID,
772
taskconfig->pButtons[count].pszButtonText, TRUE);
773
774
#define TASKDIALOG_INIT_COMMON_BUTTON(id) \
775
do \
776
{ \
777
taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
778
FALSE); \
779
} while (0)
780
781
if (flags & TDCBF_OK_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(OK);
782
if (flags & TDCBF_YES_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(YES);
783
if (flags & TDCBF_NO_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(NO);
784
if (flags & TDCBF_RETRY_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
785
if (flags & TDCBF_CANCEL_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
786
if (flags & TDCBF_CLOSE_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
787
788
if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK);
789
#undef TASKDIALOG_INIT_COMMON_BUTTON
790
791
dialog_info->button_count = count;
792
}
793
794
static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info)
795
{
796
if (!dialog_info->taskconfig->hFooterIcon) return;
797
798
dialog_info->footer_icon =
799
CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, 0);
800
taskdialog_set_icon(dialog_info, TDIE_ICON_FOOTER, dialog_info->taskconfig->hFooterIcon);
801
}
802
803
static void taskdialog_add_footer_text(struct taskdialog_info *dialog_info)
804
{
805
dialog_info->footer_text = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszFooter,
806
dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
807
}
808
809
static LONG taskdialog_get_dialog_width(struct taskdialog_info *dialog_info)
810
{
811
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
812
BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
813
LONG max_width, main_icon_width, screen_width;
814
RECT rect;
815
SIZE size;
816
817
screen_width = taskdialog_get_reference_rect(taskconfig, &rect);
818
if ((taskconfig->dwFlags & TDF_SIZE_TO_CONTENT) && !taskconfig->cxWidth)
819
{
820
max_width = DIALOG_MIN_WIDTH;
821
taskdialog_du_to_px(dialog_info, &max_width, NULL);
822
main_icon_width = dialog_info->m.h_spacing;
823
if (dialog_info->main_icon) main_icon_width += GetSystemMetrics(SM_CXICON);
824
if (dialog_info->content)
825
{
826
taskdialog_get_label_size(dialog_info, dialog_info->content, 0, &size, syslink);
827
max_width = max(max_width, size.cx + main_icon_width + dialog_info->m.h_spacing * 2);
828
}
829
}
830
else
831
{
832
max_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
833
taskdialog_du_to_px(dialog_info, &max_width, NULL);
834
}
835
max_width = min(max_width, screen_width);
836
return max_width;
837
}
838
839
static void taskdialog_label_layout(struct taskdialog_info *dialog_info, HWND hwnd, INT start_x, LONG dialog_width,
840
LONG *dialog_height, BOOL syslink)
841
{
842
LONG x, y, max_width;
843
SIZE size;
844
845
if (!hwnd) return;
846
847
x = start_x + dialog_info->m.h_spacing;
848
y = *dialog_height + dialog_info->m.v_spacing;
849
max_width = dialog_width - x - dialog_info->m.h_spacing;
850
taskdialog_get_label_size(dialog_info, hwnd, max_width, &size, syslink);
851
SetWindowPos(hwnd, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
852
*dialog_height = y + size.cy;
853
}
854
855
static void taskdialog_layout(struct taskdialog_info *dialog_info)
856
{
857
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
858
BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
859
RECT ref_rect;
860
LONG dialog_width, dialog_height = 0;
861
LONG h_spacing, v_spacing;
862
LONG main_icon_right, main_icon_bottom;
863
LONG expando_right, expando_bottom;
864
struct button_layout_info *button_layout_infos;
865
LONG button_min_width, button_height;
866
LONG *line_widths, line_count, align;
867
LONG footer_icon_right, footer_icon_bottom;
868
LONG x, y;
869
SIZE size;
870
INT i;
871
872
taskdialog_get_reference_rect(dialog_info->taskconfig, &ref_rect);
873
dialog_width = taskdialog_get_dialog_width(dialog_info);
874
875
h_spacing = dialog_info->m.h_spacing;
876
v_spacing = dialog_info->m.v_spacing;
877
878
/* Main icon */
879
main_icon_right = 0;
880
main_icon_bottom = 0;
881
if (dialog_info->main_icon)
882
{
883
x = h_spacing;
884
y = dialog_height + v_spacing;
885
size.cx = GetSystemMetrics(SM_CXICON);
886
size.cy = GetSystemMetrics(SM_CYICON);
887
SetWindowPos(dialog_info->main_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
888
main_icon_right = x + size.cx;
889
main_icon_bottom = y + size.cy;
890
}
891
892
/* Main instruction */
893
taskdialog_label_layout(dialog_info, dialog_info->main_instruction, main_icon_right, dialog_width, &dialog_height,
894
FALSE);
895
896
/* Content */
897
taskdialog_label_layout(dialog_info, dialog_info->content, main_icon_right, dialog_width, &dialog_height, syslink);
898
899
/* Expanded information */
900
if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
901
taskdialog_label_layout(dialog_info, dialog_info->expanded_info, main_icon_right, dialog_width, &dialog_height,
902
syslink);
903
904
/* Progress bar */
905
if (dialog_info->progress_bar)
906
{
907
x = main_icon_right + h_spacing;
908
y = dialog_height + v_spacing;
909
size.cx = dialog_width - x - h_spacing;
910
size.cy = GetSystemMetrics(SM_CYVSCROLL);
911
SetWindowPos(dialog_info->progress_bar, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
912
dialog_height = y + size.cy;
913
}
914
915
/* Radio buttons */
916
for (i = 0; i < dialog_info->radio_button_count; i++)
917
{
918
x = main_icon_right + h_spacing;
919
y = dialog_height + v_spacing;
920
taskdialog_get_button_size(dialog_info, dialog_info->radio_buttons[i], dialog_width - x - h_spacing, &size);
921
size.cx = dialog_width - x - h_spacing;
922
SetWindowPos(dialog_info->radio_buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
923
dialog_height = y + size.cy;
924
}
925
926
/* Command links */
927
for (i = 0; i < dialog_info->command_link_count; i++)
928
{
929
x = main_icon_right + h_spacing;
930
y = dialog_height;
931
/* Only add spacing for the first command links. There is no vertical spacing between command links */
932
if (!i)
933
y += v_spacing;
934
taskdialog_get_button_size(dialog_info, dialog_info->command_links[i], dialog_width - x - h_spacing, &size);
935
size.cx = dialog_width - x - h_spacing;
936
/* Add spacing */
937
size.cy += 4;
938
SetWindowPos(dialog_info->command_links[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
939
dialog_height = y + size.cy;
940
}
941
942
dialog_height = max(dialog_height, main_icon_bottom);
943
944
expando_right = 0;
945
expando_bottom = dialog_height;
946
/* Expando control */
947
if (dialog_info->expando_button)
948
{
949
x = h_spacing;
950
y = dialog_height + v_spacing;
951
taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
952
SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
953
expando_right = x + size.cx;
954
expando_bottom = y + size.cy;
955
}
956
957
/* Verification box */
958
if (dialog_info->verification_box)
959
{
960
x = h_spacing;
961
y = expando_bottom + v_spacing;
962
size.cx = DIALOG_MIN_WIDTH / 2;
963
taskdialog_du_to_px(dialog_info, &size.cx, NULL);
964
taskdialog_get_button_size(dialog_info, dialog_info->verification_box, size.cx, &size);
965
SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
966
expando_right = max(expando_right, x + size.cx);
967
expando_bottom = y + size.cy;
968
}
969
970
/* Common and custom buttons */
971
button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
972
line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
973
974
button_min_width = DIALOG_BUTTON_WIDTH;
975
button_height = DIALOG_BUTTON_HEIGHT;
976
taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
977
for (i = 0; i < dialog_info->button_count; i++)
978
{
979
taskdialog_get_button_size(dialog_info, dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2, &size);
980
button_layout_infos[i].width = max(size.cx, button_min_width);
981
}
982
983
/* Separate buttons into lines */
984
x = expando_right + h_spacing;
985
for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
986
{
987
button_layout_infos[i].line = line_count;
988
x += button_layout_infos[i].width + h_spacing;
989
line_widths[line_count] += button_layout_infos[i].width + h_spacing;
990
991
if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
992
{
993
x = expando_right + h_spacing;
994
line_count++;
995
}
996
}
997
998
if (dialog_info->button_count > 0)
999
line_count++;
1000
1001
/* Try to balance lines so they are about the same size */
1002
for (i = 1; i < line_count - 1; i++)
1003
{
1004
int diff_now = abs(line_widths[i] - line_widths[i - 1]);
1005
unsigned int j, last_button = 0;
1006
int diff_changed;
1007
1008
for (j = 0; j < dialog_info->button_count; j++)
1009
if (button_layout_infos[j].line == i - 1) last_button = j;
1010
1011
/* Difference in length of both lines if we wrapped the last button from the last line into this one */
1012
diff_changed = abs(2 * button_layout_infos[last_button].width + line_widths[i] - line_widths[i - 1]);
1013
1014
if (diff_changed < diff_now)
1015
{
1016
button_layout_infos[last_button].line = i;
1017
line_widths[i] += button_layout_infos[last_button].width;
1018
line_widths[i - 1] -= button_layout_infos[last_button].width;
1019
}
1020
}
1021
1022
/* Calculate left alignment so all lines are as far right as possible. */
1023
align = dialog_width - h_spacing;
1024
for (i = 0; i < line_count; i++)
1025
{
1026
int new_alignment = dialog_width - line_widths[i];
1027
if (new_alignment < align) align = new_alignment;
1028
}
1029
1030
/* Now that we got them all positioned, move all buttons */
1031
x = align;
1032
size.cy = button_height;
1033
for (i = 0; i < dialog_info->button_count; i++)
1034
{
1035
/* New line */
1036
if (i > 0 && button_layout_infos[i].line != button_layout_infos[i - 1].line)
1037
{
1038
x = align;
1039
dialog_height += size.cy + v_spacing;
1040
}
1041
1042
y = dialog_height + v_spacing;
1043
size.cx = button_layout_infos[i].width;
1044
SetWindowPos(dialog_info->buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1045
x += button_layout_infos[i].width + h_spacing;
1046
}
1047
1048
/* Add height for last row button and spacing */
1049
if (dialog_info->button_count > 0)
1050
dialog_height += size.cy + v_spacing;
1051
dialog_height = max(dialog_height, expando_bottom);
1052
1053
Free(button_layout_infos);
1054
Free(line_widths);
1055
1056
/* Footer icon */
1057
footer_icon_right = 0;
1058
footer_icon_bottom = dialog_height;
1059
if (dialog_info->footer_icon)
1060
{
1061
x = h_spacing;
1062
y = dialog_height + v_spacing;
1063
size.cx = GetSystemMetrics(SM_CXSMICON);
1064
size.cy = GetSystemMetrics(SM_CYSMICON);
1065
SetWindowPos(dialog_info->footer_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
1066
footer_icon_right = x + size.cx;
1067
footer_icon_bottom = y + size.cy;
1068
}
1069
1070
/* Footer text */
1071
taskdialog_label_layout(dialog_info, dialog_info->footer_text, footer_icon_right, dialog_width, &dialog_height,
1072
syslink);
1073
dialog_height = max(dialog_height, footer_icon_bottom);
1074
1075
/* Expanded information */
1076
if ((taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
1077
taskdialog_label_layout(dialog_info, dialog_info->expanded_info, 0, dialog_width, &dialog_height, syslink);
1078
1079
/* Add height for spacing, title height and frame height */
1080
dialog_height += v_spacing;
1081
dialog_height += GetSystemMetrics(SM_CYCAPTION);
1082
dialog_height += GetSystemMetrics(SM_CXDLGFRAME);
1083
1084
if (!dialog_info->had_first_layout)
1085
{
1086
x = (ref_rect.left + ref_rect.right - dialog_width) / 2;
1087
y = (ref_rect.top + ref_rect.bottom - dialog_height) / 2;
1088
SetWindowPos(dialog_info->hwnd, 0, x, y, dialog_width, dialog_height, SWP_NOZORDER);
1089
dialog_info->had_first_layout = TRUE;
1090
}
1091
else
1092
SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
1093
}
1094
1095
static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
1096
{
1097
HWND hwnd;
1098
HDC hdc;
1099
RECT rect = {0};
1100
WCHAR *text;
1101
LONG icon_width, icon_height;
1102
INT text_offset;
1103
UINT style = DFCS_FLAT;
1104
BOOL draw_focus;
1105
1106
hdc = dis->hDC;
1107
hwnd = dis->hwndItem;
1108
1109
SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
1110
1111
icon_width = DIALOG_EXPANDO_ICON_WIDTH;
1112
icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
1113
taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
1114
rect.right = icon_width;
1115
rect.bottom = icon_height;
1116
style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
1117
DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
1118
1119
GetCharWidthW(hdc, '0', '0', &text_offset);
1120
text_offset /= 2;
1121
1122
rect = dis->rcItem;
1123
rect.left += icon_width + text_offset;
1124
text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
1125
DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
1126
1127
draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
1128
if(draw_focus) DrawFocusRect(hdc, &rect);
1129
}
1130
1131
static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
1132
{
1133
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
1134
NONCLIENTMETRICSW ncm;
1135
HDC hdc;
1136
INT id;
1137
1138
ncm.cbSize = sizeof(ncm);
1139
SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
1140
1141
memset(dialog_info, 0, sizeof(*dialog_info));
1142
dialog_info->taskconfig = taskconfig;
1143
dialog_info->hwnd = hwnd;
1144
dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont);
1145
1146
hdc = GetDC(dialog_info->hwnd);
1147
SelectObject(hdc, dialog_info->font);
1148
dialog_info->m.x_baseunit = GdiGetCharDimensions(hdc, NULL, &dialog_info->m.y_baseunit);
1149
ReleaseDC(dialog_info->hwnd, hdc);
1150
1151
dialog_info->m.h_spacing = DIALOG_SPACING;
1152
dialog_info->m.v_spacing = DIALOG_SPACING;
1153
taskdialog_du_to_px(dialog_info, &dialog_info->m.h_spacing, &dialog_info->m.v_spacing);
1154
1155
if (taskconfig->dwFlags & TDF_CALLBACK_TIMER)
1156
{
1157
SetTimer(hwnd, ID_TIMER, DIALOG_TIMER_MS, NULL);
1158
dialog_info->last_timer_tick = GetTickCount();
1159
}
1160
1161
taskdialog_add_main_icon(dialog_info);
1162
taskdialog_add_main_instruction(dialog_info);
1163
taskdialog_add_content(dialog_info);
1164
taskdialog_add_expanded_info(dialog_info);
1165
taskdialog_add_progress_bar(dialog_info);
1166
taskdialog_add_radio_buttons(dialog_info);
1167
taskdialog_add_command_links(dialog_info);
1168
taskdialog_add_expando_button(dialog_info);
1169
taskdialog_add_verification_box(dialog_info);
1170
taskdialog_add_buttons(dialog_info);
1171
taskdialog_add_footer_icon(dialog_info);
1172
taskdialog_add_footer_text(dialog_info);
1173
1174
/* Set default button */
1175
if (!dialog_info->default_button && dialog_info->command_links)
1176
dialog_info->default_button = dialog_info->command_links[0];
1177
if (!dialog_info->default_button) dialog_info->default_button = dialog_info->buttons[0];
1178
SendMessageW(dialog_info->hwnd, WM_NEXTDLGCTL, (WPARAM)dialog_info->default_button, TRUE);
1179
id = GetWindowLongW(dialog_info->default_button, GWLP_ID);
1180
SendMessageW(dialog_info->hwnd, DM_SETDEFID, id, 0);
1181
1182
dialog_info->has_cancel =
1183
(taskconfig->dwFlags & TDF_ALLOW_DIALOG_CANCELLATION)
1184
|| taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, IDCANCEL)
1185
|| taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, IDCANCEL);
1186
1187
if (!dialog_info->has_cancel) DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
1188
1189
taskdialog_layout(dialog_info);
1190
}
1191
1192
static BOOL CALLBACK takdialog_destroy_control(HWND hwnd, LPARAM lParam)
1193
{
1194
DestroyWindow(hwnd);
1195
return TRUE;
1196
}
1197
1198
static void taskdialog_destroy(struct taskdialog_info *dialog_info)
1199
{
1200
EnumChildWindows(dialog_info->hwnd, takdialog_destroy_control, 0);
1201
1202
if (dialog_info->taskconfig->dwFlags & TDF_CALLBACK_TIMER) KillTimer(dialog_info->hwnd, ID_TIMER);
1203
if (dialog_info->font) DeleteObject(dialog_info->font);
1204
if (dialog_info->main_instruction_font) DeleteObject(dialog_info->main_instruction_font);
1205
Free(dialog_info->buttons);
1206
Free(dialog_info->radio_buttons);
1207
Free(dialog_info->command_links);
1208
Free(dialog_info->expanded_text);
1209
Free(dialog_info->collapsed_text);
1210
}
1211
1212
static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1213
{
1214
static const WCHAR taskdialog_info_propnameW[] = L"TaskDialogInfo";
1215
struct taskdialog_info *dialog_info;
1216
LRESULT result;
1217
1218
TRACE("hwnd %p, msg 0x%04x, wparam %Ix, lparam %Ix\n", hwnd, msg, wParam, lParam);
1219
1220
if (msg != WM_INITDIALOG)
1221
dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
1222
1223
switch (msg)
1224
{
1225
case TDM_NAVIGATE_PAGE:
1226
dialog_info->taskconfig = (const TASKDIALOGCONFIG *)lParam;
1227
taskdialog_destroy(dialog_info);
1228
taskdialog_init(dialog_info, hwnd);
1229
taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1230
/* Default radio button click notification is sent before TDN_NAVIGATED */
1231
taskdialog_check_default_radio_buttons(dialog_info);
1232
taskdialog_notify(dialog_info, TDN_NAVIGATED, 0, 0);
1233
break;
1234
case TDM_CLICK_BUTTON:
1235
taskdialog_click_button(dialog_info, wParam);
1236
break;
1237
case TDM_ENABLE_BUTTON:
1238
taskdialog_enable_button(dialog_info, wParam, lParam);
1239
break;
1240
case TDM_SET_MARQUEE_PROGRESS_BAR:
1241
{
1242
BOOL marquee = wParam;
1243
LONG style;
1244
if(!dialog_info->progress_bar) break;
1245
style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1246
style = marquee ? style | PBS_MARQUEE : style & (~PBS_MARQUEE);
1247
SetWindowLongW(dialog_info->progress_bar, GWL_STYLE, style);
1248
break;
1249
}
1250
case TDM_SET_PROGRESS_BAR_STATE:
1251
result = SendMessageW(dialog_info->progress_bar, PBM_SETSTATE, wParam, 0);
1252
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1253
break;
1254
case TDM_SET_PROGRESS_BAR_RANGE:
1255
result = SendMessageW(dialog_info->progress_bar, PBM_SETRANGE, 0, lParam);
1256
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1257
break;
1258
case TDM_SET_PROGRESS_BAR_POS:
1259
result = 0;
1260
if (dialog_info->progress_bar)
1261
{
1262
LONG style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
1263
if (!(style & PBS_MARQUEE)) result = SendMessageW(dialog_info->progress_bar, PBM_SETPOS, wParam, 0);
1264
}
1265
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
1266
break;
1267
case TDM_SET_PROGRESS_BAR_MARQUEE:
1268
SendMessageW(dialog_info->progress_bar, PBM_SETMARQUEE, wParam, lParam);
1269
break;
1270
case TDM_SET_ELEMENT_TEXT:
1271
taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1272
taskdialog_layout(dialog_info);
1273
break;
1274
case TDM_UPDATE_ELEMENT_TEXT:
1275
taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
1276
break;
1277
case TDM_CLICK_RADIO_BUTTON:
1278
taskdialog_click_radio_button(dialog_info, wParam);
1279
break;
1280
case TDM_ENABLE_RADIO_BUTTON:
1281
taskdialog_enable_radio_button(dialog_info, wParam, lParam);
1282
break;
1283
case TDM_CLICK_VERIFICATION:
1284
{
1285
BOOL checked = (BOOL)wParam;
1286
BOOL focused = (BOOL)lParam;
1287
dialog_info->verification_checked = checked;
1288
if (dialog_info->verification_box)
1289
{
1290
SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
1291
taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
1292
if (focused) SetFocus(dialog_info->verification_box);
1293
}
1294
break;
1295
}
1296
case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE:
1297
taskdialog_button_set_shield(dialog_info, wParam, lParam);
1298
break;
1299
case TDM_UPDATE_ICON:
1300
taskdialog_set_icon(dialog_info, wParam, (HICON)lParam);
1301
break;
1302
case WM_INITDIALOG:
1303
dialog_info = (struct taskdialog_info *)lParam;
1304
1305
taskdialog_init(dialog_info, hwnd);
1306
1307
SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
1308
taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
1309
taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
1310
/* Default radio button click notification sent after TDN_CREATED */
1311
taskdialog_check_default_radio_buttons(dialog_info);
1312
return FALSE;
1313
case WM_COMMAND:
1314
if (dialog_info && HIWORD(wParam) == BN_CLICKED)
1315
{
1316
taskdialog_on_button_click(dialog_info, (HWND)lParam, LOWORD(wParam));
1317
break;
1318
}
1319
return FALSE;
1320
case WM_HELP:
1321
taskdialog_notify(dialog_info, TDN_HELP, 0, 0);
1322
break;
1323
case WM_TIMER:
1324
if (ID_TIMER == wParam)
1325
{
1326
DWORD elapsed = GetTickCount() - dialog_info->last_timer_tick;
1327
if (taskdialog_notify(dialog_info, TDN_TIMER, elapsed, 0) == S_FALSE)
1328
dialog_info->last_timer_tick = GetTickCount();
1329
}
1330
break;
1331
case WM_NOTIFY:
1332
{
1333
PNMLINK pnmLink = (PNMLINK)lParam;
1334
HWND hwndFrom = pnmLink->hdr.hwndFrom;
1335
if ((taskdialog_hyperlink_enabled(dialog_info))
1336
&& (hwndFrom == dialog_info->content || hwndFrom == dialog_info->expanded_info
1337
|| hwndFrom == dialog_info->footer_text)
1338
&& (pnmLink->hdr.code == NM_CLICK || pnmLink->hdr.code == NM_RETURN))
1339
{
1340
taskdialog_notify(dialog_info, TDN_HYPERLINK_CLICKED, 0, (LPARAM)pnmLink->item.szUrl);
1341
break;
1342
}
1343
return FALSE;
1344
}
1345
case WM_DRAWITEM:
1346
{
1347
LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
1348
if (dis->hwndItem == dialog_info->expando_button)
1349
{
1350
taskdialog_draw_expando_control(dialog_info, dis);
1351
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE);
1352
break;
1353
}
1354
return FALSE;
1355
}
1356
case WM_DESTROY:
1357
taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
1358
RemovePropW(hwnd, taskdialog_info_propnameW);
1359
taskdialog_destroy(dialog_info);
1360
break;
1361
case WM_CLOSE:
1362
if (dialog_info->has_cancel)
1363
{
1364
if(taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, IDCANCEL, 0) == S_OK)
1365
EndDialog(hwnd, IDCANCEL);
1366
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 0);
1367
break;
1368
}
1369
return FALSE;
1370
default:
1371
return FALSE;
1372
}
1373
return TRUE;
1374
}
1375
1376
/***********************************************************************
1377
* TaskDialogIndirect [COMCTL32.@]
1378
*/
1379
HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
1380
int *radio_button, BOOL *verification_flag_checked)
1381
{
1382
struct taskdialog_info dialog_info;
1383
DLGTEMPLATE *template;
1384
INT ret;
1385
1386
TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
1387
1388
if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
1389
return E_INVALIDARG;
1390
1391
dialog_info.taskconfig = taskconfig;
1392
1393
template = create_taskdialog_template(taskconfig);
1394
ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
1395
taskdialog_proc, (LPARAM)&dialog_info);
1396
Free(template);
1397
1398
if (button) *button = ret;
1399
if (radio_button) *radio_button = dialog_info.selected_radio_id;
1400
if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
1401
1402
return S_OK;
1403
}
1404
1405
/***********************************************************************
1406
* TaskDialog [COMCTL32.@]
1407
*/
1408
HRESULT WINAPI TaskDialog(HWND owner, HINSTANCE hinst, const WCHAR *title, const WCHAR *main_instruction,
1409
const WCHAR *content, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons, const WCHAR *icon, int *button)
1410
{
1411
TASKDIALOGCONFIG taskconfig;
1412
1413
TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner, hinst, debugstr_w(title), debugstr_w(main_instruction),
1414
debugstr_w(content), common_buttons, debugstr_w(icon), button);
1415
1416
memset(&taskconfig, 0, sizeof(taskconfig));
1417
taskconfig.cbSize = sizeof(taskconfig);
1418
taskconfig.hwndParent = owner;
1419
taskconfig.hInstance = hinst;
1420
taskconfig.dwCommonButtons = common_buttons;
1421
taskconfig.pszWindowTitle = title;
1422
taskconfig.pszMainIcon = icon;
1423
taskconfig.pszMainInstruction = main_instruction;
1424
taskconfig.pszContent = content;
1425
return TaskDialogIndirect(&taskconfig, button, NULL, NULL);
1426
}
1427
1428