#include "alfpriv.h" typedef struct { ALFWindowVTable *vtbl; void *closure; ALFWindowFonts fonts; int modalResult; ALFLayout layout; WORD defid; HWND hwndFocus; HFONT font; ALFColor bgcolor; } ALFWindowPriv; static void ALF_InitializeWindowPriv(HWND hwnd, ALFWindowPriv *priv, void *closure) { priv->vtbl = (ALFWindowVTable*)GetClassLongPtr(hwnd, 0); priv->closure = closure; priv->defid = (WORD)-1; priv->bgcolor = ALF_COLOR_SYS(COLOR_BTNFACE); ALF_Layout_Init(&priv->layout); } static void ALF_DestroyWindowPriv(ALFWindowPriv *priv) { if (priv->vtbl->postdestroy) priv->vtbl->postdestroy(priv->closure); ALF_Layout_Clear(&priv->layout); if (priv->fonts.hMessageFont) DeleteObject(priv->fonts.hMessageFont); ALF_Free(priv); } static void ALF_UpdateFontsPriv(HWND win, ALFWindowPriv *priv) { priv->fonts.dpi = ALF_Compat_GetDpiForWindow(win); // XXX: SystemParametersInfoForDpi is Unicode-only and needs Vista+ NONCLIENTMETRICS, ALF_NONCLIENTMETRICSW_VISTA ncm; ZeroMemory(&ncm, sizeof(ncm)); ncm.cbSize = sizeof(ncm); if (ALF_Compat_SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, priv->fonts.dpi)) { #ifdef UNICODE priv->fonts.lfMessageFont = ncm.lfMessageFont; #else ALF_Compat_LogFontWtoA(&ncm.lfMessageFont, &priv->fonts.lfMessageFont); #endif } else { // FIXME! fallback to default font, 8pt MS Shell Dlg ZeroMemory(&priv->fonts.lfMessageFont, sizeof(priv->fonts.lfMessageFont)); priv->fonts.lfMessageFont.lfHeight = -MulDiv(8, (int)priv->fonts.dpi, 72); lstrcpy(priv->fonts.lfMessageFont.lfFaceName, TEXT("MS Shell Dlg")); } if (priv->fonts.hMessageFont) { DeleteObject(priv->fonts.hMessageFont); } priv->fonts.hMessageFont = CreateFontIndirect(&priv->fonts.lfMessageFont); SendMessage(win, WM_SETFONT, (WPARAM)priv->fonts.hMessageFont, (LPARAM)1); } static int ALF_CentipointsToPxPriv(ALFWindowPriv *priv, int cptValue) { return MulDiv(cptValue, (int)priv->fonts.dpi, 7200); } static void ALF_ApplyFocus(HWND hwnd, ALFWindowPriv *priv, BOOL restoringFocus) { if (priv->hwndFocus) { if (!restoringFocus && SendMessage(priv->hwndFocus, WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL) SendMessage(priv->hwndFocus, EM_SETSEL, 0, -1); if (GetForegroundWindow() == hwnd) { HWND oldFocus = SetFocus(priv->hwndFocus); // FIXME! restore default button state if (oldFocus && SendMessage(oldFocus, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON) { SendMessage(oldFocus, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } if (priv->defid != (WORD)-1) { HWND hwndOld = ALF_WidgetHwndById(hwnd, priv->defid); if (hwndOld && SendMessage(hwndOld, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON) { SendMessage(hwndOld, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } } if (priv->hwndFocus && SendMessage(priv->hwndFocus, WM_GETDLGCODE, 0, 0) & DLGC_UNDEFPUSHBUTTON) { SendMessage(priv->hwndFocus, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); } else { HWND hwndDef = ALF_WidgetHwndById(hwnd, priv->defid); if (hwndDef && SendMessage(hwndDef, WM_GETDLGCODE, 0, 0) & DLGC_UNDEFPUSHBUTTON) { SendMessage(hwndDef, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); } } } } else { priv->hwndFocus = GetNextDlgTabItem(hwnd, NULL, FALSE); if (priv->hwndFocus) ALF_ApplyFocus(hwnd, priv, FALSE); } } static BOOL ALF_PreTranslateMessagePriv(HWND win, ALFWindowPriv *priv, MSG *message) { BOOL ret = FALSE; if (priv->vtbl->pretranslatemessage) ret = priv->vtbl->pretranslatemessage(priv->closure, win, message); if (!ret) ret = IsDialogMessage(win, message); return ret; } static LRESULT CALLBACK ALF_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_NCCREATE) { CREATESTRUCT *cs = (CREATESTRUCT*)(void*)lparam; if (cs->lpCreateParams) { ALFWindowPriv *priv = ALF_New(ALFWindowPriv, 1); SetWindowLongPtr(hwnd, 0, (LONG_PTR)priv); ALF_InitializeWindowPriv(hwnd, priv, *((void**)cs->lpCreateParams)); } } ALFWindowPriv *priv = (ALFWindowPriv*)(void*)GetWindowLongPtr(hwnd, 0); if (priv != NULL) { if (priv->vtbl->message) { return priv->vtbl->message(priv->closure, hwnd, msg, wparam, lparam); } else { return ALF_DefWindowProc(hwnd, msg, wparam, lparam); } } else { return DefWindowProc(hwnd, msg, wparam, lparam); } } static void ALF_Window_Paint(ALFWindowPriv *priv, HWND hwnd, HDC dc, RECT *r) { if (priv->vtbl && priv->vtbl->paint) { priv->vtbl->paint(priv->closure, hwnd, dc, r); } else { ALF_FillRect(dc, r, priv->bgcolor); } } LRESULT ALF_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ALFWindowPriv *priv = (ALFWindowPriv*)GetWindowLongPtr(hwnd, 0); if (msg == ALF_WM_GETMODALRESULT) { return (LRESULT)priv->modalResult; } if (msg == ALF_WM_SETMODALRESULT) { priv->modalResult = (int)wparam; return 0; } if (msg == ALF_WM_CENTIPOINTTOPX) { return (LRESULT)ALF_CentipointsToPxPriv(priv, (int)wparam); } if (msg == ALF_WM_SETFOCUS) { priv->hwndFocus = (HWND)lparam; ALF_ApplyFocus(hwnd, priv, FALSE); return 0; } if (msg == ALF_WM_UPDATEFONTS) { ALF_UpdateFontsPriv(hwnd, priv); return 0; } if (msg == WM_SETFONT) { priv->font = (HFONT)wparam; if (LOWORD(lparam) != 0) InvalidateRect(hwnd, NULL, TRUE); // fallthrough to layout, will propagate font to children } if (msg == ALF_WM_SETBGCOLOR) { priv->bgcolor = (ALFColor)wparam; InvalidateRect(hwnd, NULL, TRUE); // fallthrough to layout, will propagate color to children } if (msg == ALF_WM_GETBGCOLOR) { return (LRESULT)priv->bgcolor; } if (msg == WM_ERASEBKGND) { return TRUE; // handled in WM_PAINT } if (msg == WM_PRINTCLIENT) { RECT r = { 0, 0, 0, 0 }; GetClientRect(hwnd, &r); ALF_Window_Paint(priv, hwnd, (HDC)wparam, &r); return TRUE; } if (msg == WM_PAINT) { PAINTSTRUCT ps; HDC dc = BeginPaint(hwnd, &ps); ALF_Window_Paint(priv, hwnd, dc, &ps.rcPaint); EndPaint(hwnd, &ps); return 0; } if (msg == WM_GETFONT) { return (LRESULT)priv->font; } if (msg == ALF_WM_PRETRANSLATEMSG) { return (LRESULT)ALF_PreTranslateMessagePriv(hwnd, priv, (MSG *)lparam); } if (msg == WM_COMMAND) { HWND source = (HWND)lparam; WORD code = HIWORD(wparam); WORD id = LOWORD(wparam); LRESULT ret = 0; if (source != 0) ret = SendMessage(source, 0x2000 + WM_COMMAND, wparam, lparam); if (ret == 0 && priv->vtbl->command) ret = priv->vtbl->command(priv->closure, hwnd, code, id, source); return ret; } if (msg == WM_NOTIFY) { NMHDR *nmhdr = (NMHDR *)lparam; LRESULT ret = 0; if (nmhdr->hwndFrom) ret = SendMessage(nmhdr->hwndFrom, 0x2000 + WM_NOTIFY, wparam, lparam); if (ret == 0 && priv->vtbl->notify) ret = priv->vtbl->notify(priv->closure, hwnd, wparam, nmhdr); return ret; } if (msg == WM_DRAWITEM) { LPDRAWITEMSTRUCT dis = (DRAWITEMSTRUCT *)lparam; LRESULT ret = 0; if (wparam && dis->hwndItem) { ret = SendMessage(dis->hwndItem, 0x2000 + WM_DRAWITEM, wparam, lparam); } if (ret) return ret; } if (msg == WM_ACTIVATE) { if (!HIWORD(wparam)) { // if !minimized if (LOWORD(wparam)) { ALF_ApplyFocus(hwnd, priv, TRUE); } else { priv->hwndFocus = GetFocus(); // FIXME! remove default button state // there has to be a better way to do this if (priv->defid != (WORD)-1) { HWND hwndDefBtn = ALF_WidgetHwndById(hwnd, priv->defid); if (hwndDefBtn && SendMessage(hwndDefBtn, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON) { SendMessage(hwndDefBtn, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } } if (priv->hwndFocus && SendMessage(priv->hwndFocus, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON) { SendMessage(priv->hwndFocus, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } } } return 0; } if (msg == WM_SIZE) { ALF_Layout_Apply(&priv->layout, hwnd); } if (msg == WM_SHOWWINDOW) { if (wparam) ALF_Layout_Validate(&priv->layout, hwnd); } if (msg == WM_GETMINMAXINFO) { RECT tmp; ZeroMemory(&tmp, sizeof(tmp)); SIZE s; ZeroMemory(&s, sizeof(s)); ALF_Layout_GetMinSize(&priv->layout, hwnd, &s); tmp.right = s.cx; tmp.bottom = s.cy; if (ALF_Compat_AdjustWindowRectExForDpi( &tmp, (DWORD)GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, (DWORD)GetWindowLong(hwnd, GWL_EXSTYLE), priv->fonts.dpi)) { MINMAXINFO *i = (MINMAXINFO *)lparam; i->ptMinTrackSize.x = tmp.right - tmp.left; i->ptMinTrackSize.y = tmp.bottom - tmp.top; return 0; } } if (msg == WM_CREATE) { ALF_UpdateFontsPriv(hwnd, priv); if (priv->vtbl->create) { priv->vtbl->create(priv->closure, hwnd); } BOOL alwaysUnderline = FALSE; SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &alwaysUnderline, 0); if (!alwaysUnderline) { SendMessage(hwnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0); } ALF_Layout_Apply(&priv->layout, hwnd); } if (msg == WM_DESTROY && priv->vtbl->destroy) { priv->vtbl->destroy(priv->closure, hwnd); } if (msg == WM_CLOSE) { if (!priv->vtbl->close || !priv->vtbl->close(priv->closure, hwnd)) { priv->modalResult = 2; ShowWindow(hwnd, FALSE); } return TRUE; } if (msg == WM_NCDESTROY) { SetWindowLongPtr(hwnd, 0, 0); ALF_DestroyWindowPriv(priv); } if (msg == WM_DPICHANGED) { ALF_UpdateFontsPriv(hwnd, priv); ALF_Layout_Invalidate(&priv->layout, hwnd); RECT *r = (RECT*)lparam; SetWindowPos(hwnd, NULL, r->left, r->top, r->right-r->left, r->bottom-r->top, SWP_NOACTIVATE|SWP_NOZORDER); } if (msg == WM_THEMECHANGED || msg == WM_SETTINGCHANGE) { ALF_UpdateFontsPriv(hwnd, priv); ALF_Layout_Invalidate(&priv->layout, hwnd); } if (msg == DM_GETDEFID) { if (priv->defid == (WORD)-1) { return 0; } else { return MAKELONG(priv->defid, DC_HASDEFID); } } if (msg == DM_SETDEFID) { if (priv->defid != (WORD)-1) { HWND hwndOld = ALF_WidgetHwndById(hwnd, priv->defid); if (hwndOld && SendMessage(hwndOld, WM_GETDLGCODE, 0, 0) & DLGC_DEFPUSHBUTTON) { SendMessage(hwndOld, BM_SETSTYLE, BS_PUSHBUTTON, TRUE); } } priv->defid = (WORD)wparam; HWND hwndNew = ALF_WidgetHwndById(hwnd, priv->defid); if (hwndNew && SendMessage(hwndNew, WM_GETDLGCODE, 0, 0) & DLGC_UNDEFPUSHBUTTON) { SendMessage(hwndNew, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); } return TRUE; } LRESULT ret = 0; if (ALF_Layout_HandleMessage(&priv->layout, hwnd, msg, wparam, lparam, &ret)) { // if the layout was changed, our current size might be too small // windows will call WM_GETMINMAXINFO and fix it if (msg == ALF_WM_VALIDATELAYOUT) { RECT r; if (GetWindowRect(hwnd, &r)) { SetWindowPos(hwnd, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); } } return ret; } return DefWindowProc(hwnd, msg, wparam, lparam); } LPTSTR ALF_RegisterWindowClass(HINSTANCE hInstance, const ALFWindowClassParams *params) { WNDCLASS cls; ZeroMemory(&cls, sizeof(cls)); ALFWindowVTable *pvtbl = ALF_New(ALFWindowVTable, 1); *pvtbl = params->vtbl; const TCHAR *classNamePtr = params->className; TCHAR classNameBuf[256]; if (!classNamePtr) { ZeroMemory(classNameBuf, sizeof(classNameBuf)); classNamePtr = classNameBuf; ALF_BuildUniqueName(classNameBuf, TEXT("ALFWindow."), (ULONG_PTR)pvtbl); } cls.style = params->classStyle; cls.hInstance = hInstance; cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW); cls.hbrBackground = NULL; cls.lpszClassName = classNamePtr; cls.cbWndExtra = sizeof(void*); cls.cbClsExtra = sizeof(void*); cls.lpfnWndProc = ALF_WindowProc; ATOM classatom = RegisterClass(&cls); if (!classatom) { ALF_Free(pvtbl); return NULL; } // XXX: This could have been a HWND_MESSAGE window, but Win95 doesn't support these HWND tmp = CreateWindowEx(0, MAKEINTATOM(classatom), TEXT("dummy"), 0, 0, 0, 0, 0, NULL, 0, hInstance, 0); SetClassLongPtr(tmp, 0, (LONG_PTR)pvtbl); DestroyWindow(tmp); return MAKEINTATOM(classatom); } void ALF_UnregisterWindowClass(HINSTANCE hinstance, LPCTSTR className) { HWND tmp = CreateWindowEx(0, className, TEXT("dummy"), 0, 0, 0, 0, 0, NULL, 0, hinstance, 0); ALFWindowVTable *pvtbl = (ALFWindowVTable*)GetClassLongPtr(tmp, 0); DestroyWindow(tmp); ALF_Free(pvtbl); UnregisterClass(className, hinstance); } HWND ALF_InstantiateWindow(HINSTANCE hinstance, LPCTSTR className, HWND hwndParent, void *closure) { return CreateWindowEx(0, className, TEXT("Window"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, //FIXME hwndParent, NULL, hinstance, &closure); }