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