/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/
/****************************** Module Header *******************************
* Module Name: TABLE.C
*
* Standard table class and main interface functions.
*
* Functions:
*
* gtab_init()
* gtab_deltools()
* gtab_sendtq()
* gtab_freelinedata()
* gtab_wndproc()
* gtab_createtools()
* gtab_deltable()
* gtab_buildtable()
* gtab_setsize()
* gtab_newsize()
* gtab_calcwidths()
* gtab_alloclinedata()
* gtab_invallines()
* gtab_append()
*
* Comments:
*
* The table class communicates with its 'owner' window to
* get the layout info and the data to display. The owner window handle
* can be sent as the lParam in CreateWindow - if not, the parent window will
* be used.
*
* After creating the window, send it a TM_NEWID message, with a 'data id'
* as the lParam. This is any non-zero 32-bit value. The table will then call
* back to its owner window to find out how many rows/columns, then to fetch
* the name/properties of each column, and finally to get the data to display.
*
* Send TM_NEWID of 0 to close (or destroy the window) - wait for TQ_CLOSE
* (in either case) before discarding data. Send
* TM_REFRESH if data or row-count changes; send TM_NEWLAYOUT if column
* properties or nr cols change etc - this is the same as sending TM_NEWID
* except that no TQ_CLOSE happens on TM_NEWLAYOUT.
*
* TQ_SELECT is sent whenever the current selection changes. TQ_ENTER is sent
* when enter or double-click occurs.
*
****************************************************************************/
#include <windows.h>
#include <commdlg.h>
#include "gutils.h"
#include "table.h"
#include "tpriv.h"
/* global tools etc */
extern HANDLE hLibInst;
HANDLE hVertCurs;
HANDLE hNormCurs;
HPEN hpenDotted;
UINT gtab_msgcode;
/* function prototypes */
long FAR PASCAL gtab_wndproc(HWND, UINT, UINT, long);
void gtab_createtools(void);
void gtab_deltable(HWND hwnd, lpTable ptab);
lpTable gtab_buildtable(HWND hwnd, DWORD id);
void gtab_setsize(HWND hwnd, lpTable ptab);
void gtab_newsize(HWND hwnd, lpTable ptab);
void gtab_calcwidths(HWND hwnd, lpTable ptab);
BOOL gtab_alloclinedata(HWND hwnd, HANDLE heap, lpTable ptab);
void gtab_invallines(HWND hwnd, lpTable ptab, int start, int count);
void gtab_append(HWND hwnd, lpTable ptab, int rows, DWORD id);
/***************************************************************************
* Function: gtab_init
*
* Purpose:
*
* Initialise window class - called from DLL main init
*/
void
gtab_init(void)
{
WNDCLASS wc;
gtab_createtools();
gtab_msgcode = RegisterWindowMessage(TableMessage);
wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
wc.lpfnWndProc = gtab_wndproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = WLTOTAL;
wc.hInstance = hLibInst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszClassName = TableClassName;
wc.lpszMenuName = NULL;
RegisterClass(&wc);
}
/***************************************************************************
* Function: gtab_createtools
*
* Purpose:
*
* Load cursors and pens.
*/
void
gtab_createtools(void)
{
hVertCurs = LoadCursor(hLibInst, "VertLine");
hNormCurs = LoadCursor(NULL, IDC_ARROW);
hpenDotted = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
}
/***************************************************************************
* Function: gtab_deltools
*
* Purpose:
*
* Delete pen
*/
void
gtab_deltools(void)
{
DeleteObject(hpenDotted);
}
/***************************************************************************
* Function: gtab_wndproc
*
* Purpose:
*
* Window procedure for table
*/
long FAR PASCAL
gtab_wndproc(HWND hwnd, UINT msg, UINT wParam, long lParam)
{
CREATESTRUCT FAR * csp;
HWND hOwner;
lpTable ptab;
HANDLE hHeap;
PAINTSTRUCT ps;
int y, y2, i;
HDC hDC;
lpTableSelection pselect;
long oldtop;
long change;
switch(msg) {
case WM_CREATE:
/* create window. set the wnd extra bytes to
* contain the owner window, a heap and a null table.
* Owner window is either in lParam or the parent.
* Then wait for TM_NEWID.
*/
csp = (CREATESTRUCT FAR *) lParam;
if (csp->lpCreateParams == NULL) {
hOwner = GetParent(hwnd);
} else {
hOwner = (HWND) (long) csp->lpCreateParams;
}
ptab = NULL;
hHeap = gmem_init();
SetWindowLong(hwnd, WL_TABLE, (LONG) ptab);
SetWindowLong(hwnd, WW_OWNER, (LONG) hOwner);
SetWindowLong(hwnd, WW_HEAP, (LONG) hHeap);
SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
break;
case TM_NEWID:
/* complete change of table.
* close old table, discard memory and
* build new table
*/
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_sendtq(hwnd, TQ_CLOSE, ptab->hdr.id);
gtab_deltable(hwnd, ptab);
SetCursor(hNormCurs);
SetWindowLong(hwnd, WL_TABLE, 0);
}
if ( (ptab = gtab_buildtable(hwnd, lParam)) != NULL) {
SetWindowLong(hwnd, WL_TABLE, (long) (LPSTR) ptab);
gtab_setsize(hwnd, ptab);
} else {
SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
}
InvalidateRect(hwnd, NULL, TRUE);
break;
case TM_NEWLAYOUT:
/* change of layout but for same id. no TQ_CLOSE,
* but otherwise same as TM_NEWID
*/
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_deltable(hwnd, ptab);
SetCursor(hNormCurs);
SetWindowLong(hwnd, WL_TABLE, 0);
}
if ( (ptab = gtab_buildtable(hwnd, lParam)) != NULL) {
SetWindowLong(hwnd, WL_TABLE, (long) (LPSTR) ptab);
gtab_setsize(hwnd, ptab);
} else {
SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
}
InvalidateRect(hwnd, NULL, TRUE);
break;
case TM_REFRESH:
/* data in table has changed. nrows may have
* changed. ncols and col types have not changed
*/
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_newsize(hwnd, ptab);
}
InvalidateRect(hwnd, NULL, TRUE);
break;
case TM_SELECT:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
pselect = (lpTableSelection) lParam;
/*
* we only support TM_SINGLE - so force the
* selection to a single row or cell.
*/
gtab_select(hwnd, ptab, pselect->startrow,
pselect->startcell,
1,
(ptab->hdr.selectmode & TM_ROW) ?
ptab->hdr.ncols : 1,
TRUE);
gtab_showsel_middle(hwnd, ptab);
}
break;
case TM_PRINT:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
if (ptab != NULL) {
gtab_print(hwnd, ptab, hHeap, (lpPrintContext) lParam);
return(TRUE);
}
case TM_TOPROW:
/* return top row. if wParam is TRUE, set lParam
* as the new toprow
*/
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab == NULL) {
return(0);
}
oldtop = ptab->toprow;
if ((wParam) && (lParam < ptab->hdr.nrows)) {
change = lParam - ptab->toprow;
change -= ptab->hdr.fixedrows;
gtab_dovscroll(hwnd, ptab, change);
}
return(oldtop);
case TM_ENDROW:
/* return the last visible row in the window */
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab == NULL) {
return(0);
}
return(ptab->nlines + ptab->toprow - 1);
case TM_APPEND:
/* new rows have been added to the end of the
* table, but the rest of the table has no
* been change. Update without forcing redraw of
* everything.
* lParam contains the new total nr of rows
*/
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_append(hwnd, ptab, wParam, lParam);
return(TRUE);
}
break;
case WM_SIZE:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_setsize(hwnd, ptab);
}
break;
case WM_DESTROY:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_sendtq(hwnd, TQ_CLOSE, ptab->hdr.id);
gtab_deltable(hwnd, ptab);
}
hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
gmem_freeall(hHeap);
break;
case WM_PAINT:
hDC = BeginPaint(hwnd, &ps);
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
/* separator lines between fixed rows/columns
* (ie headers) and the rest - if enabled
*/
/* paint here first for good impression,
* and again after to clean up!!
*/
if (ptab->hdr.vseparator) {
gtab_vsep(hwnd, ptab, hDC);
}
if (ptab->hdr.hseparator) {
gtab_hsep(hwnd, ptab, hDC);
}
/* paint only the rows that need painting */
for (i = 0; i < ptab->nlines; i++) {
y = ptab->pdata[i].linepos.start;
y2 = y + ptab->pdata[i].linepos.size;
if ( (y <= ps.rcPaint.bottom) &&
(y2 >= ps.rcPaint.top)) {
gtab_paint(hwnd, hDC, ptab, i);
}
}
if (ptab->hdr.vseparator) {
gtab_vsep(hwnd, ptab, hDC);
}
if (ptab->hdr.hseparator) {
gtab_hsep(hwnd, ptab, hDC);
}
if (ptab->selvisible) {
gtab_invertsel(hwnd, ptab, hDC);
}
}
EndPaint(hwnd, &ps);
break;
case WM_HSCROLL:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_msg_hscroll(hwnd, ptab,
GET_SCROLL_OPCODE(wParam, lParam),
GET_SCROLL_POS(wParam, lParam));
}
break;
case WM_VSCROLL:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_msg_vscroll(hwnd, ptab,
GET_SCROLL_OPCODE(wParam, lParam),
GET_SCROLL_POS(wParam, lParam));
}
break;
case WM_MOUSEMOVE:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_move(hwnd, ptab, LOWORD(lParam), HIWORD(lParam));
} else {
SetCursor(hNormCurs);
}
break;
case WM_LBUTTONDOWN:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_press(hwnd, ptab, LOWORD(lParam), HIWORD(lParam));
}
break;
case WM_LBUTTONUP:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_release(hwnd, ptab,
LOWORD(lParam), HIWORD(lParam));
}
break;
case WM_LBUTTONDBLCLK:
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
gtab_dblclick(hwnd, ptab,
LOWORD(lParam), HIWORD(lParam));
}
break;
case WM_KEYDOWN:
/* handle key presses for cursor movement about
* the table, and return/space for selection.
* Any key we don't handle is passed to the owner window
* for him to handle.
* The table window should have the focus
*/
ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
if (ptab != NULL) {
if (gtab_key(hwnd, ptab, wParam) != 0) {
/* pass key to owner since
* we don't know what to do with it
*/
hOwner = (HANDLE) GetWindowLong(hwnd, WW_OWNER);
return(SendMessage(hOwner, WM_KEYDOWN,
wParam, lParam));
} else {
return(0);
}
}
break;
default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
return(TRUE);
}
/***************************************************************************
* Function: gtab_sendtq
*
* Purpose:
*
* Send a table-query message to the owner window. Returns message
* value.
*/
long
gtab_sendtq(HWND hwnd, UINT cmd, long lParam)
{
HWND hOwner;
hOwner = (HANDLE) GetWindowLong(hwnd, WW_OWNER);
return (SendMessage(hOwner, gtab_msgcode, cmd, lParam));
}
/***************************************************************************
* Function: gtab_freelinedata
*
* Purpose:
*
* Free the memory allocated for the array of lines (each containing
* an array of Cells, each containing an array of chars for the actual
* data). Called on any occasion that would change the number of visible lines
*/
void
gtab_freelinedata(HANDLE hHeap, lpTable ptab)
{
int i, j, ncols;
lpCellData cd;
ncols = ptab->hdr.ncols;
/* for each line */
for(i = 0; i < ptab->nlines; i++) {
/* for each cell */
for (j = 0; j < ncols; j++) {
/* free up the actual text space */
cd = &ptab->pdata[i].pdata[j];
gmem_free(hHeap, (LPSTR) cd->ptext, cd->nchars);
}
/* dealloc array of CellData */
gmem_free(hHeap, (LPSTR) ptab->pdata[i].pdata,
sizeof(CellData) * ncols);
}
/* de-alloc array of linedatas */
gmem_free(hHeap, (LPSTR) ptab->pdata,
sizeof(LineData) * ptab->nlines);
ptab->pdata = NULL;
}
/***************************************************************************
* Function: gtab_alloclinedata
*
* Purpose:
*
* Allocate and init array of linedatas (include cell array
* and text for each cell)
*/
BOOL
gtab_alloclinedata(HWND hwnd, HANDLE heap, lpTable ptab)
{
lpLineData pline;
lpCellData cd;
int i, j;
ptab->pdata = (lpLineData) gmem_get(heap,
sizeof(LineData) * ptab->nlines);
if (ptab->pdata == NULL) {
return(FALSE);
}
for (i = 0; i < ptab->nlines; i++) {
pline = &ptab->pdata[i];
pline->linepos.size = ptab->rowheight;
pline->pdata = (lpCellData) gmem_get(heap,
sizeof(CellData) * ptab->hdr.ncols);
if (pline->pdata == NULL) {
return(FALSE);
}
for (j = 0; j < ptab->hdr.ncols; j++) {
cd = &pline->pdata[j];
cd->props.valid = 0;
cd->flags = 0;
cd->nchars = ptab->pcolhdr[j].nchars;
if (cd->nchars > 0) {
cd->ptext = gmem_get(heap, cd->nchars);
if (cd->ptext == NULL) {
return(FALSE);
}
}
}
}
}
/***************************************************************************
* Function: gtab_deltable
*
* Purpose:
*
* Free up all table data structures. Called for new layout or new data.
*/
void
gtab_deltable(HWND hwnd, lpTable ptab)
{
HANDLE hHeap;
int ncols;
if (ptab == NULL) {
return;
}
hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
ncols = ptab->hdr.ncols;
if (ptab->pcolhdr != NULL) {
gmem_free(hHeap, (LPSTR) ptab->pcolhdr,
sizeof(ColProps) * ncols);
}
if (ptab->pcellpos != NULL) {
gmem_free(hHeap, (LPSTR) ptab->pcellpos,
sizeof(CellPos) * ncols);
}
if (ptab->pdata != NULL) {
gtab_freelinedata(hHeap, ptab);
}
gmem_free(hHeap, (LPSTR) ptab, sizeof(Table));
}
/***************************************************************************
* Function: gtab_buildtable
*
* Purpose:
*
* Build up a Table struct (excluding data allocation and
* anything to do with font or window size).
* Return ptr to this or NULL if error
*/
lpTable
gtab_buildtable(HWND hwnd, DWORD id)
{
lpTable ptab;
HANDLE hHeap;
int ncols, i;
ColPropsList cplist;
hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
ptab = (lpTable) gmem_get(hHeap, sizeof(Table));
if (ptab == NULL) {
return(NULL);
}
/* get the row/column count from owner window */
ptab->hdr.id = id;
ptab->hdr.props.valid = 0;
ptab->hdr.sendscroll = FALSE;
if (gtab_sendtq(hwnd, TQ_GETSIZE, (long) (LPSTR)&ptab->hdr) == FALSE) {
return(NULL);
}
ncols = ptab->hdr.ncols;
ptab->pcolhdr = (lpColProps) gmem_get(hHeap, sizeof(ColProps) * ncols);
if (ptab->pcolhdr == NULL) {
/* should prob send TQ_CLOSE at this point */
return(NULL);
}
/* init col properties to default */
for (i=0; i < ncols; i++) {
ptab->pcolhdr[i].props.valid = 0;
ptab->pcolhdr[i].nchars = 0;
}
/* get the column props from owner */
cplist.plist = ptab->pcolhdr;
cplist.id = id;
cplist.startcol = 0;
cplist.ncols = ncols;
gtab_sendtq(hwnd, TQ_GETCOLPROPS, (long) (LPSTR)&cplist);
/* init remaining fields */
ptab->pcellpos = (lpCellPos) gmem_get(hHeap, sizeof(CellPos) * ncols);
if (ptab->pcellpos == NULL) {
return(NULL);
}
ptab->scrollscale = 1;
ptab->scroll_dx = 0;
ptab->toprow = 0;
ptab->pdata = NULL;
ptab->nlines = 0;
ptab->trackmode = TRACK_NONE;
/* we have to notify owner of the current selection
* whenever it is changed
*/
ptab->select.id = id;
gtab_select(hwnd, ptab, 0, 0, 0, 0, TRUE);
/* calc ave height/width, cell widths and min height.
* these change only when cell properties / col count changes -
* ie only on rebuild-header events
*/
gtab_calcwidths(hwnd, ptab);
return(ptab);
}
/***************************************************************************
* Function: gtab_setsize
*
* Purpose:
*
* Set sizes that are based on window size and scroll pos
* set:
* winwidth
* nlines
* cellpos start, clip start/end
* Alloc linedata and init
*/
void
gtab_setsize(HWND hwnd, lpTable ptab)
{
RECT rc;
int nlines;
HANDLE heap;
long range, change;
GetClientRect(hwnd, &rc);
ptab->winwidth = rc.right - rc.left;
nlines = (rc.bottom - rc.top) / ptab->rowheight;
/* nlines is the number of whole lines - add one extra
* for the partial line at the bottom
*/
nlines += 1;
/* alloc space for nlines of data - if nlines has changed */
if (nlines != ptab->nlines) {
heap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
gtab_freelinedata(heap, ptab);
ptab->nlines = nlines;
if (!gtab_alloclinedata(hwnd, heap, ptab)) {
ptab->nlines = 0;
return;
}
}
/* set scroll vertical range */
range = ptab->hdr.nrows - (ptab->nlines - 1);
if (range < 0) {
range = 0;
change = -(ptab->toprow);
} else if (ptab->toprow > range) {
change = range - ptab->toprow;
} else {
change = 0;
}
/* the scroll range must be 16-bits for Win3
* scale until this is true
*/
ptab->scrollscale = 1;
while (range > 32766) {
ptab->scrollscale *= 16;
range /= 16;
}
SetScrollRange(hwnd, SB_VERT, 0, (int) range, TRUE);
gtab_dovscroll(hwnd, ptab, change);
/* set horz scroll range */
range = ptab->rowwidth - ptab->winwidth;
if (range < 0) {
range = 0;
change = -(ptab->scroll_dx);
} else if (ptab->scroll_dx > range) {
change = range - ptab->scroll_dx;
} else {
change = 0;
}
/* horz scroll range will always be < 16 bits */
SetScrollRange(hwnd, SB_HORZ, 0, (int) range, TRUE);
gtab_dohscroll(hwnd, ptab, change);
}
/***************************************************************************
* Function: gtab_calcwidths
*
* Purpose:
*
* Set column widths/height and totals (based on column props)
* - no assumption of window size (see gtab_setsize)
* sets avewidth,rowheight,cellpos.size,rowwidth (total of cellpos.size)
*/
void
gtab_calcwidths(HWND hwnd, lpTable ptab)
{
int i, cxtotal, cx, ave;
TEXTMETRIC tm, tmcol;
HDC hdc;
lpProps hdrprops, cellprops;
HFONT hfont;
hdrprops = &ptab->hdr.props;
hdc = GetDC(hwnd);
if (hdrprops->valid & P_FONT) {
hfont = SelectObject(hdc, hdrprops->hFont);
}
GetTextMetrics(hdc, &tm);
if (hdrprops->valid & P_FONT) {
SelectObject(hdc, hfont);
}
ReleaseDC(hwnd, hdc);
/* get width and height of average character */
ptab->avewidth = tm.tmAveCharWidth;
ptab->rowheight = tm.tmHeight + tm.tmExternalLeading;
if (hdrprops->valid & P_HEIGHT) {
ptab->rowheight = hdrprops->height;
}
/* set pixel width of each cell (and add up for row total)
* based on ave width * nr chars, unless P_WIDTH set
*/
cxtotal = 0;
for (i = 0; i < ptab->hdr.ncols; i++) {
cellprops = &ptab->pcolhdr[i].props;
if (cellprops->valid & P_WIDTH) {
cx = cellprops->width;
} else if (hdrprops->valid & P_WIDTH) {
cx = hdrprops->width;
} else {
if (cellprops->valid & P_FONT) {
hdc = GetDC(hwnd);
hfont = SelectObject(hdc, cellprops->hFont);
GetTextMetrics(hdc, &tmcol);
SelectObject(hdc, hfont);
ReleaseDC(hwnd, hdc);
ave = tmcol.tmAveCharWidth;
} else {
ave = ptab->avewidth;
}
/* ave width * nchars */
cx = ptab->pcolhdr[i].nchars + 1;
cx *= ave;
}
/* add 2 pixels for box lines */
cx += 2;
ptab->pcellpos[i].size = cx;
cxtotal += cx;
}
ptab->rowwidth = cxtotal;
}
/***************************************************************************
* Function: gtab_newsize
*
* Purpose:
*
* Called when row data + possible nrows changes.
* other changes are ignored
*/
void
gtab_newsize(HWND hwnd, lpTable ptab)
{
TableHdr hdr;
/* get new row count */
hdr = ptab->hdr;
gtab_sendtq(hwnd, TQ_GETSIZE, (long) (LPSTR) &hdr);
if (hdr.nrows != ptab->hdr.nrows) {
ptab->hdr.nrows = hdr.nrows;
gtab_setsize(hwnd, ptab);
}
gtab_invallines(hwnd, ptab, 0, ptab->nlines);
InvalidateRect(hwnd, NULL, TRUE);
}
void
gtab_invallines(HWND hwnd, lpTable ptab, int start, int count)
{
int i, j;
for (i = start; i < start + count; i++) {
for (j = 0; j < ptab->hdr.ncols; j++) {
ptab->pdata[i].pdata[j].flags = 0;
}
}
}
/***************************************************************************
* Function: gtab_append
*
* Purpose:
*
* New rows have been added to the table. Adjust the scroll range and
* position, and redraw the rows if the end of the table is currently
* visible.
* rows = the new total row count.
*/
void
gtab_append(HWND hwnd, lpTable ptab, int rows, DWORD id)
{
long range;
long oldrows;
int line, nupdates;
RECT rc;
/* change to the new id */
ptab->hdr.id = id;
ptab->select.id = id;
/* update the header, but remember the old nr of rows
* so we know where to start updating
*/
oldrows = ptab->hdr.nrows;
/* check that the new nr of rows is not smaller. this is
* illegal at this point and should be ignored
*/
if (oldrows >= rows) {
return;
}
ptab->hdr.nrows = rows;
/* set the vertical scroll range */
range = rows - (ptab->nlines - 1);
if (range < 0) {
range = 0;
}
/* force the scroll range into 16-bits for win 3.1 */
ptab->scrollscale = 1;
while (range > 32766) {
ptab->scrollscale *= 16;
range /= 16;
}
/* now set the scroll bar range and position */
SetScrollRange(hwnd, SB_VERT, 0, (int) range, TRUE);
if (range > 0) {
SetScrollPos(hwnd, SB_VERT,
(int) (ptab->toprow / ptab->scrollscale), TRUE);
}
/* calculate which screen lines need to be updated - find what
* screen line the start of the new section is at
*/
line = gtab_rowtoline(hwnd, ptab, oldrows);
if (line == -1) {
/* not visible -> no more to do */
return;
}
/* how many lines to update - rest of screen or nr of
* new lines if less than rest of screen
*/
nupdates = min((ptab->nlines - line), (int)(rows - oldrows));
/* invalidate the screen line buffers to indicate data
* needs to be refetch from parent window
*/
gtab_invallines(hwnd, ptab, line, nupdates);
/* calculate the region of the screen to be repainted -
* left and right are same as window. top and bottom
* need to be calculated from screen line height
*/
GetClientRect(hwnd, &rc);
rc.top += line * ptab->rowheight;
rc.bottom = rc.top + (nupdates * ptab->rowheight);
/* force a repaint of the updated region */
InvalidateRect(hwnd, &rc, TRUE);
}