src/video/windows/SDL_windowsmessagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 28 Feb 2018 01:54:22 -0500
changeset 11909 b2e68bf41993
parent 11811 5d94cb6b24d3
child 11910 9947d9f539e8
permissions -rw-r--r--
windows: Message boxes use Task Dialogs if possible (thanks, Jack!).

This lets the message box have an icon. Unless the app has opted-in to using
the v6 common controls, though, this will fall back to the usual SDL message
boxes.
slouken@6620
     1
/*
slouken@6620
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
slouken@6620
     4
slouken@6620
     5
  This software is provided 'as-is', without any express or implied
slouken@6620
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@6620
     7
  arising from the use of this software.
slouken@6620
     8
slouken@6620
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@6620
    10
  including commercial applications, and to alter it and redistribute it
slouken@6620
    11
  freely, subject to the following restrictions:
slouken@6620
    12
slouken@6620
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@6620
    14
     claim that you wrote the original software. If you use this software
slouken@6620
    15
     in a product, an acknowledgment in the product documentation would be
slouken@6620
    16
     appreciated but is not required.
slouken@6620
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@6620
    18
     misrepresented as being the original software.
slouken@6620
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@6620
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@6620
    22
slouken@6620
    23
#if SDL_VIDEO_DRIVER_WINDOWS
slouken@6620
    24
slouken@7828
    25
#include "../../core/windows/SDL_windows.h"
slouken@7828
    26
slouken@7828
    27
#include "SDL_assert.h"
slouken@6620
    28
#include "SDL_windowsvideo.h"
icculus@11909
    29
#include "SDL_windowstaskdialog.h"
slouken@6620
    30
slouken@7030
    31
#ifndef SS_EDITCONTROL
slouken@7030
    32
#define SS_EDITCONTROL  0x2000
slouken@7030
    33
#endif
slouken@7030
    34
slouken@6620
    35
/* Display a Windows message box */
slouken@6620
    36
slouken@7028
    37
#pragma pack(push, 1)
slouken@7028
    38
slouken@6620
    39
typedef struct
slouken@6620
    40
{
slouken@7191
    41
    WORD dlgVer;
slouken@7191
    42
    WORD signature;
slouken@7191
    43
    DWORD helpID;
slouken@7191
    44
    DWORD exStyle;
slouken@7191
    45
    DWORD style;
slouken@7191
    46
    WORD cDlgItems;
slouken@7191
    47
    short x;
slouken@7191
    48
    short y;
slouken@7191
    49
    short cx;
slouken@7191
    50
    short cy;
slouken@7028
    51
} DLGTEMPLATEEX;
slouken@7028
    52
slouken@7028
    53
typedef struct
slouken@7028
    54
{
slouken@7191
    55
    DWORD helpID;
slouken@7191
    56
    DWORD exStyle;
slouken@7191
    57
    DWORD style;
slouken@7191
    58
    short x;
slouken@7191
    59
    short y;
slouken@7191
    60
    short cx;
slouken@7191
    61
    short cy;
slouken@7191
    62
    DWORD id;
slouken@7028
    63
} DLGITEMTEMPLATEEX;
slouken@7028
    64
slouken@7028
    65
#pragma pack(pop)
slouken@7028
    66
slouken@7028
    67
typedef struct
slouken@7028
    68
{
slouken@7028
    69
    DLGTEMPLATEEX* lpDialog;
slouken@6620
    70
    Uint8 *data;
slouken@6620
    71
    size_t size;
slouken@6620
    72
    size_t used;
slouken@6620
    73
} WIN_DialogData;
slouken@6620
    74
slouken@6620
    75
slouken@6620
    76
static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
slouken@6620
    77
{
slouken@6620
    78
    switch ( iMessage ) {
slouken@6620
    79
    case WM_COMMAND:
slouken@6620
    80
        /* Return the ID of the button that was pushed */
slouken@7191
    81
        EndDialog(hDlg, LOWORD(wParam));
slouken@6620
    82
        return TRUE;
slouken@6620
    83
slouken@6620
    84
    default:
slouken@6620
    85
        break;
slouken@6620
    86
    }
slouken@6620
    87
    return FALSE;
slouken@6620
    88
}
slouken@6620
    89
slouken@6620
    90
