#include "alfpriv.h" #include static LONG _alf_initLock = 0; static LONG _alf_initCounter = 0; /* ALF App and Window */ static void ALF_InitializeWindowPriv(HWND hwnd, ALFWindowPriv *priv, void *closure) { priv->vtbl = (ALFWindowVTable*)GetClassLongPtr(hwnd, 0); 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); ALF_Free(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 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, 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_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_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(); } } 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 = 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); } } void ALF_Initialize(void) { // acquire init lock while (InterlockedExchange(&_alf_initLock, 1)) { Sleep(0); } // we have the lock! if (!_alf_initCounter++) { InitCommonControls(); ALF_LoadCompatFunctions(); ALF_RegisterComboClass(); ALF_RegisterPanelClass(); ALF_RegisterSpacerClass(); ALF_Compat_BufferedPaintInit(); } // release init lock InterlockedExchange(&_alf_initLock, 0); } void ALF_UnInitialize(void) { // acquire init lock while (InterlockedExchange(&_alf_initLock, 1)) { Sleep(0); } // we have the lock! if (!--_alf_initCounter) { ALF_Compat_BufferedPaintUnInit(); UnregisterClass(_alf_comboClass, ALF_HINSTANCE); UnregisterClass(_alf_panelClass, ALF_HINSTANCE); UnregisterClass(_alf_spacerClass, ALF_HINSTANCE); ALF_UnloadCompatFunctions(); } // release init lock InterlockedExchange(&_alf_initLock, 0); } void * ALF_Alloc(SIZE_T nmemb, SIZE_T size) { // FIXME! potential overflow return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, nmemb * size); } void * ALF_ReAlloc(void *ptr, SIZE_T nmemb, SIZE_T size) { // FIXME! potential overflow if (ptr) return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, ptr, nmemb * size); else return ALF_Alloc(nmemb, size); } void ALF_Free(const void *p) { HeapFree(GetProcessHeap(), 0, (void*)p); } void ALF_BuildUniqueName(TCHAR *buf, const TCHAR *prefix, ULONG_PTR uniquifier) { int prefixlen = lstrlen(prefix); CopyMemory(buf, prefix, prefixlen * sizeof(*prefix)); int numlen = sizeof(LONG_PTR)*2; int i = numlen - 1; while (i >= 0) { buf[prefixlen + i] = "0123456789ABCDEF"[uniquifier & 0xf]; uniquifier >>= 4; i--; } } 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); 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*); 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); } void ALF_DestroyWindow(HWND win) { DestroyWindow(win); } 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 ALF_Free ALF_Text(HWND hwnd) { int len = GetWindowTextLength(hwnd); if (len > 0) { TCHAR *buf = ALF_New(TCHAR, len+1); if (GetWindowText(hwnd, buf, len)) { return buf; } else { ALF_Free(buf); } } return NULL; } TCHAR * // free with ALF_Free 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_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; }