[USER32] Code formatting only.
[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 LONG btnCnt;
77 LONG 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 typedef struct _MSGBOXINFO
101 {
102 MSGBOXPARAMSW; // Wine passes this too.
103 // ReactOS
104 HICON Icon;
105 int DefBtn;
106 int nButtons;
107 LONG *Btns;
108 UINT Timeout;
109 } MSGBOXINFO, *PMSGBOXINFO;
110
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 hwndOwner;
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(hi);
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 {
324 mbi->lpfnMsgBoxCallback(&hi);
325 }
326 else
327 {
328 hwndOwner = GetWindow(hwnd, GW_OWNER);
329 if (hwndOwner)
330 SendMessageW(hwndOwner, WM_HELP, 0, (LPARAM)&hi);
331 }
332 return 0;
333 }
334
335 case WM_CLOSE:
336 {
337 mbi = (PMSGBOXINFO)GetPropW(hwnd, L"ROS_MSGBOX");
338 if (!mbi)
339 return 0;
340 switch (mbi->dwStyle & MB_TYPEMASK)
341 {
342 case MB_ABORTRETRYIGNORE:
343 case MB_YESNO:
344 return 1;
345 }
346 EndDialog(hwnd, IDCANCEL);
347 return 1;
348 }
349
350 case WM_TIMER:
351 if (wParam == 0)
352 {
353 EndDialog(hwnd, IDTIMEOUT);
354 }
355 return 0;
356 }
357 return 0;
358 }
359
360 static int
361 MessageBoxTimeoutIndirectW(
362 CONST MSGBOXPARAMSW *lpMsgBoxParams, UINT dwTimeout)
363 {
364 DLGTEMPLATE *tpl;
365 DLGITEMTEMPLATE *iico, *itxt;
366 NONCLIENTMETRICSW nclm;
367 LPVOID buf;
368 BYTE *dest;
369 LPCWSTR caption, text;
370 HFONT hFont, hOldFont;
371 HICON Icon;
372 HWND hDCWnd;
373 HDC hDC;
374 SIZE units;
375 int bufsize, ret, caplen, textlen, i, btnleft, btntop, lmargin;
376 MSGBTNINFO Buttons;
377 LPCWSTR ButtonText[MSGBOXEX_MAXBTNS];
378 int ButtonLen[MSGBOXEX_MAXBTNS];
379 DLGITEMTEMPLATE *ibtn[MSGBOXEX_MAXBTNS];
380 RECT btnrect, txtrect, rc;
381 SIZE btnsize;
382 MSGBOXINFO mbi;
383 BOOL defbtn = FALSE;
384
385 if (!lpMsgBoxParams->lpszCaption)
386 {
387 /* No caption, use the default one */
388 caplen = LoadStringW(User32Instance, IDS_ERROR, (LPWSTR)&caption, 0);
389 }
390 else if (IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
391 {
392 /* User-defined resource string */
393 caplen = LoadStringW(lpMsgBoxParams->hInstance,
394 PtrToUlong(lpMsgBoxParams->lpszCaption),
395 (LPWSTR)&caption, 0);
396 }
397 else
398 {
399 /* UNICODE string pointer */
400 caption = lpMsgBoxParams->lpszCaption;
401 caplen = strlenW(caption);
402 }
403
404 if (!lpMsgBoxParams->lpszText)
405 {
406 /* No text, use blank */
407 text = L"";
408 textlen = 0;
409 }
410 else if (IS_INTRESOURCE(lpMsgBoxParams->lpszText))
411 {
412 /* User-defined resource string */
413 textlen = LoadStringW(lpMsgBoxParams->hInstance,
414 PtrToUlong(lpMsgBoxParams->lpszText),
415 (LPWSTR)&text, 0);
416 }
417 else
418 {
419 /* UNICODE string pointer */
420 text = lpMsgBoxParams->lpszText;
421 textlen = strlenW(text);
422 }
423
424 /* Create the selected buttons; unknown types will fall back to MB_OK */
425 i = (lpMsgBoxParams->dwStyle & MB_TYPEMASK);
426 if (i >= ARRAYSIZE(MsgBtnInfo))
427 i = MB_OK;
428
429 /* Get buttons IDs */
430 Buttons = MsgBtnInfo[i];
431
432 /* Add the Help button */
433 if (lpMsgBoxParams->dwStyle & MB_HELP)
434 {
435 Buttons.btnIdx[Buttons.btnCnt] = IDHELP;
436 Buttons.btnIds[Buttons.btnCnt] = IDS_HELP;
437 Buttons.btnCnt++;
438 }
439
440 switch (lpMsgBoxParams->dwStyle & MB_ICONMASK)
441 {
442 case MB_ICONEXCLAMATION: // case MB_ICONWARNING:
443 Icon = LoadIconW(0, IDI_EXCLAMATIONW);
444 MessageBeep(MB_ICONEXCLAMATION);
445 break;
446 case MB_ICONQUESTION:
447 Icon = LoadIconW(0, IDI_QUESTIONW);
448 MessageBeep(MB_ICONQUESTION);
449 break;
450 case MB_ICONASTERISK: // case MB_ICONINFORMATION:
451 Icon = LoadIconW(0, IDI_ASTERISKW);
452 MessageBeep(MB_ICONASTERISK);
453 break;
454 case MB_ICONHAND: // case MB_ICONSTOP: case MB_ICONERROR:
455 Icon = LoadIconW(0, IDI_HANDW);
456 MessageBeep(MB_ICONHAND);
457 break;
458 case MB_USERICON:
459 Icon = LoadIconW(lpMsgBoxParams->hInstance, lpMsgBoxParams->lpszIcon);
460 MessageBeep(MB_OK);
461 break;
462 default:
463 /*
464 * By default, Windows 95/98/NT does not associate an icon
465 * to message boxes. So ReactOS should do the same.
466 */
467 Icon = NULL;
468 MessageBeep(MB_OK);
469 break;
470 }
471
472 /* Basic space */
473 bufsize = sizeof(DLGTEMPLATE) +
474 2 * sizeof(WORD) + /* menu and class */
475 (caplen + 1) * sizeof(WCHAR) + /* title */
476 sizeof(WORD); /* font height */
477
478 /* Space for icon */
479 if (Icon)
480 {
481 bufsize = ALIGN_UP(bufsize, DWORD);
482 bufsize += sizeof(DLGITEMTEMPLATE) +
483 4 * sizeof(WORD) +
484 sizeof(WCHAR);
485 }
486
487 /* Space for text */
488 bufsize = ALIGN_UP(bufsize, DWORD);
489 bufsize += sizeof(DLGITEMTEMPLATE) +
490 3 * sizeof(WORD) +
491 (textlen + 1) * sizeof(WCHAR);
492
493 for (i = 0; i < Buttons.btnCnt; i++)
494 {
495 /* Get the default text of the buttons */
496 if (Buttons.btnIds[i])
497 {
498 ButtonLen[i] = LoadStringW(User32Instance,
499 Buttons.btnIds[i],
500 (LPWSTR)&ButtonText[i], 0);
501 }
502 else
503 {
504 /* No text, use blank */
505 ButtonText[i] = L"";
506 ButtonLen[i] = 0;
507 }
508
509 /* Space for buttons */
510 bufsize = ALIGN_UP(bufsize, DWORD);
511 bufsize += sizeof(DLGITEMTEMPLATE) +
512 3 * sizeof(WORD) +
513 (ButtonLen[i] + 1) * sizeof(WCHAR);
514 }
515
516 buf = RtlAllocateHeap(GetProcessHeap(), 0, bufsize);
517 if (!buf)
518 return 0;
519
520 iico = itxt = NULL;
521
522 nclm.cbSize = sizeof(nclm);
523 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
524 hFont = CreateFontIndirectW(&nclm.lfMessageFont);
525 if (!hFont)
526 {
527 ERR("Cannot retrieve nclm.lfMessageFont!\n");
528 goto Quit;
529 }
530
531 tpl = (DLGTEMPLATE *)buf;
532
533 tpl->style = WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SYSMENU |
534 DS_CENTER | DS_SETFONT | DS_MODALFRAME | DS_NOIDLEMSG;
535 tpl->dwExtendedStyle = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
536 if (lpMsgBoxParams->dwStyle & MB_TOPMOST)
537 tpl->dwExtendedStyle |= WS_EX_TOPMOST;
538 if (lpMsgBoxParams->dwStyle & MB_RIGHT)
539 tpl->dwExtendedStyle |= WS_EX_RIGHT;
540 tpl->x = 100;
541 tpl->y = 100;
542 tpl->cdit = Buttons.btnCnt + (Icon ? 1 : 0) + 1;
543
544 dest = (BYTE *)(tpl + 1);
545
546 *(WORD*)dest = 0; /* no menu */
547 *(((WORD*)dest) + 1) = 0; /* use default window class */
548 dest += 2 * sizeof(WORD);
549 memcpy(dest, caption, caplen * sizeof(WCHAR));
550 dest += caplen * sizeof(WCHAR);
551 *(WCHAR*)dest = L'\0';
552 dest += sizeof(WCHAR);
553
554 /*
555 * A font point size (height) of 0x7FFF means that we use
556 * the message box font (NONCLIENTMETRICSW.lfMessageFont).
557 */
558 *(WORD*)dest = 0x7FFF;
559 dest += sizeof(WORD);
560
561 /* Create icon */
562 if (Icon)
563 {
564 dest = ALIGN_UP_POINTER(dest, DWORD);
565 iico = (DLGITEMTEMPLATE *)dest;
566 iico->style = WS_CHILD | WS_VISIBLE | SS_ICON;
567 iico->dwExtendedStyle = 0;
568 iico->id = MSGBOX_IDICON;
569
570 dest += sizeof(DLGITEMTEMPLATE);
571 *(WORD*)dest = 0xFFFF;
572 dest += sizeof(WORD);
573 *(WORD*)dest = 0x0082; /* static control */
574 dest += sizeof(WORD);
575 *(WORD*)dest = 0xFFFF;
576 dest += sizeof(WORD);
577 *(WCHAR*)dest = 0;
578 dest += sizeof(WCHAR);
579 *(WORD*)dest = 0;
580 dest += sizeof(WORD);
581 }
582
583 /* Create static for text */
584 dest = ALIGN_UP_POINTER(dest, DWORD);
585 itxt = (DLGITEMTEMPLATE *)dest;
586 itxt->style = WS_CHILD | WS_VISIBLE | SS_NOPREFIX;
587 if (lpMsgBoxParams->dwStyle & MB_RIGHT)
588 itxt->style |= SS_RIGHT;
589 else
590 itxt->style |= SS_LEFT;
591 itxt->dwExtendedStyle = 0;
592 itxt->id = MSGBOX_IDTEXT;
593 dest += sizeof(DLGITEMTEMPLATE);
594 *(WORD*)dest = 0xFFFF;
595 dest += sizeof(WORD);
596 *(WORD*)dest = 0x0082; /* static control */
597 dest += sizeof(WORD);
598 memcpy(dest, text, textlen * sizeof(WCHAR));
599 dest += textlen * sizeof(WCHAR);
600 *(WCHAR*)dest = 0;
601 dest += sizeof(WCHAR);
602 *(WORD*)dest = 0;
603 dest += sizeof(WORD);
604
605 hDCWnd = NULL;
606 hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE);
607 if (!hDC)
608 {
609 /* Retry with the DC of the owner window */
610 hDCWnd = lpMsgBoxParams->hwndOwner;
611 hDC = GetDCEx(hDCWnd, NULL, DCX_WINDOW | DCX_CACHE);
612 }
613 if (!hDC)
614 {
615 ERR("GetDCEx() failed, bail out!\n");
616 goto Quit;
617 }
618 hOldFont = SelectObject(hDC, hFont);
619
620 units.cx = GdiGetCharDimensions(hDC, NULL, &units.cy);
621 if (!units.cx)
622 {
623 DWORD defUnits;
624 ERR("GdiGetCharDimensions() failed, falling back to default values!\n");
625 defUnits = GetDialogBaseUnits();
626 units.cx = LOWORD(defUnits);
627 units.cy = HIWORD(defUnits);
628 }
629
630 /* Create buttons */
631 btnsize.cx = BTN_CX;
632 btnsize.cy = BTN_CY;
633 btnrect.left = btnrect.top = 0;
634
635 for (i = 0; i < Buttons.btnCnt; i++)
636 {
637 dest = ALIGN_UP_POINTER(dest, DWORD);
638 ibtn[i] = (DLGITEMTEMPLATE *)dest;
639
640 ibtn[i]->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
641 if (!defbtn && (i == ((lpMsgBoxParams->dwStyle & MB_DEFMASK) >> 8)))
642 {
643 ibtn[i]->style |= BS_DEFPUSHBUTTON;
644 mbi.DefBtn = Buttons.btnIdx[i];
645 defbtn = TRUE;
646 }
647 else
648 {
649 ibtn[i]->style |= BS_PUSHBUTTON;
650 }
651 ibtn[i]->dwExtendedStyle = 0;
652 ibtn[i]->id = Buttons.btnIdx[i];
653 dest += sizeof(DLGITEMTEMPLATE);
654 *(WORD*)dest = 0xFFFF;
655 dest += sizeof(WORD);
656 *(WORD*)dest = 0x0080; /* button control */
657 dest += sizeof(WORD);
658 memcpy(dest, ButtonText[i], ButtonLen[i] * sizeof(WCHAR));
659 dest += ButtonLen[i] * sizeof(WCHAR);
660 *(WORD*)dest = 0;
661 dest += sizeof(WORD);
662 *(WORD*)dest = 0;
663 dest += sizeof(WORD);
664
665 // btnrect.right = btnrect.bottom = 0; // FIXME: Is it needed??
666 DrawTextW(hDC, ButtonText[i], ButtonLen[i], &btnrect,
667 DT_LEFT | DT_SINGLELINE | DT_CALCRECT);
668 btnsize.cx = max(btnsize.cx, btnrect.right);
669 btnsize.cy = max(btnsize.cy, btnrect.bottom);
670 }
671
672 /* make first button the default button if no other is */
673 if (!defbtn)
674 {
675 ibtn[0]->style &= ~BS_PUSHBUTTON;
676 ibtn[0]->style |= BS_DEFPUSHBUTTON;
677 mbi.DefBtn = Buttons.btnIdx[0];
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 = (Buttons.btnCnt * (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 = (Buttons.btnCnt * (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 < Buttons.btnCnt; 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 mbi.Icon = Icon;
789 mbi.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
790 mbi.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
791 mbi.dwStyle = lpMsgBoxParams->dwStyle;
792 mbi.nButtons = Buttons.btnCnt;
793 mbi.Btns = Buttons.btnIdx;
794 mbi.Timeout = dwTimeout;
795
796 /* Pass on to Justin Case so he can peek the message? */
797 mbi.cbSize = lpMsgBoxParams->cbSize;
798 mbi.hwndOwner = lpMsgBoxParams->hwndOwner;
799 mbi.hInstance = lpMsgBoxParams->hInstance;
800 mbi.lpszText = lpMsgBoxParams->lpszText;
801 mbi.lpszCaption = lpMsgBoxParams->lpszCaption;
802 mbi.lpszIcon = lpMsgBoxParams->lpszIcon;
803 mbi.dwLanguageId = lpMsgBoxParams->dwLanguageId;
804
805 ret = DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, tpl,
806 lpMsgBoxParams->hwndOwner,
807 MessageBoxProc, (LPARAM)&mbi);
808
809 Quit:
810 RtlFreeHeap(GetProcessHeap(), 0, buf);
811 return ret;
812 }
813
814
815 /* FUNCTIONS *****************************************************************/
816
817 /*
818 * @implemented
819 */
820 int
821 WINAPI
822 MessageBoxA(
823 IN HWND hWnd,
824 IN LPCSTR lpText,
825 IN LPCSTR lpCaption,
826 IN UINT uType)
827 {
828 return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
829 }
830
831 /*
832 * @implemented
833 */
834 int
835 WINAPI
836 MessageBoxW(
837 IN HWND hWnd,
838 IN LPCWSTR lpText,
839 IN LPCWSTR lpCaption,
840 IN UINT uType)
841 {
842 return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
843 }
844
845
846 /*
847 * @implemented
848 */
849 int
850 WINAPI
851 MessageBoxExA(
852 IN HWND hWnd,
853 IN LPCSTR lpText,
854 IN LPCSTR lpCaption,
855 IN UINT uType,
856 IN WORD wLanguageId)
857 {
858 MSGBOXPARAMSA msgbox;
859
860 msgbox.cbSize = sizeof(msgbox);
861 msgbox.hwndOwner = hWnd;
862 msgbox.hInstance = 0;
863 msgbox.lpszText = lpText;
864 msgbox.lpszCaption = lpCaption;
865 msgbox.dwStyle = uType;
866 msgbox.lpszIcon = NULL;
867 msgbox.dwContextHelpId = 0;
868 msgbox.lpfnMsgBoxCallback = NULL;
869 msgbox.dwLanguageId = wLanguageId;
870
871 return MessageBoxIndirectA(&msgbox);
872 }
873
874 /*
875 * @implemented
876 */
877 int
878 WINAPI
879 MessageBoxExW(
880 IN HWND hWnd,
881 IN LPCWSTR lpText,
882 IN LPCWSTR lpCaption,
883 IN UINT uType,
884 IN WORD wLanguageId)
885 {
886 MSGBOXPARAMSW msgbox;
887
888 msgbox.cbSize = sizeof(msgbox);
889 msgbox.hwndOwner = hWnd;
890 msgbox.hInstance = 0;
891 msgbox.lpszText = lpText;
892 msgbox.lpszCaption = lpCaption;
893 msgbox.dwStyle = uType;
894 msgbox.lpszIcon = NULL;
895 msgbox.dwContextHelpId = 0;
896 msgbox.lpfnMsgBoxCallback = NULL;
897 msgbox.dwLanguageId = wLanguageId;
898
899 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)-1);
900 }
901
902
903 /*
904 * @implemented
905 */
906 int
907 WINAPI
908 MessageBoxIndirectA(
909 IN CONST MSGBOXPARAMSA* lpMsgBoxParams)
910 {
911 MSGBOXPARAMSW msgboxW;
912 UNICODE_STRING textW, captionW, iconW;
913 int ret;
914
915 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
916 {
917 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText);
918 /*
919 * UNICODE_STRING objects are always allocated with an extra byte so you
920 * can null-term if you want
921 */
922 textW.Buffer[textW.Length / sizeof(WCHAR)] = L'\0';
923 }
924 else
925 textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText;
926
927 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
928 {
929 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption);
930 /*
931 * UNICODE_STRING objects are always allocated with an extra byte so you
932 * can null-term if you want
933 */
934 captionW.Buffer[captionW.Length / sizeof(WCHAR)] = L'\0';
935 }
936 else
937 captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption;
938
939 if (lpMsgBoxParams->dwStyle & MB_USERICON)
940 {
941 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszIcon))
942 {
943 RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon);
944 /*
945 * UNICODE_STRING objects are always allocated with an extra byte so you
946 * can null-term if you want
947 */
948 iconW.Buffer[iconW.Length / sizeof(WCHAR)] = L'\0';
949 }
950 else
951 iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon;
952 }
953 else
954 iconW.Buffer = NULL;
955
956 msgboxW.cbSize = sizeof(msgboxW);
957 msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner;
958 msgboxW.hInstance = lpMsgBoxParams->hInstance;
959 msgboxW.lpszText = textW.Buffer;
960 msgboxW.lpszCaption = captionW.Buffer;
961 msgboxW.dwStyle = lpMsgBoxParams->dwStyle;
962 msgboxW.lpszIcon = iconW.Buffer;
963 msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
964 msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
965 msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId;
966
967 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)-1);
968
969 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszText))
970 RtlFreeUnicodeString(&textW);
971
972 if (!IS_INTRESOURCE(lpMsgBoxParams->lpszCaption))
973 RtlFreeUnicodeString(&captionW);
974
975 if ((lpMsgBoxParams->dwStyle & MB_USERICON) && !IS_INTRESOURCE(iconW.Buffer))
976 RtlFreeUnicodeString(&iconW);
977
978 return ret;
979 }
980
981 /*
982 * @implemented
983 */
984 int
985 WINAPI
986 MessageBoxIndirectW(
987 IN CONST MSGBOXPARAMSW* lpMsgBoxParams)
988 {
989 return MessageBoxTimeoutIndirectW(lpMsgBoxParams, (UINT)-1);
990 }
991
992
993 /*
994 * @implemented
995 */
996 int
997 WINAPI
998 MessageBoxTimeoutA(
999 IN HWND hWnd,
1000 IN LPCSTR lpText,
1001 IN LPCSTR lpCaption,
1002 IN UINT uType,
1003 IN WORD wLanguageId,
1004 IN DWORD dwTimeout)
1005 {
1006 MSGBOXPARAMSW msgboxW;
1007 UNICODE_STRING textW, captionW;
1008 int ret;
1009
1010 if (!IS_INTRESOURCE(lpText))
1011 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText);
1012 else
1013 textW.Buffer = (LPWSTR)lpText;
1014
1015 if (!IS_INTRESOURCE(lpCaption))
1016 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpCaption);
1017 else
1018 captionW.Buffer = (LPWSTR)lpCaption;
1019
1020 msgboxW.cbSize = sizeof(msgboxW);
1021 msgboxW.hwndOwner = hWnd;
1022 msgboxW.hInstance = 0;
1023 msgboxW.lpszText = textW.Buffer;
1024 msgboxW.lpszCaption = captionW.Buffer;
1025 msgboxW.dwStyle = uType;
1026 msgboxW.lpszIcon = NULL;
1027 msgboxW.dwContextHelpId = 0;
1028 msgboxW.lpfnMsgBoxCallback = NULL;
1029 msgboxW.dwLanguageId = wLanguageId;
1030
1031 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)dwTimeout);
1032
1033 if (!IS_INTRESOURCE(textW.Buffer))
1034 RtlFreeUnicodeString(&textW);
1035
1036 if (!IS_INTRESOURCE(captionW.Buffer))
1037 RtlFreeUnicodeString(&captionW);
1038
1039 return ret;
1040 }
1041
1042 /*
1043 * @implemented
1044 */
1045 int
1046 WINAPI
1047 MessageBoxTimeoutW(
1048 IN HWND hWnd,
1049 IN LPCWSTR lpText,
1050 IN LPCWSTR lpCaption,
1051 IN UINT uType,
1052 IN WORD wLanguageId,
1053 IN DWORD dwTimeout)
1054 {
1055 MSGBOXPARAMSW msgbox;
1056
1057 msgbox.cbSize = sizeof(msgbox);
1058 msgbox.hwndOwner = hWnd;
1059 msgbox.hInstance = 0;
1060 msgbox.lpszText = lpText;
1061 msgbox.lpszCaption = lpCaption;
1062 msgbox.dwStyle = uType;
1063 msgbox.lpszIcon = NULL;
1064 msgbox.dwContextHelpId = 0;
1065 msgbox.lpfnMsgBoxCallback = NULL;
1066 msgbox.dwLanguageId = wLanguageId;
1067
1068 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)dwTimeout);
1069 }
1070
1071
1072 /*
1073 * @unimplemented
1074 */
1075 DWORD
1076 WINAPI
1077 SoftModalMessageBox(DWORD Unknown0)
1078 {
1079 UNIMPLEMENTED;
1080 return 0;
1081 }
1082
1083
1084 /*
1085 * @implemented
1086 */
1087 BOOL
1088 WINAPI
1089 MessageBeep(IN UINT uType)
1090 {
1091 return NtUserxMessageBeep(uType);
1092 }
1093
1094
1095 /*
1096 * @implemented
1097 *
1098 * See: https://msdn.microsoft.com/en-us/library/windows/desktop/dn910915(v=vs.85).aspx
1099 * and: http://undoc.airesoft.co.uk/user32.dll/MB_GetString.php
1100 * for more information.
1101 */
1102 LPCWSTR
1103 WINAPI
1104 MB_GetString(IN UINT wBtn)
1105 {
1106 LPCWSTR btnStr = NULL;
1107
1108 /*
1109 * The allowable IDs are between "IDOK - 1" (0) and "IDCONTINUE - 1" (10) inclusive.
1110 * See psdk/winuser.h and user32/include/resource.h .
1111 */
1112 if (wBtn > IDCONTINUE - 1)
1113 return NULL;
1114
1115 wBtn += 800; // See user32/include/resource.h
1116 LoadStringW(User32Instance, wBtn, (LPWSTR)&btnStr, 0);
1117 return btnStr;
1118 }
1119
1120 /* EOF */