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