#include "alf.h" #include #include #include #include #include typedef struct ALFListHeader { struct ALFListHeader *prev; struct ALFListHeader *next; } ALFListHeader; #define ALF_LIST_CONTAINER(ContainerType, containermember, listp) \ ((ContainerType *)((char *)(listp) - offsetof(ContainerType, containermember))) #define ALF_FOR_LIST(ContainerType, containermember, listp, iteratorvar) \ for (ALFListHeader *alf_list_##iteratorvar##_curr = (listp)->next, \ *alf_list_##iteratorvar##_next = alf_list_##iteratorvar##_curr->next; \ alf_list_##iteratorvar##_curr != (listp); \ alf_list_##iteratorvar##_curr = alf_list_##iteratorvar##_next, \ alf_list_##iteratorvar##_next = alf_list_##iteratorvar##_next->next) \ for (ContainerType *iteratorvar = (ContainerType *)((char *)alf_list_##iteratorvar##_curr - offsetof(ContainerType, containermember)); \ iteratorvar; iteratorvar = NULL) \ static inline BOOL ALF_ListIsEmpty(ALFListHeader *list) { return list->next == list; } static inline void ALF_ListInsert(ALFListHeader *list, ALFListHeader *newel) { newel->prev = list; newel->next = list->next; newel->next->prev = newel; list->next = newel; } static inline void ALF_ListRemove(ALFListHeader *member) { member->prev->next = member->next; member->next->prev = member->prev; member->next = NULL; member->prev = NULL; } static inline void ALF_ListInit(ALFListHeader *list) { list->next = list->prev = list; } typedef struct { ALFListHeader list; HWND hwnd; UINT x; UINT y; UINT cptWidth; UINT cptHeight; UINT cptMarginTop; UINT cptMarginRight; UINT cptMarginBottom; UINT cptMarginLeft; DWORD flags; } ALFWidgetPriv; typedef struct { int minWidth; int allocatedWidth; int allocatedPosition; int expand : 1; } ALFRowOrColumn; typedef struct { ALFRowOrColumn *columns; ALFRowOrColumn *rows; int nColumns; int nRows; int totalMinWidth; int totalMinHeight; int occupiedColumnCount; int occupiedRowCount; } ALFLayout; typedef struct { ALFWindowVTable *vtbl; void *closure; ALFWindowFonts fonts; ALFListHeader widgets; int modalResult; ALFLayout layout; } ALFWindowPriv; static void ALF_InitializeWindowPriv(HWND hwnd, ALFWindowPriv *priv, ALFWindowInstanceParams *params) { priv->vtbl = (ALFWindowVTable*)GetClassLongPtr(hwnd, 0); priv->closure = params->closure; ALF_ListInit(&priv->widgets); } 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); 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); } } void ALF_UpdateFonts(HWND win) { // TODO per-monitor DPI aware: GetDpiForWindow, SystemParametersInfoForDpi etc. ALFWindowPriv *priv = (ALFWindowPriv*)GetWindowLongPtr(win, 0); priv->fonts.dpi = 0; HDC hdcScreen = GetDC(NULL); if (hdcScreen) { priv->fonts.dpi = GetDeviceCaps(hdcScreen, LOGPIXELSY); ReleaseDC(NULL, hdcScreen); } if (!priv->fonts.dpi) { priv->fonts.dpi = 96; // FIXME! fallback to default DPI } NONCLIENTMETRICS ncm; ZeroMemory(&ncm, sizeof(ncm)); ncm.cbSize = sizeof(ncm); if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0)) { 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); lstrcpyW(priv->fonts.lfMessageFont.lfFaceName, L"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 - extraWidthPart * 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) { 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); hdwp = DeferWindowPos(hdwp, c->hwnd, 0, win->layout.columns[c->x].allocatedPosition + marginleft, win->layout.rows[c->y].allocatedPosition + margintop, win->layout.columns[c->x].allocatedWidth - marginleft - marginright, win->layout.rows[c->y].allocatedWidth - margintop - marginbottom, SWP_NOACTIVATE | SWP_NOZORDER); } EndDeferWindowPos(hdwp); } static void ALF_InternalAddWidget(HWND hwnd, ALFWindowPriv *priv, ALFAddWidgetParams *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); } 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, (ALFAddWidgetParams *)lparam); } 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; // TODO ..ForDpi if (AdjustWindowRectEx(&tmp, GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, GetWindowLong(hwnd, GWL_EXSTYLE))) { 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_UpdateFonts(hwnd); ALF_RecalculateLayout(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); } return DefWindowProc(hwnd, msg, wparam, lparam); } void ALF_AddWidget(HWND win, UINT x, UINT y, HWND widget, UINT minWidth, UINT minHeight, DWORD flags) { ALFAddWidgetParams 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 ALFAddWidgetParams *p) { SendMessage(win, ALF_WM_ADDWIDGET, 0, (LPARAM)p); } static LRESULT CALLBACK ALF_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_NCCREATE) { CREATESTRUCT *cs = (CREATESTRUCT*)(void*)lparam; ALFWindowInstanceParams *params = (ALFWindowInstanceParams*)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, params); } 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); } } LPTSTR ALF_RegisterWindowClass(HINSTANCE hInstance, const ALFWindowClassParams *params) { WNDCLASS cls; ZeroMemory(&cls, sizeof(cls)); // TODO: autogenerate class name cls.style = params->classStyle; cls.hInstance = hInstance; cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW); cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); cls.lpszClassName = params->className; cls.cbWndExtra = sizeof(void*); cls.cbClsExtra = sizeof(void*); 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; HWND tmp = CreateWindowEx(0, MAKEINTATOM(classatom), TEXT("dummy"), 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0); SetClassLongPtr(tmp, 0, (LONG_PTR)pvtbl); SetClassLongPtr(tmp, GCLP_WNDPROC, (LONG_PTR)ALF_WindowProc); DestroyWindow(tmp); return MAKEINTATOM(classatom); } HWND ALF_InstantiateWindow(HINSTANCE hInstance, LPCTSTR className, const ALFWindowInstanceParams *params) { return CreateWindowEx(params->windowExStyle, className, L"Window", params->windowStyle, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, //FIXME params->hwndParent, NULL, hInstance, (void*)params); } 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, UINT cptWidth, UINT cptHeight) { int pxwidth = ALF_CentipointsToPixels(win, cptWidth); int pxheight = ALF_CentipointsToPixels(win, cptHeight); 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); } /* LABEL */ static LRESULT CALLBACK ALF__LabelSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { (void)uIdSubclass; (void)dwRefData; if (uMsg == ALF_WM_QUERYSIZE) { HDC hdcLabel = GetDC(hwnd); HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); HFONT oldFont = 0; if (font) oldFont = SelectFont(hdcLabel, font); // calc drawtext style DWORD style = GetWindowLong(hwnd, GWL_STYLE); UINT format = DT_LEFT | DT_EXPANDTABS | DT_CALCRECT; if (style & SS_NOPREFIX) format |= DT_NOPREFIX; if (style & SS_EDITCONTROL) format |= DT_EDITCONTROL; if (style & SS_ENDELLIPSIS || style & SS_PATHELLIPSIS || style & SS_WORDELLIPSIS) format |= DT_SINGLELINE; RECT r = { 0, 0, 100, 100 }; int textlen = GetWindowTextLength(hwnd); TCHAR *textbuf = (TCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, (textlen + 1)*sizeof(TCHAR)); GetWindowText(hwnd, textbuf, textlen+1); DrawText(hdcLabel, textbuf, -1, &r, format); HeapFree(GetProcessHeap(), 0, textbuf); SIZE *pSize = (SIZE*)(void*)lParam; if (pSize->cx == 0) pSize->cx = r.right - r.left; if (pSize->cy == 0) pSize->cy = r.bottom - r.top; if (font) SelectFont(hdcLabel, oldFont); ReleaseDC(hwnd, hdcLabel); } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } HWND ALF_AddLabel(HWND win, int id, UINT x, UINT y, const WCHAR *text) { HWND hwndLabel = CreateWindow(L"STATIC", text, WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 0, 0, 100, 100, win, (HMENU)id, (HINSTANCE)GetWindowLongPtr(win, GWLP_HINSTANCE), NULL); SetWindowSubclass(hwndLabel, ALF__LabelSubclassProc, 0, 0); ALFAddWidgetParams p; ZeroMemory(&p, sizeof(p)); p.hwnd = hwndLabel; p.x = x; p.y = y; p.width = 0; p.height = 0; p.flags = ALF_QUERYSIZE | ALF_MESSAGEFONT; p.margins[0] = 300; p.margins[2] = 375; ALF_AddWidgetEx(win, &p); return hwndLabel; } /* EDIT */ static LRESULT CALLBACK ALF__EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { (void)uIdSubclass; (void)dwRefData; if (uMsg == ALF_WM_QUERYSIZE) { HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); if (font) { LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); if (GetObject(font, sizeof(lf), &lf)) { SIZE *ps = (SIZE*)lParam; if (!ps->cx) { ps->cx = ALF_CentipointsToPixels(GetParent(hwnd), 12000); } if (!ps->cy) { ps->cy = ALF_CentipointsToPixels(GetParent(hwnd), 525) + MulDiv(16, -lf.lfHeight, 11); } } } return 0; } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } HWND ALF_AddEdit(HWND win, int id, UINT x, UINT y, const WCHAR *text) { HWND hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", text, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, 0, 0, 100, 100, win, (HMENU)id, (HINSTANCE)GetWindowLongPtr(win, GWLP_HINSTANCE), NULL); SetWindowSubclass(hwndEdit, ALF__EditSubclassProc, 0, 0); ALFAddWidgetParams p; ZeroMemory(&p, sizeof(p)); p.hwnd = hwndEdit; p.x = x; p.y = y; p.width = 0; p.height = 0; p.flags = ALF_QUERYSIZE | ALF_MESSAGEFONT; ALF_AddWidgetEx(win, &p); return hwndEdit; } /* BUTTON */ static LRESULT CALLBACK ALF__ButtonSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { (void)uIdSubclass; (void)dwRefData; if (uMsg == ALF_WM_QUERYSIZE) { HDC hdc = GetDC(hwnd); HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); HFONT oldFont = 0; if (font) oldFont = SelectFont(hdc, font); // calc drawtext style DWORD style = GetWindowLong(hwnd, GWL_STYLE); UINT format = DT_LEFT | DT_EXPANDTABS | DT_CALCRECT; if ((style & BS_MULTILINE) == 0) format |= DT_SINGLELINE; RECT r = { 0, 0, 100, 100 }; int textlen = GetWindowTextLength(hwnd); TCHAR *textbuf = (TCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, (textlen + 1)*sizeof(TCHAR)); GetWindowText(hwnd, textbuf, textlen+1); DrawText(hdc, textbuf, -1, &r, format); HeapFree(GetProcessHeap(), 0, textbuf); int padding = ALF_CentipointsToPixels(GetParent(hwnd), 525); int minheight = ALF_CentipointsToPixels(GetParent(hwnd), 1725); SIZE *pSize = (SIZE*)(void*)lParam; if (pSize->cx < r.right - r.left + padding) { pSize->cx = r.right - r.left + padding; } if (pSize->cx < minheight) { pSize->cx = minheight; } if (pSize->cy < r.bottom - r.top + padding) { pSize->cy = r.bottom - r.top + padding; } if (pSize->cy < minheight) { pSize->cy = minheight; } if (font) SelectFont(hdc, oldFont); ReleaseDC(hwnd, hdc); } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } HWND ALF_AddButton(HWND win, int id, UINT x, UINT y, const WCHAR *text) { HWND hwndButton = CreateWindowEx(0, L"BUTTON", text, WS_CHILD | WS_TABSTOP | WS_VISIBLE | BS_PUSHBUTTON | BS_MULTILINE, 0, 0, 100, 100, win, (HMENU)id, (HINSTANCE)GetWindowLongPtr(win, GWLP_HINSTANCE), NULL); SetWindowSubclass(hwndButton, ALF__ButtonSubclassProc, 0, 0); ALFAddWidgetParams p; ZeroMemory(&p, sizeof(p)); p.hwnd = hwndButton; p.x = x; p.y = y; p.width = 5625; p.height = 0; p.flags = ALF_QUERYSIZE | ALF_MESSAGEFONT; ALF_AddWidgetEx(win, &p); return hwndButton; }