#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; ALF_ListInit(&priv->widgets); priv->defid = (WORD)-1; } static void ALF_DestroyWindowPriv(ALFWindowPriv *priv) { if (priv->vtbl->postdestroy) priv->vtbl->postdestroy(priv->closure); ALF_FOR_LIST(ALFWidgetPriv, list, &priv->widgets, w) { HeapFree(GetProcessHeap(), 0, w); } ALF_ListInit(&priv->widgets); 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); } static void ALF_UpdateFontForWidget(ALFWindowPriv *priv, ALFWidgetPriv *widget) { if (widget->hwnd && (widget->flags & ALF_MESSAGEFONT) == ALF_MESSAGEFONT) { SendMessage(widget->hwnd, WM_SETFONT, (WPARAM)priv->fonts.hMessageFont, (LPARAM)NULL); // XXX: Invalidating should IMHO be the decision of the control, but at // least the commctl32 V5 static control doesn't do it. InvalidateRect(widget->hwnd, NULL, TRUE); } } void ALF_UpdateFonts(HWND win) { SendMessage(win, ALF_WM_UPDATEFONTS, 0, 0); } void ALF_UpdateFontsPriv(HWND win, ALFWindowPriv *priv) { priv->fonts.dpi = priv->app->compatFn->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 (priv->app->compatFn->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_FOR_LIST(ALFWidgetPriv, list, &priv->widgets, i) { ALF_UpdateFontForWidget(priv, i); } if (priv->vtbl->updatefonts) { priv->vtbl->updatefonts(priv->closure, win, &priv->fonts); } } 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_CalculateSizes(ALFWindowPriv *win) { for (int i = 0; i < win->layout.nColumns; ++i) { ZeroMemory(&win->layout.columns[i], sizeof(win->layout.columns[i])); } for (int i = 0; i < win->layout.nRows; ++i) { ZeroMemory(&win->layout.rows[i], sizeof(win->layout.rows[i])); } ALF_FOR_LIST(ALFWidgetPriv, list, &win->widgets, c) { while ((int)c->x >= win->layout.nColumns) { // FIXME! overflow, use reallocarray(2) equivalent if (win->layout.nColumns == 0) { win->layout.nColumns = 1; win->layout.columns = (ALFRowOrColumn*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, win->layout.nColumns*sizeof(win->layout.columns[0])); } else { win->layout.nColumns *= 2; win->layout.columns = (ALFRowOrColumn*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, win->layout.columns, win->layout.nColumns*sizeof(win->layout.columns[0])); } } while ((int)c->y >= win->layout.nRows) { // FIXME! overflow, use reallocarray(2) equivalent if (win->layout.nRows == 0) { win->layout.nRows = 1; win->layout.rows = (ALFRowOrColumn*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, win->layout.nRows*sizeof(win->layout.rows[0])); } else { win->layout.nRows *= 2; win->layout.rows = (ALFRowOrColumn*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, win->layout.rows, win->layout.nRows*sizeof(win->layout.rows[0])); } } // TODO: ignore spanning cell SIZE qs = { ALF_CentipointsToPxPriv(win, c->cptWidth), ALF_CentipointsToPxPriv(win, c->cptHeight) }; if ((c->flags & ALF_QUERYSIZE) == ALF_QUERYSIZE) { SendMessage(c->hwnd, ALF_WM_QUERYSIZE, 0, (LPARAM)&qs); } qs.cx += ALF_CentipointsToPxPriv(win, c->cptMarginLeft); qs.cx += ALF_CentipointsToPxPriv(win, c->cptMarginRight); qs.cy += ALF_CentipointsToPxPriv(win, c->cptMarginTop); qs.cy += ALF_CentipointsToPxPriv(win, c->cptMarginBottom); if (qs.cx > win->layout.columns[c->x].minWidth) win->layout.columns[c->x].minWidth = qs.cx; if (qs.cy > win->layout.rows[c->y].minWidth) win->layout.rows[c->y].minWidth = qs.cy; // TODO: expand flag } // TODO: second pass for spanning cells // update min width bookkeeping win->layout.totalMinWidth = 0; win->layout.occupiedColumnCount = 0; for (int i = 0; i < win->layout.nColumns; ++i) { if (win->layout.columns[i].minWidth > 0) { win->layout.totalMinWidth += win->layout.columns[i].minWidth; win->layout.occupiedColumnCount++; // TODO: expand flag } } win->layout.totalMinHeight = 0; win->layout.occupiedRowCount = 0; for (int i = 0; i < win->layout.nRows; ++i) { if (win->layout.rows[i].minWidth > 0) { win->layout.totalMinHeight += win->layout.rows[i].minWidth; win->layout.occupiedRowCount++; // TODO: expand flag } } // TODO: split here into allocation function } static void ALF_ApplyLayout(HWND hwnd, ALFWindowPriv *win) { // allocate cell positions // distribute free space int extraWidth = 0; int extraHeight = 0; RECT client; if (GetClientRect(hwnd, &client)) { if (client.right - client.left > win->layout.totalMinWidth) extraWidth = client.right - client.left - win->layout.totalMinWidth; if (client.bottom - client.top > win->layout.totalMinHeight) extraHeight = client.bottom - client.top - win->layout.totalMinHeight; } int extraWidthPart = 0; int extraWidthError = 0; if (win->layout.occupiedColumnCount) { extraWidthPart = extraWidth / win->layout.occupiedColumnCount; extraWidthError = extraWidth - extraWidthPart * win->layout.occupiedColumnCount; } for (int i = 0; i < win->layout.nColumns; ++i) { win->layout.columns[i].allocatedWidth = win->layout.columns[i].minWidth; if (win->layout.columns[i].minWidth > 0) { win->layout.columns[i].allocatedWidth += extraWidthPart; if (extraWidthError) { win->layout.columns[i].allocatedWidth++; extraWidthError--; } } if (i == 0) { win->layout.columns[i].allocatedPosition = 0; } else { win->layout.columns[i].allocatedPosition = win->layout.columns[i-1].allocatedPosition + win->layout.columns[i-1].allocatedWidth; } } int extraHeightPart = 0; int extraHeightError = 0; if (win->layout.occupiedRowCount) { extraHeightPart = extraHeight / win->layout.occupiedRowCount; extraHeightError = extraHeight - extraHeightPart * win->layout.occupiedRowCount; } for (int i = 0; i < win->layout.nRows; ++i) { win->layout.rows[i].allocatedWidth = win->layout.rows[i].minWidth; if (win->layout.rows[i].minWidth > 0) { win->layout.rows[i].allocatedWidth += extraHeightPart; if (extraHeightError) { win->layout.rows[i].allocatedWidth++; extraHeightError--; } } if (i == 0) { win->layout.rows[i].allocatedPosition = 0; } else { win->layout.rows[i].allocatedPosition = win->layout.rows[i-1].allocatedPosition + win->layout.rows[i-1].allocatedWidth; } } HDWP hdwp = BeginDeferWindowPos(win->layout.occupiedColumnCount * win->layout.occupiedRowCount); ALF_FOR_LIST(ALFWidgetPriv, list, &win->widgets, c) { if ((int)c->x >= win->layout.nColumns || (int)c->y >= win->layout.nRows) continue; int marginleft = ALF_CentipointsToPxPriv(win, c->cptMarginLeft); int marginright = ALF_CentipointsToPxPriv(win, c->cptMarginRight); int margintop = ALF_CentipointsToPxPriv(win, c->cptMarginTop); int marginbottom = ALF_CentipointsToPxPriv(win, c->cptMarginBottom); RECT r = { 0,0,0,0 }; r.left = win->layout.columns[c->x].allocatedPosition + marginleft; r.right = r.left + win->layout.columns[c->x].allocatedWidth - marginleft - marginright; r.top = win->layout.rows[c->y].allocatedPosition + margintop; r.bottom = r.top + win->layout.rows[c->y].allocatedWidth - margintop - marginbottom; if (c->flags & ALF_APPLYSIZE) { hdwp = (HDWP)SendMessage(c->hwnd, ALF_WM_APPLYSIZE, (WPARAM)hdwp, (LPARAM)&r); } else { hdwp = DeferWindowPos(hdwp, c->hwnd, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE | SWP_NOZORDER); } } EndDeferWindowPos(hdwp); } static void ALF_InternalAddWidget(HWND hwnd, ALFWindowPriv *priv, ALFWidgetLayoutParams *params) { ALFWidgetPriv *w = (ALFWidgetPriv*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, sizeof(ALFWidgetPriv)); w->hwnd = params->hwnd; w->x = params->x; w->y = params->y; w->cptWidth = params->width; w->cptHeight = params->height; w->flags = params->flags; w->cptMarginTop = params->margins[0]; w->cptMarginRight = params->margins[1]; w->cptMarginBottom = params->margins[2]; w->cptMarginLeft = params->margins[3]; if (GetParent(w->hwnd) != hwnd) SetParent(w->hwnd, hwnd); // TODO: QUERYUISTATE on parent, then replicate in child. But wait, XP appears to do this automatically!?? ALF_ListInsert(priv->widgets.prev, &w->list); ALF_UpdateFontForWidget(priv, w); } static BOOL ALF_InternalSetLayoutParams(ALFWindowPriv *priv, HWND widget, const ALFWidgetLayoutParams *params) { ALF_FOR_LIST(ALFWidgetPriv, list, &priv->widgets, w) { if (w->hwnd == widget) { w->hwnd = params->hwnd; w->x = params->x; w->y = params->y; w->cptWidth = params->width; w->cptHeight = params->height; w->flags = params->flags; w->cptMarginTop = params->margins[0]; w->cptMarginRight = params->margins[1]; w->cptMarginBottom = params->margins[2]; w->cptMarginLeft = params->margins[3]; return TRUE; } } return FALSE; } static BOOL ALF_InternalGetLayoutParams(ALFWindowPriv *priv, HWND widget, ALFWidgetLayoutParams *params) { ALF_FOR_LIST(ALFWidgetPriv, list, &priv->widgets, w) { if (w->hwnd == widget) { params->hwnd = w->hwnd; params->x = w->x; params->y = w->y; params->width = w->cptWidth; params->height = w->cptHeight; params->flags = w->flags; params->margins[0] = w->cptMarginTop; params->margins[1] = w->cptMarginRight; params->margins[2] = w->cptMarginBottom; params->margins[3] = w->cptMarginLeft; return TRUE; } } return FALSE; } static HWND ALF_InternalGetWidgetAtPos(ALFWindowPriv *priv, UINT x, UINT y) { ALF_FOR_LIST(ALFWidgetPriv, list, &priv->widgets, w) { if (w->x == x && w->y == y) { return w->hwnd; } } return NULL; } 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_QUERYSIZE) { ALF_CalculateSizes(priv); SIZE *ps = (SIZE*)lparam; ps->cx = priv->layout.totalMinWidth; ps->cy = priv->layout.totalMinHeight; return 0; } if (msg == ALF_WM_APPLYLAYOUT) { ALF_ApplyLayout(hwnd, priv); return 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_ADDWIDGET) { ALF_InternalAddWidget(hwnd, priv, (ALFWidgetLayoutParams *)lparam); return 0; } if (msg == ALF_WM_GETLAYOUTPARAMS) { return (LRESULT)ALF_InternalGetLayoutParams(priv, (HWND)wparam, (ALFWidgetLayoutParams *)lparam); } if (msg == ALF_WM_SETLAYOUTPARAMS) { return (LRESULT)ALF_InternalSetLayoutParams(priv, (HWND)wparam, (const ALFWidgetLayoutParams *)lparam); } if (msg == ALF_WM_GETWIDGETATPOS) { return (LRESULT)ALF_InternalGetWidgetAtPos(priv, ((UINT*)lparam)[0], ((UINT*)lparam)[1]); } 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 == 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_ApplyLayout(hwnd, priv); return 0; } if (msg == WM_GETMINMAXINFO) { RECT tmp; ZeroMemory(&tmp, sizeof(tmp)); tmp.right = priv->layout.totalMinWidth; tmp.bottom = priv->layout.totalMinHeight; if (priv->app->compatFn->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_CalculateSizes(priv); ALF_ApplyLayout(hwnd, priv); } 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_CalculateSizes(priv); 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_CalculateSizes(priv); ALF_ApplyLayout(hwnd, priv); } 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; } 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(); app->compatFn = ALF_CreateCompatFuncTable(); ALF_RegisterComboClass(app); return app; } void ALF_TeardownApplication(ALFAPP app) { UnregisterClass(app->comboClass, app->hInstance); HeapFree(GetProcessHeap(), 0, app->compatFn); HeapFree(GetProcessHeap(), 0, app); } static void ALF_IntToHex(TCHAR *buf, unsigned long num, int chars) { char letters[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; for (int i = 0; i < chars; ++i) { buf[chars-i-1] = letters[(num >> (i*4)) & 0xf]; } } static void ALF_UuidToString(const GUID *guid, TCHAR *buf) { ALF_IntToHex(buf, guid->Data1, 8); buf[8] = '-'; ALF_IntToHex(buf + 9, guid->Data2, 4); buf[13] = '-'; ALF_IntToHex(buf + 14, guid->Data3, 4); buf[18] = '-'; for (int i = 0; i < 8; ++i) { ALF_IntToHex(buf + 19 + 2*i, guid->Data4[i], 2); } buf[35] = 0; } void ALF_BuildRandomClassName(ALFAPP app, const TCHAR* prefix, TCHAR* buf) { UUID uuid; app->compatFn->UuidCreate(&uuid); lstrcpy(buf, prefix); ALF_UuidToString(&uuid, &buf[lstrlen(buf)]); } 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(app, TEXT("ALFWindow."), classNameBuf); } 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 = DefWindowProc; 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); SetClassLongPtr(tmp, GCLP_WNDPROC, (LONG_PTR)ALF_WindowProc); 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); } 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 (!IsDialogMessage(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); }