#include "alfpriv.h" #define ALF_PROGRESS_MARQUEE_WIDTH 5 #define ALF_PROGRESS_MARQUEE_TIMER_ID 42 // need special messages for gauge on Win32s because Win32s only keeps the // low word of WPARAM where we occasionally need to pass 32bit LONGs #define ALF_WM_PROGRESS_SETRANGE (ALF_WM__BASE + 201) #define ALF_WM_PROGRESS_SETPOS (ALF_WM__BASE + 202) typedef struct { int min; int max; int pos; int marquee; HFONT font; } ALFProgressGaugePriv; static void ALF_ProgressGauge_Init(ALFProgressGaugePriv *priv) { priv->min = 0; priv->max = 100; priv->pos = 0; priv->marquee = 0; } static LRESULT ALF_ProgressGauge_GetRange(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM retminormax, PBRANGE *pRange) { (void)hwnd; if (pRange) { pRange->iLow = priv->min; pRange->iHigh = priv->max; } if (retminormax) return priv->min; else return priv->max; } static LRESULT ALF_ProgressGauge_SetRange(ALFProgressGaugePriv *priv, HWND hwnd, int min, int max) { priv->min = min; priv->max = max; if (max < min) priv->max = min; if (priv->pos < min) priv->pos = min; if (priv->pos > max) priv->pos = max; InvalidateRect(hwnd, NULL, TRUE); return 1; } static LRESULT ALF_ProgressGauge_GetPos(ALFProgressGaugePriv *priv, HWND hwnd) { (void)hwnd; return (LRESULT)priv->pos; } static LRESULT ALF_ProgressGauge_SetPos(ALFProgressGaugePriv *priv, HWND hwnd, int pos) { priv->pos = pos; if (priv->pos < priv->min) priv->pos = priv->min; if (priv->pos > priv->max) priv->pos = priv->max; InvalidateRect(hwnd, NULL, TRUE); return 1; } static LRESULT ALF_ProgressGauge_Size(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM wparam, LPARAM lparam) { (void)priv; InvalidateRect(hwnd, NULL, TRUE); return DefWindowProc(hwnd, WM_SIZE, wparam, lparam); } static void ALF_ProgressGauge_DoPaint(ALFProgressGaugePriv *priv, HWND hwnd, HDC dc, RECT *rcClip) { (void)rcClip; RECT rc; GetClientRect(hwnd, &rc); OffsetRect(&rc, -rc.left, -rc.top); DrawEdge(dc, &rc, BDR_SUNKENOUTER, BF_RECT | BF_ADJUST); FrameRect(dc, &rc, GetSysColorBrush(COLOR_BTNFACE)); InflateRect(&rc, -1, -1); TCHAR textbuf[64]; // FIXME! make this variable length? ZeroMemory(textbuf, sizeof(textbuf)); int len = GetWindowText(hwnd, textbuf, sizeof(textbuf)/sizeof(textbuf[0])); HFONT oldfont = SelectFont(dc, (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0)); TEXTMETRIC tm; ZeroMemory(&tm, sizeof(tm)); GetTextMetrics(dc, &tm); int refx = rc.left + (rc.right - rc.left) / 2; int refy = rc.top + (rc.bottom - rc.top + tm.tmAscent - tm.tmDescent) / 2; SetTextAlign(dc, TA_CENTER | TA_BASELINE); COLORREF clrHilight = GetSysColor(COLOR_HIGHLIGHT); COLORREF clrHilightText = GetSysColor(COLOR_HIGHLIGHTTEXT); COLORREF clrBtnFace = GetSysColor(COLOR_BTNFACE); COLORREF clrBtnText = GetSysColor(COLOR_BTNTEXT); if (GetWindowLong(hwnd, GWL_STYLE) & PBS_MARQUEE) { RECT rcMarquee = { rc.left + priv->marquee, rc.top, rc.left + priv->marquee + ALF_PROGRESS_MARQUEE_WIDTH, rc.bottom }; if (rcMarquee.left < rc.left) rcMarquee.left = rc.left; if (rcMarquee.right > rc.right) rcMarquee.right = rc.right; RECT rcMarqueeWrap = { rc.left, rc.top, rc.left + priv->marquee + ALF_PROGRESS_MARQUEE_WIDTH - rc.right + rc.left, rc.bottom }; if (rcMarqueeWrap.right < rcMarqueeWrap.left) rcMarqueeWrap.right = rcMarqueeWrap.left; RECT rcLeftSpace = { rcMarqueeWrap.right, rc.top, rcMarquee.left, rc.bottom }; RECT rcRightSpace = { rcMarquee.right, rc.top, rc.right, rc.bottom }; SetBkColor(dc, clrHilight); SetTextColor(dc, clrHilightText); ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcMarquee, textbuf, (UINT)len, NULL); ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcMarqueeWrap, textbuf, (UINT)len, NULL); SetBkColor(dc, clrBtnFace); SetTextColor(dc, clrBtnText); ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcLeftSpace, textbuf, (UINT)len, NULL); ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcRightSpace, textbuf, (UINT)len, NULL); } else { int barwidth = MulDiv(priv->pos - priv->min, rc.right - rc.left, priv->max - priv->min); // XXX: make the painting pixel-compatible to the original progress bar // IMHO this seems to be some kind of off-by-one error in the microsoft code if (barwidth >= rc.right - rc.left) barwidth -= 1; RECT rcLeft = { rc.left, rc.top, rc.left + barwidth, rc.bottom }; RECT rcRight = { rc.left + barwidth, rc.top, rc.right, rc.bottom }; SetBkColor(dc, clrHilight); SetTextColor(dc, clrHilightText); ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcLeft, textbuf, (UINT)len, NULL); SetBkColor(dc, clrBtnFace); SetTextColor(dc, clrBtnText); ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcRight, textbuf, (UINT)len, NULL); } SelectFont(dc, oldfont); } static LRESULT ALF_ProgressGauge_Paint(ALFProgressGaugePriv *priv, HWND hwnd) { PAINTSTRUCT ps; HDC dc = BeginPaint(hwnd, &ps); ALF_ProgressGauge_DoPaint(priv, hwnd, dc, &ps.rcPaint); EndPaint(hwnd, &ps); return 0; } static LRESULT ALF_ProgressGauge_PrintClient(ALFProgressGaugePriv *priv, HWND hwnd, HDC dc) { RECT rc; GetClientRect(hwnd, &rc); OffsetRect(&rc, -rc.left, -rc.top); ALF_ProgressGauge_DoPaint(priv, hwnd, dc, &rc); return 0; } static LRESULT ALF_ProgressGauge_Timer(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM id, LPARAM lparam) { if (id == ALF_PROGRESS_MARQUEE_TIMER_ID) { if (GetWindowLong(hwnd, GWL_STYLE) & PBS_MARQUEE) { RECT rcClient; GetClientRect(hwnd, &rcClient); priv->marquee += 1; if (priv->marquee >= rcClient.right - rcClient.left - 4) { priv->marquee = 0; } InvalidateRect(hwnd, NULL, TRUE); } else { KillTimer(hwnd, id); } return 0; } else { return DefWindowProc(hwnd, WM_TIMER, id, lparam); } } static LRESULT ALF_ProgressGauge_SetMarquee(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM onoff, UINT milliseconds) { if (GetWindowLong(hwnd, GWL_STYLE) & PBS_MARQUEE) { if (onoff) { priv->pos = priv->min; priv->marquee = -2; InvalidateRect(hwnd, NULL, TRUE); if (milliseconds == 0) milliseconds = 30; SetTimer(hwnd, ALF_PROGRESS_MARQUEE_TIMER_ID, milliseconds, NULL); } else { KillTimer(hwnd, ALF_PROGRESS_MARQUEE_TIMER_ID); } } return 0; } static LRESULT ALF_ProgressGauge_StyleChanged(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM wparam, LPARAM lparam) { (void)priv; InvalidateRect(hwnd, NULL, TRUE); return DefWindowProc(hwnd, WM_STYLECHANGED, wparam, lparam); } static LRESULT ALF_ProgressGauge_SetFont(ALFProgressGaugePriv *priv, HWND hwnd, HFONT font) { priv->font = font; InvalidateRect(hwnd, NULL, TRUE); return 0; } static LRESULT ALF_ProgressGauge_GetFont(ALFProgressGaugePriv *priv, HWND hwnd) { (void)hwnd; return (LRESULT)priv->font; } static LRESULT ALF_ProgressGauge_SetText(ALFProgressGaugePriv *priv, HWND hwnd, const TCHAR *text) { (void)priv; InvalidateRect(hwnd, NULL, TRUE); return DefWindowProc(hwnd, WM_SETTEXT, 0, (LPARAM)text); } static LRESULT ALF_ProgressGauge_CalcSize(ALFProgressGaugePriv *priv, HWND hwnd, SIZE *pSize) { if (GetWindowTextLength(hwnd) > 0) { HDC dc = GetDC(hwnd); HFONT oldfont = SelectFont(dc, priv->font); TEXTMETRIC tm; ZeroMemory(&tm, sizeof(tm)); GetTextMetrics(dc, &tm); int cy = tm.tmHeight + 8; if (pSize->cy < cy) pSize->cy = cy; SelectFont(dc, oldfont); ReleaseDC(hwnd, dc); } return 0; } static LRESULT WINAPI ALF_ProgressGauge_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ALFProgressGaugePriv *priv = (ALFProgressGaugePriv *)GetWindowLongPtr(hwnd, 0); if (!priv) { if (msg == WM_CREATE) { priv = ALF_New(ALFProgressGaugePriv, 1); ALF_ProgressGauge_Init(priv); SetWindowLongPtr(hwnd, 0, (LONG_PTR)priv); } else { return DefWindowProc(hwnd, msg, wparam, lparam); } } switch (msg) { case WM_DESTROY: SetWindowLongPtr(hwnd, 0, 0); ALF_Free(priv); break; case PBM_GETPOS: return ALF_ProgressGauge_GetPos(priv, hwnd); case PBM_SETPOS: return ALF_ProgressGauge_SetPos(priv, hwnd, (int)wparam); case ALF_WM_PROGRESS_SETPOS: return ALF_ProgressGauge_SetPos(priv, hwnd, (int)lparam); case PBM_GETRANGE: return ALF_ProgressGauge_GetRange(priv, hwnd, wparam, (PBRANGE *)lparam); case PBM_SETRANGE32: return ALF_ProgressGauge_SetRange(priv, hwnd, (int)wparam, (int)lparam); case ALF_WM_PROGRESS_SETRANGE: return ALF_ProgressGauge_SetRange(priv, hwnd, ((PBRANGE *)lparam)->iLow, ((PBRANGE *)lparam)->iHigh); case WM_PAINT: return ALF_ProgressGauge_Paint(priv, hwnd); case WM_ERASEBKGND: return TRUE; case WM_PRINTCLIENT: return ALF_ProgressGauge_PrintClient(priv, hwnd, (HDC)wparam); case WM_TIMER: return ALF_ProgressGauge_Timer(priv, hwnd, wparam, lparam); case PBM_SETMARQUEE: return ALF_ProgressGauge_SetMarquee(priv, hwnd, wparam, (UINT)lparam); case WM_STYLECHANGED: return ALF_ProgressGauge_StyleChanged(priv, hwnd, wparam, lparam); case WM_SIZE: return ALF_ProgressGauge_Size(priv, hwnd, wparam, lparam); case WM_SETFONT: return ALF_ProgressGauge_SetFont(priv, hwnd, (HFONT)wparam); case WM_GETFONT: return ALF_ProgressGauge_GetFont(priv, hwnd); case WM_SETTEXT: return ALF_ProgressGauge_SetText(priv, hwnd, (const TCHAR *)lparam); case ALF_WM_QUERYSIZE: return ALF_ProgressGauge_CalcSize(priv, hwnd, (SIZE *)lparam); } return DefWindowProc(hwnd, msg, wparam, lparam); } HWND ALF_AddNativeProgressBar(HWND parent, WORD id, int x, int y) { HWND hwnd = CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 0, 0, 10, 10, parent, (HMENU)(ULONG_PTR)id, ALF_HINSTANCE, NULL); ALF_AddControl(parent, x, y, hwnd, 12000, 1125, ALF_LAYOUT_TRANSPARENTBG); return hwnd; } HWND ALF_AddProgressGauge(HWND parent, WORD id, int x, int y) { HWND hwnd = ALF_CreateControlWindow(0, TEXT(""), WS_CHILD | WS_VISIBLE, 0, 0, 10, 10, parent, (HMENU)(ULONG_PTR)id, ALF_ProgressGauge_WindowProc, NULL); ALF_AddControl(parent, x, y, hwnd, 12000, 1125, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT); return hwnd; } HWND ALF_AddProgressBar(HWND parent, WORD id, int x, int y) { if (ALF_Compat_IsComCtlV6()) return ALF_AddNativeProgressBar(parent, id, x, y); else return ALF_AddProgressGauge(parent, id, x, y); } void ALF_Progress_Range(HWND progress, int *pMin, int *pMax) { PBRANGE r = { 0, 0 }; SendMessage(progress, PBM_GETRANGE, 0, (LPARAM)&r); if (pMin) *pMin = r.iLow; if (pMax) *pMax = r.iHigh; } void ALF_Progress_SetRange(HWND progress, int min, int max) { PBRANGE r = { min, max }; if (!SendMessage(progress, ALF_WM_PROGRESS_SETRANGE, 0, (LPARAM)&r)) { SendMessage(progress, PBM_SETRANGE32, (WPARAM)min, (LPARAM)max); } } int ALF_Progress_Value(HWND progress) { return (int)SendMessage(progress, PBM_GETPOS, 0, 0); } void ALF_Progress_SetValue(HWND progress, int value) { if (!SendMessage(progress, ALF_WM_PROGRESS_SETPOS, 0, (LPARAM)value)) SendMessage(progress, PBM_SETPOS, (WPARAM)value, 0); } enum ALFProgressState ALF_Progress_State(HWND progress) { LRESULT state = SendMessage(progress, /*PBM_GETSTATE*/(WM_USER + 17), 0, 0); BOOL marquee = GetWindowLong(progress, GWL_STYLE) & PBS_MARQUEE; if (marquee) { return ALF_PROGRESS_STATE_INDETERMINATE; } else if (state == 0) { // state not supported by this progress bar return ALF_PROGRESS_STATE_NORMAL; } else { // enum values have been chosen to be equal to PBST_* return (enum ALFProgressState)state; } } void ALF_Progress_SetState(HWND progress, enum ALFProgressState state) { switch (state) { case ALF_PROGRESS_STATE_INDETERMINATE: SetWindowLong(progress, GWL_STYLE, GetWindowLong(progress, GWL_STYLE) | PBS_MARQUEE); SendMessage(progress, WM_USER+16/*PBM_SETSTATE*/, 1/*PBST_NORMAL*/, 0); SendMessage(progress, PBM_SETMARQUEE, TRUE, 0); break; default: if (GetWindowLong(progress, GWL_STYLE) & PBS_MARQUEE) { SendMessage(progress, PBM_SETMARQUEE, FALSE, 0); SetWindowLong(progress, GWL_STYLE, GetWindowLong(progress, GWL_STYLE) & ~PBS_MARQUEE); } // enum values have been chosen to equal to PBST_* SendMessage(progress, WM_USER+16/*PBM_SETSTATE*/, (WPARAM)state, 0); break; } }