// 29 november 2014 static LONG rowHeight(struct table *t) { HFONT thisfont, prevfont; TEXTMETRICW tm; HDC dc; LONG ret; dc = GetDC(t->hwnd); if (dc == NULL) abort(); thisfont = t->font; // in case WM_SETFONT happens before we return prevfont = (HFONT) SelectObject(dc, thisfont); if (prevfont == NULL) abort(); if (GetTextMetricsW(dc, &tm) == 0) abort(); if (SelectObject(dc, prevfont) != (HGDIOBJ) (thisfont)) abort(); if (ReleaseDC(t->hwnd, dc) == 0) abort(); ret = tm.tmHeight; if (ret < t->imagelistHeight) ret = t->imagelistHeight; if (ret < t->checkboxHeight) ret = t->checkboxHeight; return ret; } static void redrawAll(struct table *t) { if (InvalidateRect(t->hwnd, NULL, TRUE) == 0) abort(); if (UpdateWindow(t->hwnd) == 0) abort(); } static RECT realClientRect(struct table *t) { RECT r; if (GetClientRect(t->hwnd, &r) == 0) abort(); r.top += t->headerHeight; return r; } static void repositionHeader(struct table *t) { RECT r; HDLAYOUT headerlayout; WINDOWPOS headerpos; if (GetClientRect(t->hwnd, &r) == 0) // use the whole client rect abort(); // grow the rectangle to the left to fake scrolling r.left -= t->hpos; headerlayout.prc = &r; headerlayout.pwpos = &headerpos; if (SendMessageW(t->header, HDM_LAYOUT, 0, (LPARAM) (&headerlayout)) == FALSE) abort(); if (SetWindowPos(t->header, headerpos.hwndInsertAfter, headerpos.x, headerpos.y, headerpos.cx, headerpos.cy, headerpos.flags | SWP_SHOWWINDOW) == 0) abort(); t->headerHeight = headerpos.cy; } // this counts partially visible rows // for all fully visible rows use t->pagesize // cliprect and rowHeight must be specified here to avoid recomputing things multiple times static intptr_t lastVisible(struct table *t, RECT cliprect, LONG rowHeight) { intptr_t last; last = ((cliprect.bottom + rowHeight - 1) / rowHeight) + t->firstVisible; if (last >= t->count) last = t->count; return last; } static void redrawRow(struct table *t, intptr_t row) { RECT r; intptr_t height; r = realClientRect(t); height = rowHeight(t); if (row < t->firstVisible || row > lastVisible(t, r, height)) // not visible; don't bother return; r.top = (row - t->firstVisible) * height + t->headerHeight; r.bottom = r.top + height; // keep the width and height the same; it spans the client area anyway if (InvalidateRect(t->hwnd, &r, TRUE) == 0) abort(); if (UpdateWindow(t->hwnd) == 0) abort(); } static intptr_t hitTestColumn(struct table *t, int x) { HDITEMW item; intptr_t i; // TODO count dividers for (i = 0; i < t->nColumns; i++) { ZeroMemory(&item, sizeof (HDITEMW)); item.mask = HDI_WIDTH; if (SendMessageW(t->header, HDM_GETITEM, (WPARAM) i, (LPARAM) (&item)) == FALSE) abort(); if (x < item.cxy) return i; x -= item.cxy; // not yet } // no column return -1; } static void lParamToRowColumn(struct table *t, LPARAM lParam, intptr_t *row, intptr_t *column) { int x, y; LONG h; x = GET_X_LPARAM(lParam); y = GET_Y_LPARAM(lParam); h = rowHeight(t); y += t->firstVisible * h; y -= t->headerHeight; y /= h; if (row != NULL) { *row = y; if (*row >= t->count) *row = -1; } if (column != NULL) *column = hitTestColumn(t, x); }