summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--wintable/imagelist_windows.c196
-rw-r--r--wintable/main.c30
2 files changed, 222 insertions, 4 deletions
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 <windows.h>
+#include <commctrl.h>
+#include <stdint.h>
+#include <uxtheme.h>
+#include <string.h>
+#include <wchar.h>
+#include <windowsx.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+#include <oleacc.h>
+
+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 <uxtheme.h>
#include <string.h>
#include <wchar.h>
+extern HIMAGELIST makeCheckboxImageList(HWND hwnddc, HTHEME *theme);
+enum {
+ checkboxStateChecked = 1 << 0,
+ checkboxStateHot = 1 << 1,
+ checkboxStatePushed = 1 << 2,
+ checkboxnStates = 1 << 3,
+};
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <oleacc.h>
-// #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;