static SDL_bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space)
slouken@6620
    91
{
slouken@6620
    92
    size_t size = dialog->size;
slouken@6620
    93
slouken@6620
    94
    if (size == 0) {
slouken@6620
    95
        size = space;
slouken@6620
    96
    } else {
slouken@6620
    97
        while ((dialog->used + space) > size) {
slouken@6620
    98
            size *= 2;
slouken@6620
    99
        }
slouken@6620
   100
    }
slouken@6620
   101
    if (size > dialog->size) {
slouken@6620
   102
        void *data = SDL_realloc(dialog->data, size);
slouken@6620
   103
        if (!data) {
slouken@6620
   104
            SDL_OutOfMemory();
slouken@6620
   105
            return SDL_FALSE;
slouken@6620
   106
        }
slouken@6620
   107
        dialog->data = data;
slouken@6620
   108
        dialog->size = size;
slouken@7028
   109
        dialog->lpDialog = (DLGTEMPLATEEX*)dialog->data;
slouken@6620
   110
    }
slouken@6620
   111
    return SDL_TRUE;
slouken@6620
   112
}
slouken@7191
   113
slouken@6620
   114
static SDL_bool AlignDialogData(WIN_DialogData *dialog, size_t size)
slouken@6620
   115
{
slouken@6620
   116
    size_t padding = (dialog->used % size);
slouken@6620
   117
slouken@6620
   118
    if (!ExpandDialogSpace(dialog, padding)) {
slouken@6620
   119
        return SDL_FALSE;
slouken@6620
   120
    }
slouken@6620
   121
slouken@6620
   122
    dialog->used += padding;
slouken@6620
   123
slouken@6620
   124
    return SDL_TRUE;
slouken@6620
   125
}
slouken@6620
   126
slouken@6620
   127
static SDL_bool AddDialogData(WIN_DialogData *dialog, const void *data, size_t size)
slouken@6620
   128
{
slouken@6620
   129
    if (!ExpandDialogSpace(dialog, size)) {
slouken@6620
   130
        return SDL_FALSE;
slouken@6620
   131
    }
slouken@6620
   132
slouken@6620
   133
    SDL_memcpy(dialog->data+dialog->used, data, size);
slouken@6620
   134
    dialog->used += size;
slouken@6620
   135
slouken@6620
   136
    return SDL_TRUE;
slouken@6620
   137
}
slouken@6620
   138
slouken@6620
   139
static SDL_bool AddDialogString(WIN_DialogData *dialog, const char *string)
slouken@6620
   140
{
slouken@6620
   141
    WCHAR *wstring;
slouken@6620
   142
    WCHAR *p;
slouken@6620
   143
    size_t count;
slouken@6620
   144
    SDL_bool status;
slouken@6620
   145
slouken@6620
   146
    if (!string) {
slouken@6620
   147
        string = "";
slouken@6620
   148
    }
slouken@6620
   149
slouken@6620
   150
    wstring = WIN_UTF8ToString(string);
slouken@6620
   151
    if (!wstring) {
slouken@6620
   152
        return SDL_FALSE;
slouken@6620
   153
    }
slouken@6620
   154
slouken@6620
   155
    /* Find out how many characters we have, including null terminator */
slouken@6620
   156
    count = 0;
slouken@6620
   157
    for (p = wstring; *p; ++p) {
slouken@6620
   158
        ++count;
slouken@6620
   159
    }
slouken@6620
   160
    ++count;
slouken@6620
   161
slouken@6620
   162
    status = AddDialogData(dialog, wstring, count*sizeof(WCHAR));
slouken@6620
   163
    SDL_free(wstring);
slouken@6620
   164
    return status;
slouken@6620
   165
}
slouken@6620
   166
slouken@7028
   167
static int s_BaseUnitsX;
slouken@7028
   168
static int s_BaseUnitsY;
icculus@7052
   169
static void Vec2ToDLU(short *x, short *y)
slouken@7028
   170
{
slouken@7191
   171
    SDL_assert(s_BaseUnitsX != 0); /* we init in WIN_ShowMessageBox(), which is the only public function... */
slouken@7028
   172
slouken@7028
   173
    *x = MulDiv(*x, 4, s_BaseUnitsX);
slouken@7028
   174
    *y = MulDiv(*y, 8, s_BaseUnitsY);
slouken@7028
   175
}
slouken@7028
   176
slouken@7028
   177
