/* * set selection and send 'TQ_ENTER' event to owner */ void gtab_enter(HWND hwnd, lpTable ptab, long row, long col, long nrows, long ncells) { /* clear existing sel if valid and visible */ if ((ptab->select.nrows != 0) && (ptab->selvisible == TRUE)) { /* only clear sel if it is different from the new one */ if ((ptab->select.startrow != row) || (ptab->select.startcell != col) || (ptab->select.nrows != nrows) || (ptab->select.ncells != ncells)) { gtab_invertsel(hwnd, ptab, NULL); ptab->selvisible = FALSE; } } /* set select fields and send TQ_ENTER */ if (row < ptab->hdr.nrows) { ptab->select.startrow = row; ptab->select.startcell = col; ptab->select.nrows = nrows; ptab->select.ncells = ncells; } else { ptab->select.nrows = 0; ptab->select.startrow = 0; ptab->select.startcell = 0; ptab->select.ncells = 0; } /* paint in selection */ if (nrows != 0) { if (!ptab->selvisible) { gtab_invertsel(hwnd, ptab, NULL); ptab->selvisible = TRUE; } /* do this at end because it could cause a layout-change */ gtab_sendtq(hwnd, TQ_ENTER, (LPARAM) &ptab->select); } else { if (ptab->selvisible) { gtab_invertsel(hwnd, ptab, NULL); } ptab->selvisible = FALSE; } }
/* 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, (LPARAM) &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, FALSE); }
/* * update a contiguous block of invalid cells by calling the owner window */ void gtab_updatecontig(HWND hwnd, lpTable ptab, int line, int cell1, int count) { lpLineData pline; lpCellData cd; CellDataList list; lpProps colprops; int i; pline = &ptab->pdata[line]; cd = &pline->pdata[cell1]; list.id = ptab->hdr.id; list.row = gtab_linetorow(hwnd, ptab, line); list.startcell = cell1; list.ncells = count; list.plist = cd; /* clear out prop flags */ for (i = 0; i < count; i++) { cd[i].props.valid = 0; if (cd[i].nchars > 0) { cd[i].ptext[0] = '\0'; } } if (list.row < ptab->hdr.nrows) { gtab_sendtq(hwnd, TQ_GETDATA, (long) (LPSTR) &list); } /* for each cell, mark valid and set properties */ for (i = 0; i < count; i++) { cd[i].flags |= CELL_VALID; gtab_delcr(cd[i].ptext); /* fetch properties from hdr and colhdr */ colprops = &ptab->pcolhdr[i + cell1].props; if (!(cd[i].props.valid & P_FCOLOUR)) { if (colprops->valid & P_FCOLOUR) { cd[i].props.valid |= P_FCOLOUR; cd[i].props.forecolour = colprops->forecolour; } else if (ptab->hdr.props.valid & P_FCOLOUR) { cd[i].props.valid |= P_FCOLOUR; cd[i].props.forecolour = ptab->hdr.props.forecolour; } } if (!(cd[i].props.valid & P_BCOLOUR)) { if (colprops->valid & P_BCOLOUR) { cd[i].props.valid |= P_BCOLOUR; cd[i].props.backcolour = colprops->backcolour; } else if (ptab->hdr.props.valid & P_BCOLOUR) { cd[i].props.valid |= P_BCOLOUR; cd[i].props.backcolour = ptab->hdr.props.backcolour; } } if (!(cd[i].props.valid & P_FONT)) { if (colprops->valid & P_FONT) { cd[i].props.valid |= P_FONT; cd[i].props.hFont = colprops->hFont; } else if (ptab->hdr.props.valid & P_FONT) { cd[i].props.valid |= P_FONT; cd[i].props.hFont = ptab->hdr.props.hFont; } } if (!(cd[i].props.valid & P_ALIGN)) { if (colprops->valid & P_ALIGN) { cd[i].props.valid |= P_ALIGN; cd[i].props.alignment = colprops->alignment; } else if (ptab->hdr.props.valid & P_ALIGN) { cd[i].props.valid |= P_ALIGN; cd[i].props.alignment = ptab->hdr.props.alignment; } } if (!(cd[i].props.valid & P_BOX)) { if (colprops->valid & P_BOX) { cd[i].props.valid |= P_BOX; cd[i].props.box = colprops->box; } else if (ptab->hdr.props.valid & P_BOX) { cd[i].props.valid |= P_BOX; cd[i].props.box = ptab->hdr.props.box; } } /* you can't set width/height per cell - this * is ignored at cell level. */ } }
LRESULT gtab_wndproc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { CREATESTRUCT FAR * csp; HWND hOwner; lpTable ptab; lpTableSelection pselect; long oldtop; long change; switch (msg) { case WM_CREATE: /* create window. set the wnd extra bytes to * contain the owner window 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) csp->lpCreateParams; } ptab = NULL; SetWindowLongPtr(hwnd, WL_TABLE, (LONG_PTR) ptab); SetWindowLongPtr(hwnd, WW_OWNER, (LONG_PTR) hOwner); 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) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_sendtq(hwnd, TQ_CLOSE, ptab->hdr.id); gtab_deltable(hwnd, ptab); SetCursor((HCURSOR)hNormCurs); SetWindowLongPtr(hwnd, WL_TABLE, 0); } if ( (ptab = gtab_buildtable(hwnd, (DWORD_PTR)lParam)) != NULL) { SetWindowLongPtr(hwnd, WL_TABLE, (LONG_PTR) 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) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_deltable(hwnd, ptab); SetCursor((HCURSOR)hNormCurs); SetWindowLongPtr(hwnd, WL_TABLE, 0); } if ( (ptab = gtab_buildtable(hwnd, (DWORD_PTR)lParam)) != NULL) { SetWindowLongPtr(hwnd, WL_TABLE, (LONG_PTR) 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) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_newsize(hwnd, ptab); gtab_sendtq(hwnd, TQ_SHOWWHITESPACE, (LPARAM) &ptab->show_whitespace); } InvalidateRect(hwnd, NULL, TRUE); break; case TM_SELECT: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { pselect = (lpTableSelection) lParam; gtab_select(hwnd, ptab, pselect->startrow, pselect->startcell, pselect->nrows, pselect->ncells, TRUE); gtab_showsel_middle(hwnd, ptab, pselect->dyRowsFromTop); } break; case TM_GETSELECTION: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { pselect = (lpTableSelection) lParam; *pselect = ptab->select; } break; case TM_PRINT: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { return gtab_print(hwnd, ptab, (lpPrintContext) lParam); } return FALSE; case TM_SETTABWIDTH: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (!ptab) return 0; ptab->tabchars = (int)lParam; InvalidateRect(hwnd, NULL, FALSE); break; case TM_TOPROW: /* return top row. if wParam is TRUE, set lParam * as the new toprow */ ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab == NULL) { return(0); } oldtop = ptab->toprow; if ((wParam) && (lParam < ptab->hdr.nrows)) { change = (long)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) GetWindowLongPtr(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 not * been changed. Update without forcing redraw of * everything. * lParam contains the new total nr of rows */ ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_append(hwnd, ptab, (int) wParam, (DWORD_PTR)lParam); return(TRUE); } break; case WM_SIZE: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_setsize(hwnd, ptab); } break; case WM_ERASEBKGND: return TRUE; case WM_DESTROY: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_sendtq(hwnd, TQ_CLOSE, ptab->hdr.id); gtab_deltable(hwnd, ptab); } break; case WM_SYSCOLORCHANGE: InvalidateRect(hwnd, NULL, TRUE); break; case WM_PAINT: gtab_paint(hwnd); break; case WM_HSCROLL: ptab = (lpTable) GetWindowLongPtr(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) GetWindowLongPtr(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) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_move(hwnd, ptab, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)); } else { SetCursor((HCURSOR)hNormCurs); } break; case WM_LBUTTONDOWN: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_press(hwnd, ptab, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)); } break; case WM_RBUTTONDOWN: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_rightclick(hwnd, ptab, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)); } break; case WM_LBUTTONUP: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_release(hwnd, ptab, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam)); } break; case WM_LBUTTONDBLCLK: ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { gtab_dblclick(hwnd, ptab, (int)(short)LOWORD(lParam), (int)(short)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) GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { if (gtab_key(hwnd, ptab, (int)wParam) != 0) { hOwner = (HWND) GetWindowLongPtr(hwnd, WW_OWNER); return(SendMessage(hOwner, WM_KEYDOWN, wParam, lParam)); } else { return(0); } } break; #ifdef WM_MOUSEWHEEL case WM_MOUSEWHEEL: ptab = (lpTable)GetWindowLongPtr(hwnd, WL_TABLE); if (ptab != NULL) { if (gtab_mousewheel(hwnd,ptab, LOWORD(wParam), (short)HIWORD(wParam))) { hOwner = (HWND)GetWindowLongPtr(hwnd, WW_OWNER); return SendMessage(hOwner, WM_MOUSEWHEEL, wParam, lParam); } } break; #endif default: return(DefWindowProc(hwnd, msg, wParam, lParam)); } return(TRUE); }
/* * 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_PTR id ) { lpTable ptab; int ncols, i; ColPropsList cplist; ptab = (lpTable) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Table)); if (ptab == NULL) { return(NULL); } // get the tab width. most clients will not support this if (gtab_sendtq(hwnd, TQ_TABS, (LPARAM) &ptab->tabchars) == FALSE) { ptab->tabchars = TABWIDTH_DEFAULT; } // get the show whitespace value if (gtab_sendtq(hwnd, TQ_SHOWWHITESPACE, (LPARAM) &ptab->show_whitespace) == FALSE) { ptab->show_whitespace = FALSE; } /* 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, (LPARAM) &ptab->hdr) == FALSE) { return(NULL); } ncols = ptab->hdr.ncols; ptab->pcolhdr = (lpColProps) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 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, (LPARAM) &cplist); /* init remaining fields */ ptab->pcellpos = (lpCellPos) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 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); }
/* * replace old selection with new. Notify owner if bNotify. Change * display to reflect new display. */ void gtab_select( HWND hwnd, lpTable ptab, long row, long col, long nrows, long ncells, BOOL bNotify) { /* if in ROW mode, force col and ncells to reflect the entire row. */ if (ptab->hdr.selectmode & TM_ROW) { col = 0; ncells = ptab->hdr.ncols; } /* clear existing sel if valid and visible */ if ((ptab->select.nrows != 0) && (ptab->selvisible == TRUE)) { /* only clear sel if it is different from the new one */ if ((ptab->select.startrow != row) || (ptab->select.startcell != col) || (ptab->select.nrows != nrows) || (ptab->select.ncells != ncells)) { gtab_invertsel(hwnd, ptab, NULL); ptab->selvisible = FALSE; } } /* set select fields and send TQ_SELECT */ if (row < ptab->hdr.nrows) { ptab->select.startrow = row; ptab->select.startcell = col; ptab->select.nrows = nrows; ptab->select.ncells = ncells; } else { ptab->select.nrows = 0; ptab->select.startrow = 0; ptab->select.startcell = 0; ptab->select.ncells = 0; } if (bNotify) { gtab_sendtq(hwnd, TQ_SELECT, (LPARAM) &ptab->select); } /* paint in selection */ if (nrows != 0) { if (!ptab->selvisible) { gtab_invertsel(hwnd, ptab, NULL); ptab->selvisible = TRUE; } } else { if (ptab->selvisible) { gtab_invertsel(hwnd, ptab, NULL); ptab->selvisible = FALSE; } ptab->selvisible = FALSE; } }
/* * set new vertical scroll pos, * adjust linedata array * set line win-relative start posns & clip top/bottom posns * revise display. */ void gtab_dovscroll(HWND hwnd, lpTable ptab, long change) { int cury, i; long ncopy; lpCellPos cp; LineData ldtemp; RECT rc, rcpaint; long range; long newtop; int newpos; BOOL fWasVisible = FALSE; if (ptab->selvisible) { fWasVisible = TRUE; ptab->selvisible = FALSE; gtab_invertsel(hwnd, ptab, NULL); } range = ptab->hdr.nrows - (ptab->nlines - 1); newtop = ptab->toprow + change; if (range < 0) { range = 0; } if (newtop > range) { change = range - ptab->toprow; } else if (newtop < 0) { change = -(ptab->toprow); } ptab->toprow += change; newpos = (int) (newtop / ptab->scrollscale); SetScrollPos(hwnd, SB_VERT, newpos, TRUE); if (ptab->hdr.sendscroll) { gtab_sendtq(hwnd, TQ_SCROLL, ptab->toprow); } /* adjust data ptrs rather than invalidate, to retain the * data we know is still valid */ if (abs(change) >= ptab->nlines) { gtab_invallines(hwnd, ptab, ptab->hdr.fixedrows, ptab->nlines - ptab->hdr.fixedrows); InvalidateRect(hwnd, NULL, FALSE); change = 0; } else if (change < 0) { /* copy data down */ ncopy = (ptab->nlines - ptab->hdr.fixedrows) - abs(change); for (i = ptab->nlines - 1; i >= (ptab->hdr.fixedrows + abs(change)); i--) { ldtemp = ptab->pdata[i - abs(change)]; ptab->pdata[i - abs(change)] = ptab->pdata[i]; ptab->pdata[i] = ldtemp; } gtab_invallines(hwnd, ptab, ptab->hdr.fixedrows, (int) abs(change)); } else if (change > 0) { ncopy = (ptab->nlines - ptab->hdr.fixedrows) - change; for (i = ptab->hdr.fixedrows; i < (ncopy + ptab->hdr.fixedrows); i++) { ldtemp = ptab->pdata[i + change]; ptab->pdata[i + change] = ptab->pdata[i]; ptab->pdata[i] = ldtemp; } gtab_invallines(hwnd, ptab, (int) ncopy + ptab->hdr.fixedrows, (int) change); } /* scroll window */ GetClientRect(hwnd, &rc); rcpaint = rc; if (change > 0) { rc.top += (int) (change + ptab->hdr.fixedrows) * ptab->rowheight; rcpaint.top = (ptab->hdr.fixedrows * ptab->rowheight); rcpaint.top += rc.bottom - rc.top; } else if (change < 0) { rc.top += (ptab->hdr.fixedrows * ptab->rowheight); rc.bottom += (int) (change * ptab->rowheight); rcpaint.bottom -= rc.bottom - rc.top; } /* loop through each line setting relative posn and clipping */ /* set up all rows - the fixed/moveable difference for * rows is made at fetch-time during painting, when we remember * which absolute row nr to ask for, for a given screen line */ cury = 0; for (i = 0; i < ptab->nlines; i++) { cp = &ptab->pdata[i].linepos; cp->start = cury; cp->clipstart = cury; cp->clipend = cury + cp->size; cury += cp->size; } /* now move and repaint the window */ if (change != 0) { if (rc.top < rc.bottom) { ScrollWindow(hwnd, 0, (int) -(change * ptab->rowheight), &rc, NULL); } // don't repaint the fixed rows rc.top = 0; rc.bottom = ptab->hdr.fixedrows * ptab->rowheight; ValidateRect(hwnd, &rc); /* force repaint now, not just post message for later, * since we want to repaint that line before the next * scroll down occurs */ ValidateRect(hwnd, &rcpaint); RedrawWindow(hwnd, &rcpaint, NULL, RDW_NOERASE | RDW_INVALIDATE | RDW_INTERNALPAINT); } if (fWasVisible) { gtab_invertsel(hwnd, ptab, NULL); ptab->selvisible = TRUE; } }