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