1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /*
20  * PROJECT:         ReactOS user32.dll
21  * FILE:            win32ss/user/user32/windows/messagebox.c
22  * PURPOSE:         Message Boxes
23  * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
24  *                  Thomas Weidenmueller (w3seek@users.sourceforge.net)
25  * UPDATE HISTORY:
26  *      2003/07/28  Added some NT features
27  *      2003/07/27  Code ported from wine
28  *      09-05-2001  CSH  Created
29  */
30 
31 #include <user32.h>
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(user32);
34 
35 /* DEFINES *******************************************************************/
36 
37 #define MSGBOX_IDICON   (1088)
38 #define MSGBOX_IDTEXT   (0xffff)
39 
40 #define IDI_HANDW          MAKEINTRESOURCEW(32513)
41 #define IDI_QUESTIONW      MAKEINTRESOURCEW(32514)
42 #define IDI_EXCLAMATIONW   MAKEINTRESOURCEW(32515)
43 #define IDI_ASTERISKW      MAKEINTRESOURCEW(32516)
44 #define IDI_WINLOGOW       MAKEINTRESOURCEW(32517)
45 
46 
47 /* MessageBox metrics */
48 
49 #define BTN_CX (75)
50 #define BTN_CY (23)
51 
52 #define MSGBOXEX_SPACING        (16)
53 #define MSGBOXEX_BUTTONSPACING  (6)
54 #define MSGBOXEX_MARGIN         (12)
55 #define MSGBOXEX_MAXBTNSTR      (32)
56 #define MSGBOXEX_MAXBTNS        (4)
57 
58 /* Rescale logical coordinates */
59 #define RESCALE_X(_x, _units)   (((_x) * 4 + (_units).cx - 1) / (_units).cx)
60 #define RESCALE_Y(_y, _units)   (((_y) * 8 + (_units).cy - 1) / (_units).cy)
61 
62 
63 /* MessageBox button helpers */
64 
65 #define DECLARE_MB_1(_btn0) \
66     { 1, { ID##_btn0, 0, 0 }, { IDS_##_btn0, 0, 0 } }
67 
68 #define DECLARE_MB_2(_btn0, _btn1) \
69     { 2, { ID##_btn0, ID##_btn1, 0 }, { IDS_##_btn0, IDS_##_btn1, 0 } }
70 
71 #define DECLARE_MB_3(_btn0, _btn1, _btn2) \
72     { 3, { ID##_btn0, ID##_btn1, ID##_btn2 }, { IDS_##_btn0, IDS_##_btn1, IDS_##_btn2 } }
73 
74 typedef struct _MSGBTNINFO
75 {
76     DWORD btnCnt;
77     INT   btnIdx[MSGBOXEX_MAXBTNS];
78     UINT  btnIds[MSGBOXEX_MAXBTNS];
79 } MSGBTNINFO, *PMSGBTNINFO;
80 
81 /* Default MessageBox buttons */
82 static const MSGBTNINFO MsgBtnInfo[] =
83 {
84     /* MB_OK (0) */
85     DECLARE_MB_1(OK),
86     /* MB_OKCANCEL (1) */
87     DECLARE_MB_2(OK, CANCEL),
88     /* MB_ABORTRETRYIGNORE (2) */
89     DECLARE_MB_3(ABORT, RETRY, IGNORE),
90     /* MB_YESNOCANCEL (3) */
91     DECLARE_MB_3(YES, NO, CANCEL),
92     /* MB_YESNO (4) */
93     DECLARE_MB_2(YES, NO),
94     /* MB_RETRYCANCEL (5) */
95     DECLARE_MB_2(RETRY, CANCEL),
96     /* MB_CANCELTRYCONTINUE (6) */
97     DECLARE_MB_3(CANCEL, TRYAGAIN, CONTINUE)
98 };
99 
100 
101 /* INTERNAL FUNCTIONS ********************************************************/
102 
103 static VOID MessageBoxTextToClipboard(HWND DialogWindow)
104 {
105     HWND hwndText;
106     PMSGBOXDATA mbd;
107     int cchTotal, cchTitle, cchText, cchButton, i, n, cchBuffer;
108     LPWSTR pszBuffer, pszBufferPos, pMessageBoxText, pszTitle, pszText, pszButton;
109     WCHAR szButton[MSGBOXEX_MAXBTNSTR];
110     HGLOBAL hGlobal;
111 
112     static const WCHAR szLine[] = L"---------------------------\r\n";
113 
114     mbd = (PMSGBOXDATA)GetPropW(DialogWindow, L"ROS_MSGBOX");
115     hwndText = GetDlgItem(DialogWindow, MSGBOX_IDTEXT);
116     cchTitle = GetWindowTextLengthW(DialogWindow) + 1;
117     cchText = GetWindowTextLengthW(hwndText) + 1;
118 
119     if (!mbd)
120         return;
121 
122     pMessageBoxText = (LPWSTR)RtlAllocateHeap(GetProcessHeap(), 0, (cchTitle + cchText) * sizeof(WCHAR));
123 
124     if (pMessageBoxText == NULL)
125     {
126         RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
127         return;
128     }
129 
130     pszTitle = pMessageBoxText;
131     pszText = pMessageBoxText + cchTitle;
132 
133     if (GetWindowTextW(DialogWindow, pszTitle, cchTitle) == 0 ||
134         GetWindowTextW(hwndText, pszText, cchText) == 0)
135     {
136         RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
137         return;
138     }
139 
140     /*
141      * Calculate the total buffer size.
142      */
143     cchTotal = 6 + cchTitle + cchText + (lstrlenW(szLine) * 4) + (mbd->dwButtons * MSGBOXEX_MAXBTNSTR + 3);
144 
145     hGlobal = GlobalAlloc(GHND, cchTotal * sizeof(WCHAR));
146 
147     pszBuffer = (LPWSTR)GlobalLock(hGlobal);
148 
149     if (pszBuffer == NULL)
150     {
151         RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
152         GlobalFree(hGlobal);
153         return;
154     }
155 
156     /*
157      * First format title and text.
158      * ------------------
159      * Title
160      * ------------------
161      * Text
162      * ------------------
163      */
164     cchBuffer = wsprintfW(pszBuffer, L"%s%s\r\n%s%s\r\n%s", szLine, pszTitle, szLine, pszText, szLine);
165     pszBufferPos = pszBuffer + cchBuffer;
166 
167     for (i = 0; i < mbd->dwButtons; i++)
168     {
169         GetDlgItemTextW(DialogWindow, mbd->pidButton[i], szButton, MSGBOXEX_MAXBTNSTR);
170 
171         cchButton = strlenW(szButton);
172         pszButton = szButton;
173 
174         /* Skip '&' character. */
175         if (szButton[0] == '&')
176         {
177             pszButton = pszButton + 1;
178             cchButton = cchButton - 1;
179         }
180 
181         for (n = 0; n < cchButton; n++)
182             *(pszBufferPos++) = pszButton[n];
183 
184         /* Add spaces. */
185         *(pszBufferPos++) = L' ';
186         *(pszBufferPos++) = L' ';
187         *(pszBufferPos++) = L' ';
188     }
189 
190     wsprintfW(pszBufferPos, L"\r\n%s", szLine);
191 
192     GlobalUnlock(hGlobal);
193 
194     if (OpenClipboard(DialogWindow))
195     {
196         EmptyClipboard();
197         SetClipboardData(CF_UNICODETEXT, hGlobal);
198         CloseClipboard();
199     }
200     else
201     {
202         GlobalFree(hGlobal);
203     }
204     RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
205 }
206 
207 static INT_PTR CALLBACK MessageBoxProc(
208     HWND hwnd, UINT message,
209     WPARAM wParam, LPARAM lParam)
210 {
211     int Alert;
212     PMSGBOXDATA mbd;
213     HELPINFO hi;
214     HWND hwndOwner;
215 
216     switch (message)
217     {
218     case WM_INITDIALOG:
219     {
220         mbd = (PMSGBOXDATA)lParam;
221 
222         SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)mbd);
223         NtUserxSetMessageBox(hwnd);
224 
225         if (!GetPropW(hwnd, L"ROS_MSGBOX"))
226         {
227             SetPropW(hwnd, L"ROS_MSGBOX", (HANDLE)lParam);
228 
229             if (mbd->mbp.dwContextHelpId)
230                 SetWindowContextHelpId(hwnd, mbd->mbp.dwContextHelpId);
231 
232             if (mbd->mbp.lpszIcon)
233             {
234                 SendDlgItemMessageW(hwnd, MSGBOX_IDICON, STM_SETICON, (WPARAM)(HICON)mbd->mbp.lpszIcon, 0);
235                 Alert = ALERT_SYSTEM_WARNING;
236             }
237             else // Setup the rest of the alerts.
238             {
239                 switch (mbd->mbp.dwStyle & MB_ICONMASK)
240                 {
241                 case MB_ICONWARNING:
242                     Alert = ALERT_SYSTEM_WARNING;
243                     break;
244                 case MB_ICONERROR:
245                     Alert = ALERT_SYSTEM_ERROR;
246                     break;
247                 case MB_ICONQUESTION:
248                     Alert = ALERT_SYSTEM_QUERY;
249                     break;
250                 default:
251                     Alert = ALERT_SYSTEM_INFORMATIONAL;
252                     /* fall through */
253                 }
254             }
255             /* Send out the alert notifications. */
256             NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_ALERT, Alert);
257 
258             switch (mbd->mbp.dwStyle & MB_TYPEMASK)
259             {
260             case MB_ABORTRETRYIGNORE:
261             case MB_YESNO:
262                 RemoveMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
263                 break;
264             }
265             ASSERT(mbd->uDefButton < mbd->dwButtons);
266             SetFocus(GetDlgItem(hwnd, mbd->pidButton[mbd->uDefButton]));
267             if (mbd->dwTimeout && (mbd->dwTimeout != (UINT)-1))
268                 SetTimer(hwnd, 0, mbd->dwTimeout, NULL);
269         }
270         return 0;
271     }
272 
273     case WM_COMMAND:
274         switch (LOWORD(wParam))
275         {
276             case IDOK:
277             case IDCANCEL:
278             case IDABORT:
279             case IDRETRY:
280             case IDIGNORE:
281             case IDYES:
282             case IDNO:
283             case IDTRYAGAIN:
284             case IDCONTINUE:
285                 EndDialog(hwnd, wParam);
286                 return 0;
287             case IDHELP:
288                 /* send WM_HELP message to messagebox window */
289                 hi.cbSize = sizeof(hi);
290                 hi.iContextType = HELPINFO_WINDOW;
291                 hi.iCtrlId = LOWORD(wParam);
292                 hi.hItemHandle = (HANDLE)lParam;
293                 hi.dwContextId = 0;
294                 GetCursorPos(&hi.MousePos);
295                 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
296                 return 0;
297         }
298         return 0;
299 
300     case WM_COPY:
301         MessageBoxTextToClipboard(hwnd);
302         return 0;
303 
304     case WM_HELP:
305     {
306         mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX");
307         if (!mbd)
308             return 0;
309         memcpy(&hi, (void *)lParam, sizeof(hi));
310         hi.dwContextId = GetWindowContextHelpId(hwnd);
311 
312         if (mbd->mbp.lpfnMsgBoxCallback)
313         {
314             mbd->mbp.lpfnMsgBoxCallback(&hi);
315         }
316         else
317         {
318             hwndOwner = GetWindow(hwnd, GW_OWNER);
319             if (hwndOwner)
320                 SendMessageW(hwndOwner, WM_HELP, 0, (LPARAM)&hi);
321         }
322         return 0;
323     }
324 
325     case WM_CLOSE:
326     {
327         mbd = (PMSGBOXDATA)GetPropW(hwnd, L"ROS_MSGBOX");
328         if (!mbd)
329             return 0;
330         switch (mbd->mbp.dwStyle & MB_TYPEMASK)
331         {
332         case MB_ABORTRETRYIGNORE:
333         case MB_YESNO:
334             return 1;
335         }
336         EndDialog(hwnd, IDCANCEL);
337         return 1;
338     }
339 
340     case WM_TIMER:
341         if (wParam == 0)
342         {
343             EndDialog(hwnd, IDTIMEOUT);
344         }
345         return 0;
346     }
347     return 0;
348 }
349 
350 static int
351 MessageBoxTimeoutIndirectW(
352     CONST MSGBOXPARAMSW *lpMsgBoxParams, UINT dwTimeout)
353 {
354     int ret = 0;
355     MSGBOXDATA mbd;
356     MSGBTNINFO Buttons;
357     LPCWSTR ButtonText[MSGBOXEX_MAXBTNS];
358     DLGTEMPLATE *tpl;
359     DLGITEMTEMPLATE *iico, *itxt, *ibtn;
360     NONCLIENTMETRICSW nclm;
361     LPVOID buf;
362     BYTE *dest;
363     LPCWSTR caption, text;
364     HFONT hFont, hOldFont;
365     HICON hIcon;
366     HWND hDCWnd;
367     HDC hDC;
368     SIZE units;
369     int bufsize, caplen, textlen, i, btnleft, btntop;
370     size_t ButtonLen;
371     RECT btnrect, txtrect, rc;
372     SIZE btnsize;
373     POINT iconPos; SIZE iconSize;
374 
375 
376     ZeroMemory(&mbd, sizeof(mbd));
377     memcpy(&mbd.mbp, lpMsgBoxParams, sizeof(mbd.mbp));
378     lpMsgBoxParams = &mbd.mbp;
379 
380     mbd.wLanguageId = (WORD)lpMsgBoxParams->dwLanguageId; // FIXME!
381     mbd.dwTimeout   = dwTimeout;
382 
383     /* Create the selected buttons; unknown types will fall back to MB_OK */
384     i = (lpMsgBoxParams->dwStyle & MB_TYPEMASK);
385     if (i >= ARRAYSIZE(MsgBtnInfo))
386         i = MB_OK;
387 
388     /* Get buttons IDs */
389     Buttons = MsgBtnInfo[i];
390 
391     /* Add the Help button */
392     if (lpMsgBoxParams->dwStyle & MB_HELP)
393     {
394         Buttons.btnIdx[Buttons.btnCnt] = IDHELP;
395         Buttons.btnIds[Buttons.btnCnt] = IDS_HELP;
396         Buttons.btnCnt++;
397     }
398 
399     ASSERT(Buttons.btnCnt <= MSGBOXEX_MAXBTNS);
400 
401     mbd.pidButton = Buttons.btnIdx;
402     mbd.ppszButtonText = ButtonText;
403     mbd.dwButtons = Buttons.btnCnt;
404 
405     mbd.uDefButton = ((lpMsgBoxParams->dwStyle & MB_DEFMASK) >> 8);
406     /* Make the first button the default button if none other is */
407     if (mbd.uDefButton >= mbd.dwButtons)
408         mbd.uDefButton = 0;
409     // mbd.uCancelId;
410 
411 
412     /* Load the caption */
413     if (!lpMsgBoxParams->lpszCaption)
414     {
415         /* No caption, use the default one */
416         caplen = LoadStringW(User32Instance, IDS_ERROR, (LPWSTR)&caption, 0);
417     }
418     else if (IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
419     {
420         /* User-defined resource string */
421         caplen = LoadStringW(lpMsgBoxParams->hInstance,
422                              PtrToUlong(lpMsgBoxParams->lpszCaption),
423                              (LPWSTR)&caption, 0);
424     }
425     else
426     {
427         /* UNICODE string pointer */
428         caption = lpMsgBoxParams->lpszCaption;
429         caplen  = wcslen(caption);
430     }
431 
432     /* Load the text */
433     if (!lpMsgBoxParams->lpszText)
434     {
435         /* No text, use blank */
436         text    = L"";
437         textlen = 0;
438     }
439     else if (IS_INTRESOURCE(lpMsgBoxParams->lpszText))
440     {
441         /* User-defined resource string */
442         textlen = LoadStringW(lpMsgBoxParams->hInstance,
443                               PtrToUlong(lpMsgBoxParams->lpszText),
444                               (LPWSTR)&text, 0);
445     }
446     else
447     {
448         /* UNICODE string pointer */
449         text    = lpMsgBoxParams->lpszText;
450         textlen = wcslen(text);
451     }
452 
453     /* Load the icon */
454     switch (lpMsgBoxParams->dwStyle & MB_ICONMASK)
455     {
456         case MB_ICONEXCLAMATION: // case MB_ICONWARNING:
457             hIcon = LoadIconW(NULL, IDI_EXCLAMATIONW);
458             MessageBeep(MB_ICONEXCLAMATION);
459             break;
460         case MB_ICONQUESTION:
461             hIcon = LoadIconW(NULL, IDI_QUESTIONW);
462             MessageBeep(MB_ICONQUESTION);
463             break;
464         case MB_ICONASTERISK: // case MB_ICONINFORMATION:
465             hIcon = LoadIconW(NULL, IDI_ASTERISKW);
466             MessageBeep(MB_ICONASTERISK);
467             break;
468         case MB_ICONHAND: // case MB_ICONSTOP: case MB_ICONERROR:
469             hIcon = LoadIconW(NULL, IDI_HANDW);
470             MessageBeep(MB_ICONHAND);
471             break;
472         case MB_USERICON:
473             hIcon = LoadIconW(lpMsgBoxParams->hInstance, lpMsgBoxParams->lpszIcon);
474             MessageBeep(MB_OK);
475             break;
476         default:
477             /*
478              * By default, Windows 95/98/NT does not associate an icon
479              * to message boxes. So ReactOS should do the same.
480              */
481             hIcon = NULL;
482             MessageBeep(MB_OK);
483             break;
484     }
485     /* Reuse the internal pointer! */
486     ((MSGBOXPARAMSW*)lpMsgBoxParams)->lpszIcon = (LPCWSTR)hIcon;
487 
488     /* Basic space */
489     bufsize = sizeof(DLGTEMPLATE) +
490               2 * sizeof(WORD) +                /* menu and class */
491               (caplen + 1) * sizeof(WCHAR) +    /* title */
492               sizeof(WORD);                     /* font height */
493 
494     /* Space for the icon */
495     if (hIcon)
496     {
497         bufsize = ALIGN_UP(bufsize, DWORD);
498         bufsize += sizeof(DLGITEMTEMPLATE) +
499                    4 * sizeof(WORD) +
500                    sizeof(WCHAR);
501     }
502 
503     /* Space for the text */
504     bufsize = ALIGN_UP(bufsize, DWORD);
505     bufsize += sizeof(DLGITEMTEMPLATE) +
506                3 * sizeof(WORD) +
507                (textlen + 1) * sizeof(WCHAR);
508 
509     /* Space for the buttons */
510     for (i = 0; i < mbd.dwButtons; i++)
511     {
512         /* Get the default text of the buttons */
513         if (Buttons.btnIds[i])
514         {
515             LPCWSTR pStr;
516             ButtonLen = LoadStringW(User32Instance,
517                                     Buttons.btnIds[i],
518                                     (LPWSTR)&pStr, 0);
519             mbd.ppszButtonText[i] = RtlAllocateHeap(GetProcessHeap(), 0, (ButtonLen + 1) * sizeof(WCHAR));
520             if (mbd.ppszButtonText[i])
521             {
522                 memcpy((LPWSTR)mbd.ppszButtonText[i], pStr, ButtonLen * sizeof(WCHAR));
523                 ((LPWSTR)mbd.ppszButtonText[i])[ButtonLen] = 0;
524             }
525             else
526             {
527                 mbd.ppszButtonText[i] = L"";
528                 ButtonLen = 0;
529             }
530         }
531         else
532         {
533             /* No text, use blank */
534             mbd.ppszButtonText[i] = L"";
535             ButtonLen = 0;
536         }
537 
538         bufsize = ALIGN_UP(bufsize, DWORD);
539         bufsize += sizeof(DLGITEMTEMPLATE) +
540                    3 * sizeof(WORD) +
541                    (ButtonLen + 1) * sizeof(WCHAR);
542     }
543 
544     /* Allocate the dialog template */
545     buf = RtlAllocateHeap(GetProcessHeap(), 0, bufsize);
546     if (!buf)
547         goto Quit;
548 
549     iico = itxt = NULL;
550 
551 
552     nclm.cbSize = sizeof(nclm);
553     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
554     hFont = CreateFontIndirectW(&nclm.lfMessageFont);
555     if (!hFont)
556     {
557         ERR("Cannot retrieve nclm.lfMessageFont!\n");
558         goto Quit;
559     }
560 
561     hDCWnd = NULL;
562     hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE);
563     if (!hDC)
564     {
565         /* Retry with the DC of the owner window */
566         hDCWnd = lpMsgBoxParams->hwndOwner;
567         hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE);
568     }
569     if (!hDC)
570     {
571         ERR("GetDCEx() failed, bail out!\n");
572         goto Quit;
573     }
574     hOldFont = SelectObject(hDC, hFont);
575 
576     units.cx = GdiGetCharDimensions(hDC, NULL, &units.cy);
577     if (!units.cx)
578     {
579         DWORD defUnits;
580         ERR("GdiGetCharDimensions() failed, falling back to default values!\n");
581         defUnits = GetDialogBaseUnits();
582         units.cx = LOWORD(defUnits);
583         units.cy = HIWORD(defUnits);
584     }
585 
586     /* Calculate the caption rectangle */
587     txtrect.right = MulDiv(GetSystemMetrics(SM_CXSCREEN), 4, 5);
588     if (hIcon)
589         txtrect.right -= GetSystemMetrics(SM_CXICON) + MSGBOXEX_SPACING;
590     txtrect.top = txtrect.left = txtrect.bottom = 0;
591     if (textlen != 0)
592     {
593         DrawTextW(hDC, text, textlen, &txtrect,
594                   DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_EXPANDTABS | DT_EXTERNALLEADING | DT_EDITCONTROL | DT_CALCRECT);
595     }
596     else
597     {
598         txtrect.right = txtrect.left + 1;
599         txtrect.bottom = txtrect.top + 1;
600     }
601     txtrect.right++;
602 
603     /* Calculate the maximum buttons size */
604     btnsize.cx = BTN_CX;
605     btnsize.cy = BTN_CY;
606     btnrect.left = btnrect.top = 0;
607     for (i = 0; i < mbd.dwButtons; i++)
608     {
609         // btnrect.right = btnrect.bottom = 0; // FIXME: Is it needed??
610         DrawTextW(hDC, mbd.ppszButtonText[i], wcslen(mbd.ppszButtonText[i]),
611                   &btnrect, DT_LEFT | DT_SINGLELINE | DT_CALCRECT);
612         btnsize.cx = max(btnsize.cx, btnrect.right);
613         btnsize.cy = max(btnsize.cy, btnrect.bottom);
614     }
615 
616     if (hOldFont)
617         SelectObject(hDC, hOldFont);
618 
619     ReleaseDC(hDCWnd, hDC);
620 
621     if (hFont)
622         DeleteObject(hFont);
623 
624 
625     /* Calculate position and size of controls */
626 
627 
628     /* Calculate position and size of the icon */
629     rc.left = rc.bottom = rc.right = 0;
630     btntop = 0;
631     if (hIcon)
632     {
633         rc.right = GetSystemMetrics(SM_CXICON);
634         rc.bottom = GetSystemMetrics(SM_CYICON);
635 #ifdef MSGBOX_ICONVCENTER
636         rc.top = MSGBOXEX_MARGIN + ((max(txtrect.bottom, rc.bottom) - rc.bottom) / 2);
637         rc.top = max(MSGBOXEX_SPACING, rc.top);
638 #else
639         rc.top = MSGBOXEX_MARGIN;
640 #endif
641         btnleft = (mbd.dwButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
642         if (btnleft > txtrect.right + rc.right + MSGBOXEX_SPACING)
643         {
644 #ifdef MSGBOX_TEXTHCENTER
645             rc.left = MSGBOXEX_MARGIN + ((btnleft - txtrect.right - rc.right - MSGBOXEX_SPACING) / 2);
646 #else
647             rc.left = MSGBOXEX_MARGIN;
648 #endif
649             btnleft = MSGBOXEX_MARGIN;
650         }
651         else
652         {
653             rc.left = MSGBOXEX_MARGIN;
654             btnleft = MSGBOXEX_MARGIN + ((txtrect.right + rc.right + MSGBOXEX_SPACING - btnleft) / 2);
655         }
656 
657         iconPos.x = RESCALE_X(rc.left, units);
658         iconPos.y = RESCALE_Y(rc.top, units);
659         iconSize.cx = RESCALE_X(rc.right, units);
660         iconSize.cy = RESCALE_Y(rc.bottom, units);
661 
662         btntop = rc.top + rc.bottom + MSGBOXEX_SPACING;
663         rc.left += rc.right + MSGBOXEX_SPACING;
664     }
665     else
666     {
667         btnleft = (mbd.dwButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
668         if (btnleft > txtrect.right)
669         {
670 #ifdef MSGBOX_TEXTHCENTER
671             rc.left = MSGBOXEX_MARGIN + ((btnleft - txtrect.right) / 2);
672 #else
673             rc.left = MSGBOXEX_MARGIN;
674 #endif
675             btnleft = MSGBOXEX_MARGIN;
676         }
677         else
678         {
679             rc.left = MSGBOXEX_MARGIN;
680             btnleft = MSGBOXEX_MARGIN + ((txtrect.right - btnleft) / 2);
681         }
682     }
683 
684 
685 
686     /* Initialize the dialog template */
687     tpl = (DLGTEMPLATE *)buf;
688 
689     tpl->style = WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SYSMENU |
690                  DS_CENTER | DS_SETFONT | DS_MODALFRAME | DS_NOIDLEMSG;
691     tpl->dwExtendedStyle = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
692     if (lpMsgBoxParams->dwStyle & MB_TOPMOST)
693         tpl->dwExtendedStyle |= WS_EX_TOPMOST;
694     if (lpMsgBoxParams->dwStyle & MB_RIGHT)
695         tpl->dwExtendedStyle |= WS_EX_RIGHT;
696     tpl->x = 100;
697     tpl->y = 100;
698     tpl->cdit = mbd.dwButtons + (hIcon ? 1 : 0) + 1; /* Buttons, icon and text */
699 
700     dest = (BYTE *)(tpl + 1);
701 
702     *(DWORD*)dest = 0;  /* no menu and use default window class */
703     dest += 2 * sizeof(WORD);
704     memcpy(dest, caption, caplen * sizeof(WCHAR));
705     dest += caplen * sizeof(WCHAR);
706     *(WCHAR*)dest = L'\0';
707     dest += sizeof(WCHAR);
708 
709     /*
710      * A font point size (height) of 0x7FFF means that we use
711      * the message box font (NONCLIENTMETRICSW.lfMessageFont).
712      */
713     *(WORD*)dest = 0x7FFF;
714     dest += sizeof(WORD);
715 
716     /* Create the icon */
717     if (hIcon)
718     {
719         dest = ALIGN_UP_POINTER(dest, DWORD);
720         iico = (DLGITEMTEMPLATE *)dest;
721         iico->style = WS_CHILD | WS_VISIBLE | SS_ICON;
722         iico->dwExtendedStyle = 0;
723         iico->id = MSGBOX_IDICON;
724 
725         iico->x = iconPos.x;
726         iico->y = iconPos.y;
727         iico->cx = iconSize.cx;
728         iico->cy = iconSize.cy;
729 
730         dest += sizeof(DLGITEMTEMPLATE);
731         *(WORD*)dest = 0xFFFF;
732         dest += sizeof(WORD);
733         *(WORD*)dest = 0x0082; /* static control */
734         dest += sizeof(WORD);
735         *(WORD*)dest = 0xFFFF;
736         dest += sizeof(WORD);
737         *(WCHAR*)dest = 0;
738         dest += sizeof(WCHAR);
739         *(WORD*)dest = 0;
740         dest += sizeof(WORD);
741     }
742 
743     /* Create static for text */
744     dest = ALIGN_UP_POINTER(dest, DWORD);
745     itxt = (DLGITEMTEMPLATE *)dest;
746     itxt->style = WS_CHILD | WS_VISIBLE | SS_NOPREFIX;
747     if (lpMsgBoxParams->dwStyle & MB_RIGHT)
748         itxt->style |= SS_RIGHT;
749     else
750         itxt->style |= SS_LEFT;
751     itxt->dwExtendedStyle = 0;
752     itxt->id = MSGBOX_IDTEXT;
753     dest += sizeof(DLGITEMTEMPLATE);
754     *(WORD*)dest = 0xFFFF;
755     dest += sizeof(WORD);
756     *(WORD*)dest = 0x0082; /* static control */
757     dest += sizeof(WORD);
758     memcpy(dest, text, textlen * sizeof(WCHAR));
759     dest += textlen * sizeof(WCHAR);
760     *(WCHAR*)dest = 0;
761     dest += sizeof(WCHAR);
762     *(WORD*)dest = 0;
763     dest += sizeof(WORD);
764 
765 
766     /* Calculate position of the text */
767     rc.top = MSGBOXEX_MARGIN + ((rc.bottom - txtrect.bottom) / 2);
768     rc.top = max(rc.top, MSGBOXEX_MARGIN);
769 
770 
771     /* Make the first button the default button if none other is */
772     if (mbd.uDefButton >= mbd.dwButtons)
773         mbd.uDefButton = 0;
774 
775     /* Create and calculate the position of the buttons */
776     btntop = max(rc.top + txtrect.bottom + MSGBOXEX_SPACING, btntop);
777     for (i = 0; i < mbd.dwButtons; i++)
778     {
779         ButtonLen = wcslen(mbd.ppszButtonText[i]);
780 
781         dest = ALIGN_UP_POINTER(dest, DWORD);
782         ibtn = (DLGITEMTEMPLATE *)dest;
783 
784         ibtn->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
785         if (i == mbd.uDefButton)
786             ibtn->style |= BS_DEFPUSHBUTTON;
787         else
788             ibtn->style |= BS_PUSHBUTTON;
789 
790         ibtn->dwExtendedStyle = 0;
791         ibtn->id = mbd.pidButton[i];
792         dest += sizeof(DLGITEMTEMPLATE);
793         *(WORD*)dest = 0xFFFF;
794         dest += sizeof(WORD);
795         *(WORD*)dest = 0x0080; /* button control */
796         dest += sizeof(WORD);
797         memcpy(dest, mbd.ppszButtonText[i], ButtonLen * sizeof(WCHAR));
798         dest += ButtonLen * sizeof(WCHAR);
799         *(WORD*)dest = 0;
800         dest += sizeof(WORD);
801         *(WORD*)dest = 0;
802         dest += sizeof(WORD);
803 
804         ibtn->x = RESCALE_X(btnleft, units);
805         ibtn->y = RESCALE_Y(btntop, units);
806         ibtn->cx = RESCALE_X(btnsize.cx, units);
807         ibtn->cy = RESCALE_Y(btnsize.cy, units);
808         btnleft += btnsize.cx + MSGBOXEX_BUTTONSPACING;
809     }
810 
811     /* Calculate the size and position of the messagebox window */
812     btnleft = max(btnleft - MSGBOXEX_BUTTONSPACING, rc.left + txtrect.right);
813     btnleft += MSGBOXEX_MARGIN;
814     btntop +=  btnsize.cy + MSGBOXEX_MARGIN;
815 
816     /* Set the size and position of the static message */
817     itxt->x = RESCALE_X(rc.left, units);
818     itxt->y = RESCALE_Y(rc.top, units);
819     itxt->cx = RESCALE_X(btnleft - rc.left - MSGBOXEX_MARGIN, units);
820     itxt->cy = RESCALE_Y(txtrect.bottom, units);
821 
822     /* Set the size of the window */
823     tpl->cx = RESCALE_X(btnleft, units);
824     tpl->cy = RESCALE_Y(btntop, units);
825 
826     /* Finally show the messagebox */
827     ret = DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, tpl,
828                                   lpMsgBoxParams->hwndOwner,
829                                   MessageBoxProc, (LPARAM)&mbd);
830 
831 Quit:
832     RtlFreeHeap(GetProcessHeap(), 0, buf);
833 
834     for (i = 0; i < mbd.dwButtons; i++)
835     {
836         if (mbd.ppszButtonText[i] && *(mbd.ppszButtonText[i]))
837             RtlFreeHeap(GetProcessHeap(), 0, (LPWSTR)mbd.ppszButtonText[i]);
838     }
839 
840     return ret;
841 }
842 
843 
844 /* FUNCTIONS *****************************************************************/
845 
846 /*
847  * @implemented
848  */
849 int
850 WINAPI
851 MessageBoxA(
852     IN HWND hWnd,
853     IN LPCSTR lpText,
854     IN LPCSTR lpCaption,
855     IN UINT uType)
856 {
857     return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
858 }
859 
860 /*
861  * @implemented
862  */
863 int
864 WINAPI
865 MessageBoxW(
866     IN HWND hWnd,
867     IN LPCWSTR lpText,
868     IN LPCWSTR lpCaption,
869     IN UINT uType)
870 {
871     return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
872 }
873 
874 
875 /*
876  * @implemented
877  */
878 int
879 WINAPI
880 MessageBoxExA(
881     IN HWND hWnd,
882     IN LPCSTR lpText,
883     IN LPCSTR lpCaption,
884     IN UINT uType,
885     IN WORD wLanguageId)
886 {
887     MSGBOXPARAMSA msgbox;
888 
889     msgbox.cbSize = sizeof(msgbox);
890     msgbox.hwndOwner = hWnd;
891     msgbox.hInstance = 0;
892     msgbox.lpszText = lpText;
893     msgbox.lpszCaption = lpCaption;
894     msgbox.dwStyle = uType;
895     msgbox.lpszIcon = NULL;
896     msgbox.dwContextHelpId = 0;
897     msgbox.lpfnMsgBoxCallback = NULL;
898     msgbox.dwLanguageId = wLanguageId;
899 
900     return MessageBoxIndirectA(&msgbox);
901 }
902 
903 /*
904  * @implemented
905  */
906 int
907 WINAPI
908 MessageBoxExW(
909     IN HWND hWnd,
910     IN LPCWSTR lpText,
911     IN LPCWSTR lpCaption,
912     IN UINT uType,
913     IN WORD wLanguageId)
914 {
915     MSGBOXPARAMSW msgbox;
916 
917     msgbox.cbSize = sizeof(msgbox);
918     msgbox.hwndOwner = hWnd;
919     msgbox.hInstance = 0;
920     msgbox.lpszText = lpText;
921     msgbox.lpszCaption = lpCaption;
922     msgbox.dwStyle = uType;
923     msgbox.lpszIcon = NULL;
924     msgbox.dwContextHelpId = 0;
925     msgbox.lpfnMsgBoxCallback = NULL;
926     msgbox.dwLanguageId = wLanguageId;
927 
928     return MessageBoxTimeoutIndirectW(&msgbox, (UINT)-1);
929 }
930 
931 
932 /*
933  * @implemented
934  */
935 int
936 WINAPI
937 MessageBoxIndirectA(
938     IN CONST MSGBOXPARAMSA* lpMsgBoxParams)
939 {
940     MSGBOXPARAMSW msgboxW;
941     UNICODE_STRING textW, captionW, iconW;
942     int ret;
943 
944     if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
945     {
946         RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText);
947         /*
948          * UNICODE_STRING objects are always allocated with an extra byte so you
949          * can null-term if you want
950          */
951         textW.Buffer[textW.Length / sizeof(WCHAR)] = L'\0';
952     }
953     else
954         textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText;
955 
956     if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
957     {
958         RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption);
959         /*
960          * UNICODE_STRING objects are always allocated with an extra byte so you
961          * can null-term if you want
962          */
963         captionW.Buffer[captionW.Length / sizeof(WCHAR)] = L'\0';
964     }
965     else
966         captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption;
967 
968     if (lpMsgBoxParams->dwStyle & MB_USERICON)
969     {
970         if (!IS_INTRESOURCE(lpMsgBoxParams->lpszIcon))
971         {
972             RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon);
973             /*
974              * UNICODE_STRING objects are always allocated with an extra byte so you
975              * can null-term if you want
976              */
977             iconW.Buffer[iconW.Length / sizeof(WCHAR)] = L'\0';
978         }
979         else
980             iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon;
981     }
982     else
983         iconW.Buffer = NULL;
984 
985     msgboxW.cbSize = sizeof(msgboxW);
986     msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner;
987     msgboxW.hInstance = lpMsgBoxParams->hInstance;
988     msgboxW.lpszText = textW.Buffer;
989     msgboxW.lpszCaption = captionW.Buffer;
990     msgboxW.dwStyle = lpMsgBoxParams->dwStyle;
991     msgboxW.lpszIcon = iconW.Buffer;
992     msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
993     msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
994     msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId;
995 
996     ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)-1);
997 
998     if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
999         RtlFreeUnicodeString(&textW);
1000 
1001     if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
1002         RtlFreeUnicodeString(&captionW);
1003 
1004     if ((lpMsgBoxParams->dwStyle & MB_USERICON) && !IS_INTRESOURCE(iconW.Buffer))
1005         RtlFreeUnicodeString(&iconW);
1006 
1007     return ret;
1008 }
1009 
1010 /*
1011  * @implemented
1012  */
1013 int
1014 WINAPI
1015 MessageBoxIndirectW(
1016     IN CONST MSGBOXPARAMSW* lpMsgBoxParams)
1017 {
1018     return MessageBoxTimeoutIndirectW(lpMsgBoxParams, (UINT)-1);
1019 }
1020 
1021 
1022 /*
1023  * @implemented
1024  */
1025 int
1026 WINAPI
1027 MessageBoxTimeoutA(
1028     IN HWND hWnd,
1029     IN LPCSTR lpText,
1030     IN LPCSTR lpCaption,
1031     IN UINT uType,
1032     IN WORD wLanguageId,
1033     IN DWORD dwTimeout)
1034 {
1035     MSGBOXPARAMSW msgboxW;
1036     UNICODE_STRING textW, captionW;
1037     int ret;
1038 
1039     if (!IS_INTRESOURCE(lpText))
1040         RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText);
1041     else
1042         textW.Buffer = (LPWSTR)lpText;
1043 
1044     if (!IS_INTRESOURCE(lpCaption))
1045         RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpCaption);
1046     else
1047         captionW.Buffer = (LPWSTR)lpCaption;
1048 
1049     msgboxW.cbSize = sizeof(msgboxW);
1050     msgboxW.hwndOwner = hWnd;
1051     msgboxW.hInstance = 0;
1052     msgboxW.lpszText = textW.Buffer;
1053     msgboxW.lpszCaption = captionW.Buffer;
1054     msgboxW.dwStyle = uType;
1055     msgboxW.lpszIcon = NULL;
1056     msgboxW.dwContextHelpId = 0;
1057     msgboxW.lpfnMsgBoxCallback = NULL;
1058     msgboxW.dwLanguageId = wLanguageId;
1059 
1060     ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)dwTimeout);
1061 
1062     if (!IS_INTRESOURCE(textW.Buffer))
1063         RtlFreeUnicodeString(&textW);
1064 
1065     if (!IS_INTRESOURCE(captionW.Buffer))
1066         RtlFreeUnicodeString(&captionW);
1067 
1068     return ret;
1069 }
1070 
1071 /*
1072  * @implemented
1073  */
1074 int
1075 WINAPI
1076 MessageBoxTimeoutW(
1077     IN HWND hWnd,
1078     IN LPCWSTR lpText,
1079     IN LPCWSTR lpCaption,
1080     IN UINT uType,
1081     IN WORD wLanguageId,
1082     IN DWORD dwTimeout)
1083 {
1084     MSGBOXPARAMSW msgbox;
1085 
1086     msgbox.cbSize = sizeof(msgbox);
1087     msgbox.hwndOwner = hWnd;
1088     msgbox.hInstance = 0;
1089     msgbox.lpszText = lpText;
1090     msgbox.lpszCaption = lpCaption;
1091     msgbox.dwStyle = uType;
1092     msgbox.lpszIcon = NULL;
1093     msgbox.dwContextHelpId = 0;
1094     msgbox.lpfnMsgBoxCallback = NULL;
1095     msgbox.dwLanguageId = wLanguageId;
1096 
1097     return MessageBoxTimeoutIndirectW(&msgbox, (UINT)dwTimeout);
1098 }
1099 
1100 
1101 /*
1102  * @unimplemented
1103  */
1104 DWORD
1105 WINAPI
1106 SoftModalMessageBox(DWORD Unknown0)
1107 {
1108   UNIMPLEMENTED;
1109   return 0;
1110 }
1111 
1112 
1113 /*
1114  * @implemented
1115  */
1116 BOOL
1117 WINAPI
1118 MessageBeep(IN UINT uType)
1119 {
1120     return NtUserxMessageBeep(uType);
1121 }
1122 
1123 
1124 /*
1125  * @implemented
1126  *
1127  * See: https://msdn.microsoft.com/en-us/library/windows/desktop/dn910915(v=vs.85).aspx
1128  * and: http://undoc.airesoft.co.uk/user32.dll/MB_GetString.php
1129  * for more information.
1130  */
1131 LPCWSTR
1132 WINAPI
1133 MB_GetString(IN UINT wBtn)
1134 {
1135     LPCWSTR btnStr = NULL;
1136 
1137     /*
1138      * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive.
1139      * See psdk/winuser.h and user32/include/resource.h .
1140      */
1141     if (wBtn > IDCONTINUE - 1)
1142         return NULL;
1143 
1144     wBtn += 800; // See user32/include/resource.h
1145     LoadStringW(User32Instance, wBtn, (LPWSTR)&btnStr, 0);
1146     return btnStr;
1147 }
1148 
1149 /* EOF */
1150