diff options
| author | Pietro Gagliardi <[email protected]> | 2015-01-06 17:27:41 -0500 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2015-01-06 17:27:41 -0500 |
| commit | 22123fb676d26d309d428a892c453e35e684e367 (patch) | |
| tree | 9552f1a91a8b8995065e74573e8f59f448f2b08c /wintable/checkboxes.h | |
| parent | 720049bfd3c6d205859f3150dee33dee78dc3dc3 (diff) | |
Merged wintable/new/ into wintable/.
Diffstat (limited to 'wintable/checkboxes.h')
| -rw-r--r-- | wintable/checkboxes.h | 250 |
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? +} |
