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