a53fcb89706669dd851d5f7c854bbee7da63d0ce
[reactos.git] / reactos / 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 * 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
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 (100)
44
45 #define IDI_HANDA MAKEINTRESOURCEA(32513)
46 #define IDI_HANDW MAKEINTRESOURCEW(32513)
47 #define IDI_QUESTIONA MAKEINTRESOURCEA(32514)
48 #define IDI_QUESTIONW MAKEINTRESOURCEW(32514)
49 #define IDI_EXCLAMATIONA MAKEINTRESOURCEA(32515)
50 #define IDI_EXCLAMATIONW MAKEINTRESOURCEW(32515)
51 #define IDI_ASTERISKA MAKEINTRESOURCEA(32516)
52 #define IDI_ASTERISKW MAKEINTRESOURCEW(32516)
53 #define IDI_WINLOGOA MAKEINTRESOURCEA(32517)
54 #define IDI_WINLOGOW MAKEINTRESOURCEW(32517)
55
56 #define BTN_CX (75)
57 #define BTN_CY (23)
58
59 #define MSGBOXEX_SPACING (16)
60 #define MSGBOXEX_BUTTONSPACING (6)
61 #define MSGBOXEX_MARGIN (12)
62 #define MSGBOXEX_MAXBTNSTR (32)
63 #define MSGBOXEX_MAXBTNS (4)
64
65 typedef struct _MSGBOXINFO {
66 MSGBOXPARAMSW; // Wine passes this too.
67 // ReactOS
68 HICON Icon;
69 HFONT Font;
70 int DefBtn;
71 int nButtons;
72 LONG *Btns;
73 UINT Timeout;
74 } MSGBOXINFO, *PMSGBOXINFO;
75
76 /* INTERNAL FUNCTIONS ********************************************************/
77
78 static VOID MessageBoxTextToClipboard(HWND DialogWindow)
79 {
80 HWND hwndText;
81 PMSGBOXINFO mbi;
82 int cchTotal, cchTitle, cchText, cchButton, i, n, cchBuffer;
83 LPWSTR pszBuffer, pszBufferPos, pMessageBoxText, pszTitle, pszText, pszButton;
84 WCHAR szButton[MSGBOXEX_MAXBTNSTR];
85 HGLOBAL hGlobal;
86
87 static const WCHAR szLine[30] =
88 {'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-',
89 '-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0};
90
91 mbi = (PMSGBOXINFO)GetPropW(DialogWindow, L"ROS_MSGBOX");
92 hwndText = GetDlgItem(DialogWindow, MSGBOX_IDTEXT);
93 cchTitle = GetWindowTextLengthW(DialogWindow) + 1;
94 cchText = GetWindowTextLengthW(hwndText) + 1;
95
96 if(!mbi)
97 return;
98
99 pMessageBoxText = (LPWSTR)RtlAllocateHeap(GetProcessHeap(), 0, (cchTitle + cchText) * sizeof(WCHAR));
100
101 if(pMessageBoxText == NULL)
102 {
103 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
104 return;
105 }
106
107 pszTitle = pMessageBoxText;
108 pszText = pMessageBoxText + cchTitle;
109
110
111
112 if(GetWindowTextW(DialogWindow, pszTitle, cchTitle) == 0 ||
113 GetWindowTextW(hwndText, pszText, cchText) == 0)
114 {
115 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
116 return;
117 }
118
119 /*
120 * Calculate the total buffer size.
121 */
122 cchTotal = 6 + cchTitle + cchText + (lstrlenW(szLine) * 4) + (mbi->nButtons * MSGBOXEX_MAXBTNSTR + 3);
123
124 hGlobal = GlobalAlloc(GHND, cchTotal * sizeof(WCHAR));
125
126 pszBuffer = (LPWSTR)GlobalLock(hGlobal);
127
128 if(pszBuffer == NULL)
129 {
130 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
131 GlobalFree(hGlobal);
132 return;
133 }
134
135 /*
136 * First format title and text.
137 * ------------------
138 * Title
139 * ------------------
140 * Text
141 * ------------------
142 */
143 cchBuffer = wsprintfW(pszBuffer, L"%s%s\r\n%s%s\r\n%s", szLine, pszTitle, szLine, pszText, szLine);
144 pszBufferPos = pszBuffer + cchBuffer;
145
146 for(i = 0; i < mbi->nButtons; i++)
147 {
148 GetDlgItemTextW(DialogWindow, mbi->Btns[i], szButton, MSGBOXEX_MAXBTNSTR);
149
150 cchButton = strlenW(szButton);
151 pszButton = szButton;
152
153 /* Skip '&' character. */
154 if(szButton[0] == '&')
155 {
156 pszButton = pszButton + 1;
157 cchButton = cchButton - 1;
158 }
159
160 for(n = 0; n < cchButton; n++)
161 *(pszBufferPos++) = pszButton[n];
162
163 /* Add spaces. */
164 *(pszBufferPos++) = L' ';
165 *(pszBufferPos++) = L' ';
166 *(pszBufferPos++) = L' ';
167 }
168
169 wsprintfW(pszBufferPos, L"\r\n%s", szLine);
170
171 GlobalUnlock(hGlobal);
172
173 if(OpenClipboard(DialogWindow))
174 {
175 EmptyClipboard();
176 SetClipboardData(CF_UNICODETEXT, hGlobal);
177 CloseClipboard();
178 }
179 else
180 {
181 GlobalFree(hGlobal);
182 }
183 RtlFreeHeap(GetProcessHeap(), 0, pMessageBoxText);
184 }
185
186 static INT_PTR CALLBACK MessageBoxProc( HWND hwnd, UINT message,
187 WPARAM wParam, LPARAM lParam )
188 {
189 int i, Alert;
190 PMSGBOXINFO mbi;
191 HELPINFO hi;
192 HWND owner;
193
194 switch(message) {
195 case WM_INITDIALOG:
196 mbi = (PMSGBOXINFO)lParam;
197
198 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)mbi);
199 NtUserxSetMessageBox(hwnd);
200
201 if(!GetPropW(hwnd, L"ROS_MSGBOX"))
202 {
203 SetPropW(hwnd, L"ROS_MSGBOX", (HANDLE)lParam);
204
205 if (mbi->dwContextHelpId)
206 SetWindowContextHelpId(hwnd, mbi->dwContextHelpId);
207
208 if (mbi->Icon)
209 {
210 SendDlgItemMessageW(hwnd, MSGBOX_IDICON, STM_SETICON, (WPARAM)mbi->Icon, 0);
211 Alert = ALERT_SYSTEM_WARNING;
212 }
213 else // Setup the rest of the alerts.
214 {
215 switch(mbi->dwStyle & MB_ICONMASK)
216 {
217 case MB_ICONWARNING:
218 Alert = ALERT_SYSTEM_WARNING;
219 break;
220 case MB_ICONERROR:
221 Alert = ALERT_SYSTEM_ERROR;
222 break;
223 case MB_ICONQUESTION:
224 Alert = ALERT_SYSTEM_QUERY;
225 break;
226 default:
227 Alert = ALERT_SYSTEM_INFORMATIONAL;
228 /* fall through */
229 }
230 }
231 /* Send out the alert notifications. */
232 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd, OBJID_ALERT, Alert);
233
234 /* set control fonts */
235 SendDlgItemMessageW(hwnd, MSGBOX_IDTEXT, WM_SETFONT, (WPARAM)mbi->Font, 0);
236 for(i = 0; i < mbi->nButtons; i++)
237 {
238 SendDlgItemMessageW(hwnd, mbi->Btns[i], WM_SETFONT, (WPARAM)mbi->Font, 0);
239 }
240 switch(mbi->dwStyle & MB_TYPEMASK)
241 {
242 case MB_ABORTRETRYIGNORE:
243 case MB_YESNO:
244 RemoveMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
245 break;
246 }
247 SetFocus(GetDlgItem(hwnd, mbi->DefBtn));
248 if(mbi->Timeout && (mbi->Timeout != (UINT)-1))
249 SetTimer(hwnd, 0, mbi->Timeout, NULL);
250 }
251 return 0;
252
253 case WM_COMMAND:
254 switch (LOWORD(wParam))
255 {
256 case IDOK:
257 case IDCANCEL:
258 case IDABORT:
259 case IDRETRY:
260 case IDIGNORE:
261 case IDYES:
262 case IDNO:
263 case IDTRYAGAIN:
264 case IDCONTINUE:
265 EndDialog(hwnd, wParam);
266 return 0;
267 case IDHELP:
268 /* send WM_HELP message to messagebox window */
269 hi.cbSize = sizeof(HELPINFO);
270 hi.iContextType = HELPINFO_WINDOW;
271 hi.iCtrlId = LOWORD(wParam);
272 hi.hItemHandle = (HANDLE)lParam;
273 hi.dwContextId = 0;
274 GetCursorPos(&hi.MousePos);
275 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
276 return 0;
277 }
278 return 0;
279
280 case WM_COPY:
281 MessageBoxTextToClipboard(hwnd);
282 return 0;
283
284 case WM_HELP:
285 mbi = (PMSGBOXINFO)GetPropW(hwnd, L"ROS_MSGBOX");
286 if(!mbi)
287 return 0;
288 memcpy(&hi, (void *)lParam, sizeof(hi));
289 hi.dwContextId = GetWindowContextHelpId(hwnd);
290
291 if (mbi->lpfnMsgBoxCallback)
292 mbi->lpfnMsgBoxCallback(&hi);
293 else
294 {
295 owner = GetWindow(hwnd, GW_OWNER);
296 if(owner)
297 SendMessageW(GetWindow(hwnd, GW_OWNER), WM_HELP, 0, (LPARAM)&hi);
298 }
299 return 0;
300
301 case WM_CLOSE:
302 mbi = (PMSGBOXINFO)GetPropW(hwnd, L"ROS_MSGBOX");
303 if(!mbi)
304 return 0;
305 switch(mbi->dwStyle & MB_TYPEMASK)
306 {
307 case MB_ABORTRETRYIGNORE:
308 case MB_YESNO:
309 return 1;
310 }
311 EndDialog(hwnd, IDCANCEL);
312 return 1;
313
314 case WM_TIMER:
315 if(wParam == 0)
316 {
317 EndDialog(hwnd, 32000);
318 }
319 return 0;
320 }
321 return 0;
322 }
323
324 static int
325 MessageBoxTimeoutIndirectW(
326 CONST MSGBOXPARAMSW *lpMsgBoxParams, UINT Timeout)
327 {
328 DLGTEMPLATE *tpl;
329 DLGITEMTEMPLATE *iico, *itxt;
330 NONCLIENTMETRICSW nclm;
331 WCHAR capbuf[32];
332 LPVOID buf;
333 BYTE *dest;
334 LPCWSTR caption, text;
335 HFONT hFont;
336 HICON Icon;
337 HDC hDC;
338 int bufsize, ret, caplen, textlen, btnlen, i, btnleft, btntop, lmargin, nButtons = 0;
339 LONG Buttons[MSGBOXEX_MAXBTNS];
340 WCHAR ButtonText[MSGBOXEX_MAXBTNS][MSGBOXEX_MAXBTNSTR];
341 DLGITEMTEMPLATE *ibtn[MSGBOXEX_MAXBTNS];
342 RECT btnrect, txtrect, rc;
343 SIZE btnsize;
344 MSGBOXINFO mbi;
345 BOOL defbtn = FALSE;
346 DWORD units = GetDialogBaseUnits();
347
348 if(!lpMsgBoxParams->lpszCaption || !HIWORD((LPWSTR)lpMsgBoxParams->lpszCaption))
349 {
350 LoadStringW(User32Instance, IDS_ERROR, &capbuf[0], 32);
351 caption = &capbuf[0];
352 }
353 else
354 caption = (LPWSTR)lpMsgBoxParams->lpszCaption;
355
356 if(!lpMsgBoxParams->lpszText || !HIWORD(lpMsgBoxParams->lpszText))
357 text = L"";
358 else
359 text = lpMsgBoxParams->lpszText;
360
361 caplen = strlenW(caption);
362 textlen = strlenW(text);
363
364 /* Create selected buttons */
365 switch(lpMsgBoxParams->dwStyle & MB_TYPEMASK)
366 {
367 case MB_OKCANCEL:
368 Buttons[0] = IDOK;
369 Buttons[1] = IDCANCEL;
370 nButtons = 2;
371 break;
372 case MB_CANCELTRYCONTINUE:
373 Buttons[0] = IDCANCEL;
374 Buttons[1] = IDTRYAGAIN;
375 Buttons[2] = IDCONTINUE;
376 nButtons = 3;
377 break;
378 case MB_ABORTRETRYIGNORE:
379 Buttons[0] = IDABORT;
380 Buttons[1] = IDRETRY;
381 Buttons[2] = IDIGNORE;
382 nButtons = 3;
383 break;
384 case MB_YESNO:
385 Buttons[0] = IDYES;
386 Buttons[1] = IDNO;
387 nButtons = 2;
388 break;
389 case MB_YESNOCANCEL:
390 Buttons[0] = IDYES;
391 Buttons[1] = IDNO;
392 Buttons[2] = IDCANCEL;
393 nButtons = 3;
394 break;
395 case MB_RETRYCANCEL:
396 Buttons[0] = IDRETRY;
397 Buttons[1] = IDCANCEL;
398 nButtons = 2;
399 break;
400 case MB_OK:
401 /* fall through */
402 default:
403 Buttons[0] = IDOK;
404 nButtons = 1;
405 break;
406 }
407 /* Create Help button */
408 if(lpMsgBoxParams->dwStyle & MB_HELP)
409 Buttons[nButtons++] = IDHELP;
410
411 switch(lpMsgBoxParams->dwStyle & MB_ICONMASK)
412 {
413 case MB_ICONEXCLAMATION:
414 Icon = LoadIconW(0, IDI_EXCLAMATIONW);
415 MessageBeep(MB_ICONEXCLAMATION);
416 break;
417 case MB_ICONQUESTION:
418 Icon = LoadIconW(0, IDI_QUESTIONW);
419 MessageBeep(MB_ICONQUESTION);
420 break;
421 case MB_ICONASTERISK:
422 Icon = LoadIconW(0, IDI_ASTERISKW);
423 MessageBeep(MB_ICONASTERISK);
424 break;
425 case MB_ICONHAND:
426 Icon = LoadIconW(0, IDI_HANDW);
427 MessageBeep(MB_ICONHAND);
428 break;
429 case MB_USERICON:
430 Icon = LoadIconW(lpMsgBoxParams->hInstance, lpMsgBoxParams->lpszIcon);
431 MessageBeep(MB_OK);
432 break;
433 default:
434 /* By default, Windows 95/98/NT does not associate an icon to message boxes.
435 * So ReactOS should do the same.
436 */
437 Icon = (HICON)0;
438 MessageBeep(MB_OK);
439 break;
440 }
441
442 /* Basic space */
443 bufsize = sizeof(DLGTEMPLATE) +
444 2 * sizeof(WORD) + /* menu and class */
445 (caplen + 1) * sizeof(WCHAR); /* title */
446
447 /* Space for icon */
448 if (NULL != Icon)
449 {
450 bufsize = (bufsize + 3) & ~3;
451 bufsize += sizeof(DLGITEMTEMPLATE) +
452 4 * sizeof(WORD) +
453 sizeof(WCHAR);
454 }
455
456 /* Space for text */
457 bufsize = (bufsize + 3) & ~3;
458 bufsize += sizeof(DLGITEMTEMPLATE) +
459 3 * sizeof(WORD) +
460 (textlen + 1) * sizeof(WCHAR);
461
462
463 for(i = 0; i < nButtons; i++)
464 {
465 switch(Buttons[i])
466 {
467 case IDOK:
468 LoadStringW(User32Instance, IDS_OK, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
469 break;
470 case IDCANCEL:
471 LoadStringW(User32Instance, IDS_CANCEL, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
472 break;
473 case IDYES:
474 LoadStringW(User32Instance, IDS_YES, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
475 break;
476 case IDNO:
477 LoadStringW(User32Instance, IDS_NO, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
478 break;
479 case IDTRYAGAIN:
480 LoadStringW(User32Instance, IDS_TRYAGAIN, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
481 break;
482 case IDCONTINUE:
483 LoadStringW(User32Instance, IDS_CONTINUE, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
484 break;
485 case IDABORT:
486 LoadStringW(User32Instance, IDS_ABORT, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
487 break;
488 case IDRETRY:
489 LoadStringW(User32Instance, IDS_RETRY, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
490 break;
491 case IDIGNORE:
492 LoadStringW(User32Instance, IDS_IGNORE, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
493 break;
494 case IDHELP:
495 LoadStringW(User32Instance, IDS_HELP, ButtonText[i], MSGBOXEX_MAXBTNSTR - 1);
496 break;
497 default:
498 ButtonText[i][0] = (WCHAR)0;
499 break;
500 }
501
502 /* Space for buttons */
503 bufsize = (bufsize + 3) & ~3;
504 bufsize += sizeof(DLGITEMTEMPLATE) +
505 3 * sizeof(WORD) +
506 (wcslen(ButtonText[i]) + 1) * sizeof(WCHAR);
507 }
508
509 buf = RtlAllocateHeap(GetProcessHeap(), 0, bufsize);
510 if(!buf)
511 {
512 return 0;
513 }
514 iico = itxt = NULL;
515
516 hDC = CreateCompatibleDC(0);
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 = nButtons + ((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 /* create buttons */
589 btnsize.cx = BTN_CX;
590 btnsize.cy = BTN_CY;
591 btnrect.left = btnrect.top = 0;
592 for(i = 0; i < nButtons; i++)
593 {
594 dest = (BYTE*)(((UINT_PTR)dest + 3) & ~3);
595 ibtn[i] = (DLGITEMTEMPLATE *)dest;
596 ibtn[i]->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
597 if(!defbtn && (i == ((lpMsgBoxParams->dwStyle & MB_DEFMASK) >> 8)))
598 {
599 ibtn[i]->style |= BS_DEFPUSHBUTTON;
600 mbi.DefBtn = Buttons[i];
601 defbtn = TRUE;
602 }
603 else
604 ibtn[i]->style |= BS_PUSHBUTTON;
605 ibtn[i]->dwExtendedStyle = 0;
606 ibtn[i]->id = Buttons[i];
607 dest += sizeof(DLGITEMTEMPLATE);
608 *(WORD*)dest = 0xFFFF;
609 dest += sizeof(WORD);
610 *(WORD*)dest = 0x0080; /* button control */
611 dest += sizeof(WORD);
612 btnlen = strlenW(ButtonText[i]);
613 memcpy(dest, ButtonText[i], btnlen * sizeof(WCHAR));
614 dest += btnlen * sizeof(WCHAR);
615 *(WORD*)dest = 0;
616 dest += sizeof(WORD);
617 *(WORD*)dest = 0;
618 dest += sizeof(WORD);
619 SelectObject(hDC, hFont);
620 DrawTextW(hDC, ButtonText[i], btnlen, &btnrect, DT_LEFT | DT_SINGLELINE | DT_CALCRECT);
621 btnsize.cx = max(btnsize.cx, btnrect.right);
622 btnsize.cy = max(btnsize.cy, btnrect.bottom);
623 }
624
625 /* make first button the default button if no other is */
626 if(!defbtn)
627 {
628 ibtn[0]->style &= ~BS_PUSHBUTTON;
629 ibtn[0]->style |= BS_DEFPUSHBUTTON;
630 mbi.DefBtn = Buttons[0];
631 }
632
633 /* calculate position and size of controls */
634 txtrect.right = GetSystemMetrics(SM_CXSCREEN) / 5 * 4;
635 if(Icon)
636 txtrect.right -= GetSystemMetrics(SM_CXICON) + MSGBOXEX_SPACING;
637 txtrect.top = txtrect.left = txtrect.bottom = 0;
638 SelectObject(hDC, hFont);
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 /* calculate position and size of the icon */
651 rc.left = rc.bottom = rc.right = 0;
652 btntop = 0;
653
654 if(iico)
655 {
656 rc.right = GetSystemMetrics(SM_CXICON);
657 rc.bottom = GetSystemMetrics(SM_CYICON);
658 #ifdef MSGBOX_ICONVCENTER
659 rc.top = MSGBOXEX_MARGIN + (max(txtrect.bottom, rc.bottom) / 2) - (GetSystemMetrics(SM_CYICON) / 2);
660 rc.top = max(MSGBOXEX_SPACING, rc.top);
661 #else
662 rc.top = MSGBOXEX_MARGIN;
663 #endif
664 btnleft = (nButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
665 if(btnleft > txtrect.right + rc.right + MSGBOXEX_SPACING)
666 {
667 #ifdef MSGBOX_TEXTHCENTER
668 lmargin = MSGBOXEX_MARGIN + ((btnleft - txtrect.right - rc.right - MSGBOXEX_SPACING) / 2);
669 #else
670 lmargin = MSGBOXEX_MARGIN;
671 #endif
672 btnleft = MSGBOXEX_MARGIN;
673 }
674 else
675 {
676 lmargin = MSGBOXEX_MARGIN;
677 btnleft = MSGBOXEX_MARGIN + ((txtrect.right + rc.right + MSGBOXEX_SPACING) / 2) - (btnleft / 2);
678 }
679 rc.left = lmargin;
680 iico->x = (rc.left * 4) / LOWORD(units);
681 iico->y = (rc.top * 8) / HIWORD(units);
682 iico->cx = (rc.right * 4) / LOWORD(units);
683 iico->cy = (rc.bottom * 8) / HIWORD(units);
684 btntop = rc.top + rc.bottom + MSGBOXEX_SPACING;
685 rc.left += rc.right + MSGBOXEX_SPACING;
686 }
687 else
688 {
689 btnleft = (nButtons * (btnsize.cx + MSGBOXEX_BUTTONSPACING)) - MSGBOXEX_BUTTONSPACING;
690 if(btnleft > txtrect.right)
691 {
692 #ifdef MSGBOX_TEXTHCENTER
693 lmargin = MSGBOXEX_MARGIN + ((btnleft - txtrect.right) / 2);
694 #else
695 lmargin = MSGBOXEX_MARGIN;
696 #endif
697 btnleft = MSGBOXEX_MARGIN;
698 }
699 else
700 {
701 lmargin = MSGBOXEX_MARGIN;
702 btnleft = MSGBOXEX_MARGIN + (txtrect.right / 2) - (btnleft / 2);
703 }
704 rc.left = lmargin;
705 }
706 /* calculate position of the text */
707 rc.top = MSGBOXEX_MARGIN + (rc.bottom / 2) - (txtrect.bottom / 2);
708 rc.top = max(rc.top, MSGBOXEX_MARGIN);
709 /* calculate position of the buttons */
710 btntop = max(rc.top + txtrect.bottom + MSGBOXEX_SPACING, btntop);
711 for(i = 0; i < nButtons; i++)
712 {
713 ibtn[i]->x = (btnleft * 4) / LOWORD(units);
714 ibtn[i]->y = (btntop * 8) / HIWORD(units);
715 ibtn[i]->cx = (btnsize.cx * 4) / LOWORD(units);
716 ibtn[i]->cy = (btnsize.cy * 8) / HIWORD(units);
717 btnleft += btnsize.cx + MSGBOXEX_BUTTONSPACING;
718 }
719 /* calculate size and position of the messagebox window */
720 btnleft = max(btnleft - MSGBOXEX_BUTTONSPACING, rc.left + txtrect.right);
721 btnleft += MSGBOXEX_MARGIN;
722 btntop += btnsize.cy + MSGBOXEX_MARGIN;
723 /* set size and position of the message static */
724 itxt->x = (rc.left * 4) / LOWORD(units);
725 itxt->y = (rc.top * 8) / HIWORD(units);
726 itxt->cx = (((btnleft - rc.left - MSGBOXEX_MARGIN) * 4) / LOWORD(units));
727 itxt->cy = ((txtrect.bottom * 8) / HIWORD(units));
728 /* set size of the window */
729 tpl->cx = (btnleft * 4) / LOWORD(units);
730 tpl->cy = (btntop * 8) / HIWORD(units);
731
732 /* finally show the messagebox */
733 mbi.Icon = Icon;
734 mbi.Font = hFont;
735 mbi.dwContextHelpId = lpMsgBoxParams->dwContextHelpId;
736 mbi.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback;
737 mbi.dwStyle = lpMsgBoxParams->dwStyle;
738 mbi.nButtons = nButtons;
739 mbi.Btns = &Buttons[0];
740 mbi.Timeout = Timeout;
741
742 /* Pass on to Justin Case so he can peek the message? */
743 mbi.cbSize = lpMsgBoxParams->cbSize;
744 mbi.hwndOwner = lpMsgBoxParams->hwndOwner;
745 mbi.hInstance = lpMsgBoxParams->hInstance;
746 mbi.lpszText = lpMsgBoxParams->lpszText;
747 mbi.lpszCaption = lpMsgBoxParams->lpszCaption;
748 mbi.lpszIcon = lpMsgBoxParams->lpszIcon;
749 mbi.dwLanguageId = lpMsgBoxParams->dwLanguageId;
750
751 if(hDC)
752 DeleteDC(hDC);
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 (HIWORD((UINT_PTR)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 (HIWORD((UINT_PTR)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 (HIWORD((UINT_PTR)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 (HIWORD((UINT_PTR)lpMsgBoxParams->lpszText))
907 RtlFreeUnicodeString(&textW);
908
909 if (HIWORD((UINT_PTR)lpMsgBoxParams->lpszCaption))
910 RtlFreeUnicodeString(&captionW);
911
912 if ((lpMsgBoxParams->dwStyle & MB_USERICON) && HIWORD((UINT_PTR)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 (HIWORD((UINT_PTR)lpText))
963 RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpText);
964 else
965 textW.Buffer = (LPWSTR)lpText;
966
967 if (HIWORD((UINT_PTR)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 (HIWORD(textW.Buffer))
986 RtlFreeUnicodeString(&textW);
987
988 if (HIWORD(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 LPWSTR WINAPI MB_GetString(DWORD string)
1051 {
1052 UNIMPLEMENTED;
1053 return NULL;
1054 }
1055
1056 /* EOF */