diff options
Diffstat (limited to 'alf/alfprogress.cpp')
| -rw-r--r-- | alf/alfprogress.cpp | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/alf/alfprogress.cpp b/alf/alfprogress.cpp new file mode 100644 index 0000000..3a5c4fc --- /dev/null +++ b/alf/alfprogress.cpp @@ -0,0 +1,476 @@ +#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; + } +} |
