2004-08-15 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / lib / 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 /* $Id: messagebox.c,v 1.27 2004/08/15 21:36:30 chorns Exp $
20 *
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/messagebox.c
23 * PURPOSE: Input
24 * PROGRAMMER: 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 #include <messages.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <debug.h>
40 #include "resource.h"
41
42 #include <mmsystem.h>
43
44 /* DEFINES *******************************************************************/
45
46 #define MSGBOX_IDICON (1088)
47 #define MSGBOX_IDTEXT (100)
48
49 #define IDI_HANDA MAKEINTRESOURCEA(32513)
50 #define IDI_HANDW MAKEINTRESOURCEW(32513)
51 #define IDI_QUESTIONA MAKEINTRESOURCEA(32514)
52 #define IDI_QUESTIONW MAKEINTRESOURCEW(32514)
53 #define IDI_EXCLAMATIONA MAKEINTRESOURCEA(32515)
54 #define IDI_EXCLAMATIONW MAKEINTRESOURCEW(32515)
55 #define IDI_ASTERISKA MAKEINTRESOURCEA(32516)
56 #define IDI_ASTERISKW MAKEINTRESOURCEW(32516)
57 #define IDI_WINLOGOA MAKEINTRESOURCEA(32517)
58 #define IDI_WINLOGOW MAKEINTRESOURCEW(32517)
59
60 #ifndef MB_TYPEMASK
61 #define MB_TYPEMASK 0x0000000F
62 #endif
63 #ifndef MB_ICONMASK
64 #define MB_ICONMASK 0x000000F0
65 #endif
66 #ifndef MB_DEFMASK
67 #define MB_DEFMASK 0x00000F00
68 #endif
69
70 #define BTN_CX (75)
71 #define BTN_CY (23)
72
73 #define MSGBOXEX_SPACING (16)
74 #define MSGBOXEX_BUTTONSPACING (6)
75 #define MSGBOXEX_MARGIN (12)
76 #define MSGBOXEX_MAXBTNSTR (32)
77 #define MSGBOXEX_MAXBTNS (4)
78
79 typedef struct _MSGBOXINFO {
80 HICON Icon;
81 HFONT Font;
82 DWORD ContextHelpId;
83 MSGBOXCALLBACK Callback;
84 DWORD Style;
85 int DefBtn;
86 int nButtons;
87 LONG *Btns;
88 UINT Timeout;
89 } MSGBOXINFO, *PMSGBOXINFO;
90
91 /* INTERNAL FUNCTIONS ********************************************************/
92
93 static inline unsigned int strlenW( const WCHAR *str )
94 {
95 const WCHAR *s = str;
96 while (*s) s++;
97 return s - str;
98 }
99
100 static INT_PTR CALLBACK MessageBoxProc( HWND hwnd, UINT message,
101 WPARAM wParam, LPARAM lParam )
102 {
103 int i;
104 PMSGBOXINFO mbi;
105 HELPINFO hi;
106 HWND owner;
107
108 switch(message) {
109 case WM_INITDIALOG:
110 mbi = (PMSGBOXINFO)lParam;
111 if(!GetPropW(hwnd, L"ROS_MSGBOX"))
112 {
113 SetPropW(hwnd, L"ROS_MSGBOX", (HANDLE)lParam);
114 if(mbi->Icon)
115 SendDlgItemMessageW(hwnd, MSGBOX_IDICON, STM_SETICON, (WPARAM)mbi->Icon, 0);
116 SetWindowContextHelpId(hwnd, mbi->ContextHelpId);
117
118 /* set control fonts */
119 SendDlgItemMessageW(hwnd, MSGBOX_IDTEXT, WM_SETFONT, (WPARAM)mbi->Font, 0);
120 for(i = 0; i < mbi->nButtons; i++)
121 {
122 SendDlgItemMessageW(hwnd, mbi->Btns[i], WM_SETFONT, (WPARAM)mbi->Font, 0);
123 }
124 switch(mbi->Style & MB_TYPEMASK)
125 {
126 case MB_ABORTRETRYIGNORE:
127 case MB_YESNO:
128 RemoveMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
129 break;
130 }
131 SetFocus(GetDlgItem(hwnd, mbi->DefBtn));
132 if(mbi->Timeout && (mbi->Timeout != (UINT)-1))
133 SetTimer(hwnd, 0, mbi->Timeout, NULL);
134 }
135 return 0;
136
137 case WM_COMMAND:
138 switch (LOWORD(wParam))
139 {
140 case IDOK:
141 case IDCANCEL:
142 case IDABORT:
143 case IDRETRY:
144 case IDIGNORE:
145 case IDYES:
146 case IDNO:
147 case IDTRYAGAIN:
148 case IDCONTINUE:
149 EndDialog(hwnd, wParam);
150 return 0;
151 case IDHELP:
152 /* send WM_HELP message to messagebox window */
153 hi.cbSize = sizeof(HELPINFO);
154 hi.iContextType = HELPINFO_WINDOW;
155 hi.iCtrlId = LOWORD(wParam);
156 hi.hItemHandle = (HANDLE)lParam;
157 hi.dwContextId = 0;
158 GetCursorPos(&hi.MousePos);
159 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
160 return 0;
161 }
162 return 0;
163
164 case WM_HELP:
165 mbi = (PMSGBOXINFO)GetPropW(hwnd, L"ROS_MSGBOX");
166 if(!mbi)
167 return 0;
168 memcpy(&hi, (void *)lParam, sizeof(hi));
169 hi.dwContextId = GetWindowContextHelpId(hwnd);
170
171 if (mbi->Callback)
172 mbi->Callback(&hi);
173 else
174 {
175 owner = GetWindow(hwnd, GW_OWNER);
176 if(owner)
177 SendMessageW(GetWindow(hwnd, GW_OWNER), WM_HELP, 0, (LPARAM)&hi);
178 }
179 return 0;
180
181 case WM_CLOSE:
182 mbi = (PMSGBOXINFO)GetPropW(hwnd, L"ROS_MSGBOX");
183 if(!mbi)
184 return 0;
185 switch(mbi->Style & MB_TYPEMASK)
186 {
187 case MB_ABORTRETRYIGNORE:
188 case MB_YESNO:
189 return 1;
190 }
191 EndDialog(hwnd, IDCANCEL);
192 return 1;
193
194 case WM_TIMER:
195 if(wParam == 0)
196 {
197 EndDialog(hwnd, 32000);
198 }
199 return 0;
200 }
201 return 0;
202 }
203
204 #define SAFETY_MARGIN 32 /* Extra number of bytes to allocate in case we counted wrong */
205 static int
206 MessageBoxTimeoutIndirectW(
207 CONST MSGBOXPARAMS *lpMsgBoxParams, UINT Timeout)
208 {
209 DLGTEMPLATE *tpl;
210 DLGITEMTEMPLATE *iico, *itxt;
211 NONCLIENTMETRICSW nclm;
212 WCHAR capbuf[32];
213 HMODULE hUser32;
214 LPVOID buf;
215 BYTE *dest;
216 LPWSTR caption, text;
217 HFONT hFont;
218 HICON Icon;
219 HDC hDC;
220 int bufsize, ret, caplen, textlen, btnlen, i, btnleft, btntop, lmargin, nButtons = 0;
221 LONG Buttons[MSGBOXEX_MAXBTNS];
222 WCHAR ButtonText[MSGBOXEX_MAXBTNS][MSGBOXEX_MAXBTNSTR];
223 DLGITEMTEMPLATE *ibtn[MSGBOXEX_MAXBTNS];
224 RECT btnrect, txtrect, rc;
225 SIZE btnsize;
226 MSGBOXINFO mbi;
227 BOOL defbtn = FALSE;
228 DWORD units = GetDialogBaseUnits();
229
230 hUser32 = GetModuleHandleW(L"USER32");
231
232 if(!lpMsgBoxParams->lpszCaption || !HIWORD((LPWSTR)lpMsgBoxParams->lpszCaption))
233 {
234 LoadStringW(hUser32, IDS_ERROR, &capbuf[0], 32);
235 caption = &capbuf[0];
236 }
237 else
238 caption = (LPWSTR)lpMsgBoxParams->lpszCaption;
239
240 if(!lpMsgBoxParams->lpszText || !HIWORD((LPWSTR)lpMsgBoxParams->lpszText))
241 text = L"";
242 else
243 text = (LPWSTR)lpMsgBoxParams->lpszText;
244
245 caplen = strlenW(caption);
246 textlen = strlenW(text);
247
248 /* Create selected buttons */
249 switch(lpMsgBoxParams->dwStyle & MB_TYPEMASK)
250 {
251 case MB_OKCANCEL:
252 Buttons[0] = IDOK;
253 Buttons[1] = IDCANCEL;
254 nButtons = 2;
255 break;
256 case MB_CANCELTRYCONTINUE:
257 Buttons[0] = IDCANCEL;
258 Buttons[1] = IDTRYAGAIN;
259 Buttons[2] = IDCONTINUE;
260 nButtons = 3;
261 break;
262 case MB_ABORTRETRYIGNORE:
263 Buttons[0] = IDABORT;
264 Buttons[1] = IDRETRY;
265 Buttons[2] = IDIGNORE;
266 nButtons = 3;
267 break;
268 case MB_YESNO:
269 Buttons[0] = IDYES;
270 Buttons[1] = IDNO;
271 nButtons = 2;
272 break;
273 case MB_YESNOCANCEL:
274 Buttons[0] = IDYES;
275 Buttons[1] = IDNO;
276 Buttons[2] = IDCANCEL;
277 nButtons = 3;
278 break;
279 case MB_RETRYCANCEL:
280 Buttons[0] = IDRETRY;
281 Buttons[1] = IDCANCEL;
282 nButtons = 2;
283 break;
284 case MB_OK:
285 /* fall through */
286 default:
287 Buttons[0] = IDOK;
288 nButtons = 1;
289 break;
290 }
291 /* Create Help button */
292 if(lpMsgBoxParams->dwStyle & MB_HELP)
293 Buttons[nButtons++] = IDHELP;
294
295 switch(lpMsgBoxParams->dwStyle & MB_ICONMASK)
296 {
297 case MB_ICONEXCLAMATION:
298 Icon = LoadIconW(0, IDI_EXCLAMATIONW);
299 MessageBeep(MB_ICONEXCLAMATION);
300 break;
301 case MB_ICONQUESTION:
302 Icon = LoadIconW(0, IDI_QUESTIONW);
303 MessageBeep(MB_ICONQUESTION);
304 break;
305 case MB_ICONASTERISK:
306 Icon = LoadIconW(0, IDI_ASTERISKW);
307 MessageBeep(MB_ICONASTERISK);
308 break;
309 case MB_ICONHAND:
310 Icon = LoadIconW(0, IDI_HANDW);
311 MessageBeep(MB_ICONHAND);
312 break;
313 case MB_USERICON:
314 Icon = LoadIconW(lpMsgBoxParams->hInstance, (LPCWSTR)lpMsgBoxParams->lpszIcon);
315 MessageBeep(MB_OK);
316 break;
317 default:
318 /* By default, Windows 95/98/NT does not associate an icon to message boxes.
319 * So ReactOS should do the same.
320 */
321 Icon = (HICON)0;
322 MessageBeep(MB_OK);
323 break;
324 }
325
326 /* Basic space */
327 bufsize = sizeof(DLGTEMPLATE) +
328 2 * sizeof(WORD) + /* menu and class */
329 (caplen + 1) * sizeof(WCHAR); /* title */
330
331 /* Space for icon */
332 if (NULL != Icon)
333 {
334 bufsize = (bufsize + 3) & ~3;
335 bufsize += sizeof(DLGITEMTEMPLATE) +
336 4 * sizeof(WORD) +
337 sizeof(WCHAR);
338 }
339
340 /* Space for text */
341 bufsize = (bufsize + 3) & ~3;
342 bufsize += sizeof(DLGITEMTEMPLATE) +
343 3 * sizeof(WORD) +
344 (textlen + 1) * sizeof(WCHAR);
345
346
347 for(i = 0; i < nButtons; i++)
348 {
349 switch(Buttons[i])
350 {
351 case IDOK:
352 LoadStringW(hUser32, IDS_OK, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
353 break;
354 case IDCANCEL:
355 LoadStringW(hUser32, IDS_CANCEL, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
356 break;
357 case IDYES:
358 LoadStringW(hUser32, IDS_YES, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
359 break;
360 case IDNO:
361 LoadStringW(hUser32, IDS_NO, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
362 break;
363 case IDTRYAGAIN:
364 LoadStringW(hUser32, IDS_TRYAGAIN, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
365 break;
366 case IDCONTINUE:
367 LoadStringW(hUser32, IDS_CONTINUE, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
368 break;
369 case IDABORT:
370 LoadStringW(hUser32, IDS_ABORT, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
371 break;
372 case IDRETRY:
373 LoadStringW(hUser32, IDS_RETRY, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
374 break;
375 case IDIGNORE:
376 LoadStringW(hUser32, IDS_IGNORE, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
377 break;
378 case IDHELP:
379 LoadStringW(hUser32, IDS_HELP, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
380 break;
381 default:
382 ButtonText[i][0] = (WCHAR)0;
383 break;
384 }
385
386 /* Space for buttons */
387 bufsize = (bufsize + 3) & ~3;
388 bufsize += sizeof(DLGITEMTEMPLATE) +
389 3 * sizeof(WORD) +
390 (wcslen(ButtonText[i]) + 1) * sizeof(WCHAR);
391 }
392
393 buf = RtlAllocateHeap(GetProcessHeap(), 0, bufsize + SAFETY_MARGIN);
394 /* Just to be safe.... */
395 if(!buf)
396 {
397 return 0;
398 }
399 iico = itxt = NULL;
400
401 hDC = CreateCompatibleDC(0);
402
403 nclm.cbSize = sizeof(nclm);
404 SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
405 hFont = CreateFontIndirectW (&nclm.lfMessageFont);
406
407 tpl = (DLGTEMPLATE *)buf;
408
409 tpl->style = WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SYSMENU | DS_CENTER | DS_MODALFRAME | DS_NOIDLEMSG;
410 tpl->dwExtendedStyle = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
411 if(lpMsgBoxParams->dwStyle & MB_TOPMOST)
412 tpl->dwExtendedStyle |= WS_EX_TOPMOST;
413 if(lpMsgBoxParams->dwStyle & MB_RIGHT)
414 tpl->dwExtendedStyle |= WS_EX_RIGHT;
415 tpl->x = 100;
416 tpl->y = 100;
417 tpl->cdit = nButtons + (Icon != (HICON)0) + 1;
418
419 dest = (BYTE *)(tpl + 1);
420
421 *(WORD*)dest = 0; /* no menu */
422 *(((WORD*)dest) + 1) = 0; /* use default window class */
423 dest += 2 * sizeof(WORD);
424 memcpy(dest, caption, caplen * sizeof(WCHAR));
425 dest += caplen * sizeof(WCHAR);
426 *(WCHAR*)dest = L'\0';
427 dest += sizeof(WCHAR);
428
429 /* Create icon */
430 if(Icon)
431 {
432 dest = (BYTE*)(((ULONG_PTR)dest + 3) & ~3);
433 iico = (DLGITEMTEMPLATE *)dest;
434 iico->style = WS_CHILD | WS_VISIBLE | SS_ICON;
435 iico->dwExtendedStyle = 0;
436 iico->id = MSGBOX_IDICON;
437
438 dest += sizeof(DLGITEMTEMPLATE);
439 *(WORD*)dest = 0xFFFF;
440 dest += sizeof(WORD);
441 *(WORD*)dest = 0x0082; /* static control */
442 dest += sizeof(WORD);
443 *(WORD*)dest = 0xFFFF;
444 dest += sizeof(WORD);
445 *(WCHAR*)dest = 0;
446 dest += sizeof(WCHAR);
447 *(WORD*)dest = 0;
448 dest += sizeof(WORD);
449 }
450
451 /* create static for text */
452 dest = (BYTE*)(((DWORD)dest + 3) & ~3);
453 itxt = (DLGITEMTEMPLATE *)dest;
454 itxt->style = WS_CHILD | WS_VISIBLE | SS_NOPREFIX;
455 if(lpMsgBoxParams->dwStyle & MB_RIGHT)
456 itxt->style |= SS_RIGHT;
457 else
458 itxt->style |= SS_LEFT;
459 itxt->dwExtendedStyle = 0;
460 itxt->id = MSGBOX_IDTEXT;
461 dest += sizeof(DLGITEMTEMPLATE);
462 *(WORD*)dest = 0xFFFF;
463 dest += sizeof(WORD);
464 *(WORD*)dest = 0x0082; /* static control */
465 dest += sizeof(WORD);
466 memcpy(dest, text, textlen * sizeof(WCHAR));
467 dest += textlen * sizeof(WCHAR);
468 *(WCHAR*)dest = 0;
469 dest += sizeof(WCHAR);
470 *(WORD*)dest = 0;
471 dest += sizeof(WORD);
472
473 /* create buttons */
474 btnsize.cx = BTN_CX;
475 btnsize.cy = BTN_CY;
476 btnrect.left = btnrect.top = 0;
477 for(i = 0; i < nButtons; i++)
478 {
479 dest = (BYTE*)(((DWORD)dest + 3) & ~3);
480 ibtn[i] = (DLGITEMTEMPLATE *)dest;
481 ibtn[i]->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
482 if(!defbtn && (i == ((lpMsgBoxParams->dwStyle & MB_DEFMASK) >> 8)))
483 {
484 ibtn[i]->style |= BS_DEFPUSHBUTTON;
485 mbi.DefBtn = Buttons[i];
486 defbtn = TRUE;
487 }
488 else
489 ibtn[i]->style |= BS_PUSHBUTTON;
490 ibtn[i]->dwExtendedStyle = 0;
491 ibtn[i]->id = Buttons[i];
492 dest += sizeof(DLGITEMTEMPLATE);
493 *(WORD*)dest = 0xFFFF;
494 dest += sizeof(WORD);
495 *(WORD*)dest = 0x0080; /* button control */
496 dest += sizeof(WORD);
497 btnlen = strlenW(ButtonText[i]);
498 memcpy(dest, ButtonText[i], btnlen * sizeof(WCHAR));
499 dest += btnlen * sizeof(WCHAR);
500 *(WORD*)dest = 0;
501 dest += sizeof(WORD);
502 *(WORD*)dest = 0;
503 dest += sizeof(WORD);
504 SelectObject(hDC, hFont);
505 DrawTextW(hDC, ButtonText[i], btnlen, &btnrect, DT_LEFT | DT_SINGLELINE | DT_CALCRECT);
506 btnsize.cx = max(btnsize.cx, btnrect.right);
507 btnsize.cy = max(btnsize.cy, btnrect.bottom);
508 }
509
510 if ((ULONG_PTR) dest != ((ULONG_PTR) buf + (ULONG_PTR) bufsize))
511 {
512 DbgPrint("Tell GvG he can't count: bufsize is %lu, but should be %lu\n", bufsize, (ULONG_PTR) dest - (ULONG_PTR) buf);
513 }
514
515 /* make first button the default button if no other is */
516 if(!defbtn)
517 {
518 ibtn[0]->style &= ~BS_PUSHBUTTON;
519 ibtn[0]->style |= BS_DEFPUSHBUTTON;
520 mbi.DefBtn = Buttons[0];
521 }
522
523 /* calculate position and size of controls */
524 txtrect.right = GetSystemMetrics(SM_CXSCREEN) / 5 * 4;
525 if(Icon)
526 txtrect.right -= GetSystemMetrics(SM_CXICON) + MSGBOXEX_SPACING;
527 txtrect.top = txtrect.left = txtrect.bottom = 0;
528 SelectObject(hDC, hFont);
529 DrawTextW(hDC, text, textlen, &txtrect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
530 txtrect.right++;
531
532 /* calculate position and size of the icon */
533 rc.left = rc.bottom = rc.right = 0;
534 btntop = 0;
535
536 if(iico)
537 {
538 rc.right = GetSystemMetrics(SM_CXICON);
539 rc.bottom = GetSystemMetrics(SM_CYICON);
540 #ifdef MSGBOX_ICONVCENTER
541 rc.top = MSGBOXEX_MARGIN + (max(txtrect.bottom, rc.bottom) / 2) - (GetSystemMetrics(SM_CYICON) / 2);
542 rc.top = max(MSGBOXEX_SPACING, rc.top);
543 #else
544 rc.top = MSGBOXEX_MARGIN;
545 #endif
546 btnleft = (nButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
547 if(btnleft > txtrect.right + rc.right + MSGBOXEX_SPACING)
548 {
549 #ifdef MSGBOX_TEXTHCENTER
550 lmargin = MSGBOXEX_MARGIN + ((btnleft - txtrect.right - rc.right - MSGBOXEX_SPACING) / 2);
551 #else
552 lmargin = MSGBOXEX_MARGIN;
553 #endif
554 btnleft = MSGBOXEX_MARGIN;
555 }
556 else
557 {
558 lmargin = MSGBOXEX_MARGIN;
559 btnleft = MSGBOXEX_MARGIN + ((txtrect.right + rc.right + MSGBOXEX_SPACING) / 2) - (btnleft / 2);
560 }
561 rc.left = lmargin;
562 iico->x = (rc.left * 4) / LOWORD(units);
563 iico->y = (rc.top * 8) / HIWORD(units);
564 iico->cx = (rc.right * 4) / LOWORD(units);
565 iico->cy = (rc.bottom * 8) / HIWORD(units);
566 btntop = rc.top + rc.bottom + MSGBOXEX_SPACING;
567 rc.left += rc.right + MSGBOXEX_SPACING;
568 }
569 else
570 {
571 btnleft = (nButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
572 if(btnleft > txtrect.right)
573 {
574 #ifdef MSGBOX_TEXTHCENTER
575 lmargin = MSGBOXEX_MARGIN + ((btnleft - txtrect.right) / 2);
576 #else
577 lmargin = MSGBOXEX_MARGIN;
578 #endif
579 btnleft = MSGBOXEX_MARGIN;
580 }
581 else
582 {
583 lmargin = MSGBOXEX_MARGIN;
584 btnleft = MSGBOXEX_MARGIN + (txtrect.right / 2) - (btnleft / 2);
585 }
586 rc.left = lmargin;
587 }
588 /* calculate position of the text */
589 rc.top = MSGBOXEX_MARGIN + (rc.bottom / 2) - (txtrect.bottom / 2);
590 rc.top = max(rc.top, MSGBOXEX_MARGIN);
591 /* calculate position of the buttons */
592 btntop = max(rc.top + txtrect.bottom + MSGBOXEX_SPACING, btntop);
593 for(i = 0; i < nButtons; i++)
594 {
595 ibtn[i]->x = (btnleft * 4) / LOWORD(units);
596 ibtn[i]->y = (btntop * 8) / HIWORD(units);
597 ibtn[i]->cx = (btnsize.cx * 4) / LOWORD(units);
598 ibtn[i]->cy = (btnsize.cy * 8) / HIWORD(units);
599 btnleft += btnsize.cx + MSGBOXEX_BUTTONSPACING;
600 }
601 /* calculate size and position of the messagebox window */
602 btnleft = max(btnleft - MSGBOXEX_BUTTONSPACING, rc.left + txtrect.right);
603 btnleft += MSGBOXEX_MARGIN;
604 btntop += btnsize.cy + MSGBOXEX_MARGIN;
605 /* set size and position of the message static */
606 itxt->x = (rc.left * 4) / LOWORD(units);
607 itxt->y = (rc.top * 8) / HIWORD(units);
608 itxt->cx = (((btnleft - rc.left - MSGBOXEX_MARGIN) * 4) / LOWORD(units));
609 itxt->cy = ((txtrect.bottom * 8) / HIWORD(units));
610 /* set size of the window */
611 tpl->cx = (btnleft * 4) / LOWORD(units);
612 tpl->cy = (btntop * 8) / HIWORD(units);
613
614 /* finally show the messagebox */
615 mbi.Icon = Icon;
616 mbi.Font = hFont;
617 mbi.ContextHelpId = lpMsgBoxParams->dwContextHelpId;
618 mbi.Callback = lpMsgBoxParams->lpfnMsgBoxCallback;
619 mbi.Style = lpMsgBoxParams->dwStyle;
620 mbi.nButtons = nButtons;
621 mbi.Btns = &Buttons[0];
622 mbi.Timeout = Timeout;
623
624 if(hDC)
625 DeleteDC(hDC);
626
627 ret = DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, tpl, lpMsgBoxParams->hwndOwner,
628 MessageBoxProc, (LPARAM)&mbi);
629
630 if(hFont)
631 DeleteObject(hFont);
632
633 RtlFreeHeap(GetProcessHeap(), 0, buf);
634 return ret;
635 }
636
637 /* FUNCTIONS *****************************************************************/
638
639
640 /*
641 * @implemented
642 */
643 int
644 STDCALL
645 MessageBoxA(
646 HWND hWnd,
647 LPCSTR lpText,
648 LPCSTR lpCaption,
649 UINT uType)
650 {
651 return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
652 }
653
654
655 /*
656 * @implemented
657 */
658 int
659 STDCALL
660 MessageBoxExA(
661 HWND hWnd,
662 LPCSTR lpText,
663 LPCSTR lpCaption,
664 UINT uType,
665 WORD wLanguageId)
666 {
667 MSGBOXPARAMSA msgbox;
668
669 msgbox.cbSize = sizeof(msgbox);
670 msgbox.hwndOwner = hWnd;
671 msgbox.hInstance = 0;
672 msgbox.lpszText = lpText;
673 msgbox.lpszCaption = lpCaption;
674 msgbox.dwStyle = uType;
675 msgbox.lpszIcon = NULL;
676 msgbox.dwContextHelpId = 0;
677 msgbox.lpfnMsgBoxCallback = NULL;
678 msgbox.dwLanguageId = wLanguageId;
679
680 return MessageBoxIndirectA(&msgbox);
681 }
682
683
684 /*
685 * @implemented
686 */
687 int
688 STDCALL
689 MessageBoxExW(
690 HWND hWnd,
691 LPCWSTR lpText,
692 LPCWSTR lpCaption,
693 UINT uType,
694 WORD wLanguageId)
695 {
696 MSGBOXPARAMSW msgbox;
697
698 msgbox.cbSize = sizeof(msgbox);
699 msgbox.hwndOwner = hWnd;
700 msgbox.hInstance = 0;
701 msgbox.lpszText = lpText;
702 msgbox.lpszCaption = lpCaption;
703 msgbox.dwStyle = uType;
704 msgbox.lpszIcon = NULL;
705 msgbox.dwContextHelpId = 0;
706 msgbox.lpfnMsgBoxCallback = NULL;
707 msgbox.dwLanguageId = wLanguageId;
708
709 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)-1);
710 }
711
712
713 /*
714 * @implemented
715 */
716 int
717 STDCALL
718 MessageBoxIndirectA(
719 CONST MSGBOXPARAMSA *lpMsgBoxParams)
720 {
721 MSGBOXPARAMSW msgboxW;
722 UNICODE_STRING textW, captionW, iconW;
723 int ret;
724
725 if (HIWORD((UINT)lpMsgBoxParams->lpszText))
726 {
727 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText);
728 /*
729 * UNICODE_STRING objects are always allocated with an extra byte so you
730 * can null-term if you want
731 */
732 textW.Buffer[textW.Length / sizeof(WCHAR)] = L'\0';
733 }
734 else
735 textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText;
736
737 if (HIWORD((UINT)lpMsgBoxParams->lpszCaption))
738 {
739 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption);
740 /*
741 * UNICODE_STRING objects are always allocated with an extra byte so you
742 * can null-term if you want
743 */
744 captionW.Buffer[captionW.Length / sizeof(WCHAR)] = L'\0';
745 }
746 else
747 captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption;
748
749 if (HIWORD((UINT)lpMsgBoxParams->lpszIcon))
750 {
751 RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon);
752 /*
753 * UNICODE_STRING objects are always allocated with an extra byte so you
754 * can null-term if you want
755 */
756 iconW.Buffer[iconW.Length / sizeof(WCHAR)] = L'\0';
757 }
758 else
759 iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon;
760
761 msgboxW.cbSize = sizeof(msgboxW);
762 msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner;
763 msgboxW.hInstance = lpMsgBoxParams->hInstance;
764 msgboxW.lpszText = textW.Buffer;
765 msgboxW.lpszCaption = captionW.Buffer;
766 msgboxW.dwStyle = lpMsgBoxParams->dwStyle;
767 msgboxW.lpszIcon = iconW.Buffer;
768 msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
769 msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
770 msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId;
771
772 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)-1);
773
774 if (HIWORD((UINT)lpMsgBoxParams->lpszText))
775 RtlFreeUnicodeString(&textW);
776
777 if (HIWORD((UINT)lpMsgBoxParams->lpszCaption))
778 RtlFreeUnicodeString(&captionW);
779
780 if (HIWORD((UINT)lpMsgBoxParams->lpszIcon))
781 RtlFreeUnicodeString(&iconW);
782
783 return ret;
784 }
785
786
787 /*
788 * @implemented
789 */
790 int
791 STDCALL
792 MessageBoxIndirectW(
793 CONST MSGBOXPARAMSW *lpMsgBoxParams)
794 {
795 return MessageBoxTimeoutIndirectW(lpMsgBoxParams, (UINT)-1);
796 }
797
798
799 /*
800 * @implemented
801 */
802 int
803 STDCALL
804 MessageBoxW(
805 HWND hWnd,
806 LPCWSTR lpText,
807 LPCWSTR lpCaption,
808 UINT uType)
809 {
810 return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL);
811 }
812
813 /*
814 * @implemented
815 */
816 int
817 STDCALL
818 MessageBoxTimeoutA(
819 HWND hWnd,
820 LPCSTR lpText,
821 LPCSTR lpCaption,
822 UINT uType,
823 WORD wLanguageId,
824 DWORD dwTime)
825 {
826 MSGBOXPARAMSW msgboxW;
827 UNICODE_STRING textW, captionW;
828 int ret;
829
830 if (HIWORD((UINT)lpText))
831 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText);
832 else
833 textW.Buffer = (LPWSTR)lpText;
834
835 if (HIWORD((UINT)lpCaption))
836 RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpCaption);
837 else
838 captionW.Buffer = (LPWSTR)lpCaption;
839
840 msgboxW.cbSize = sizeof(msgboxW);
841 msgboxW.hwndOwner = hWnd;
842 msgboxW.hInstance = 0;
843 msgboxW.lpszText = textW.Buffer;
844 msgboxW.lpszCaption = captionW.Buffer;
845 msgboxW.dwStyle = uType;
846 msgboxW.lpszIcon = NULL;
847 msgboxW.dwContextHelpId = 0;
848 msgboxW.lpfnMsgBoxCallback = NULL;
849 msgboxW.dwLanguageId = wLanguageId;
850
851 ret = MessageBoxTimeoutIndirectW(&msgboxW, (UINT)dwTime);
852
853 if (HIWORD(textW.Buffer))
854 RtlFreeUnicodeString(&textW);
855
856 if (HIWORD(captionW.Buffer))
857 RtlFreeUnicodeString(&captionW);
858
859 return ret;
860 }
861
862 /*
863 * @implemented
864 */
865 int
866 STDCALL
867 MessageBoxTimeoutW(
868 HWND hWnd,
869 LPCWSTR lpText,
870 LPCWSTR lpCaption,
871 UINT uType,
872 WORD wLanguageId,
873 DWORD dwTime)
874 {
875 MSGBOXPARAMSW msgbox;
876
877 msgbox.cbSize = sizeof(msgbox);
878 msgbox.hwndOwner = hWnd;
879 msgbox.hInstance = 0;
880 msgbox.lpszText = lpText;
881 msgbox.lpszCaption = lpCaption;
882 msgbox.dwStyle = uType;
883 msgbox.lpszIcon = NULL;
884 msgbox.dwContextHelpId = 0;
885 msgbox.lpfnMsgBoxCallback = NULL;
886 msgbox.dwLanguageId = wLanguageId;
887
888 return MessageBoxTimeoutIndirectW(&msgbox, (UINT)dwTime);
889 }
890
891
892 /*
893 * @unimplemented
894 */
895 DWORD
896 STDCALL
897 SoftModalMessageBox(DWORD Unknown0)
898 {
899 UNIMPLEMENTED;
900 return 0;
901 }
902
903
904 /*
905 * @implemented
906 */
907 BOOL
908 STDCALL
909 MessageBeep(UINT uType)
910 {
911 #if 0
912 LPWSTR EventName;
913
914 switch(uType)
915 {
916 case 0xFFFFFFFF:
917 if(waveOutGetNumDevs() == 0)
918 return Beep(500, 100); // Beep through speaker
919 /* fall through */
920 case MB_OK:
921 EventName = L"SystemDefault";
922 break;
923 case MB_ICONASTERISK:
924 EventName = L"SystemAsterisk";
925 break;
926 case MB_ICONEXCLAMATION:
927 EventName = L"SystemExclamation";
928 break;
929 case MB_ICONHAND:
930 EventName = L"SystemHand";
931 break;
932 case MB_ICONQUESTION:
933 EventName = L"SystemQuestion";
934 break;
935 }
936
937 return PlaySoundW((LPCWSTR)EventName, NULL, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC);
938 #else
939 return Beep(500, 100); // Beep through speaker
940 #endif
941 }
942
943 /* EOF */