From 56ad7024445fa1e65ef2d15a80c74b410e60b974 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 10 Nov 2014 14:57:26 -0500 Subject: [PATCH] Added the code for the checkbox image lists to the Windows Table test. --- wintable/imagelist_windows.c | 196 +++++++++++++++++++++++++++++++++++ wintable/main.c | 30 +++++- 2 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 wintable/imagelist_windows.c diff --git a/wintable/imagelist_windows.c b/wintable/imagelist_windows.c new file mode 100644 index 0000000..c97fc0f --- /dev/null +++ b/wintable/imagelist_windows.c @@ -0,0 +1,196 @@ +// 16 august 2014 + +#define UNICODE +#define _UNICODE +#define STRICT +#define STRICT_TYPED_ITEMIDS +#define CINTERFACE +// get Windows version right; right now Windows XP +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 +#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */ +#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */ +#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + checkboxStateChecked = 1 << 0, + checkboxStateHot = 1 << 1, + checkboxStatePushed = 1 << 2, + checkboxnStates = 1 << 3, +}; + +#define xpanic(...) abort() +#define xpanichresult(...) abort() + +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 void dfcImage(HDC dc, RECT *r, int cbState, HTHEME theme) +{ + if (DrawFrameControl(dc, r, DFC_BUTTON, dfcState(cbState)) == 0) + xpanic("error drawing checkbox image", GetLastError()); +} + +static void dfcSize(HDC dc, int *width, int *height, HTHEME theme) +{ + // 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); +} + +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, HTHEME theme) +{ + SIZE s; + HRESULT res; + + res = GetThemePartSize(theme, dc, BP_CHECKBOX, themestates[cbState], NULL, TS_DRAW, &s); + if (res != S_OK) + xpanichresult("error getting theme part size", res); + return s; +} + +static void themeImage(HDC dc, RECT *r, int cbState, HTHEME theme) +{ + HRESULT res; + + res = DrawThemeBackground(theme, dc, BP_CHECKBOX, themestates[cbState], r, NULL); + if (res != S_OK) + xpanichresult("error drawing checkbox image", res); +} + +static void themeSize(HDC dc, int *width, int *height, HTHEME theme) +{ + SIZE size; + int cbState; + + size = getStateSize(dc, 0, theme); + for (cbState = 1; cbState < checkboxnStates; cbState++) { + SIZE against; + + against = getStateSize(dc, cbState, theme); + if (size.cx != against.cx || size.cy != against.cy) + xpanic("size mismatch in checkbox states", GetLastError()); + } + *width = (int) size.cx; + *height = (int) size.cy; +} + +static HBITMAP makeCheckboxImageListEntry(HDC dc, int width, int height, int cbState, void (*drawfunc)(HDC, RECT *, int, HTHEME), HTHEME theme) +{ + 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()); + (*drawfunc)(drawDC, &r, cbState, theme); + 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; +} + +static HIMAGELIST newCheckboxImageList(HWND hwnddc, void (*sizefunc)(HDC, int *, int *, HTHEME), void (*drawfunc)(HDC, RECT *, int, HTHEME), HTHEME theme) +{ + int width, height; + int cbState; + HDC dc; + HIMAGELIST il; + + dc = GetDC(hwnddc); + if (dc == NULL) + xpanic("error getting DC for making the checkbox image list", GetLastError()); + (*sizefunc)(dc, &width, &height, theme); + il = ImageList_Create(width, height, ILC_COLOR32, 20, 20); // should be reasonable + if (il == NULL) + xpanic("error creating checkbox image list", GetLastError()); + for (cbState = 0; cbState < checkboxnStates; cbState++) { + HBITMAP bitmap; + + bitmap = makeCheckboxImageListEntry(dc, width, height, cbState, drawfunc, theme); + if (ImageList_Add(il, 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()); + return il; +} + +HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme) +{ + if (*theme != NULL) { + HRESULT res; + + res = CloseThemeData(*theme); + if (res != S_OK) + xpanichresult("error closing theme", res); + *theme = NULL; + } + // ignore error; if it can't be done, we can fall back to DrawFrameControl() + if (*theme == NULL) // try to open the theme + *theme = OpenThemeData(hwnddc, L"button"); + if (*theme != NULL) // use the theme + return newCheckboxImageList(hwnddc, themeSize, themeImage, *theme); + // couldn't open; fall back + return newCheckboxImageList(hwnddc, dfcSize, dfcImage, *theme); +} diff --git a/wintable/main.c b/wintable/main.c index 9f09bb3..2a571ac 100644 --- a/wintable/main.c +++ b/wintable/main.c @@ -16,12 +16,19 @@ #include #include #include +extern HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme); +enum { + checkboxStateChecked = 1 << 0, + checkboxStateHot = 1 << 1, + checkboxStatePushed = 1 << 2, + checkboxnStates = 1 << 3, +}; #include #include #include #include -// #qo LIBS: user32 kernel32 gdi32 comctl32 +// #qo LIBS: user32 kernel32 gdi32 comctl32 uxtheme // TODO // - http://blogs.msdn.com/b/oldnewthing/archive/2003/09/09/54826.aspx (relies on the integrality parts? IDK) @@ -51,6 +58,8 @@ struct table { intptr_t width; intptr_t hpagesize; intptr_t hpos; + HIMAGELIST checkboxes; + HTHEME theme; }; static LONG rowHeight(struct table *t) @@ -459,8 +468,8 @@ static void drawItem(struct table *t, HDC dc, intptr_t i, LONG y, LONG height, R ZeroMemory(&ip, sizeof (IMAGELISTDRAWPARAMS)); ip.cbSize = sizeof (IMAGELISTDRAWPARAMS); - ip.himl = t->imagelist; - ip.i = 0; + ip.himl = t->checkboxes;//t->imagelist; + ip.i = (i%8);//0; ip.hdcDst = dc; ip.x = headeritem.left + xoff; ip.y = y; @@ -596,6 +605,7 @@ if (ImageList_AddIcon(t->imagelist, icon) == -1)abort(); if (ImageList_GetIconSize(t->imagelist, &unused, &(t->imagelistHeight)) == 0)abort(); } } + t->checkboxes = makeCheckboxImageList(t->hwnd, &(t->theme)); SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) t); } // even if we did the above, fall through @@ -662,6 +672,18 @@ if (ImageList_GetIconSize(t->imagelist, &unused, &(t->imagelistHeight)) == 0)abo return FALSE; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); + // TODO others? + case WM_WININICHANGE: + case WM_SYSCOLORCHANGE: + case WM_THEMECHANGED: + if (ImageList_Destroy(t->checkboxes) == 0) + abort(); + t->checkboxes = makeCheckboxImageList(t->hwnd, &(t->theme)); + resize(t); // TODO needed? + redrawAll(t); + // now defer back to DefWindowProc() in case other things are needed + // TODO needed? + return DefWindowProcW(hwnd, uMsg, wParam, lParam); case WM_GETOBJECT: // accessibility /* if (((DWORD) lParam) == OBJID_CLIENT) { @@ -670,7 +692,7 @@ if (ImageList_GetIconSize(t->imagelist, &unused, &(t->imagelistHeight)) == 0)abo // TODO create the server object lResult = LresultFromObject(IID_IAccessible, wParam, server); - if (/* TODO failure */) + if (/* TODO failure *|/) abort(); // TODO release object return lResult;