Started splitting the new Windows Table code into more manageable chunks.

This commit is contained in:
Pietro Gagliardi 2014-11-29 18:29:50 -05:00
parent 6cce68074a
commit d23cdd7682
5 changed files with 376 additions and 367 deletions

25
wintable/api.h Normal file
View File

@ -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);
}

114
wintable/hscroll.h Normal file
View File

@ -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);
}

View File

@ -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)
{

136
wintable/util.h Normal file
View File

@ -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);
}

97
wintable/vscroll.h Normal file
View File

@ -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);
}