slouken@6620
   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)
slouken@6620
   179
{
slouken@7028
   180
    DLGITEMTEMPLATEEX item;
slouken@6620
   181
    WORD marker = 0xFFFF;
slouken@6620
   182
    WORD extraData = 0;
slouken@6620
   183
slouken@6620
   184
    SDL_zero(item);
slouken@6620
   185
    item.style = style;
slouken@7028
   186
    item.exStyle = exStyle;
slouken@6620
   187
    item.x = x;
slouken@6620
   188
    item.y = y;
slouken@6620
   189
    item.cx = w;
slouken@6620
   190
    item.cy = h;
slouken@6620
   191
    item.id = id;
slouken@6620
   192
slouken@7028
   193
    Vec2ToDLU(&item.x, &item.y);
slouken@7028
   194
    Vec2ToDLU(&item.cx, &item.cy);
slouken@7028
   195
slouken@6620
   196
    if (!AlignDialogData(dialog, sizeof(DWORD))) {
slouken@6620
   197
        return SDL_FALSE;
slouken@6620
   198
    }
slouken@6620
   199
    if (!AddDialogData(dialog, &item, sizeof(item))) {
slouken@6620
   200
        return SDL_FALSE;
slouken@6620
   201
    }
slouken@6620
   202
    if (!AddDialogData(dialog, &marker, sizeof(marker))) {
slouken@6620
   203
        return SDL_FALSE;
slouken@6620
   204
    }
slouken@6620
   205
    if (!AddDialogData(dialog, &type, sizeof(type))) {
slouken@6620
   206
        return SDL_FALSE;
slouken@6620
   207
    }
slouken@6620
   208
    if (!AddDialogString(dialog, caption)) {
slouken@6620
   209
        return SDL_FALSE;
slouken@6620
   210
    }
slouken@6620
   211
    if (!AddDialogData(dialog, &extraData, sizeof(extraData))) {
slouken@6620
   212
        return SDL_FALSE;
slouken@6620
   213
    }
slouken@7028
   214
    ++dialog->lpDialog->cDlgItems;
slouken@6620
   215
slouken@6620
   216
    return SDL_TRUE;
slouken@6620
   217
}
slouken@6620
   218
slouken@6620
   219
static SDL_bool AddDialogStatic(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
slouken@6620
   220
{
slouken@7028
   221
    DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL;
slouken@6620
   222
    return AddDialogControl(dialog, 0x0082, style, 0, x, y, w, h, -1, text);
slouken@6620
   223
}
slouken@6620
   224
slouken@6620
   225
static SDL_bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, SDL_bool isDefault)
slouken@6620
   226
{
slouken@6620
   227
    DWORD style = WS_VISIBLE | WS_CHILD;
slouken@6620
   228
    if (isDefault) {
slouken@6620
   229
        style |= BS_DEFPUSHBUTTON;
slouken@6620
   230
    } else {
slouken@6620
   231
        style |= BS_PUSHBUTTON;
slouken@6620
   232
    }
slouken@6620
   233
    return AddDialogControl(dialog, 0x0080, style, 0, x, y, w, h, id, text);
slouken@6620
   234
}
slouken@6620
   235
slouken@6620
   236
static void FreeDialogData(WIN_DialogData *dialog)
slouken@6620
   237
{
slouken@7719
   238
    SDL_free(dialog->data);
slouken@6620
   239
    SDL_free(dialog);
slouken@6620
   240
}
slouken@6620
   241
slouken@6620
   242
