#include "alfpriv.h" #include /* ALF App and Window */ static void ALF_InitializeWindowPriv(HWND hwnd, ALFWindowPriv *priv, void *closure) { priv->vtbl = (ALFWindowVTable*)GetClassLongPtr(hwnd, 0); priv->app = (ALFAPP)GetClassLongPtr(hwnd, sizeof(void*)); priv->closure = closure; priv->defid = (WORD)-1; 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); HeapFree(GetProcessHeap(), 0, priv->layout.columns); HeapFree(GetProcessHeap(), 0, priv->layout.rows); HeapFree(GetProcessHeap(), 0, priv); } int ALF_CentipointsToPxPriv(ALFWindowPriv *priv, int cptValue) { return MulDiv(cptValue, priv->fonts.dpi, 7200); } void ALF_UpdateFonts(HWND win) { SendMessage(win, ALF_WM_UPDATEFONTS, 0, 0); } void ALF_ApplyFontsPriv(HWND win, ALFWindowPriv *priv) { ALF_Layout_ApplyFonts(&priv->layout, win, &priv->fonts); if (priv->vtbl->applyfonts) { priv->vtbl->applyfonts(priv->closure, win, &priv->fonts); } } void ALF_UpdateFontsPriv(HWND win, ALFWindowPriv *priv) { priv->fonts.dpi = ALF_Compat_GetDpiForWindow(win); if (LOBYTE(LOWORD(GetVersion())) < 4) { // NT 3.x uses System font for everything, we copy that priv->fonts.hMessageFont = (HFONT)GetStockObject(SYSTEM_FONT); GetObject(priv->fonts.hMessageFont, sizeof(priv->fonts.lfMessageFont), &priv->fonts.lfMessageFont); } else { // XXX: SystemParametersInfoForDpi needs the Vista+ NONCLIENTMETRICS, // but we want to be able to build with WINVER = 0x0500 and PSDK2003 ALF_NONCLIENTMETRICS_VISTA ncm; ZeroMemory(&ncm, sizeof(ncm)); ncm.cbSize = ALF_SizeOf_NONCLIENTMETRICS(); if (ALF_Compat_SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, priv->fonts.dpi)) { priv->fonts.lfMessageFont = ncm.lfMessageFont; } else { // FIXME! fallback to default font, 8pt MS Shell Dlg ZeroMemory(&priv->fonts.lfMessageFont, sizeof(priv->fonts.lfMessageFont)); priv->fonts.lfMessageFont.lfHeight = -MulDiv(8, 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); } ALF_ApplyFontsPriv(win, priv); } void ALF_ApplyFonts(HWND win) { SendMessage(win, ALF_WM_APPLYFONTS, 0, 0); } void ALF_RecalculateLayout(HWND hwnd) { SIZE dummy; SendMessage(hwnd, ALF_WM_QUERYSIZE, 0, (LPARAM)&dummy); SendMessage(hwnd, ALF_WM_APPLYLAYOUT, 0, 0); } 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) SetFocus(priv->hwndFocus); } else { priv->hwndFocus = GetNextDlgTabItem(hwnd, NULL, FALSE); if (priv->hwndFocus) ALF_ApplyFocus(hwnd, priv, FALSE); } } 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_GETAPPLICATION) { return (LRESULT)priv->app; } if (msg == ALF_WM_UPDATEFONTS) { ALF_UpdateFontsPriv(hwnd, priv); return 0; } if (msg == ALF_WM_APPLYFONTS) { ALF_ApplyFontsPriv(hwnd, priv); return 0; } 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_ACTIVATE) { if (!HIWORD(wparam)) { // if !minimized if (LOWORD(wparam)) { ALF_ApplyFocus(hwnd, priv, TRUE); } else { priv->hwndFocus = GetFocus(); } } return 0; } if (msg == WM_SIZE) { ALF_Layout_Apply(&priv->layout, hwnd); } if (msg == WM_GETMINMAXINFO) { RECT tmp; ZeroMemory(&tmp, sizeof(tmp)); tmp.right = priv->layout.totalMinWidth; tmp.bottom = priv->layout.totalMinHeight; if (ALF_Compat_AdjustWindowRectExForDpi( &tmp, GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, 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) { 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_UpdateFontsPriv(hwnd, priv); ALF_Layout_CalcSizes(&priv->layout, hwnd); 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_CalcSizes(&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_CalcSizes(&priv->layout, hwnd); ALF_Layout_Apply(&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)) { return ret; } return DefWindowProc(hwnd, msg, wparam, lparam); } void ALF_AddWidget(HWND win, UINT x, UINT y, HWND widget, UINT minWidth, UINT minHeight, DWORD flags) { ALFWidgetLayoutParams params; ZeroMemory(¶ms, sizeof(params)); params.hwnd = widget; params.x = x; params.y = y; params.width = minWidth; params.height = minHeight; params.flags = flags; ALF_AddWidgetEx(win, ¶ms); } void ALF_AddWidgetEx(HWND win, const ALFWidgetLayoutParams* params) { SendMessage(win, ALF_WM_ADDWIDGET, 0, (LPARAM)params); } 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 = (ALFWindowPriv*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, sizeof(ALFWindowPriv)); 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); } } ALFAPP ALF_CreateApplication(HINSTANCE hInstance) { ALFAPP app = (ALFAPP)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, sizeof(struct ALFAppPriv)); app->hInstance = hInstance; InitCommonControls(); ALF_RegisterComboClass(app); ALF_RegisterPanelClass(app); ALF_RegisterSpacerClass(app); return app; } void ALF_TeardownApplication(ALFAPP app) { UnregisterClass(app->comboClass, app->hInstance); UnregisterClass(app->panelClass, app->hInstance); UnregisterClass(app->spacerClass, app->hInstance); HeapFree(GetProcessHeap(), 0, app); } void ALF_BuildRandomClassName(const TCHAR* prefix, TCHAR* buf, DWORD cchBuf) { LONG_PTR c1, c2; ALF_UniqueCounterValue(&c1, &c2); DWORD_PTR params[] = { (DWORD_PTR)prefix, (DWORD_PTR)c1, (DWORD_PTR)c2 }; FormatMessage(FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING, TEXT("%1.%2!d!.%3!d!"), 0, 0, buf, cchBuf, (va_list*)params); } LPTSTR ALF_RegisterWindowClass(ALFAPP app, const ALFWindowClassParams *params) { WNDCLASS cls; ZeroMemory(&cls, sizeof(cls)); const TCHAR *classNamePtr = params->className; TCHAR classNameBuf[256]; if (!classNamePtr) { ZeroMemory(classNameBuf, sizeof(classNameBuf)); classNamePtr = classNameBuf; ALF_BuildRandomClassName(TEXT("ALFWindow"), classNameBuf, 256); } cls.style = params->classStyle; cls.hInstance = app->hInstance; cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW); if (LOBYTE(LOWORD(GetVersion())) >= 4) { cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); } else { // NT 3.x has white dialog backgrounds cls.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); } cls.lpszClassName = classNamePtr; cls.cbWndExtra = sizeof(void*); cls.cbClsExtra = sizeof(void*)*2; cls.lpfnWndProc = ALF_WindowProc; ATOM classatom = RegisterClass(&cls); if (!classatom) return NULL; ALFWindowVTable *pvtbl = (ALFWindowVTable*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, sizeof(ALFWindowVTable)); *pvtbl = params->vtbl; // 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, app->hInstance, 0); SetClassLongPtr(tmp, 0, (LONG_PTR)pvtbl); SetClassLongPtr(tmp, sizeof(void*), (LONG_PTR)app); DestroyWindow(tmp); return MAKEINTATOM(classatom); } void ALF_UnregisterWindowClass(ALFAPP app, LPCTSTR className) { HWND tmp = CreateWindowEx(0, className, TEXT("dummy"), 0, 0, 0, 0, 0, NULL, 0, app->hInstance, 0); ALFWindowVTable *pvtbl = (ALFWindowVTable*)GetClassLongPtr(tmp, 0); DestroyWindow(tmp); HeapFree(GetProcessHeap(), 0, pvtbl); UnregisterClass(className, app->hInstance); } HWND ALF_InstantiateWindow(ALFAPP app, 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, app->hInstance, &closure); } void ALF_DestroyWindow(HWND win) { DestroyWindow(win); } ALFAPP ALF_ApplicationFromWindow(HWND hwnd) { return (ALFAPP)SendMessage(hwnd, ALF_WM_GETAPPLICATION, 0, 0); } BOOL ALF_PreTranslateMessage(HWND hwnd, MSG *message) { return (BOOL)SendMessage(hwnd, ALF_WM_PRETRANSLATEMSG, 0, (LPARAM)message); } 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; } int ALF_ShowModal(HWND win) { MSG msg; ALF_SetModalResult(win, 0); // TODO: disable parent window ShowWindow(win, SW_SHOW); while (GetMessage(&msg, NULL, 0, 0) > 0) { // TODO: call application message hooks // TODO: call preprocess message hook if (!ALF_PreTranslateMessage(win, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } int mr = ALF_GetModalResult(win); if (mr) return mr; } return 0; } void ALF_SetModalResult(HWND win, int result) { SendMessage(win, ALF_WM_SETMODALRESULT, (WPARAM)result, 0); } int ALF_GetModalResult(HWND win) { return (int)SendMessage(win, ALF_WM_GETMODALRESULT, 0, 0); } int ALF_CentipointsToPixels(HWND win, int cptValue) { return (int)SendMessage(win, ALF_WM_CENTIPOINTTOPX, (WPARAM)cptValue, 0); } void ALF_ResizeWindow(HWND win, int cptWidth, int cptHeight) { int pxwidth = ALF_CentipointsToPixels(win, cptWidth); int pxheight = ALF_CentipointsToPixels(win, cptHeight); ALF_ResizeWindowPx(win, pxwidth, pxheight); } void ALF_ResizeWindowPx(HWND win, int pxwidth, int pxheight) { MINMAXINFO tmp = { { 0, 0 }, { pxwidth, pxheight }, { 0, 0 }, { pxwidth, pxheight }, { pxwidth, pxheight } }; SendMessage(win, WM_GETMINMAXINFO, 0, (LPARAM)&tmp); if (tmp.ptMinTrackSize.x > pxwidth) pxwidth = tmp.ptMinTrackSize.x; if (tmp.ptMinTrackSize.y > pxheight) pxheight = tmp.ptMinTrackSize.y; SetWindowPos(win, NULL, 0, 0, pxwidth, pxheight, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); } struct ALF_WidgetHwndById_Closure { HWND result; WORD needle; }; static BOOL CALLBACK ALF_WidgetHwndById_EnumChildProc(HWND hwnd, LPARAM lParam) { struct ALF_WidgetHwndById_Closure *closure = (struct ALF_WidgetHwndById_Closure*)lParam; if ((WORD)GetWindowLongPtr(hwnd, GWLP_ID) == closure->needle) { closure->result = hwnd; return FALSE; } return TRUE; } HWND ALF_WidgetHwndById(HWND win, WORD id) { struct ALF_WidgetHwndById_Closure closure = { 0, id }; EnumChildWindows(win, ALF_WidgetHwndById_EnumChildProc, (LPARAM)&closure); return closure.result; } void ALF_SetText(HWND hwnd, const TCHAR *text) { SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)text); } void ALF_SetWidgetText(HWND parent, WORD id, const TCHAR *text) { HWND h = ALF_WidgetHwndById(parent, id); if (h) ALF_SetText(h, text); } TCHAR * // free with HeapFree ALF_Text(HWND hwnd) { int len = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); if (len > 0) { TCHAR *buf = (TCHAR*)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, (len + 1)*sizeof(TCHAR)); if (SendMessage(hwnd, WM_GETTEXT, 0, (LPARAM)buf)) { return buf; } else { HeapFree(GetProcessHeap(), 0, buf); } } return NULL; } TCHAR * // free with HeapFree ALF_WidgetText(HWND parent, WORD id) { HWND h = ALF_WidgetHwndById(parent, id); if (h) return ALF_Text(h); return NULL; } DWORD ALF_WidgetLayoutFlags(HWND parent, HWND widget) { ALFWidgetLayoutParams p; if (SendMessage(parent, ALF_WM_GETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p)) { return p.flags; } else { return 0; } } BOOL ALF_SetWidgetLayoutFlags(HWND parent, HWND widget, DWORD flags) { ALFWidgetLayoutParams p; if (SendMessage(parent, ALF_WM_GETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p)) { p.flags = flags; return SendMessage(parent, ALF_WM_SETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p); } return FALSE; } BOOL ALF_WidgetLayoutPosition(HWND parent, HWND widget, UINT *pX, UINT *pY) { ALFWidgetLayoutParams p; if (SendMessage(parent, ALF_WM_GETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p)) { *pX = p.x; *pY = p.y; return TRUE; } else { return FALSE; } } BOOL ALF_SetWidgetLayoutPosition(HWND parent, HWND widget, UINT x, UINT y) { ALFWidgetLayoutParams p; if (SendMessage(parent, ALF_WM_GETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p)) { p.x = x; p.y = y; return SendMessage(parent, ALF_WM_SETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p); } return FALSE; } BOOL ALF_WidgetLayoutSize(HWND parent, HWND widget, UINT *pCptWidth, UINT *pCptHeight) { ALFWidgetLayoutParams p; if (SendMessage(parent, ALF_WM_GETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p)) { *pCptWidth = p.width; *pCptHeight = p.height; return TRUE; } else { return FALSE; } } BOOL ALF_SetWidgetLayoutSize(HWND parent, HWND widget, UINT cptWidth, UINT cptHeight) { ALFWidgetLayoutParams p; if (SendMessage(parent, ALF_WM_GETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p)) { p.width = cptWidth; p.height = cptHeight; return SendMessage(parent, ALF_WM_SETLAYOUTPARAMS, (WPARAM)widget, (LPARAM)&p); } return FALSE; } HWND ALF_WidgetAtLayoutPosition(HWND parent, UINT x, UINT y) { UINT xy[2] = { x, y }; return (HWND)SendMessage(parent, ALF_WM_GETWIDGETATPOS, 0, (LPARAM)&xy); } BOOL ALF_ShouldMessageBubble(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { (void)wparam; (void)lparam; if (!GetParent(hwnd)) return FALSE; return msg == ALF_WM_GETAPPLICATION || msg == ALF_WM_CENTIPOINTTOPX || msg == WM_COMMAND || msg == WM_NOTIFY || msg == WM_MEASUREITEM || msg == WM_DRAWITEM || msg == WM_CTLCOLORBTN || msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORLISTBOX || msg == WM_CTLCOLORSCROLLBAR || msg == WM_CTLCOLORSTATIC; }