summaryrefslogtreecommitdiff
path: root/wintable/checkboxes.h
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2015-01-06 17:27:41 -0500
committerPietro Gagliardi <[email protected]>2015-01-06 17:27:41 -0500
commit22123fb676d26d309d428a892c453e35e684e367 (patch)
tree9552f1a91a8b8995065e74573e8f59f448f2b08c /wintable/checkboxes.h
parent720049bfd3c6d205859f3150dee33dee78dc3dc3 (diff)
Merged wintable/new/ into wintable/.
Diffstat (limited to 'wintable/checkboxes.h')
-rw-r--r--wintable/checkboxes.h250
1 files changed, 250 insertions, 0 deletions
diff --git a/wintable/checkboxes.h b/wintable/checkboxes.h
new file mode 100644
index 0000000..67bc745
--- /dev/null
+++ b/wintable/checkboxes.h
@@ -0,0 +1,250 @@
+// 16 august 2014
+
+enum {
+ checkboxStateChecked = 1 << 0,
+ checkboxStateHot = 1 << 1,
+ checkboxStatePushed = 1 << 2,
+ checkboxnStates = 1 << 3,
+};
+
+// TODO actually make this
+#define panichresult(a, b) panic(a)
+
+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 drawFrameControlCheckbox(HDC dc, RECT *r, int cbState)
+{
+ if (DrawFrameControl(dc, r, DFC_BUTTON, dfcState(cbState)) == 0)
+ panic("error drawing Table checkbox image with DrawFrameControl()");
+}
+
+static void getFrameControlCheckboxSize(HDC dc, int *width, int *height)
+{
+ // 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)
+ panichresult("error getting theme part size for Table checkboxes", res);
+ return s;
+}
+
+static void drawThemeCheckbox(HDC dc, RECT *r, int cbState, HTHEME theme)
+{
+ HRESULT res;
+
+ res = DrawThemeBackground(theme, dc, BP_CHECKBOX, themestates[cbState], r, NULL);
+ if (res != S_OK)
+ panichresult("error drawing Table checkbox image from theme", res);
+}
+
+static void getThemeCheckboxSize(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)
+ // TODO make this use a no-information (or two ints) panic()
+ panic("size mismatch in Table checkbox states");
+ }
+ *width = (int) size.cx;
+ *height = (int) size.cy;
+}
+
+static void drawCheckbox(struct table *t, HDC dc, RECT *r, int cbState)
+{
+ if (t->theme != NULL) {
+ drawThemeCheckbox(dc, r, cbState, t->theme);
+ return;
+ }
+ drawFrameControlCheckbox(dc, r, cbState);
+}
+
+static void freeCheckboxThemeData(struct table *t)
+{
+ if (t->theme != NULL) {
+ HRESULT res;
+
+ res = CloseThemeData(t->theme);
+ if (res != S_OK)
+ panichresult("error closing Table checkbox theme", res);
+ t->theme = NULL;
+ }
+}
+
+static void loadCheckboxThemeData(struct table *t)
+{
+ HDC dc;
+
+ freeCheckboxThemeData(t);
+ dc = GetDC(t->hwnd);
+ if (dc == NULL)
+ panic("error getting Table DC for loading checkbox theme data");
+ // ignore error; if it can't be done, we can fall back to DrawFrameControl()
+ if (t->theme == NULL) // try to open the theme
+ t->theme = OpenThemeData(t->hwnd, L"button");
+ if (t->theme != NULL) // use the theme
+ getThemeCheckboxSize(dc, &(t->checkboxWidth), &(t->checkboxHeight), t->theme);
+ else // couldn't open; fall back
+ getFrameControlCheckboxSize(dc, &(t->checkboxWidth), &(t->checkboxHeight));
+ if (ReleaseDC(t->hwnd, dc) == 0)
+ panic("error releasing Table DC for loading checkbox theme data");
+}
+
+static void redrawCheckboxRect(struct table *t, LPARAM lParam)
+{
+ struct rowcol rc;
+ RECT r;
+ POINT pt;
+
+ rc = lParamToRowColumn(t, lParam);
+ if (rc.row == -1 && rc.column == -1)
+ return;
+ if (t->columnTypes[rc.column] != tableColumnCheckbox)
+ return;
+ if (!rowColumnToClientRect(t, rc, &r))
+ return;
+ // TODO only the checkbox rect?
+ if (InvalidateRect(t->hwnd, &r, TRUE) == 0)
+ panic("error redrawing Table checkbox rect for mouse events");
+}
+
+HANDLER(checkboxMouseMoveHandler)
+{
+ // don't actually check to see if the mouse is in the checkbox rect
+ // if there's scrolling without mouse movement, that will change
+ // instead, drawCell() will handle it
+ if (!t->checkboxMouseOverLast) {
+ t->checkboxMouseOverLast = TRUE;
+ retrack(t);
+ } else
+ redrawCheckboxRect(t, t->checkboxMouseOverLastPoint);
+ t->checkboxMouseOverLastPoint = lParam;
+ redrawCheckboxRect(t, t->checkboxMouseOverLastPoint);
+ *lResult = 0;
+ return TRUE;
+}
+
+HANDLER(checkboxMouseLeaveHandler)
+{
+ if (t->checkboxMouseOverLast)
+ redrawCheckboxRect(t, t->checkboxMouseOverLastPoint);
+ // TODO remember what I wanted to do here in the case of a held mouse button
+ t->checkboxMouseOverLast = FALSE;
+ *lResult = 0;
+ return TRUE;
+}
+
+HANDLER(checkboxMouseDownHandler)
+{
+ struct rowcol rc;
+ RECT r;
+ POINT pt;
+
+ rc = lParamToRowColumn(t, lParam);
+ if (rc.row == -1 || rc.column == -1)
+ return FALSE;
+ if (t->columnTypes[rc.column] != tableColumnCheckbox)
+ return FALSE;
+ if (!rowColumnToClientRect(t, rc, &r))
+ return FALSE;
+ toCheckboxRect(t, &r, 0);
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ if (PtInRect(&r, pt) == 0)
+ return FALSE;
+ t->checkboxMouseDown = TRUE;
+ t->checkboxMouseDownRow = rc.row;
+ t->checkboxMouseDownColumn = rc.column;
+ // TODO redraw the whole cell?
+ if (InvalidateRect(t->hwnd, &r, TRUE) == 0)
+ panic("error redrawing Table checkbox after mouse down");
+ *lResult = 0;
+ return TRUE;
+}
+
+// TODO get rid of this
+struct rowcol lastCheckbox;
+
+HANDLER(checkboxMouseUpHandler)
+{
+ struct rowcol rc;
+ RECT r;
+ POINT pt;
+
+ if (!t->checkboxMouseDown)
+ return FALSE;
+ // the logic behind goto wrongUp is that the mouse must be released on the same checkbox
+ rc = lParamToRowColumn(t, lParam);
+ if (rc.row == -1 || rc.column == -1)
+ goto wrongUp;
+ if (rc.row != t->checkboxMouseDownRow || rc.column != t->checkboxMouseDownColumn)
+ goto wrongUp;
+ if (t->columnTypes[rc.column] != tableColumnCheckbox)
+ goto wrongUp;
+ if (!rowColumnToClientRect(t, rc, &r))
+ goto wrongUp;
+ toCheckboxRect(t, &r, 0);
+ pt.x = GET_X_LPARAM(lParam);
+ pt.y = GET_Y_LPARAM(lParam);
+ if (PtInRect(&r, pt) == 0)
+ goto wrongUp;
+ // TODO send toggle event
+lastCheckbox = rc;
+ t->checkboxMouseDown = FALSE;
+ // TODO redraw the whole cell?
+ if (InvalidateRect(t->hwnd, &r, TRUE) == 0)
+ panic("error redrawing Table checkbox after mouse up");
+ *lResult = 0;
+ return TRUE;
+wrongUp:
+ if (t->checkboxMouseDown) {
+ rc.row = t->checkboxMouseDownRow;
+ rc.column = t->checkboxMouseDownColumn;
+ if (rowColumnToClientRect(t, rc, &r))
+ // TODO only the checkbox rect?
+ if (InvalidateRect(t->hwnd, &r, TRUE) == 0)
+ panic("error redrawing Table checkbox rect for aborted mouse up event");
+ }
+ // if we landed on another checkbox, be sure to draw that one too
+ if (t->checkboxMouseOverLast)
+ redrawCheckboxRect(t, t->checkboxMouseOverLastPoint);
+ t->checkboxMouseDown = FALSE;
+ return FALSE; // TODO really?
+}