static WIN_DialogData *CreateDialogData(int w, int h, const char *caption)
slouken@6620
   243
{
slouken@6620
   244
    WIN_DialogData *dialog;
slouken@7028
   245
    DLGTEMPLATEEX dialogTemplate;
slouken@7028
   246
    WORD WordToPass;
slouken@6620
   247
slouken@6620
   248
    SDL_zero(dialogTemplate);
slouken@7028
   249
    dialogTemplate.dlgVer = 1;
slouken@7028
   250
    dialogTemplate.signature = 0xffff;
slouken@7028
   251
    dialogTemplate.style = (WS_CAPTION | DS_CENTER | DS_SHELLFONT);
slouken@6620
   252
    dialogTemplate.x = 0;
slouken@6620
   253
    dialogTemplate.y = 0;
slouken@6620
   254
    dialogTemplate.cx = w;
slouken@6620
   255
    dialogTemplate.cy = h;
slouken@7028
   256
    Vec2ToDLU(&dialogTemplate.cx, &dialogTemplate.cy);
slouken@6620
   257
slouken@6620
   258
    dialog = (WIN_DialogData *)SDL_calloc(1, sizeof(*dialog));
slouken@6620
   259
    if (!dialog) {
slouken@6620
   260
        return NULL;
slouken@6620
   261
    }
slouken@6620
   262
slouken@6620
   263
    if (!AddDialogData(dialog, &dialogTemplate, sizeof(dialogTemplate))) {
slouken@6620
   264
        FreeDialogData(dialog);
slouken@6620
   265
        return NULL;
slouken@6620
   266
    }
slouken@6620
   267
slouken@7191
   268
    /* No menu */
slouken@7028
   269
    WordToPass = 0;
slouken@7028
   270
    if (!AddDialogData(dialog, &WordToPass, 2)) {
slouken@6620
   271
        FreeDialogData(dialog);
slouken@6620
   272
        return NULL;
slouken@6620
   273
    }
slouken@6620
   274
slouken@7191
   275
    /* No custom class */
slouken@7028
   276
    if (!AddDialogData(dialog, &WordToPass, 2)) {
slouken@7028
   277
        FreeDialogData(dialog);
slouken@7028
   278
        return NULL;
slouken@7028
   279
    }
slouken@7028
   280
slouken@7191
   281
    /* title */
slouken@6620
   282
    if (!AddDialogString(dialog, caption)) {
slouken@6620
   283
        FreeDialogData(dialog);
slouken@6620
   284
        return NULL;
slouken@6620
   285
    }
slouken@6620
   286
slouken@7191
   287
    /* Font stuff */
slouken@7028
   288
    {
slouken@7191
   289
        /*
slouken@7191
   290
         * We want to use the system messagebox font.
slouken@7191
   291
         */
slouken@7028
   292
        BYTE ToPass;
slouken@7191
   293
slouken@7028
   294
        NONCLIENTMETRICSA NCM;
slouken@7028
   295
        NCM.cbSize = sizeof(NCM);
slouken@7028
   296
        SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
slouken@7191
   297
slouken@7191
   298
        /* Font size - convert to logical font size for dialog parameter. */
slouken@7028
   299
        {
slouken@9863
   300
            HDC ScreenDC = GetDC(NULL);
slouken@9863
   301
            int LogicalPixelsY = GetDeviceCaps(ScreenDC, LOGPIXELSY);
slouken@9863
   302
            if (!LogicalPixelsY) /* This can happen if the application runs out of GDI handles */
slouken@9863
   303
                LogicalPixelsY = 72;
slouken@9863
   304
            WordToPass = (WORD)(-72 * NCM.lfMessageFont.lfHeight / LogicalPixelsY);
slouken@9863
   305
            ReleaseDC(NULL, ScreenDC);
slouken@7028
   306
        }
slouken@7028
   307
slouken@7028
   308
        if (!AddDialogData(dialog, &WordToPass, 2)) {
slouken@7028
   309
            FreeDialogData(dialog);
slouken@7028
   310
            return NULL;
slouken@7028
   311
        }
slouken@7028
   312
slouken@7191
   313
        /* Font weight */
slouken@7028
   314
        WordToPass = (WORD)NCM.lfMessageFont.lfWeight;
slouken@7028
   315
        if (!AddDialogData(dialog, &WordToPass, 2)) {
slouken@7028
   316
            FreeDialogData(dialog);
slouken@7028
   317
            return NULL;
slouken@7028
   318
        }
slouken@7028
   319
slouken@7191
   320
        /* italic? */
slouken@7028
   321
        ToPass = NCM.lfMessageFont.lfItalic;
slouken@7028
   322
        if (!AddDialogData(dialog, &ToPass, 1)) {
slouken@7028
   323
            FreeDialogData(dialog);
slouken@7028
   324
            return NULL;
slouken@7028
   325
        }
slouken@7028
   326
slouken@7191
   327
        /* charset? */
slouken@7028
   328
        ToPass = NCM.lfMessageFont.lfCharSet;
slouken@7028
   329
        if (!AddDialogData(dialog, &ToPass, 1)) {
slouken@7028
   330
            FreeDialogData(dialog);
slouken@7028
   331
            return NULL;
slouken@7028
   332
        }
slouken@7028
   333
slouken@7191
   334
        /* font typeface. */
slouken@7028
   335
        if (!AddDialogString(dialog, NCM.lfMessageFont.lfFaceName)) {
slouken@7028
   336
            FreeDialogData(dialog);
slouken@7028
   337
            return NULL;
slouken@7028
   338
        }
slouken@7028
   339
    }
slouken@7028
   340
slouken@6620
   341
    return dialog;
slouken@6620
   342
}
slouken@6620
   343
