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;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
#include "util.h"
|
||||
#include "api.h"
|
||||
#include "hscroll.h"
|
||||
#include "vscroll.h"
|
||||
|
||||
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