3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
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.
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.
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.
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/messagebox.c
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
27 * 2003/07/28 Added some NT features
28 * 2003/07/27 Code ported from wine
29 * 09-05-2001 CSH Created
32 /* INCLUDES ******************************************************************/
46 /* DEFINES *******************************************************************/
48 #define MSGBOX_IDICON 1088
49 #define MSGBOX_IDTEXT 100
52 #define IDI_HANDA MAKEINTRESOURCEA(32513)
53 #define IDI_HANDW MAKEINTRESOURCEW(32513)
54 #define IDI_QUESTIONA MAKEINTRESOURCEA(32514)
55 #define IDI_QUESTIONW MAKEINTRESOURCEW(32514)
56 #define IDI_EXCLAMATIONA MAKEINTRESOURCEA(32515)
57 #define IDI_EXCLAMATIONW MAKEINTRESOURCEW(32515)
58 #define IDI_ASTERISKA MAKEINTRESOURCEA(32516)
59 #define IDI_ASTERISKW MAKEINTRESOURCEW(32516)
60 #define IDI_WINLOGOA MAKEINTRESOURCEA(32517)
61 #define IDI_WINLOGOW MAKEINTRESOURCEW(32517)
64 #define MB_TYPEMASK 0x0000000F
67 #define MB_ICONMASK 0x000000F0
70 #define MB_DEFMASK 0x00000F00
75 /* FUNCTIONS *****************************************************************/
77 static HWND MSGBOX_CreateButton(HWND hwnd, LONG ID, LPWSTR Caption)
79 return CreateWindowExW(0, L"BUTTON", Caption, WS_CHILD | WS_TABSTOP | WS_VISIBLE,
80 0, 0, 10, 10, hwnd, (HMENU)ID, 0, NULL);
83 static HFONT MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMS lpmb)
85 HFONT hFont = 0, hPrevFont = 0;
90 int bspace, bw, bh, theight, tleft, wwidth, wheight, bpos;
91 int borheight, borwidth, iheight, ileft, iwidth, twidth, tiheight;
95 NONCLIENTMETRICSW nclm;
99 nclm.cbSize = sizeof(nclm);
100 SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
101 hFont = CreateFontIndirectW (&nclm.lfMessageFont);
102 /* set button font */
103 for (i = 1; i < 10; i++)
104 SendDlgItemMessageW (hwnd, i, WM_SETFONT, (WPARAM)hFont, 0);
106 SendDlgItemMessageW (hwnd, MSGBOX_IDTEXT, WM_SETFONT, (WPARAM)hFont, 0);
108 if (HIWORD(lpmb->lpszCaption))
110 SetWindowTextW(hwnd, (LPCWSTR)lpmb->lpszCaption);
114 UINT res_id = LOWORD((UINT)lpmb->lpszCaption); /* FIXME: (UINT) ??? */
117 if (LoadStringW(lpmb->hInstance, res_id, buf, 256))
118 SetWindowTextW(hwnd, buf);
122 if (LoadStringW(GetModuleHandleA("user32.dll"), IDS_ERROR, buf, 256))
123 SetWindowTextW(hwnd, buf);
126 if (HIWORD(lpmb->lpszText))
128 lpszText = (LPCWSTR)lpmb->lpszText;
133 if (!LoadStringW(lpmb->hInstance, LOWORD((UINT)lpmb->lpszText), buf, 256)) /* FIXME: (UINT) ??? */
134 *buf = 0; /* FIXME ?? */
137 /* Create selected buttons */
138 switch(lpmb->dwStyle & MB_TYPEMASK)
141 Buttons[0] = MSGBOX_CreateButton(hwnd, IDOK, L"OK");
142 Buttons[1] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel");
145 case MB_CANCELTRYCONTINUE:
146 Buttons[0] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel");
147 Buttons[1] = MSGBOX_CreateButton(hwnd, IDTRYAGAIN, L"Try Again");
148 Buttons[2] = MSGBOX_CreateButton(hwnd, IDCONTINUE, L"Continue");
151 case MB_ABORTRETRYIGNORE:
152 Buttons[0] = MSGBOX_CreateButton(hwnd, IDABORT, L"Abort");
153 Buttons[1] = MSGBOX_CreateButton(hwnd, IDRETRY, L"Retry");
154 Buttons[2] = MSGBOX_CreateButton(hwnd, IDIGNORE, L"Ignore");
158 Buttons[0] = MSGBOX_CreateButton(hwnd, IDYES, L"Yes");
159 Buttons[1] = MSGBOX_CreateButton(hwnd, IDNO, L"No");
163 Buttons[0] = MSGBOX_CreateButton(hwnd, IDYES, L"Yes");
164 Buttons[1] = MSGBOX_CreateButton(hwnd, IDNO, L"No");
165 Buttons[2] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel");
169 Buttons[0] = MSGBOX_CreateButton(hwnd, IDRETRY, L"Retry");
170 Buttons[1] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel");
176 Buttons[0] = MSGBOX_CreateButton(hwnd, IDOK, L"OK");
180 /* Create Help button */
181 if(lpmb->dwStyle & MB_HELP)
182 Buttons[nButtons++] = MSGBOX_CreateButton(hwnd, IDHELP, L"Help");
185 switch(lpmb->dwStyle & MB_ICONMASK)
187 case MB_ICONEXCLAMATION:
188 SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON,
189 (WPARAM)LoadIconW(0, IDI_EXCLAMATIONW), 0);
190 MessageBeep(MB_ICONEXCLAMATION);
192 case MB_ICONQUESTION:
193 SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON,
194 (WPARAM)LoadIconW(0, IDI_QUESTIONW), 0);
195 MessageBeep(MB_ICONQUESTION);
197 case MB_ICONASTERISK:
198 SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON,
199 (WPARAM)LoadIconW(0, IDI_ASTERISKW), 0);
200 MessageBeep(MB_ICONASTERISK);
203 SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON,
204 (WPARAM)LoadIconW(0, IDI_HANDW), 0);
205 MessageBeep(MB_ICONHAND);
208 SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON,
209 (WPARAM)LoadIconW(lpmb->hInstance, (LPCWSTR)lpmb->lpszIcon), 0);
213 /* By default, Windows 95/98/NT does not associate an icon to message boxes.
214 * So ReactOS should do the same.
220 /* Position everything */
221 GetWindowRect(hwnd, &rect);
222 borheight = rect.bottom - rect.top;
223 borwidth = rect.right - rect.left;
224 GetClientRect(hwnd, &rect);
225 borheight -= rect.bottom;
226 borwidth -= rect.right;
228 /* Get the icon height */
229 GetWindowRect(GetDlgItem(hwnd, MSGBOX_IDICON), &rect);
230 if (!(lpmb->dwStyle & MB_ICONMASK))
232 rect.bottom = rect.top;
233 rect.right = rect.left;
235 iheight = rect.bottom - rect.top;
237 iwidth = rect.right - ileft;
241 hPrevFont = SelectObject(hdc, hFont);
243 /* Calculate the button's sizes */
244 bh = bw = 1; // Minimum button sizes
245 for(i = 0; i <= nButtons; i++)
247 WCHAR buttonText[1024];
249 if (GetWindowTextW(Buttons[i], buttonText, 1024))
251 DrawTextW(hdc, buttonText, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT);
252 h = rect.bottom - rect.top;
253 w = rect.right - rect.left;
261 bw = max(bw, bh * 2);
262 /* Button white space */
265 bspace = 10; /* Fixed space between buttons */
267 /* Get the text size */
268 GetClientRect(GetDlgItem(hwnd, MSGBOX_IDTEXT), &rect);
269 rect.top = rect.left = rect.bottom = 0;
270 rect.right = (((GetSystemMetrics(SM_CXSCREEN) - borwidth) * 4) / 5);
271 DrawTextW( hdc, lpszText, -1, &rect,
272 DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT);
274 /* Min text width corresponds to space for the buttons */
277 tleft += ileft + iwidth;
278 twidth = max((LONG) (((bw + bspace) * nButtons) + bspace - tleft), rect.right);
279 theight = rect.bottom;
282 SelectObject(hdc, hPrevFont);
283 ReleaseDC(hItem, hdc);
285 tiheight = 16 + max(iheight, theight) + 16;
286 wwidth = tleft + twidth + ileft + borwidth;
287 wheight = 8 + tiheight + bh + borheight;
289 /* Resize the window */
290 SetWindowPos(hwnd, 0, 0, 0, wwidth, wheight,
291 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
293 /* Position the icon */
294 SetWindowPos(GetDlgItem(hwnd, MSGBOX_IDICON), 0, ileft, (tiheight - iheight) / 2, 0, 0,
295 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
297 /* Position the text */
298 SetWindowPos(GetDlgItem(hwnd, MSGBOX_IDTEXT), 0, tleft, (tiheight - theight) / 2, twidth, theight,
299 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
300 SetWindowTextW(GetDlgItem(hwnd, MSGBOX_IDTEXT), lpszText);
302 /* Position the buttons */
303 bpos = (wwidth - ((bw + bspace) * nButtons) + bspace) / 2;
304 for(i = 0; i <= nButtons; i++)
306 if (i == ((lpmb->dwStyle & MB_DEFMASK) >> 8))
308 SetFocus(Buttons[i]);
309 SendMessageW(Buttons[i], BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
312 SetWindowPos(Buttons[i], 0, bpos, tiheight, bw, bh,
313 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
317 /* if there's no (valid) default selection, select first button */
320 SetFocus(Buttons[0]);
321 SendMessageW(Buttons[0], BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
324 if(lpmb->dwStyle & MB_RIGHT)
326 hItem = GetDlgItem(hwnd, MSGBOX_IDTEXT);
327 SetWindowLongW(hItem, GWL_STYLE,
328 GetWindowLongW(hItem, GWL_STYLE) | SS_RIGHT);
331 /* handle modal MessageBoxes */
332 if (lpmb->dwStyle & (MB_TASKMODAL|MB_SYSTEMMODAL))
334 DbgPrint("%s modal msgbox ! Not modal yet.\n",
335 lpmb->dwStyle & MB_TASKMODAL ? "task" : "system");
336 /* Probably do EnumTaskWindows etc. here for TASKMODAL
337 * and work your way up to the top - I'm lazy (HWND_TOP) */
338 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
339 SWP_NOSIZE | SWP_NOMOVE);
340 if (lpmb->dwStyle & MB_TASKMODAL)
341 /* at least MB_TASKMODAL seems to imply a ShowWindow */
342 ShowWindow(hwnd, SW_SHOW);
345 if (lpmb->dwStyle & MB_APPLMODAL)
346 DbgPrint("app modal msgbox ! Not modal yet.\n");
352 /**************************************************************************
355 * Dialog procedure for message boxes.
357 static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message,
358 WPARAM wParam, LPARAM lParam )
366 if(GetWindowLongA(hwnd, DWL_INIT))
368 LPMSGBOXPARAMS mbp = (LPMSGBOXPARAMS)lParam;
369 SetWindowLongA(hwnd, DWL_INIT, (LONG)mbp);
370 SetWindowContextHelpId(hwnd, mbp->dwContextHelpId);
371 hFont = MSGBOX_OnInit(hwnd, mbp);
372 SetPropA(hwnd, "ROS_MSGBOX_HFONT", (HANDLE)hFont);
373 SetPropA(hwnd, "ROS_MSGBOX_HELPCALLBACK", (HANDLE)mbp->lpfnMsgBoxCallback);
380 switch (LOWORD(wParam))
391 hFont = GetPropA(hwnd, "ROS_MSGBOX_HFONT");
392 EndDialog(hwnd, wParam);
397 /* send WM_HELP message to messagebox window */
398 hi.cbSize = sizeof(HELPINFO);
399 hi.iContextType = HELPINFO_WINDOW;
400 hi.iCtrlId = LOWORD(wParam);
401 hi.hItemHandle = (HANDLE)lParam;
403 GetCursorPos(&hi.MousePos);
404 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
411 MSGBOXCALLBACK callback = (MSGBOXCALLBACK)GetPropA(hwnd, "ROS_MSGBOX_HELPCALLBACK");
413 memcpy(&hi, (void *)lParam, sizeof(hi));
414 hi.dwContextId = GetWindowContextHelpId(hwnd);
419 HWND owner = GetWindow(hwnd, GW_OWNER);
421 SendMessageW(GetWindow(hwnd, GW_OWNER), WM_HELP, 0, (LPARAM)&hi);
441 return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
459 msgbox.cbSize = sizeof(msgbox);
460 msgbox.hwndOwner = hWnd;
461 msgbox.hInstance = 0;
462 msgbox.lpszText = lpText;
463 msgbox.lpszCaption = lpCaption;
464 msgbox.dwStyle = uType;
465 msgbox.lpszIcon = NULL;
466 msgbox.dwContextHelpId = 0;
467 msgbox.lpfnMsgBoxCallback = NULL;
468 msgbox.dwLanguageId = wLanguageId;
470 return MessageBoxIndirectA(&msgbox);
488 msgbox.cbSize = sizeof(msgbox);
489 msgbox.hwndOwner = hWnd;
490 msgbox.hInstance = 0;
491 msgbox.lpszText = (LPCSTR)lpText;
492 msgbox.lpszCaption =(LPCSTR) lpCaption;
493 msgbox.dwStyle = uType;
494 msgbox.lpszIcon = NULL;
495 msgbox.dwContextHelpId = 0;
496 msgbox.lpfnMsgBoxCallback = NULL;
497 msgbox.dwLanguageId = wLanguageId;
499 return MessageBoxIndirectW(&msgbox);
509 CONST LPMSGBOXPARAMS lpMsgBoxParams)
511 MSGBOXPARAMS msgboxW;
512 UNICODE_STRING textW, captionW, iconW;
515 if (HIWORD((UINT)lpMsgBoxParams->lpszText))
516 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText);
518 textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText;
520 if (HIWORD((UINT)lpMsgBoxParams->lpszCaption))
521 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption);
523 captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption;
525 if (HIWORD((UINT)lpMsgBoxParams->lpszIcon))
526 RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon);
528 iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon;
530 msgboxW.cbSize = sizeof(msgboxW);
531 msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner;
532 msgboxW.hInstance = lpMsgBoxParams->hInstance;
533 msgboxW.lpszText = (LPCSTR)textW.Buffer;
534 msgboxW.lpszCaption = (LPCSTR)captionW.Buffer;
535 msgboxW.dwStyle = lpMsgBoxParams->dwStyle;
536 msgboxW.lpszIcon = (LPCSTR)iconW.Buffer;
537 msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
538 msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
539 msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId;
541 ret = MessageBoxIndirectW(&msgboxW);
543 if (HIWORD(textW.Buffer))
544 RtlFreeUnicodeString(&textW);
546 if (HIWORD(captionW.Buffer))
547 RtlFreeUnicodeString(&captionW);
549 if (HIWORD(iconW.Buffer))
550 RtlFreeUnicodeString(&iconW);
562 CONST LPMSGBOXPARAMS lpMsgBoxParams)
564 LPVOID tmplate, ctmplate;
571 hUser32 = GetModuleHandleW(L"user32.dll");
572 if (!(hRes = FindResourceExW(hUser32, RT_DIALOGW, L"MSGBOX", lpMsgBoxParams->dwLanguageId)))
575 if (!(tmplate = (LPVOID)LoadResource(hUser32, hRes)))
579 ressize = SizeofResource(hUser32, hRes);
580 ctmplate = RtlAllocateHeap(RtlGetProcessHeap(), 0, ressize);
581 RtlMoveMemory(ctmplate, tmplate, ressize);
583 /* change dialog's style in the template before
584 passing it to DialogBoxIndirectParamW */
586 style = (WORD *)ctmplate;
588 if(*(DWORD*)style == 0xffff0001) /* DIALOGEX resource */
595 /* change window style before creating it */
596 if(lpMsgBoxParams->dwStyle & MB_RIGHT)
597 *exstyle = (WORD)(*(DWORD*)exstyle | WS_EX_RIGHT);
598 if(lpMsgBoxParams->dwStyle & MB_TOPMOST)
599 *exstyle = (WORD)(*(DWORD*)exstyle | WS_EX_TOPMOST);
601 return DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, ctmplate, lpMsgBoxParams->hwndOwner,
602 MSGBOX_DlgProc, (LPARAM)lpMsgBoxParams);
604 RtlFreeHeap(RtlGetProcessHeap(), 0, ctmplate);
619 return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
628 SoftModalMessageBox(DWORD Unknown0)
640 MessageBeep(UINT uType)
648 if(waveOutGetNumDevs() == 0)
649 return Beep(500, 100); // Beep through speaker
652 EventName = L"SystemDefault";
654 case MB_ICONASTERISK:
655 EventName = L"SystemAsterisk";
657 case MB_ICONEXCLAMATION:
658 EventName = L"SystemExclamation";
661 EventName = L"SystemHand";
663 case MB_ICONQUESTION:
664 EventName = L"SystemQuestion";
668 return PlaySoundW((LPCWSTR)EventName, NULL, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC);
670 return Beep(500, 100); // Beep through speaker