icculus@11909
   344
/* This function is called if a Task Dialog is unsupported. */
icculus@11909
   345
static int
icculus@11909
   346
WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
slouken@6620
   347
{
slouken@6620
   348
    WIN_DialogData *dialog;
slouken@7870
   349
    int i, x, y;
slouken@6620
   350
    const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
slouken@7028
   351
    HFONT DialogFont;
slouken@7028
   352
    SIZE Size;
slouken@7028
   353
    RECT TextSize;
slouken@7028
   354
    wchar_t* wmessage;
slouken@7028
   355
    TEXTMETRIC TM;
slouken@7028
   356
icculus@11036
   357
    HWND ParentWindow = NULL;
slouken@7028
   358
slouken@7028
   359
    const int ButtonWidth = 88;
slouken@7028
   360
    const int ButtonHeight = 26;
slouken@7028
   361
    const int TextMargin = 16;
slouken@7028
   362
    const int ButtonMargin = 12;
slouken@6620
   363
slouken@7028
   364
slouken@7191
   365
    /* Jan 25th, 2013 - dant@fleetsa.com
slouken@7191
   366
     *
slouken@7191
   367
     *
slouken@7191
   368
     * I've tried to make this more reasonable, but I've run in to a lot
slouken@7191
   369
     * of nonsense.
slouken@7191
   370
     *
slouken@7191
   371
     * The original issue is the code was written in pixels and not
slouken@7191
   372
     * dialog units (DLUs). All DialogBox functions use DLUs, which
slouken@7191
   373
     * vary based on the selected font (yay).
slouken@7191
   374
     *
slouken@7191
   375
     * According to MSDN, the most reliable way to convert is via
slouken@7191
   376
     * MapDialogUnits, which requires an HWND, which we don't have
slouken@7191
   377
     * at time of template creation.
slouken@7191
   378
     *
slouken@7191
   379
     * We do however have:
slouken@7191
   380
     *  The system font (DLU width 8 for me)
slouken@7191
   381
     *  The font we select for the dialog (DLU width 6 for me)
slouken@7191
   382
     *
slouken@7191
   383
     * Based on experimentation, *neither* of these return the value
slouken@7191
   384
     * actually used. Stepping in to MapDialogUnits(), the conversion
slouken@7191
   385
     * is fairly clear, and uses 7 for me.
slouken@7191
   386
     *
slouken@7191
   387
     * As a result, some of this is hacky to ensure the sizing is
slouken@7191
   388
     * somewhat correct.
slouken@7191
   389
     *
slouken@7191
   390
     * Honestly, a long term solution is to use CreateWindow, not CreateDialog.
slouken@7191
   391
     *
slouken@7191
   392
slouken@7191
   393
     *
slouken@7191
   394
     * In order to get text dimensions we need to have a DC with the desired font.
slouken@7191
   395
     * I'm assuming a dialog box in SDL is rare enough we can to the create.
slouken@7191
   396
     */
slouken@7028
   397
    HDC FontDC = CreateCompatibleDC(0);
slouken@7191
   398
slouken@7028
   399
    {
slouken@7191
   400
        /* Create a duplicate of the font used in system message boxes. */
slouken@7028
   401
        LOGFONT lf;
slouken@7028
   402
        NONCLIENTMETRICS NCM;
slouken@7028
   403
        NCM.cbSize = sizeof(NCM);
slouken@7028
   404
        SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
slouken@7028
   405
        lf = NCM.lfMessageFont;
slouken@7028
   406
        DialogFont = CreateFontIndirect(&lf);
slouken@7028
   407
    }
slouken@7028
   408
slouken@7191
   409
    /* Select the font in to our DC */
slouken@7028
   410
    SelectObject(FontDC, DialogFont);
slouken@7028
   411
slouken@7028
   412
    {
slouken@7191
   413
        /* Get the metrics to try and figure our DLU conversion. */
slouken@7028
   414
        GetTextMetrics(FontDC, &TM);
slouken@11303
   415
slouken@11303
   416
        /* Calculation from the following documentation:
slouken@11303
   417
         * https://support.microsoft.com/en-gb/help/125681/how-to-calculate-dialog-base-units-with-non-system-based-font
slouken@11303
   418
         * This fixes bug 2137, dialog box calculation with a fixed-width system font
slouken@11303
   419
         */
slouken@11303
   420
        {
slouken@11303
   421
            SIZE extent;
slouken@11303
   422
            GetTextExtentPoint32A(FontDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &extent);
slouken@11303
   423
            s_BaseUnitsX = (extent.cx / 26 + 1) / 2;
slouken@11303
   424
        }
slouken@11303
   425
        /*s_BaseUnitsX = TM.tmAveCharWidth + 1;*/
slouken@7028
   426
        s_BaseUnitsY = TM.tmHeight;
slouken@7028
   427
    }
slouken@7191
   428
slouken@7191
   429
    /* Measure the *pixel* size of the string. */
slouken@7028
   430
    wmessage = WIN_UTF8ToString(messageboxdata->message);
slouken@7028
   431
    SDL_zero(TextSize);
slouken@11303
   432
    DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT);
