diff options
Diffstat (limited to 'wintable/coord.h')
| -rw-r--r-- | wintable/coord.h | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/wintable/coord.h b/wintable/coord.h new file mode 100644 index 0000000..52a70c9 --- /dev/null +++ b/wintable/coord.h @@ -0,0 +1,164 @@ +// 4 december 2014 + +// TODO find a better place for these (metrics.h?) +static LONG textHeight(struct table *t, HDC dc, BOOL select) +{ + BOOL release; + HFONT prevfont, newfont; + TEXTMETRICW tm; + + release = FALSE; + if (dc == NULL) { + dc = GetDC(t->hwnd); + if (dc == NULL) + panic("error getting Table DC for rowHeight()"); + release = TRUE; + } + if (select) + prevfont = selectFont(t, dc, &newfont); + if (GetTextMetricsW(dc, &tm) == 0) + panic("error getting text metrics for rowHeight()"); + if (select) + deselectFont(dc, prevfont, newfont); + if (release) + if (ReleaseDC(t->hwnd, dc) == 0) + panic("error releasing Table DC for rowHeight()"); + return tm.tmHeight; +} + +#define tableImageWidth() GetSystemMetrics(SM_CXSMICON) +#define tableImageHeight() GetSystemMetrics(SM_CYSMICON) + +// TODO omit column types that are not present? +static LONG rowHeight(struct table *t, HDC dc, BOOL select) +{ + LONG height; + LONG tmHeight; + + height = tableImageHeight(); // start with this to avoid two function calls + tmHeight = textHeight(t, dc, select); + if (height < tmHeight) + height = tmHeight; + if (height < t->checkboxHeight) + height = t->checkboxHeight; + return height; +} + +#define rowht(t) rowHeight(t, NULL, TRUE) + +struct rowcol { + intptr_t row; + intptr_t column; +}; + +static struct rowcol clientCoordToRowColumn(struct table *t, POINT pt) +{ + RECT r; + struct rowcol rc; + intptr_t i; + + if (GetClientRect(t->hwnd, &r) == 0) + panic("error getting Table client rect in clientCoordToRowColumn()"); + r.top += t->headerHeight; + if (PtInRect(&r, pt) == 0) + goto outside; + + // the row is easy + pt.y -= t->headerHeight; + rc.row = (pt.y / rowht(t)) + t->vscrollpos; + if (rc.row >= t->count) + goto outside; + + // the column... not so much + // we scroll p.x, then subtract column widths until we cross the left edge of the control + pt.x += t->hscrollpos; + rc.column = 0; + for (i = 0; i < t->nColumns; i++) { + pt.x -= columnWidth(t, i); + // use <, not <=, here: + // assume r.left and t->hscrollpos == 0; + // given the first column is 100 wide, + // pt.x == 0 (first pixel of col 0) -> p.x - 100 == -100 < 0 -> break + // pt.x == 99 (last pixel of col 0) -> p.x - 100 == -1 < 0 -> break + // pt.x == 100 (first pixel of col 1) -> p.x - 100 == 0 >= 0 -> next column + if (pt.x < r.left) + break; + rc.column++; + } + if (rc.column >= t->nColumns) + goto outside; + + return rc; + +outside: + rc.row = -1; + rc.column = -1; + return rc; +} + +// same as client coordinates, but stored in a lParam (like the various mouse messages provide) +static struct rowcol lParamToRowColumn(struct table *t, LPARAM lParam) +{ + POINT pt; + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + return clientCoordToRowColumn(t, pt); +} + +// returns TRUE if the row is visible (even partially visible) and thus has a rectangle in the client area; FALSE otherwise +static BOOL rowColumnToClientRect(struct table *t, struct rowcol rc, RECT *r) +{ + RECT client; + RECT out; // don't change r if we return FALSE + LONG height; + intptr_t xpos; + intptr_t i; + + if (rc.row < t->vscrollpos) + return FALSE; + rc.row -= t->vscrollpos; // align with client.top + + if (GetClientRect(t->hwnd, &client) == 0) + panic("error getting Table client rect in rowColumnToClientRect()"); + client.top += t->headerHeight; + + height = rowht(t); + out.top = client.top + (rc.row * height); + if (out.top >= client.bottom) // >= because RECT.bottom is the first pixel outside the rectangle + return FALSE; + out.bottom = out.top + height; + + // and again the columns are the hard part + // so we start with client.left - t->hscrollpos, then keep adding widths until we get to the column we want + xpos = client.left - t->hscrollpos; + for (i = 0; i < rc.column; i++) + xpos += columnWidth(t, i); + // did we stray too far to the right? if so it's not visible + if (xpos >= client.right) // >= because RECT.right is the first pixel outside the rectangle + return FALSE; + out.left = xpos; + out.right = xpos + columnWidth(t, rc.column); + // and is this too far to the left? + if (out.right < client.left) // < because RECT.left is the first pixel inside the rect + return FALSE; + + *r = out; + return TRUE; +} + +// TODO idealCoordToRowColumn/rowColumnToIdealCoord? + +static void toCellContentRect(struct table *t, RECT *r, LRESULT xoff, intptr_t width, intptr_t height) +{ + if (xoff == 0) + xoff = SendMessageW(t->header, HDM_GETBITMAPMARGIN, 0, 0); + r->left += xoff; + if (width != 0) + r->right = r->left + width; + if (height != 0) + // TODO vertical center + r->bottom = r->top + height; +} + +#define toCheckboxRect(t, r, xoff) toCellContentRect(t, r, xoff, t->checkboxWidth, t->checkboxHeight) |
