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