slouken@7028
   433
slouken@7191
   434
    /* Add some padding for hangs, etc. */
slouken@7028
   435
    TextSize.right += 2;
slouken@7028
   436
    TextSize.bottom += 2;
slouken@7028
   437
slouken@7191
   438
    /* Done with the DC, and the string */
slouken@7028
   439
    DeleteDC(FontDC);
slouken@7028
   440
    SDL_free(wmessage);
slouken@7028
   441
slouken@7191
   442
    /* Increase the size of the dialog by some border spacing around the text. */
slouken@7028
   443
    Size.cx = TextSize.right - TextSize.left;
slouken@7028
   444
    Size.cy = TextSize.bottom - TextSize.top;
slouken@7028
   445
    Size.cx += TextMargin * 2;
slouken@7028
   446
    Size.cy += TextMargin * 2;
slouken@7028
   447
slouken@7191
   448
    /* Ensure the size is wide enough for all of the buttons. */
slouken@7028
   449
    if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin)
slouken@7028
   450
        Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
slouken@7028
   451
slouken@7191
   452
    /* Add vertical space for the buttons and border. */
slouken@7028
   453
    Size.cy += ButtonHeight + TextMargin;
slouken@7028
   454
slouken@7028
   455
    dialog = CreateDialogData(Size.cx, Size.cy, messageboxdata->title);
slouken@6620
   456
    if (!dialog) {
slouken@6620
   457
        return -1;
slouken@6620
   458
    }
slouken@6620
   459
slouken@7028
   460
    if (!AddDialogStatic(dialog, TextMargin, TextMargin, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
slouken@6620
   461
        FreeDialogData(dialog);
slouken@6620
   462
        return -1;
slouken@6620
   463
    }
slouken@6620
   464
slouken@7191
   465
    /* Align the buttons to the right/bottom. */
slouken@10478
   466
    x = Size.cx - (ButtonWidth + ButtonMargin) * messageboxdata->numbuttons;
slouken@7028
   467
    y = Size.cy - ButtonHeight - ButtonMargin;
slouken@10478
   468
    for (i = messageboxdata->numbuttons - 1; i >= 0; --i) {
slouken@6620
   469
        SDL_bool isDefault;
slouken@6620
   470
slouken@6620
   471
        if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
slouken@6620
   472
            isDefault = SDL_TRUE;
slouken@6620
   473
        } else {
slouken@6620
   474
            isDefault = SDL_FALSE;
slouken@6620
   475
        }
slouken@11228
   476
        if (!AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttons[i].text, buttons[i].buttonid, isDefault)) {
slouken@6620
   477
            FreeDialogData(dialog);
slouken@6620
   478
            return -1;
slouken@6620
   479
        }
slouken@10478
   480
        x += ButtonWidth + ButtonMargin;
slouken@6620
   481
    }
slouken@6620
   482
