/* * ReactOS kernel * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id$ * * PROJECT: ReactOS user32.dll * FILE: lib/user32/windows/messagebox.c * PURPOSE: Input * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) * Thomas Weidenmueller (w3seek@users.sourceforge.net) * UPDATE HISTORY: * 2003/07/28 Added some NT features * 2003/07/27 Code ported from wine * 09-05-2001 CSH Created */ /* INCLUDES ******************************************************************/ #include #include #include #include #include #include #include #include typedef UINT *LPUINT; #include /* DEFINES *******************************************************************/ #define MSGBOX_IDICON 1088 #define MSGBOX_IDTEXT 100 #define IDS_ERROR 2 #define IDI_HANDA MAKEINTRESOURCEA(32513) #define IDI_HANDW MAKEINTRESOURCEW(32513) #define IDI_QUESTIONA MAKEINTRESOURCEA(32514) #define IDI_QUESTIONW MAKEINTRESOURCEW(32514) #define IDI_EXCLAMATIONA MAKEINTRESOURCEA(32515) #define IDI_EXCLAMATIONW MAKEINTRESOURCEW(32515) #define IDI_ASTERISKA MAKEINTRESOURCEA(32516) #define IDI_ASTERISKW MAKEINTRESOURCEW(32516) #define IDI_WINLOGOA MAKEINTRESOURCEA(32517) #define IDI_WINLOGOW MAKEINTRESOURCEW(32517) #ifndef MB_TYPEMASK #define MB_TYPEMASK 0x0000000F #endif #ifndef MB_ICONMASK #define MB_ICONMASK 0x000000F0 #endif #ifndef MB_DEFMASK #define MB_DEFMASK 0x00000F00 #endif #define DWL_INIT (12) /* FUNCTIONS *****************************************************************/ static HWND MSGBOX_CreateButton(HWND hwnd, LONG ID, LPWSTR Caption) { return CreateWindowExW(0, L"BUTTON", Caption, WS_CHILD | WS_TABSTOP | WS_VISIBLE, 0, 0, 10, 10, hwnd, (HMENU)ID, 0, NULL); } static HFONT MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMS lpmb) { HFONT hFont = 0, hPrevFont = 0; RECT rect; HWND hItem; HDC hdc; int i; int bspace, bw, bh, theight, tleft, wwidth, wheight, bpos; int borheight, borwidth, iheight, ileft, iwidth, twidth, tiheight; BOOL sdefbtn = FALSE; LPCWSTR lpszText; WCHAR buf[256]; NONCLIENTMETRICSW nclm; int nButtons = 0; HWND Buttons[4]; nclm.cbSize = sizeof(nclm); SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0); hFont = CreateFontIndirectW (&nclm.lfMessageFont); /* set button font */ for (i = 1; i < 10; i++) SendDlgItemMessageW (hwnd, i, WM_SETFONT, (WPARAM)hFont, 0); /* set text font */ SendDlgItemMessageW (hwnd, MSGBOX_IDTEXT, WM_SETFONT, (WPARAM)hFont, 0); if (HIWORD(lpmb->lpszCaption)) { SetWindowTextW(hwnd, (LPCWSTR)lpmb->lpszCaption); } else { UINT res_id = LOWORD((UINT)lpmb->lpszCaption); /* FIXME: (UINT) ??? */ if (res_id) { if (LoadStringW(lpmb->hInstance, res_id, buf, 256)) SetWindowTextW(hwnd, buf); } else { if (LoadStringW(GetModuleHandleA("user32.dll"), IDS_ERROR, buf, 256)) SetWindowTextW(hwnd, buf); } } if (HIWORD(lpmb->lpszText)) { lpszText = (LPCWSTR)lpmb->lpszText; } else { lpszText = buf; if (!LoadStringW(lpmb->hInstance, LOWORD((UINT)lpmb->lpszText), buf, 256)) /* FIXME: (UINT) ??? */ *buf = 0; /* FIXME ?? */ } /* Create selected buttons */ switch(lpmb->dwStyle & MB_TYPEMASK) { case MB_OKCANCEL: Buttons[0] = MSGBOX_CreateButton(hwnd, IDOK, L"OK"); Buttons[1] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel"); nButtons = 2; break; case MB_CANCELTRYCONTINUE: Buttons[0] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel"); Buttons[1] = MSGBOX_CreateButton(hwnd, IDTRYAGAIN, L"Try Again"); Buttons[2] = MSGBOX_CreateButton(hwnd, IDCONTINUE, L"Continue"); nButtons = 3; break; case MB_ABORTRETRYIGNORE: Buttons[0] = MSGBOX_CreateButton(hwnd, IDABORT, L"Abort"); Buttons[1] = MSGBOX_CreateButton(hwnd, IDRETRY, L"Retry"); Buttons[2] = MSGBOX_CreateButton(hwnd, IDIGNORE, L"Ignore"); nButtons = 3; break; case MB_YESNO: Buttons[0] = MSGBOX_CreateButton(hwnd, IDYES, L"Yes"); Buttons[1] = MSGBOX_CreateButton(hwnd, IDNO, L"No"); nButtons = 2; break; case MB_YESNOCANCEL: Buttons[0] = MSGBOX_CreateButton(hwnd, IDYES, L"Yes"); Buttons[1] = MSGBOX_CreateButton(hwnd, IDNO, L"No"); Buttons[2] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel"); nButtons = 3; break; case MB_RETRYCANCEL: Buttons[0] = MSGBOX_CreateButton(hwnd, IDRETRY, L"Retry"); Buttons[1] = MSGBOX_CreateButton(hwnd, IDCANCEL, L"Cancel"); nButtons = 2; break; case MB_OK: /* fall through */ default: Buttons[0] = MSGBOX_CreateButton(hwnd, IDOK, L"OK"); nButtons = 1; break; } /* Create Help button */ if(lpmb->dwStyle & MB_HELP) Buttons[nButtons++] = MSGBOX_CreateButton(hwnd, IDHELP, L"Help"); /* Set the icon */ switch(lpmb->dwStyle & MB_ICONMASK) { case MB_ICONEXCLAMATION: SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON, (WPARAM)LoadIconW(0, IDI_EXCLAMATIONW), 0); MessageBeep(MB_ICONEXCLAMATION); break; case MB_ICONQUESTION: SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON, (WPARAM)LoadIconW(0, IDI_QUESTIONW), 0); MessageBeep(MB_ICONQUESTION); break; case MB_ICONASTERISK: SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON, (WPARAM)LoadIconW(0, IDI_ASTERISKW), 0); MessageBeep(MB_ICONASTERISK); break; case MB_ICONHAND: SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON, (WPARAM)LoadIconW(0, IDI_HANDW), 0); MessageBeep(MB_ICONHAND); break; case MB_USERICON: SendDlgItemMessageW(hwnd, 0x0440, STM_SETICON, (WPARAM)LoadIconW(lpmb->hInstance, (LPCWSTR)lpmb->lpszIcon), 0); MessageBeep(MB_OK); break; default: /* By default, Windows 95/98/NT does not associate an icon to message boxes. * So ReactOS should do the same. */ MessageBeep(MB_OK); break; } /* Position everything */ GetWindowRect(hwnd, &rect); borheight = rect.bottom - rect.top; borwidth = rect.right - rect.left; GetClientRect(hwnd, &rect); borheight -= rect.bottom; borwidth -= rect.right; /* Get the icon height */ GetWindowRect(GetDlgItem(hwnd, MSGBOX_IDICON), &rect); if (!(lpmb->dwStyle & MB_ICONMASK)) { rect.bottom = rect.top; rect.right = rect.left; } iheight = rect.bottom - rect.top; ileft = rect.left; iwidth = rect.right - ileft; hdc = GetDC(hwnd); if (hFont) hPrevFont = SelectObject(hdc, hFont); /* Calculate the button's sizes */ bh = bw = 1; // Minimum button sizes for(i = 0; i <= nButtons; i++) { WCHAR buttonText[1024]; int w, h; if (GetWindowTextW(Buttons[i], buttonText, 1024)) { DrawTextW(hdc, buttonText, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT); h = rect.bottom - rect.top; w = rect.right - rect.left; if (h > bh) bh = h; if (w > bw) bw = w ; } } bw = max(bw, bh * 2); /* Button white space */ bh = bh * 2 - 4; bw = bw * 2; bspace = 10; /* Fixed space between buttons */ /* Get the text size */ GetClientRect(GetDlgItem(hwnd, MSGBOX_IDTEXT), &rect); rect.top = rect.left = rect.bottom = 0; rect.right = (((GetSystemMetrics(SM_CXSCREEN) - borwidth) * 4) / 5); DrawTextW( hdc, lpszText, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT); /* Min text width corresponds to space for the buttons */ tleft = ileft; if (iwidth) tleft += ileft + iwidth; twidth = max((LONG) (((bw + bspace) * nButtons) + bspace - tleft), rect.right); theight = rect.bottom; if (hFont) SelectObject(hdc, hPrevFont); ReleaseDC(hItem, hdc); tiheight = 16 + max(iheight, theight) + 16; wwidth = tleft + twidth + ileft + borwidth; wheight = 8 + tiheight + bh + borheight; /* Resize the window */ SetWindowPos(hwnd, 0, 0, 0, wwidth, wheight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); /* Position the icon */ SetWindowPos(GetDlgItem(hwnd, MSGBOX_IDICON), 0, ileft, (tiheight - iheight) / 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); /* Position the text */ SetWindowPos(GetDlgItem(hwnd, MSGBOX_IDTEXT), 0, tleft, (tiheight - theight) / 2, twidth, theight, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); SetWindowTextW(GetDlgItem(hwnd, MSGBOX_IDTEXT), lpszText); /* Position the buttons */ bpos = (wwidth - ((bw + bspace) * nButtons) + bspace) / 2; for(i = 0; i <= nButtons; i++) { if (i == ((lpmb->dwStyle & MB_DEFMASK) >> 8)) { SetFocus(Buttons[i]); SendMessageW(Buttons[i], BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); sdefbtn = TRUE; } SetWindowPos(Buttons[i], 0, bpos, tiheight, bw, bh, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); bpos += bw + bspace; } /* if there's no (valid) default selection, select first button */ if(!sdefbtn) { SetFocus(Buttons[0]); SendMessageW(Buttons[0], BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); } if(lpmb->dwStyle & MB_RIGHT) { hItem = GetDlgItem(hwnd, MSGBOX_IDTEXT); SetWindowLongW(hItem, GWL_STYLE, GetWindowLongW(hItem, GWL_STYLE) | SS_RIGHT); } /* handle modal MessageBoxes */ if (lpmb->dwStyle & (MB_TASKMODAL|MB_SYSTEMMODAL)) { DbgPrint("%s modal msgbox ! Not modal yet.\n", lpmb->dwStyle & MB_TASKMODAL ? "task" : "system"); /* Probably do EnumTaskWindows etc. here for TASKMODAL * and work your way up to the top - I'm lazy (HWND_TOP) */ SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); if (lpmb->dwStyle & MB_TASKMODAL) /* at least MB_TASKMODAL seems to imply a ShowWindow */ ShowWindow(hwnd, SW_SHOW); } if (lpmb->dwStyle & MB_APPLMODAL) DbgPrint("app modal msgbox ! Not modal yet.\n"); return hFont; } /************************************************************************** * MSGBOX_DlgProc * * Dialog procedure for message boxes. */ static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { HFONT hFont; HELPINFO hi; switch(message) { case WM_INITDIALOG: { if(GetWindowLongA(hwnd, DWL_INIT)) { LPMSGBOXPARAMS mbp = (LPMSGBOXPARAMS)lParam; SetWindowLongA(hwnd, DWL_INIT, (LONG)mbp); SetWindowContextHelpId(hwnd, mbp->dwContextHelpId); hFont = MSGBOX_OnInit(hwnd, mbp); SetPropA(hwnd, "ROS_MSGBOX_HFONT", (HANDLE)hFont); SetPropA(hwnd, "ROS_MSGBOX_HELPCALLBACK", (HANDLE)mbp->lpfnMsgBoxCallback); return 1; } return 0; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: case IDABORT: case IDRETRY: case IDIGNORE: case IDYES: case IDNO: case IDTRYAGAIN: case IDCONTINUE: hFont = GetPropA(hwnd, "ROS_MSGBOX_HFONT"); EndDialog(hwnd, wParam); if (hFont) DeleteObject(hFont); return 0; case IDHELP: /* send WM_HELP message to messagebox window */ hi.cbSize = sizeof(HELPINFO); hi.iContextType = HELPINFO_WINDOW; hi.iCtrlId = LOWORD(wParam); hi.hItemHandle = (HANDLE)lParam; hi.dwContextId = 0; GetCursorPos(&hi.MousePos); SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi); return 0; } return 0; case WM_HELP: { MSGBOXCALLBACK callback = (MSGBOXCALLBACK)GetPropA(hwnd, "ROS_MSGBOX_HELPCALLBACK"); memcpy(&hi, (void *)lParam, sizeof(hi)); hi.dwContextId = GetWindowContextHelpId(hwnd); if (callback) callback(&hi); else { HWND owner = GetWindow(hwnd, GW_OWNER); if(owner) SendMessageW(GetWindow(hwnd, GW_OWNER), WM_HELP, 0, (LPARAM)&hi); } return 0; } } return 0; } /* * @implemented */ int STDCALL MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { return MessageBoxExA(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL); } /* * @implemented */ int STDCALL MessageBoxExA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, WORD wLanguageId) { MSGBOXPARAMS msgbox; msgbox.cbSize = sizeof(msgbox); msgbox.hwndOwner = hWnd; msgbox.hInstance = 0; msgbox.lpszText = lpText; msgbox.lpszCaption = lpCaption; msgbox.dwStyle = uType; msgbox.lpszIcon = NULL; msgbox.dwContextHelpId = 0; msgbox.lpfnMsgBoxCallback = NULL; msgbox.dwLanguageId = wLanguageId; return MessageBoxIndirectA(&msgbox); } /* * @implemented */ int STDCALL MessageBoxExW( HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, WORD wLanguageId) { MSGBOXPARAMS msgbox; msgbox.cbSize = sizeof(msgbox); msgbox.hwndOwner = hWnd; msgbox.hInstance = 0; msgbox.lpszText = (LPCSTR)lpText; msgbox.lpszCaption =(LPCSTR) lpCaption; msgbox.dwStyle = uType; msgbox.lpszIcon = NULL; msgbox.dwContextHelpId = 0; msgbox.lpfnMsgBoxCallback = NULL; msgbox.dwLanguageId = wLanguageId; return MessageBoxIndirectW(&msgbox); } /* * @implemented */ int STDCALL MessageBoxIndirectA( CONST LPMSGBOXPARAMS lpMsgBoxParams) { MSGBOXPARAMS msgboxW; UNICODE_STRING textW, captionW, iconW; int ret; if (HIWORD((UINT)lpMsgBoxParams->lpszText)) RtlCreateUnicodeStringFromAsciiz(&textW, (PCSZ)lpMsgBoxParams->lpszText); else textW.Buffer = (LPWSTR)lpMsgBoxParams->lpszText; if (HIWORD((UINT)lpMsgBoxParams->lpszCaption)) RtlCreateUnicodeStringFromAsciiz(&captionW, (PCSZ)lpMsgBoxParams->lpszCaption); else captionW.Buffer = (LPWSTR)lpMsgBoxParams->lpszCaption; if (HIWORD((UINT)lpMsgBoxParams->lpszIcon)) RtlCreateUnicodeStringFromAsciiz(&iconW, (PCSZ)lpMsgBoxParams->lpszIcon); else iconW.Buffer = (LPWSTR)lpMsgBoxParams->lpszIcon; msgboxW.cbSize = sizeof(msgboxW); msgboxW.hwndOwner = lpMsgBoxParams->hwndOwner; msgboxW.hInstance = lpMsgBoxParams->hInstance; msgboxW.lpszText = (LPCSTR)textW.Buffer; msgboxW.lpszCaption = (LPCSTR)captionW.Buffer; msgboxW.dwStyle = lpMsgBoxParams->dwStyle; msgboxW.lpszIcon = (LPCSTR)iconW.Buffer; msgboxW.dwContextHelpId = lpMsgBoxParams->dwContextHelpId; msgboxW.lpfnMsgBoxCallback = lpMsgBoxParams->lpfnMsgBoxCallback; msgboxW.dwLanguageId = lpMsgBoxParams->dwLanguageId; ret = MessageBoxIndirectW(&msgboxW); if (HIWORD(textW.Buffer)) RtlFreeUnicodeString(&textW); if (HIWORD(captionW.Buffer)) RtlFreeUnicodeString(&captionW); if (HIWORD(iconW.Buffer)) RtlFreeUnicodeString(&iconW); return ret; } /* * @implemented */ int STDCALL MessageBoxIndirectW( CONST LPMSGBOXPARAMS lpMsgBoxParams) { LPVOID tmplate, ctmplate; HRSRC hRes; HMODULE hUser32; DWORD ressize; WORD *style; WORD *exstyle; hUser32 = GetModuleHandleW(L"user32.dll"); if (!(hRes = FindResourceExW(hUser32, RT_DIALOGW, L"MSGBOX", lpMsgBoxParams->dwLanguageId))) return 0; if (!(tmplate = (LPVOID)LoadResource(hUser32, hRes))) return 0; /* Copy template */ ressize = SizeofResource(hUser32, hRes); ctmplate = RtlAllocateHeap(RtlGetProcessHeap(), 0, ressize); RtlMoveMemory(ctmplate, tmplate, ressize); /* change dialog's style in the template before passing it to DialogBoxIndirectParamW */ style = (WORD *)ctmplate; exstyle = style + 2; if(*(DWORD*)style == 0xffff0001) /* DIALOGEX resource */ { /* skip help id */ exstyle = style + 4; style = exstyle + 2; } /* change window style before creating it */ if(lpMsgBoxParams->dwStyle & MB_RIGHT) *exstyle = (WORD)(*(DWORD*)exstyle | WS_EX_RIGHT); if(lpMsgBoxParams->dwStyle & MB_TOPMOST) *exstyle = (WORD)(*(DWORD*)exstyle | WS_EX_TOPMOST); return DialogBoxIndirectParamW(lpMsgBoxParams->hInstance, ctmplate, lpMsgBoxParams->hwndOwner, MSGBOX_DlgProc, (LPARAM)lpMsgBoxParams); RtlFreeHeap(RtlGetProcessHeap(), 0, ctmplate); } /* * @implemented */ int STDCALL MessageBoxW( HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) { return MessageBoxExW(hWnd, lpText, lpCaption, uType, LANG_NEUTRAL); } /* * @unimplemented */ DWORD STDCALL SoftModalMessageBox(DWORD Unknown0) { UNIMPLEMENTED; return 0; } /* * @implemented */ WINBOOL STDCALL MessageBeep(UINT uType) { #if 0 LPWSTR EventName; switch(uType) { case 0xFFFFFFFF: if(waveOutGetNumDevs() == 0) return Beep(500, 100); // Beep through speaker /* fall through */ case MB_OK: EventName = L"SystemDefault"; break; case MB_ICONASTERISK: EventName = L"SystemAsterisk"; break; case MB_ICONEXCLAMATION: EventName = L"SystemExclamation"; break; case MB_ICONHAND: EventName = L"SystemHand"; break; case MB_ICONQUESTION: EventName = L"SystemQuestion"; break; } return PlaySoundW((LPCWSTR)EventName, NULL, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC); #else return Beep(500, 100); // Beep through speaker #endif } /* EOF */