2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_WINDOWS
25 #include "../../core/windows/SDL_windows.h"
27 #include "SDL_assert.h"
28 #include "SDL_windowsvideo.h"
31 #ifndef SS_EDITCONTROL
32 #define SS_EDITCONTROL 0x2000
35 /* Display a Windows message box */
69 DLGTEMPLATEEX* lpDialog;
76 static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
80 /* Return the ID of the button that was pushed */
81 EndDialog(hDlg, LOWORD(wParam));
90 static SDL_bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space)
92 size_t size = dialog->size;
97 while ((dialog->used + space) > size) {
101 if (size > dialog->size) {
102 void *data = SDL_realloc(dialog->data, size);
109 dialog->lpDialog = (DLGTEMPLATEEX*)dialog->data;
114 static SDL_bool AlignDialogData(WIN_DialogData *dialog, size_t size)
116 size_t padding = (dialog->used % size);
118 if (!ExpandDialogSpace(dialog, padding)) {
122 dialog->used += padding;
127 static SDL_bool AddDialogData(WIN_DialogData *dialog, const void *data, size_t size)
129 if (!ExpandDialogSpace(dialog, size)) {
133 SDL_memcpy(dialog->data+dialog->used, data, size);
134 dialog->used += size;
139 static SDL_bool AddDialogString(WIN_DialogData *dialog, const char *string)
150 wstring = WIN_UTF8ToString(string);
155 /* Find out how many characters we have, including null terminator */
157 for (p = wstring; *p; ++p) {
162 status = AddDialogData(dialog, wstring, count*sizeof(WCHAR));
167 static int s_BaseUnitsX;
168 static int s_BaseUnitsY;
169 static void Vec2ToDLU(short *x, short *y)
171 SDL_assert(s_BaseUnitsX != 0); /* we init in WIN_ShowMessageBox(), which is the only public function... */
173 *x = MulDiv(*x, 4, s_BaseUnitsX);
174 *y = MulDiv(*y, 8, s_BaseUnitsY);
178 static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption)
180 DLGITEMTEMPLATEEX item;
181 WORD marker = 0xFFFF;
186 item.exStyle = exStyle;
193 Vec2ToDLU(&item.x, &item.y);
194 Vec2ToDLU(&item.cx, &item.cy);
196 if (!AlignDialogData(dialog, sizeof(DWORD))) {
199 if (!AddDialogData(dialog, &item, sizeof(item))) {
202 if (!AddDialogData(dialog, &marker, sizeof(marker))) {
205 if (!AddDialogData(dialog, &type, sizeof(type))) {
208 if (!AddDialogString(dialog, caption)) {
211 if (!AddDialogData(dialog, &extraData, sizeof(extraData))) {
214 ++dialog->lpDialog->cDlgItems;
219 static SDL_bool AddDialogStatic(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
221 DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL;
222 return AddDialogControl(dialog, 0x0082, style, 0, x, y, w, h, -1, text);
225 static SDL_bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, SDL_bool isDefault)
227 DWORD style = WS_VISIBLE | WS_CHILD;
229 style |= BS_DEFPUSHBUTTON;
231 style |= BS_PUSHBUTTON;
233 return AddDialogControl(dialog, 0x0080, style, 0, x, y, w, h, id, text);
236 static void FreeDialogData(WIN_DialogData *dialog)
238 SDL_free(dialog->data);
242 static WIN_DialogData *CreateDialogData(int w, int h, const char *caption)
244 WIN_DialogData *dialog;
245 DLGTEMPLATEEX dialogTemplate;
248 SDL_zero(dialogTemplate);
249 dialogTemplate.dlgVer = 1;
250 dialogTemplate.signature = 0xffff;
251 dialogTemplate.style = (WS_CAPTION | DS_CENTER | DS_SHELLFONT);
252 dialogTemplate.x = 0;
253 dialogTemplate.y = 0;
254 dialogTemplate.cx = w;
255 dialogTemplate.cy = h;
256 Vec2ToDLU(&dialogTemplate.cx, &dialogTemplate.cy);
258 dialog = (WIN_DialogData *)SDL_calloc(1, sizeof(*dialog));
263 if (!AddDialogData(dialog, &dialogTemplate, sizeof(dialogTemplate))) {
264 FreeDialogData(dialog);
270 if (!AddDialogData(dialog, &WordToPass, 2)) {
271 FreeDialogData(dialog);
275 /* No custom class */
276 if (!AddDialogData(dialog, &WordToPass, 2)) {
277 FreeDialogData(dialog);
282 if (!AddDialogString(dialog, caption)) {
283 FreeDialogData(dialog);
290 * We want to use the system messagebox font.
294 NONCLIENTMETRICSA NCM;
295 NCM.cbSize = sizeof(NCM);
296 SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
298 /* Font size - convert to logical font size for dialog parameter. */
300 HDC ScreenDC = GetDC(NULL);
301 int LogicalPixelsY = GetDeviceCaps(ScreenDC, LOGPIXELSY);
302 if (!LogicalPixelsY) /* This can happen if the application runs out of GDI handles */
304 WordToPass = (WORD)(-72 * NCM.lfMessageFont.lfHeight / LogicalPixelsY);
305 ReleaseDC(NULL, ScreenDC);
308 if (!AddDialogData(dialog, &WordToPass, 2)) {
309 FreeDialogData(dialog);
314 WordToPass = (WORD)NCM.lfMessageFont.lfWeight;
315 if (!AddDialogData(dialog, &WordToPass, 2)) {
316 FreeDialogData(dialog);
321 ToPass = NCM.lfMessageFont.lfItalic;
322 if (!AddDialogData(dialog, &ToPass, 1)) {
323 FreeDialogData(dialog);
328 ToPass = NCM.lfMessageFont.lfCharSet;
329 if (!AddDialogData(dialog, &ToPass, 1)) {
330 FreeDialogData(dialog);
335 if (!AddDialogString(dialog, NCM.lfMessageFont.lfFaceName)) {
336 FreeDialogData(dialog);
345 WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
347 WIN_DialogData *dialog;
350 const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
358 const int ButtonWidth = 88;
359 const int ButtonHeight = 26;
360 const int TextMargin = 16;
361 const int ButtonMargin = 12;
364 /* Jan 25th, 2013 - dant@fleetsa.com
367 * I've tried to make this more reasonable, but I've run in to a lot
370 * The original issue is the code was written in pixels and not
371 * dialog units (DLUs). All DialogBox functions use DLUs, which
372 * vary based on the selected font (yay).
374 * According to MSDN, the most reliable way to convert is via
375 * MapDialogUnits, which requires an HWND, which we don't have
376 * at time of template creation.
378 * We do however have:
379 * The system font (DLU width 8 for me)
380 * The font we select for the dialog (DLU width 6 for me)
382 * Based on experimentation, *neither* of these return the value
383 * actually used. Stepping in to MapDialogUnits(), the conversion
384 * is fairly clear, and uses 7 for me.
386 * As a result, some of this is hacky to ensure the sizing is
389 * Honestly, a long term solution is to use CreateWindow, not CreateDialog.
393 * In order to get text dimensions we need to have a DC with the desired font.
394 * I'm assuming a dialog box in SDL is rare enough we can to the create.
396 HDC FontDC = CreateCompatibleDC(0);
399 /* Create a duplicate of the font used in system message boxes. */
401 NONCLIENTMETRICS NCM;
402 NCM.cbSize = sizeof(NCM);
403 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
404 lf = NCM.lfMessageFont;
405 DialogFont = CreateFontIndirect(&lf);
408 /* Select the font in to our DC */
409 SelectObject(FontDC, DialogFont);
412 /* Get the metrics to try and figure our DLU conversion. */
413 GetTextMetrics(FontDC, &TM);
414 s_BaseUnitsX = TM.tmAveCharWidth + 1;
415 s_BaseUnitsY = TM.tmHeight;
418 /* Measure the *pixel* size of the string. */
419 wmessage = WIN_UTF8ToString(messageboxdata->message);
421 Size.cx = DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT);
423 /* Add some padding for hangs, etc. */
425 TextSize.bottom += 2;
427 /* Done with the DC, and the string */
431 /* Increase the size of the dialog by some border spacing around the text. */
432 Size.cx = TextSize.right - TextSize.left;
433 Size.cy = TextSize.bottom - TextSize.top;
434 Size.cx += TextMargin * 2;
435 Size.cy += TextMargin * 2;
437 /* Ensure the size is wide enough for all of the buttons. */
438 if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin)
439 Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
441 /* Add vertical space for the buttons and border. */
442 Size.cy += ButtonHeight + TextMargin;
444 dialog = CreateDialogData(Size.cx, Size.cy, messageboxdata->title);
449 if (!AddDialogStatic(dialog, TextMargin, TextMargin, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
450 FreeDialogData(dialog);
454 /* Align the buttons to the right/bottom. */
455 x = Size.cx - ButtonWidth - ButtonMargin;
456 y = Size.cy - ButtonHeight - ButtonMargin;
457 for (i = 0; i < messageboxdata->numbuttons; ++i) {
460 if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
461 isDefault = SDL_TRUE;
463 isDefault = SDL_FALSE;
465 if (!AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttons[i].text, i, isDefault)) {
466 FreeDialogData(dialog);
469 x -= ButtonWidth + ButtonMargin;
472 /* FIXME: If we have a parent window, get the Instance and HWND for them */
473 which = DialogBoxIndirect(NULL, (DLGTEMPLATE*)dialog->lpDialog, NULL, (DLGPROC)MessageBoxDialogProc);
474 *buttonid = buttons[which].buttonid;
476 FreeDialogData(dialog);
480 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
482 /* vi: set ts=4 sw=4 expandtab: */