Started splitting the new Windows Table code into more manageable chunks.
This commit is contained in:
parent
6cce68074a
commit
d23cdd7682
|
@ -0,0 +1,25 @@
|
||||||
|
// 29 november 2014
|
||||||
|
|
||||||
|
static void addColumn(struct table *t, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
HDITEMW item;
|
||||||
|
|
||||||
|
if (((int) wParam) >= nTableColumnTypes)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
t->nColumns++;
|
||||||
|
t->columnTypes = (int *) realloc(t->columnTypes, t->nColumns * sizeof (int));
|
||||||
|
if (t->columnTypes == NULL)
|
||||||
|
abort();
|
||||||
|
t->columnTypes[t->nColumns - 1] = (int) wParam;
|
||||||
|
|
||||||
|
ZeroMemory(&item, sizeof (HDITEMW));
|
||||||
|
item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT;
|
||||||
|
item.cxy = 200; // TODO
|
||||||
|
item.pszText = (WCHAR *) lParam;
|
||||||
|
item.fmt = HDF_LEFT | HDF_STRING;
|
||||||
|
if (SendMessage(t->header, HDM_INSERTITEM, (WPARAM) (t->nColumns - 1), (LPARAM) (&item)) == (LRESULT) (-1))
|
||||||
|
abort();
|
||||||
|
// TODO resize(t)?
|
||||||
|
redrawAll(t);
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
// 29 november 2014
|
||||||
|
|
||||||
|
static void hscrollto(struct table *t, intptr_t newpos)
|
||||||
|
{
|
||||||
|
SCROLLINFO si;
|
||||||
|
RECT scrollArea;
|
||||||
|
|
||||||
|
if (newpos < 0)
|
||||||
|
newpos = 0;
|
||||||
|
if (newpos > (t->width - t->hpagesize))
|
||||||
|
newpos = (t->width - t->hpagesize);
|
||||||
|
|
||||||
|
scrollArea = realClientRect(t);
|
||||||
|
|
||||||
|
// negative because ScrollWindowEx() is "backwards"
|
||||||
|
if (ScrollWindowEx(t->hwnd, -(newpos - t->hpos), 0,
|
||||||
|
&scrollArea, &scrollArea, NULL, NULL,
|
||||||
|
SW_ERASE | SW_INVALIDATE) == ERROR)
|
||||||
|
abort();
|
||||||
|
t->hpos = newpos;
|
||||||
|
// TODO text in header controls doesn't redraw?
|
||||||
|
|
||||||
|
// TODO put this in a separate function? same for vscroll?
|
||||||
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
||||||
|
si.cbSize = sizeof (SCROLLINFO);
|
||||||
|
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
|
||||||
|
si.nPage = t->hpagesize;
|
||||||
|
si.nMin = 0;
|
||||||
|
si.nMax = t->width - 1; // nMax is inclusive
|
||||||
|
si.nPos = t->hpos;
|
||||||
|
SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE);
|
||||||
|
|
||||||
|
// and finally reposition the header
|
||||||
|
repositionHeader(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hscrollby(struct table *t, intptr_t n)
|
||||||
|
{
|
||||||
|
hscrollto(t, t->hpos + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unfortunately horizontal wheel scrolling was only added in Vista
|
||||||
|
|
||||||
|
static void hscroll(struct table *t, WPARAM wParam)
|
||||||
|
{
|
||||||
|
SCROLLINFO si;
|
||||||
|
intptr_t newpos;
|
||||||
|
|
||||||
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
||||||
|
si.cbSize = sizeof (SCROLLINFO);
|
||||||
|
si.fMask = SIF_POS | SIF_TRACKPOS;
|
||||||
|
if (GetScrollInfo(t->hwnd, SB_HORZ, &si) == 0)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
newpos = t->hpos;
|
||||||
|
switch (LOWORD(wParam)) {
|
||||||
|
case SB_LEFT:
|
||||||
|
newpos = 0;
|
||||||
|
break;
|
||||||
|
case SB_RIGHT:
|
||||||
|
newpos = t->width - t->hpagesize;
|
||||||
|
break;
|
||||||
|
case SB_LINELEFT:
|
||||||
|
newpos--;
|
||||||
|
break;
|
||||||
|
case SB_LINERIGHT:
|
||||||
|
newpos++;
|
||||||
|
break;
|
||||||
|
case SB_PAGELEFT:
|
||||||
|
newpos -= t->hpagesize;
|
||||||
|
break;
|
||||||
|
case SB_PAGERIGHT:
|
||||||
|
newpos += t->hpagesize;
|
||||||
|
break;
|
||||||
|
case SB_THUMBPOSITION:
|
||||||
|
newpos = (intptr_t) (si.nPos);
|
||||||
|
break;
|
||||||
|
case SB_THUMBTRACK:
|
||||||
|
newpos = (intptr_t) (si.nTrackPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
hscrollto(t, newpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recomputeHScroll(struct table *t)
|
||||||
|
{
|
||||||
|
HDITEMW item;
|
||||||
|
intptr_t i;
|
||||||
|
int width = 0;
|
||||||
|
RECT r;
|
||||||
|
SCROLLINFO si;
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
width += item.cxy;
|
||||||
|
}
|
||||||
|
t->width = (intptr_t) width;
|
||||||
|
|
||||||
|
if (GetClientRect(t->hwnd, &r) == 0)
|
||||||
|
abort();
|
||||||
|
t->hpagesize = r.right - r.left;
|
||||||
|
|
||||||
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
||||||
|
si.cbSize = sizeof (SCROLLINFO);
|
||||||
|
si.fMask = SIF_PAGE | SIF_RANGE;
|
||||||
|
si.nPage = t->hpagesize;
|
||||||
|
si.nMin = 0;
|
||||||
|
si.nMax = t->width - 1; // - 1 because endpoints inclusive
|
||||||
|
SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE);
|
||||||
|
}
|
371
wintable/main.c
371
wintable/main.c
|
@ -93,373 +93,10 @@ struct table {
|
||||||
int checkboxHeight;
|
int checkboxHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
static LONG rowHeight(struct table *t)
|
#include "util.h"
|
||||||
{
|
#include "api.h"
|
||||||
HFONT thisfont, prevfont;
|
#include "hscroll.h"
|
||||||
TEXTMETRICW tm;
|
#include "vscroll.h"
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void addColumn(struct table *t, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
HDITEMW item;
|
|
||||||
|
|
||||||
if (((int) wParam) >= nTableColumnTypes)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
t->nColumns++;
|
|
||||||
t->columnTypes = (int *) realloc(t->columnTypes, t->nColumns * sizeof (int));
|
|
||||||
if (t->columnTypes == NULL)
|
|
||||||
abort();
|
|
||||||
t->columnTypes[t->nColumns - 1] = (int) wParam;
|
|
||||||
|
|
||||||
ZeroMemory(&item, sizeof (HDITEMW));
|
|
||||||
item.mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT;
|
|
||||||
item.cxy = 200; // TODO
|
|
||||||
item.pszText = (WCHAR *) lParam;
|
|
||||||
item.fmt = HDF_LEFT | HDF_STRING;
|
|
||||||
if (SendMessage(t->header, HDM_INSERTITEM, (WPARAM) (t->nColumns - 1), (LPARAM) (&item)) == (LRESULT) (-1))
|
|
||||||
abort();
|
|
||||||
// TODO resize(t)?
|
|
||||||
redrawAll(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hscrollto(struct table *t, intptr_t newpos)
|
|
||||||
{
|
|
||||||
SCROLLINFO si;
|
|
||||||
RECT scrollArea;
|
|
||||||
|
|
||||||
if (newpos < 0)
|
|
||||||
newpos = 0;
|
|
||||||
if (newpos > (t->width - t->hpagesize))
|
|
||||||
newpos = (t->width - t->hpagesize);
|
|
||||||
|
|
||||||
scrollArea = realClientRect(t);
|
|
||||||
|
|
||||||
// negative because ScrollWindowEx() is "backwards"
|
|
||||||
if (ScrollWindowEx(t->hwnd, -(newpos - t->hpos), 0,
|
|
||||||
&scrollArea, &scrollArea, NULL, NULL,
|
|
||||||
SW_ERASE | SW_INVALIDATE) == ERROR)
|
|
||||||
abort();
|
|
||||||
t->hpos = newpos;
|
|
||||||
// TODO text in header controls doesn't redraw?
|
|
||||||
|
|
||||||
// TODO put this in a separate function? same for vscroll?
|
|
||||||
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
||||||
si.cbSize = sizeof (SCROLLINFO);
|
|
||||||
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
|
|
||||||
si.nPage = t->hpagesize;
|
|
||||||
si.nMin = 0;
|
|
||||||
si.nMax = t->width - 1; // nMax is inclusive
|
|
||||||
si.nPos = t->hpos;
|
|
||||||
SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE);
|
|
||||||
|
|
||||||
// and finally reposition the header
|
|
||||||
repositionHeader(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hscrollby(struct table *t, intptr_t n)
|
|
||||||
{
|
|
||||||
hscrollto(t, t->hpos + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unfortunately horizontal wheel scrolling was only added in Vista
|
|
||||||
|
|
||||||
static void hscroll(struct table *t, WPARAM wParam)
|
|
||||||
{
|
|
||||||
SCROLLINFO si;
|
|
||||||
intptr_t newpos;
|
|
||||||
|
|
||||||
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
||||||
si.cbSize = sizeof (SCROLLINFO);
|
|
||||||
si.fMask = SIF_POS | SIF_TRACKPOS;
|
|
||||||
if (GetScrollInfo(t->hwnd, SB_HORZ, &si) == 0)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
newpos = t->hpos;
|
|
||||||
switch (LOWORD(wParam)) {
|
|
||||||
case SB_LEFT:
|
|
||||||
newpos = 0;
|
|
||||||
break;
|
|
||||||
case SB_RIGHT:
|
|
||||||
newpos = t->width - t->hpagesize;
|
|
||||||
break;
|
|
||||||
case SB_LINELEFT:
|
|
||||||
newpos--;
|
|
||||||
break;
|
|
||||||
case SB_LINERIGHT:
|
|
||||||
newpos++;
|
|
||||||
break;
|
|
||||||
case SB_PAGELEFT:
|
|
||||||
newpos -= t->hpagesize;
|
|
||||||
break;
|
|
||||||
case SB_PAGERIGHT:
|
|
||||||
newpos += t->hpagesize;
|
|
||||||
break;
|
|
||||||
case SB_THUMBPOSITION:
|
|
||||||
newpos = (intptr_t) (si.nPos);
|
|
||||||
break;
|
|
||||||
case SB_THUMBTRACK:
|
|
||||||
newpos = (intptr_t) (si.nTrackPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
hscrollto(t, newpos);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void recomputeHScroll(struct table *t)
|
|
||||||
{
|
|
||||||
HDITEMW item;
|
|
||||||
intptr_t i;
|
|
||||||
int width = 0;
|
|
||||||
RECT r;
|
|
||||||
SCROLLINFO si;
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
width += item.cxy;
|
|
||||||
}
|
|
||||||
t->width = (intptr_t) width;
|
|
||||||
|
|
||||||
if (GetClientRect(t->hwnd, &r) == 0)
|
|
||||||
abort();
|
|
||||||
t->hpagesize = r.right - r.left;
|
|
||||||
|
|
||||||
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
||||||
si.cbSize = sizeof (SCROLLINFO);
|
|
||||||
si.fMask = SIF_PAGE | SIF_RANGE;
|
|
||||||
si.nPage = t->hpagesize;
|
|
||||||
si.nMin = 0;
|
|
||||||
si.nMax = t->width - 1; // - 1 because endpoints inclusive
|
|
||||||
SetScrollInfo(t->hwnd, SB_HORZ, &si, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vscrollto(struct table *t, intptr_t newpos)
|
|
||||||
{
|
|
||||||
SCROLLINFO si;
|
|
||||||
RECT scrollArea;
|
|
||||||
|
|
||||||
if (newpos < 0)
|
|
||||||
newpos = 0;
|
|
||||||
if (newpos > (t->count - t->pagesize))
|
|
||||||
newpos = (t->count - t->pagesize);
|
|
||||||
|
|
||||||
scrollArea = realClientRect(t);
|
|
||||||
|
|
||||||
// negative because ScrollWindowEx() is "backwards"
|
|
||||||
if (ScrollWindowEx(t->hwnd, 0, (-(newpos - t->firstVisible)) * rowHeight(t),
|
|
||||||
&scrollArea, &scrollArea, NULL, NULL,
|
|
||||||
SW_ERASE | SW_INVALIDATE) == ERROR)
|
|
||||||
abort();
|
|
||||||
t->firstVisible = newpos;
|
|
||||||
|
|
||||||
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
||||||
si.cbSize = sizeof (SCROLLINFO);
|
|
||||||
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
|
|
||||||
si.nPage = t->pagesize;
|
|
||||||
si.nMin = 0;
|
|
||||||
si.nMax = t->count - 1; // nMax is inclusive
|
|
||||||
si.nPos = t->firstVisible;
|
|
||||||
SetScrollInfo(t->hwnd, SB_VERT, &si, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vscrollby(struct table *t, intptr_t n)
|
|
||||||
{
|
|
||||||
vscrollto(t, t->firstVisible + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wheelscroll(struct table *t, WPARAM wParam)
|
|
||||||
{
|
|
||||||
int delta;
|
|
||||||
int lines;
|
|
||||||
UINT scrollAmount;
|
|
||||||
|
|
||||||
delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
|
||||||
if (SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &scrollAmount, 0) == 0)
|
|
||||||
abort();
|
|
||||||
if (scrollAmount == WHEEL_PAGESCROLL)
|
|
||||||
scrollAmount = t->pagesize;
|
|
||||||
if (scrollAmount == 0) // no mouse wheel scrolling (or t->pagesize == 0)
|
|
||||||
return;
|
|
||||||
// the rest of this is basically http://blogs.msdn.com/b/oldnewthing/archive/2003/08/07/54615.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/08/11/54624.aspx
|
|
||||||
// see those pages for information on subtleties
|
|
||||||
delta += t->wheelCarry;
|
|
||||||
lines = delta * ((int) scrollAmount) / WHEEL_DELTA;
|
|
||||||
t->wheelCarry = delta - lines * WHEEL_DELTA / ((int) scrollAmount);
|
|
||||||
vscrollby(t, -lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vscroll(struct table *t, WPARAM wParam)
|
|
||||||
{
|
|
||||||
SCROLLINFO si;
|
|
||||||
intptr_t newpos;
|
|
||||||
|
|
||||||
ZeroMemory(&si, sizeof (SCROLLINFO));
|
|
||||||
si.cbSize = sizeof (SCROLLINFO);
|
|
||||||
si.fMask = SIF_POS | SIF_TRACKPOS;
|
|
||||||
if (GetScrollInfo(t->hwnd, SB_VERT, &si) == 0)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
newpos = t->firstVisible;
|
|
||||||
switch (LOWORD(wParam)) {
|
|
||||||
case SB_TOP:
|
|
||||||
newpos = 0;
|
|
||||||
break;
|
|
||||||
case SB_BOTTOM:
|
|
||||||
newpos = t->count - t->pagesize;
|
|
||||||
break;
|
|
||||||
case SB_LINEUP:
|
|
||||||
newpos--;
|
|
||||||
break;
|
|
||||||
case SB_LINEDOWN:
|
|
||||||
newpos++;
|
|
||||||
break;
|
|
||||||
case SB_PAGEUP:
|
|
||||||
newpos -= t->pagesize;
|
|
||||||
break;
|
|
||||||
case SB_PAGEDOWN:
|
|
||||||
newpos += t->pagesize;
|
|
||||||
break;
|
|
||||||
case SB_THUMBPOSITION:
|
|
||||||
newpos = (intptr_t) (si.nPos);
|
|
||||||
break;
|
|
||||||
case SB_THUMBTRACK:
|
|
||||||
newpos = (intptr_t) (si.nTrackPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
vscrollto(t, newpos);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void finishSelect(struct table *t, intptr_t prev)
|
static void finishSelect(struct table *t, intptr_t prev)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
// 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);
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
// 29 november 2014
|
||||||
|
|
||||||
|
static void vscrollto(struct table *t, intptr_t newpos)
|
||||||
|
{
|
||||||
|
SCROLLINFO si;
|
||||||
|
RECT scrollArea;
|
||||||
|
|
||||||
|
if (newpos < 0)
|
||||||
|
newpos = 0;
|
||||||
|
if (newpos > (t->count - t->pagesize))
|
||||||
|
newpos = (t->count - t->pagesize);
|
||||||
|
|
||||||
|
scrollArea = realClientRect(t);
|
||||||
|
|
||||||
|
// negative because ScrollWindowEx() is "backwards"
|
||||||
|
if (ScrollWindowEx(t->hwnd, 0, (-(newpos - t->firstVisible)) * rowHeight(t),
|
||||||
|
&scrollArea, &scrollArea, NULL, NULL,
|
||||||
|
SW_ERASE | SW_INVALIDATE) == ERROR)
|
||||||
|
abort();
|
||||||
|
t->firstVisible = newpos;
|
||||||
|
|
||||||
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
||||||
|
si.cbSize = sizeof (SCROLLINFO);
|
||||||
|
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
|
||||||
|
si.nPage = t->pagesize;
|
||||||
|
si.nMin = 0;
|
||||||
|
si.nMax = t->count - 1; // nMax is inclusive
|
||||||
|
si.nPos = t->firstVisible;
|
||||||
|
SetScrollInfo(t->hwnd, SB_VERT, &si, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vscrollby(struct table *t, intptr_t n)
|
||||||
|
{
|
||||||
|
vscrollto(t, t->firstVisible + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wheelscroll(struct table *t, WPARAM wParam)
|
||||||
|
{
|
||||||
|
int delta;
|
||||||
|
int lines;
|
||||||
|
UINT scrollAmount;
|
||||||
|
|
||||||
|
delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||||
|
if (SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &scrollAmount, 0) == 0)
|
||||||
|
abort();
|
||||||
|
if (scrollAmount == WHEEL_PAGESCROLL)
|
||||||
|
scrollAmount = t->pagesize;
|
||||||
|
if (scrollAmount == 0) // no mouse wheel scrolling (or t->pagesize == 0)
|
||||||
|
return;
|
||||||
|
// the rest of this is basically http://blogs.msdn.com/b/oldnewthing/archive/2003/08/07/54615.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/08/11/54624.aspx
|
||||||
|
// see those pages for information on subtleties
|
||||||
|
delta += t->wheelCarry;
|
||||||
|
lines = delta * ((int) scrollAmount) / WHEEL_DELTA;
|
||||||
|
t->wheelCarry = delta - lines * WHEEL_DELTA / ((int) scrollAmount);
|
||||||
|
vscrollby(t, -lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vscroll(struct table *t, WPARAM wParam)
|
||||||
|
{
|
||||||
|
SCROLLINFO si;
|
||||||
|
intptr_t newpos;
|
||||||
|
|
||||||
|
ZeroMemory(&si, sizeof (SCROLLINFO));
|
||||||
|
si.cbSize = sizeof (SCROLLINFO);
|
||||||
|
si.fMask = SIF_POS | SIF_TRACKPOS;
|
||||||
|
if (GetScrollInfo(t->hwnd, SB_VERT, &si) == 0)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
newpos = t->firstVisible;
|
||||||
|
switch (LOWORD(wParam)) {
|
||||||
|
case SB_TOP:
|
||||||
|
newpos = 0;
|
||||||
|
break;
|
||||||
|
case SB_BOTTOM:
|
||||||
|
newpos = t->count - t->pagesize;
|
||||||
|
break;
|
||||||
|
case SB_LINEUP:
|
||||||
|
newpos--;
|
||||||
|
break;
|
||||||
|
case SB_LINEDOWN:
|
||||||
|
newpos++;
|
||||||
|
break;
|
||||||
|
case SB_PAGEUP:
|
||||||
|
newpos -= t->pagesize;
|
||||||
|
break;
|
||||||
|
case SB_PAGEDOWN:
|
||||||
|
newpos += t->pagesize;
|
||||||
|
break;
|
||||||
|
case SB_THUMBPOSITION:
|
||||||
|
newpos = (intptr_t) (si.nPos);
|
||||||
|
break;
|
||||||
|
case SB_THUMBTRACK:
|
||||||
|
newpos = (intptr_t) (si.nTrackPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
vscrollto(t, newpos);
|
||||||
|
}
|
Loading…
Reference in New Issue