wined3d: Add basic support for rendering to 3D textures.
[wine.git] / dlls / comctl32 / taskdialog.c
blob6b8bcfcae6e032fcfda799988ad5338b0374850c
1 /*
2 * Task dialog control
4 * Copyright 2017 Fabian Maurer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <string.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "winuser.h"
29 #include "commctrl.h"
30 #include "winerror.h"
31 #include "comctl32.h"
33 #include "wine/debug.h"
34 #include "wine/list.h"
35 #include "wine/unicode.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
39 #define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
40 #define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
41 #define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align)
42 #define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align)
44 static const UINT DIALOG_MIN_WIDTH = 180;
45 static const UINT DIALOG_SPACING = 5;
46 static const UINT DIALOG_BUTTON_WIDTH = 50;
47 static const UINT DIALOG_BUTTON_HEIGHT = 14;
49 static const UINT ID_MAIN_INSTRUCTION = 0xf000;
51 struct taskdialog_control
53 struct list entry;
54 DLGITEMTEMPLATE *template;
55 unsigned int template_size;
58 struct taskdialog_template_desc
60 const TASKDIALOGCONFIG *taskconfig;
61 unsigned int dialog_height;
62 unsigned int dialog_width;
63 struct list controls;
64 WORD control_count;
65 LONG x_baseunit;
66 LONG y_baseunit;
67 HFONT font;
70 static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
72 if (width)
73 *width = MulDiv(*width, 4, desc->x_baseunit);
74 if (height)
75 *height = MulDiv(*height, 8, desc->y_baseunit);
78 static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
80 if (width)
81 *width = MulDiv(*width, desc->x_baseunit, 4);
82 if (height)
83 *height = MulDiv(*height, desc->y_baseunit, 8);
86 static void template_write_data(char **ptr, const void *src, unsigned int size)
88 memcpy(*ptr, src, size);
89 *ptr += size;
92 static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc, WORD id, const WCHAR *class,
93 HINSTANCE hInstance, const WCHAR *text, short x, short y, short cx, short cy)
95 struct taskdialog_control *control = Alloc(sizeof(*control));
96 unsigned int size, class_size, text_size;
97 DLGITEMTEMPLATE *template;
98 static const WCHAR nulW;
99 const WCHAR *textW;
100 char *ptr;
102 class_size = (strlenW(class) + 1) * sizeof(WCHAR);
104 if (IS_INTRESOURCE(text))
105 text_size = LoadStringW(hInstance, (UINT_PTR)text, (WCHAR *)&textW, 0) * sizeof(WCHAR);
106 else
108 textW = text;
109 text_size = strlenW(textW) * sizeof(WCHAR);
112 size = sizeof(DLGITEMTEMPLATE);
113 size += class_size;
114 size += text_size + sizeof(WCHAR);
115 size += sizeof(WORD); /* creation data */
117 control->template = template = Alloc(size);
118 control->template_size = size;
120 template->style = WS_VISIBLE;
121 template->dwExtendedStyle = 0;
122 template->x = x;
123 template->y = y;
124 template->cx = cx;
125 template->cy = cy;
126 template->id = id;
127 ptr = (char *)(template + 1);
128 template_write_data(&ptr, class, class_size);
129 template_write_data(&ptr, textW, text_size);
130 template_write_data(&ptr, &nulW, sizeof(nulW));
132 list_add_tail(&desc->controls, &control->entry);
133 desc->control_count++;
134 return ALIGNED_LENGTH(size, 3);
137 static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc)
139 RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */
140 const WCHAR *textW = NULL;
141 unsigned int size, length;
142 HFONT oldfont;
143 HDC hdc;
145 if (!desc->taskconfig->pszMainInstruction)
146 return 0;
148 if (IS_INTRESOURCE(desc->taskconfig->pszMainInstruction))
150 if (!(length = LoadStringW(desc->taskconfig->hInstance, (UINT_PTR)desc->taskconfig->pszMainInstruction,
151 (WCHAR *)&textW, 0)))
153 WARN("Failed to load main instruction text\n");
154 return 0;
157 else
159 textW = desc->taskconfig->pszMainInstruction;
160 length = strlenW(textW);
163 hdc = GetDC(0);
164 oldfont = SelectObject(hdc, desc->font);
166 dialogunits_to_pixels(desc, &rect.right, NULL);
167 DrawTextW(hdc, textW, length, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK);
168 pixels_to_dialogunits(desc, &rect.right, &rect.bottom);
170 SelectObject(hdc, oldfont);
171 ReleaseDC(0, hdc);
173 size = taskdialog_add_control(desc, ID_MAIN_INSTRUCTION, WC_STATICW, desc->taskconfig->hInstance,
174 desc->taskconfig->pszMainInstruction, DIALOG_SPACING, desc->dialog_height,
175 rect.right, rect.bottom);
176 desc->dialog_height += rect.bottom;
177 return size;
180 static unsigned int taskdialog_add_common_buttons(struct taskdialog_template_desc *desc)
182 short button_x = desc->dialog_width - DIALOG_BUTTON_WIDTH - DIALOG_SPACING;
183 DWORD flags = desc->taskconfig->dwCommonButtons;
184 unsigned int size = 0;
186 #define TASKDIALOG_ADD_COMMON_BUTTON(id) \
187 do { \
188 size += taskdialog_add_control(desc, ID##id, WC_BUTTONW, COMCTL32_hModule, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
189 button_x, desc->dialog_height + DIALOG_SPACING, DIALOG_BUTTON_WIDTH, DIALOG_BUTTON_HEIGHT); \
190 button_x -= DIALOG_BUTTON_WIDTH + DIALOG_SPACING; \
191 } while(0)
192 if (flags & TDCBF_CLOSE_BUTTON)
193 TASKDIALOG_ADD_COMMON_BUTTON(CLOSE);
194 if (flags & TDCBF_CANCEL_BUTTON)
195 TASKDIALOG_ADD_COMMON_BUTTON(CANCEL);
196 if (flags & TDCBF_RETRY_BUTTON)
197 TASKDIALOG_ADD_COMMON_BUTTON(RETRY);
198 if (flags & TDCBF_NO_BUTTON)
199 TASKDIALOG_ADD_COMMON_BUTTON(NO);
200 if (flags & TDCBF_YES_BUTTON)
201 TASKDIALOG_ADD_COMMON_BUTTON(YES);
202 if (flags & TDCBF_OK_BUTTON)
203 TASKDIALOG_ADD_COMMON_BUTTON(OK);
204 /* Always add OK button */
205 if (list_empty(&desc->controls))
206 TASKDIALOG_ADD_COMMON_BUTTON(OK);
207 #undef TASKDIALOG_ADD_COMMON_BUTTON
209 /* make room for common buttons row */
210 desc->dialog_height += DIALOG_BUTTON_HEIGHT + 2 * DIALOG_SPACING;
211 return size;
214 static void taskdialog_clear_controls(struct list *controls)
216 struct taskdialog_control *control, *control2;
218 LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, struct taskdialog_control, entry)
220 list_remove(&control->entry);
221 Free(control->template);
222 Free(control);
226 static unsigned int taskdialog_get_reference_rect(const struct taskdialog_template_desc *desc, RECT *ret)
228 HMONITOR monitor = MonitorFromWindow(desc->taskconfig->hwndParent ? desc->taskconfig->hwndParent : GetActiveWindow(),
229 MONITOR_DEFAULTTOPRIMARY);
230 MONITORINFO info;
232 info.cbSize = sizeof(info);
233 GetMonitorInfoW(monitor, &info);
235 if (desc->taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && desc->taskconfig->hwndParent)
236 GetWindowRect(desc->taskconfig->hwndParent, ret);
237 else
238 *ret = info.rcWork;
240 pixels_to_dialogunits(desc, &ret->left, &ret->top);
241 pixels_to_dialogunits(desc, &ret->right, &ret->bottom);
243 pixels_to_dialogunits(desc, &info.rcWork.left, &info.rcWork.top);
244 pixels_to_dialogunits(desc, &info.rcWork.right, &info.rcWork.bottom);
245 return info.rcWork.right - info.rcWork.left;
248 static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
250 struct taskdialog_control *control, *control2;
251 unsigned int size, title_size, screen_width;
252 struct taskdialog_template_desc desc;
253 static const WORD fontsize = 0x7fff;
254 static const WCHAR emptyW[] = { 0 };
255 const WCHAR *titleW = NULL;
256 DLGTEMPLATE *template;
257 NONCLIENTMETRICSW ncm;
258 RECT ref_rect;
259 char *ptr;
260 HDC hdc;
262 /* Window title */
263 if (!taskconfig->pszWindowTitle)
264 FIXME("use executable name for window title\n");
265 else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
266 FIXME("load window title from resources\n");
267 else
268 titleW = taskconfig->pszWindowTitle;
269 if (!titleW)
270 titleW = emptyW;
271 title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
273 size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
274 if (titleW)
275 size += title_size;
276 size += 2; /* font size */
278 list_init(&desc.controls);
279 desc.taskconfig = taskconfig;
280 desc.control_count = 0;
282 ncm.cbSize = sizeof(ncm);
283 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
284 desc.font = CreateFontIndirectW(&ncm.lfMessageFont);
286 hdc = GetDC(0);
287 SelectObject(hdc, desc.font);
288 desc.x_baseunit = GdiGetCharDimensions(hdc, NULL, &desc.y_baseunit);
289 ReleaseDC(0, hdc);
291 screen_width = taskdialog_get_reference_rect(&desc, &ref_rect);
293 desc.dialog_height = DIALOG_SPACING;
294 desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
295 desc.dialog_width = min(desc.dialog_width, screen_width);
297 size += taskdialog_add_main_instruction(&desc);
298 size += taskdialog_add_common_buttons(&desc);
300 template = Alloc(size);
301 if (!template)
303 taskdialog_clear_controls(&desc.controls);
304 DeleteObject(desc.font);
305 return NULL;
308 template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
309 template->cdit = desc.control_count;
310 template->x = (ref_rect.left + ref_rect.right + desc.dialog_width) / 2;
311 template->y = (ref_rect.top + ref_rect.bottom + desc.dialog_height) / 2;
312 template->cx = desc.dialog_width;
313 template->cy = desc.dialog_height;
315 ptr = (char *)(template + 1);
316 ptr += 2; /* menu */
317 ptr += 2; /* class */
318 template_write_data(&ptr, titleW, title_size);
319 template_write_data(&ptr, &fontsize, sizeof(fontsize));
321 /* write control entries */
322 LIST_FOR_EACH_ENTRY_SAFE(control, control2, &desc.controls, struct taskdialog_control, entry)
324 ALIGN_POINTER(ptr, 3);
326 template_write_data(&ptr, control->template, control->template_size);
328 /* list item won't be needed later */
329 list_remove(&control->entry);
330 Free(control->template);
331 Free(control);
334 DeleteObject(desc.font);
335 return template;
338 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
340 TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
342 switch (msg)
344 case WM_COMMAND:
345 if (HIWORD(wParam) == BN_CLICKED)
347 WORD command_id = LOWORD(wParam);
348 EndDialog(hwnd, command_id);
349 return TRUE;
351 break;
353 return FALSE;
356 /***********************************************************************
357 * TaskDialogIndirect [COMCTL32.@]
359 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
360 int *radio_button, BOOL *verification_flag_checked)
362 DLGTEMPLATE *template;
363 INT ret;
365 TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
367 template = create_taskdialog_template(taskconfig);
368 ret = DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent, taskdialog_proc, 0);
369 Free(template);
371 if (button) *button = ret;
372 if (radio_button) *radio_button = taskconfig->nDefaultButton;
373 if (verification_flag_checked) *verification_flag_checked = TRUE;
375 return S_OK;