diff --git a/redo/imagelist_windows.c b/redo/imagelist_windows.c index a6db91f..a84ae2e 100644 --- a/redo/imagelist_windows.c +++ b/redo/imagelist_windows.c @@ -3,6 +3,8 @@ #include "winapi_windows.h" #include "_cgo_export.h" +// TODO eliminate duplicate code + HBITMAP unscaledBitmap(void *i, intptr_t dx, intptr_t dy) { BITMAPINFO bi; @@ -53,7 +55,7 @@ void addImage(HIMAGELIST il, HWND hwnd, HBITMAP bitmap, int origwid, int oright, if (winDC == NULL) xpanic("error getting DC for window", GetLastError()); origDC = CreateCompatibleDC(winDC); - if (winDC == NULL) + if (origDC == NULL) xpanic("error getting DC for original ImageList bitmap", GetLastError()); prevorig = SelectObject(origDC, bitmap); if (prevorig == NULL) @@ -81,7 +83,7 @@ void addImage(HIMAGELIST il, HWND hwnd, HBITMAP bitmap, int origwid, int oright, xpanic("error selecting previous bitmap into scaled image's DC", GetLastError()); if (DeleteDC(scaledDC) == 0) xpanic("error deleting scaled image's DC", GetLastError()); - if (DeleteDC(winDC) == 0) + if (ReleaseDC(hwnd, winDC) == 0) xpanic("error deleting window DC", GetLastError()); noscale: @@ -98,3 +100,217 @@ void applyImageList(HWND hwnd, UINT uMsg, WPARAM wParam, HIMAGELIST il) ;//TODO xpanic("error setting image list", GetLastError()); // TODO free old one here if any } + +static UINT dfcState(int cbstate) +{ + UINT ret; + + ret = DFCS_BUTTONCHECK; + if ((cbstate & checkboxStateChecked) != 0) + ret |= DFCS_CHECKED; + if ((cbstate & checkboxStateHot) != 0) + ret |= DFCS_HOT; + if ((cbstate & checkboxStatePushed) != 0) + ret |= DFCS_PUSHED; + return ret; +} + +static HBITMAP dfcImage(HDC dc, int width, int height, int cbState) +{ + BITMAPINFO bi; + VOID *ppvBits; + HBITMAP bitmap; + RECT r; + HDC drawDC; + HBITMAP prevbitmap; + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + ZeroMemory(&bi, sizeof (BITMAPINFO)); + bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bi.bmiHeader.biWidth = (LONG) width; + bi.bmiHeader.biHeight = -((LONG) height); // negative height to force top-down drawing; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biSizeImage = (DWORD) (width * height * 4); + bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &ppvBits, 0, 0); + if (bitmap == NULL) + xpanic("error creating HBITMAP for unscaled ImageList image copy", GetLastError()); + + drawDC = CreateCompatibleDC(dc); + if (drawDC == NULL) + xpanic("error getting DC for checkbox image list bitmap", GetLastError()); + prevbitmap = SelectObject(drawDC, bitmap); + if (prevbitmap == NULL) + xpanic("error selecting checkbox image list bitmap into DC", GetLastError()); + if (DrawFrameControl(drawDC, &r, DFC_BUTTON, dfcState(cbState)) == 0) + xpanic("error drawing checkbox image", GetLastError()); + if (SelectObject(drawDC, prevbitmap) != bitmap) + xpanic("error selecting previous bitmap into checkbox image's DC", GetLastError()); + if (DeleteDC(drawDC) == 0) + xpanic("error deleting checkbox image's DC", GetLastError()); + + return bitmap; +} + +HIMAGELIST checkboxImageList = NULL; + +void makeCheckboxImageList_DFC(HWND hwnddc) +{ + int width, height; + int cbState; + HDC dc; + HIMAGELIST il; + + // there's no real metric around + // let's use SM_CX/YSMICON and hope for the best + width = GetSystemMetrics(SM_CXSMICON); + height = GetSystemMetrics(SM_CYSMICON); + dc = GetDC(hwnddc); + if (dc == NULL) + xpanic("error getting DC for making the checkbox image list", GetLastError()); + checkboxImageList = (*fv_ImageList_Create)(width, height, ILC_COLOR32, 20, 20); // should be reasonable + if (checkboxImageList == NULL) + xpanic("error creating checkbox image list", GetLastError()); + for (cbState = 0; cbState < checkboxnStates; cbState++) { + HBITMAP bitmap; + + bitmap = dfcImage(dc, width, height, cbState); + if ((*fv_ImageList_Add)(checkboxImageList, bitmap, NULL) == -1) + xpanic("error adding checkbox image to image list", GetLastError()); + if (DeleteObject(bitmap) == 0) + xpanic("error deleting checkbox bitmap", GetLastError()); + } + if (ReleaseDC(hwnddc, dc) == 0) + xpanic("error deleting checkbox image list DC", GetLastError()); +} + +static HTHEME theme = NULL; + +static void openTheme(HWND hwnd) +{ + if (theme != NULL) + // TODO save HRESULT + if (CloseThemeData(theme) != S_OK) + xpanic("error closing theme", GetLastError()); + // ignore error; if it can't be done, we can fall back to DrawFrameControl() + theme = OpenThemeData(hwnd, L"button"); +} + +static int themestates[checkboxnStates] = { + CBS_UNCHECKEDNORMAL, // 0 + CBS_CHECKEDNORMAL, // checked + CBS_UNCHECKEDHOT, // hot + CBS_CHECKEDHOT, // checked | hot + CBS_UNCHECKEDPRESSED, // pushed + CBS_CHECKEDPRESSED, // checked | pushed + CBS_UNCHECKEDPRESSED, // hot | pushed + CBS_CHECKEDPRESSED, // checked | hot | pushed +}; + +static SIZE getStateSize(HDC dc, int cbState) +{ + SIZE s; + + // TODO use HRESULT + if (GetThemePartSize(theme, dc, BP_CHECKBOX, themestates[cbState], NULL, TS_DRAW, &s) != S_OK) + xpanic("error getting theme part size", GetLastError()); + return s; +} + +static HBITMAP drawThemeImage(HDC dc, int width, int height, int cbState) +{ + BITMAPINFO bi; + VOID *ppvBits; + HBITMAP bitmap; + RECT r; + HDC drawDC; + HBITMAP prevbitmap; + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + ZeroMemory(&bi, sizeof (BITMAPINFO)); + bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bi.bmiHeader.biWidth = (LONG) width; + bi.bmiHeader.biHeight = -((LONG) height); // negative height to force top-down drawing; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biSizeImage = (DWORD) (width * height * 4); + bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &ppvBits, 0, 0); + if (bitmap == NULL) + xpanic("error creating HBITMAP for unscaled ImageList image copy", GetLastError()); + + drawDC = CreateCompatibleDC(dc); + if (drawDC == NULL) + xpanic("error getting DC for checkbox image list bitmap", GetLastError()); + prevbitmap = SelectObject(drawDC, bitmap); + if (prevbitmap == NULL) + xpanic("error selecting checkbox image list bitmap into DC", GetLastError()); + // TODO get HRESULT + if (DrawThemeBackground(theme, drawDC, BP_CHECKBOX, themestates[cbState], &r, NULL) != S_OK) + xpanic("error drawing checkbox image", GetLastError()); + if (SelectObject(drawDC, prevbitmap) != bitmap) + xpanic("error selecting previous bitmap into checkbox image's DC", GetLastError()); + if (DeleteDC(drawDC) == 0) + xpanic("error deleting checkbox image's DC", GetLastError()); + + return bitmap; +} + + +void makeCheckboxImageList_theme(HWND hwnddc) +{ + int width, height; + int cbState; + SIZE size; + HDC dc; + + // first, make sure that all things have the same size + dc = GetDC(hwnddc); + if (dc == NULL) + xpanic("error getting DC for making the checkbox image list", GetLastError()); + size = getStateSize(dc, 0); + for (cbState = 1; cbState < checkboxnStates; cbState++) { + SIZE against; + + against = getStateSize(dc, cbState); + if (size.cx != against.cx || size.cy != against.cy) + xpanic("size mismatch in checkbox states", GetLastError()); + } + width = (int) size.cx; + height = (int) size.cy; + + // NOW draw + checkboxImageList = (*fv_ImageList_Create)(width, height, ILC_COLOR32, 20, 20); // should be reasonable + if (checkboxImageList == NULL) + xpanic("error creating checkbox image list", GetLastError()); + for (cbState = 0; cbState < checkboxnStates; cbState++) { + HBITMAP bitmap; + + bitmap = drawThemeImage(dc, width, height, cbState); + if ((*fv_ImageList_Add)(checkboxImageList, bitmap, NULL) == -1) + xpanic("error adding checkbox image to image list", GetLastError()); + if (DeleteObject(bitmap) == 0) + xpanic("error deleting checkbox bitmap", GetLastError()); + } + if (ReleaseDC(hwnddc, dc) == 0) + xpanic("error deleting checkbox image list DC", GetLastError()); +} + +void makeCheckboxImageList(HWND hwnddc) +{ + if (theme == NULL) // try to open the theme + openTheme(hwnddc); + if (theme != NULL) { // use the theme + makeCheckboxImageList_theme(hwnddc); + return; + } + // couldn't open; fall back + makeCheckboxImageList_DFC(hwnddc); +} diff --git a/redo/imagelist_windows.go b/redo/imagelist_windows.go index 37924bd..479e9a5 100644 --- a/redo/imagelist_windows.go +++ b/redo/imagelist_windows.go @@ -41,5 +41,6 @@ func (i *imagelist) apply(hwnd C.HWND, uMsg C.UINT, wParam C.WPARAM) { for index := range i.list { C.addImage(il, hwnd, i.list[index], C.int(i.width[index]), C.int(i.height[index]), width, height) } - C.applyImageList(hwnd, uMsg, wParam, il) +// C.applyImageList(hwnd, uMsg, wParam, il) + C.applyImageList(hwnd, uMsg, wParam, C.checkboxImageList) } diff --git a/redo/uitask_windows.c b/redo/uitask_windows.c index f80b878..7a20bbe 100644 --- a/redo/uitask_windows.c +++ b/redo/uitask_windows.c @@ -78,6 +78,10 @@ static LRESULT CALLBACK msgwinproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l if (sharedWndProc(hwnd, uMsg, wParam, lParam, &shared)) return shared; switch (uMsg) { + case WM_CREATE: + // initial + makeCheckboxImageList(hwnd); + return 0; case msgRequest: doissue((void *) lParam); return 0; diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 589fbec..c937f64 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include // global messages unique to everything enum { @@ -119,5 +121,13 @@ extern HBITMAP unscaledBitmap(void *, intptr_t, intptr_t); extern HIMAGELIST newImageList(int, int); extern void addImage(HIMAGELIST, HWND, HBITMAP, int, int, int, int); extern void applyImageList(HWND, UINT, WPARAM, HIMAGELIST); +enum { + checkboxStateChecked = 1 << 0, + checkboxStateHot = 1 << 1, + checkboxStatePushed = 1 << 2, + checkboxnStates = 1 << 3, +}; +extern HIMAGELIST checkboxImageList; +extern void makeCheckboxImageList(HWND); #endif