icculus@11036
   483
    /* If we have a parent window, get the Instance and HWND for them
icculus@11036
   484
     * so that our little dialog gets exclusive focus at all times. */
icculus@11037
   485
    if (messageboxdata->window) {
icculus@11036
   486
        ParentWindow = ((SDL_WindowData*)messageboxdata->window->driverdata)->hwnd;
icculus@11037
   487
    }
icculus@11036
   488
slouken@11375
   489
    *buttonid = (int)DialogBoxIndirect(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc);
slouken@6620
   490
slouken@6620
   491
    FreeDialogData(dialog);
slouken@6620
   492
    return 0;
slouken@6620
   493
}
slouken@6620
   494
icculus@11909
   495
/* TaskDialogIndirect procedure
icculus@11909
   496
 * This is because SDL targets Windows XP (0x501), so this is not defined in the platform SDK.
icculus@11909
   497
 */
icculus@11909
   498
typedef HRESULT(FAR WINAPI *TASKDIALOGINDIRECTPROC)(_In_ const TASKDIALOGCONFIG *pTaskConfig, _Out_opt_ int *pnButton, _Out_opt_ int *pnRadioButton, _Out_opt_ BOOL *pfVerificationFlagChecked);
icculus@11909
   499
icculus@11909
   500
int
icculus@11909
   501
WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
icculus@11909
   502
{
icculus@11909
   503
    HWND ParentWindow = NULL;
icculus@11909
   504
    wchar_t *wmessage;
icculus@11909
   505
    wchar_t *wtitle;
icculus@11909
   506
    TASKDIALOGCONFIG TaskConfig;
icculus@11909
   507
    TASKDIALOG_BUTTON *pButtons;
icculus@11909
   508
    TASKDIALOG_BUTTON *pButton;
icculus@11909
   509
    HMODULE hComctl32;
icculus@11909
   510
    TASKDIALOGINDIRECTPROC pfnTaskDialogIndirect;
icculus@11909
   511
    HRESULT hr;
icculus@11909
   512
    int nButton;
icculus@11909
   513
    int nCancelButton;
icculus@11909
   514
    int i;
icculus@11909
   515
icculus@11909
   516
    /* If we cannot load comctl32.dll use the old messagebox! */
icculus@11909
   517
    hComctl32 = LoadLibrary(TEXT("Comctl32.dll"));
icculus@11909
   518
    if (hComctl32 == NULL) {
icculus@11909
   519
        return WIN_ShowOldMessageBox(messageboxdata,buttonid);
icculus@11909
   520
    }
icculus@11909
   521
    
icculus@11909
   522
    /* If TaskDialogIndirect doesn't exist use the old messagebox!
icculus@11909
   523
       This will fail prior to Windows Vista.
icculus@11909
   524
       The manifest file in the application may require targeting version 6 of comctl32.dll, even
icculus@11909
   525
       when we use LoadLibrary here!
icculus@11909
   526
       If you don't want to bother with manifests, put this #pragma in your app's source code somewhere:
icculus@11909
   527
       pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0'  processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
icculus@11909
   528
     */
icculus@11909
   529
    pfnTaskDialogIndirect = (TASKDIALOGINDIRECTPROC) GetProcAddress(hComctl32, "TaskDialogIndirect");
icculus@11909
   530
    if (pfnTaskDialogIndirect == NULL) {
icculus@11909
   531
        FreeLibrary(hComctl32);
icculus@11909
   532
        return WIN_ShowOldMessageBox(messageboxdata, buttonid);
icculus@11909
   533
    }
icculus@11909
   534
icculus@11909
   535
    /* If we have a parent window, get the Instance and HWND for them
icculus@11909
   536
       so that our little dialog gets exclusive focus at all times. */
icculus@11909
   537
    if (messageboxdata->window) {
icculus@11909
   538
        ParentWindow = ((SDL_WindowData *) messageboxdata->window->driverdata)->hwnd;
icculus@11909
   539
    }
icculus@11909
   540
icculus@11909
   541
    wmessage = WIN_UTF8ToString(messageboxdata->message);
icculus@11909
   542
    wtitle = WIN_UTF8ToString(messageboxdata->title);
icculus@11909
   543
icculus@11909
   544
    SDL_zero(TaskConfig);
icculus@11909
   545
    TaskConfig.cbSize = sizeof (TASKDIALOGCONFIG);
icculus@11909
   546
    TaskConfig.hwndParent = ParentWindow;
icculus@11909
   547
    TaskConfig.dwFlags = TDF_SIZE_TO_CONTENT;
icculus@11909
   548
    TaskConfig.pszWindowTitle = wtitle;
icculus@11909
   549
    if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
icculus@11909
   550
        TaskConfig.pszMainIcon = TD_ERROR_ICON;
icculus@11909
   551
    } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
