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