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