icculus@11909
   552
        TaskConfig.pszMainIcon = TD_WARNING_ICON;
icculus@11909
   553
    } else if (messageboxdata->flags & SDL_MESSAGEBOX_INFORMATION) {
icculus@11909
   554
        TaskConfig.pszMainIcon = TD_INFORMATION_ICON;
icculus@11909
   555
    } else {
icculus@11909
   556
        TaskConfig.pszMainIcon = NULL;
icculus@11909
   557
    }
icculus@11909
   558
icculus@11909
   559
    TaskConfig.pszContent = wmessage;
icculus@11909
   560
    TaskConfig.cButtons = messageboxdata->numbuttons;
icculus@11909
   561
    pButtons = SDL_malloc(sizeof (TASKDIALOG_BUTTON) * messageboxdata->numbuttons);
icculus@11909
   562
    TaskConfig.nDefaultButton = 0;
icculus@11909
   563
    for (i = 0; i < messageboxdata->numbuttons; i++)
icculus@11909
   564
    {
icculus@11909
   565
        pButton = &pButtons[messageboxdata->numbuttons-1-i];
icculus@11909
   566
        if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
icculus@11909
   567
            nCancelButton = messageboxdata->buttons[i].buttonid;
icculus@11909
   568
            pButton->nButtonID = 2;
icculus@11909
   569
        } else {
icculus@11909
   570
            pButton->nButtonID = messageboxdata->buttons[i].buttonid + 1;
icculus@11909
   571
            if (pButton->nButtonID >= 2) {
icculus@11909
   572
                pButton->nButtonID++;
icculus@11909
   573
            }
icculus@11909
   574
        }
icculus@11909
   575
        pButton->pszButtonText = WIN_UTF8ToString(messageboxdata->buttons[i].text);
icculus@11909
   576
        if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
icculus@11909
   577
            TaskConfig.nDefaultButton = pButton->nButtonID;
icculus@11909
   578
        }
icculus@11909
   579
    }
icculus@11909
   580
    TaskConfig.pButtons = pButtons;
icculus@11909
   581
icculus@11909
   582
    /* Show the Task Dialog */
icculus@11909
   583
    hr = pfnTaskDialogIndirect(&TaskConfig, &nButton, NULL, NULL);
icculus@11909
   584
icculus@11909
   585
    /* Free everything */
icculus@11909
   586
    FreeLibrary(hComctl32);
icculus@11909
   587
    SDL_free(wmessage);
icculus@11909
   588
    SDL_free(wtitle);
icculus@11909
   589
    for (i = 0; i < messageboxdata->numbuttons; i++) {
icculus@11909
   590
        SDL_free((wchar_t *) pButtons[i].pszButtonText);
icculus@11909
   591
    }
icculus@11909
   592
    SDL_free(pButtons);
icculus@11909
   593
icculus@11909
   594
    /* Check the Task Dialog was successful and give the result */
icculus@11909
   595
    if (SUCCEEDED(hr)) {
icculus@11909
   596
        if (nButton == 2) {
icculus@11909
   597
            *buttonid = nCancelButton;
icculus@11909
   598
        } else if (nButton > 2) {
icculus@11909
   599
            *buttonid = nButton-1-1;
icculus@11909
   600
        } else {
icculus@11909
   601
            *buttonid = nButton-1;
icculus@11909
   602
        }
icculus@11909
   603
        return 0;
icculus@11909
   604
    }
icculus@11909
   605
icculus@11909
   606
    /* We failed showing the Task Dialog, use the old message box! */
icculus@11909
   607
    return WIN_ShowOldMessageBox(messageboxdata, buttonid);
icculus@11909
   608
}
icculus@11909
   609
slouken@6620
   610
#endif /* SDL_VIDEO_DRIVER_WINDOWS */
slouken@6620
   611
slouken@6620
   612
/* vi: set ts=4 sw=4 expandtab: */