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