int gtab_mousewheel(HWND hwnd, lpTable ptab, DWORD fwKeys, int zDelta) { static ULONG uScrollLines = 0; if (fwKeys & MK_MBUTTON) { return 1; } if (uScrollLines == 0) { SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uScrollLines, FALSE); if (uScrollLines == 0) { uScrollLines = 3; } } zDelta /= -WHEEL_DELTA; if (fwKeys & MK_CONTROL) { // // Left-Right scroll // if (ptab->hdr.selectmode & TM_ROW) { if (fwKeys & MK_SHIFT) { zDelta = (zDelta > 0) ? ptab->rowwidth : -ptab->rowwidth; } gtab_dohscroll(hwnd, ptab, ptab->avewidth * zDelta); return 0; } return 1; } if (fwKeys & MK_SHIFT) { // // Page scroll // if (ptab->nlines > 3) { zDelta *= ptab->nlines - 3; } } else { if (uScrollLines) { zDelta *= uScrollLines; zDelta = min(zDelta, ptab->nlines - 3); } } gtab_dovscroll(hwnd, ptab, zDelta); return 0; }
/* * move selection area to visible part of window. argument bToBottom * indicates whether to move the line onto the bottom or the top of the * window if not visible - this affects the smoothness of scrolling * line-by-line. */ void gtab_showsel(HWND hwnd, lpTable ptab, BOOL bToBottom) { int line; long change; line = gtab_rowtoline(hwnd, ptab, ptab->select.startrow); /* move up if last line or not at all visible */ if ( (line < 0) || line == (ptab->nlines - 1)) { change = ptab->select.startrow - ptab->toprow; if (bToBottom) { /* change to bottom of window. subtract 2 not 1 * since nlines includes one line that is only * partly visible */ change -= (ptab->nlines - 2); } change -= ptab->hdr.fixedrows; gtab_dovscroll(hwnd, ptab, change); } /* add support for TM_CELL here! */ }
/* * scroll the window so that if possible, the selected row is in the * middle 60% of the screen so that context around it is visible. */ void gtab_showsel_middle(HWND hwnd, lpTable ptab, long dyRowsFromTop) { int line = ptab->select.startrow; long change = 0; int mid_top, mid_end; BOOL fScroll = FALSE; if (dyRowsFromTop >= 0) { fScroll = TRUE; change = (ptab->select.startrow - dyRowsFromTop) - ptab->toprow; change -= ptab->hdr.fixedrows; } /* is this within the middle 60 % ? */ mid_top = ptab->toprow + (ptab->nlines * 20 / 100); mid_end = ptab->toprow + (ptab->nlines * 80 / 100); if ((line < mid_top + change) || (line > mid_end + change)) { /* no - scroll so that selected line is at * the 20% mark */ fScroll = TRUE; change = (ptab->select.startrow - mid_top); change -= ptab->hdr.fixedrows; } if (fScroll) { gtab_dovscroll(hwnd, ptab, change); } /* again - need code here for TM_CELL mode to ensure that * active cell is horizontally scrolled correctly */ }
/* handle a vscroll message */ void gtab_msg_vscroll(HWND hwnd, lpTable ptab, int opcode, int pos) { long change; switch(opcode) { case SB_THUMBPOSITION: case SB_THUMBTRACK: change = (pos * ptab->scrollscale) - ptab->toprow; break; case SB_LINEUP: change = -1; break; case SB_LINEDOWN: change = 1; break; case SB_PAGEUP: change = - (ptab->nlines - 3); if (change>=0) change = -1; // consider nlines <=3! break; case SB_PAGEDOWN: change = (ptab->nlines - 3); if (change<=0) change = 1; // consider nlines <=3! break; default: return; } gtab_dovscroll(hwnd, ptab, change); }
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); }
/* 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; long change; SCROLLINFO si; 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) { gtab_freelinedata(ptab); ptab->nlines = nlines; if (!gtab_alloclinedata(hwnd, ptab)) { ptab->nlines = 0; return; } } si.cbSize = sizeof(si); si.fMask = SIF_PAGE|SIF_RANGE; si.nMin = 0; /* set scroll vertical range */ si.nMax = ptab->hdr.nrows; si.nPage = ptab->nlines; if (si.nMax < 0) { si.nMax = 0; change = -(ptab->toprow); } else if (ptab->toprow > si.nMax) { change = si.nMax - ptab->toprow; } else { change = 0; } /* the scroll range must be 16-bits for Win3 * scale until this is true */ ptab->scrollscale = 1; while (si.nMax > 32766) { ptab->scrollscale *= 16; si.nMax /= 16; si.nPage /= 16; } if (!si.nPage) si.nPage = 1; SetScrollInfo(hwnd, SB_VERT, &si, TRUE); gtab_dovscroll(hwnd, ptab, change); /* set horz scroll range */ si.nMax = ptab->rowwidth; si.nPage = ptab->winwidth; if (si.nMax < 0) { si.nMax = 0; change = -(ptab->scroll_dx); } else if (ptab->scroll_dx > si.nMax) { change = si.nMax - ptab->scroll_dx; } else { change = 0; } /* horz scroll range will always be < 16 bits */ SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); gtab_dohscroll(hwnd, ptab, change); }
/* called on mouse-move. if tracking - adjust position, if not, * set correct cursor */ void gtab_move(HWND hwnd, lpTable ptab, int x, int y) { BOOL fOK; int line; long row; int col; lpCellPos ppos; switch(ptab->trackmode) { case TRACK_NONE: col = gtab_xtocol(hwnd, ptab, x); if (col == -1) { SetCursor((HCURSOR)hNormCurs); return; } if (gtab_isborder(hwnd, ptab, x, col)) { SetCursor((HCURSOR)hVertCurs); return; } if ( (col > 0) && gtab_isborder(hwnd, ptab, x, col-1)) { SetCursor((HCURSOR)hVertCurs); return; } SetCursor((HCURSOR)hNormCurs); return; case TRACK_CELL: line = gtab_ytoline(hwnd, ptab, y); // we used to only allow drag to extend // the selection if the shift key was down. // this doesn't seem to work as a UI - you expect // to drag and extend. /* if extending selection then * allow scrolling by dragging off window */ if (line < 0) { gtab_dovscroll(hwnd, ptab, -1); line = gtab_ytoline(hwnd, ptab, y); } else if (line >= ptab->nlines) { gtab_dovscroll(hwnd, ptab, 1); line = gtab_ytoline(hwnd, ptab, y); } row = gtab_linetorow(hwnd, ptab, line); col = gtab_xtocol(hwnd, ptab, x); // ignore if before or beyond data if ( (row < ptab->hdr.fixedrows) || (col < ptab->hdr.fixedcols)) { if (ptab->hdr.fixedselectable == FALSE) { return; } } if ((row >= ptab->hdr.nrows) || (col >= ptab->hdr.ncols)) { return; } /* * extend to this new selection end point */ gtab_extendsel(hwnd, ptab, row, col, FALSE); return; case TRACK_COLUMN: /* check that new x is still visible/valid */ ppos = &ptab->pcellpos[ptab->tracknr]; fOK = FALSE; if (ptab->tracknr < ptab->hdr.fixedcols) { if ((x > ppos->start) && (x < ptab->winwidth)) { fOK = TRUE; } } else { if ((x > ppos->clipstart) && (x < ptab->winwidth)) { fOK = TRUE; } } if (fOK == TRUE) { gtab_drawvertline(hwnd, ptab); ptab->trackline1 = x; gtab_drawvertline(hwnd, ptab); } return; } }
/* handle key-down events - scroll windows and/or move selection */ int gtab_key(HWND hwnd, lpTable ptab, int vkey) { long startrow, ncells, startcell; BOOL bControl = FALSE; BOOL bShift = FALSE; if (GetKeyState(VK_CONTROL) & 0x8000) { bControl = TRUE; } if (GetKeyState(VK_SHIFT) & 0x8000) { /* ignore shift key here if TM_MANY -multiple selection flag- is * not selected */ if (ptab->hdr.selectmode & TM_MANY) { bShift = TRUE; } } switch(vkey) { case VK_UP: if (bControl) { /* control-uparrow scrolls window without selection. * the selection is de-selected (to avoid surprises * moving back to it). */ gtab_select(hwnd, ptab, 0, 0, 0, 0, TRUE); gtab_dovscroll(hwnd, ptab, -1); } else { /* uparrow moves selection up one line */ gtab_changesel(hwnd, ptab, -1, 0, FALSE, bShift); } return(0); case VK_DOWN: if (bControl) { /* control downarrow scrolls window without * a selection. */ gtab_select(hwnd, ptab, 0, 0, 0, 0, TRUE); gtab_dovscroll(hwnd, ptab, 1); } else { /* the normal gtab_changesel behaviour is * that if the selected line is not visible now, * we scroll it to the top of the window. This is fine * in most cases but causes unacceptable jumps when * repeatedly scrolling down with the down key. * * Thus we now have an argument to changesel to say * that in this case, if you need to move the line onto * the window, move it to the bottom and not the top */ gtab_changesel(hwnd, ptab, 1, 0, TRUE, bShift); } return(0); case VK_LEFT: /* if cell-selection mode, move left one cell. * otherwise the whole row is selected - scroll * the line left a little */ if (ptab->hdr.selectmode & TM_ROW) { if (bControl) { /* ctrl-left moves to start of line */ gtab_dohscroll(hwnd, ptab, -(ptab->scroll_dx)); } else { gtab_dohscroll(hwnd, ptab, -(ptab->avewidth)); } } else { gtab_changesel(hwnd, ptab, 0, -1, FALSE, bShift); } return(0); case VK_RIGHT: /* if cell-selection mode, move right one cell. * otherwise the whole row is selected - scroll * the line right a little */ if (ptab->hdr.selectmode & TM_ROW) { if (bControl) { /* control-right moves to right end of line */ gtab_dohscroll(hwnd, ptab, ptab->rowwidth - ptab->winwidth); } else { gtab_dohscroll(hwnd, ptab, ptab->avewidth); } } else { gtab_changesel(hwnd, ptab, 0, 1, TRUE, bShift); } return(0); case VK_HOME: if (bControl) { /* control-home == top of file */ gtab_dovscroll(hwnd, ptab, -(ptab->toprow)); } /* top of window */ gtab_selhome(hwnd, ptab, bShift); gtab_showsel(hwnd, ptab, FALSE); return(0); case VK_END: if (bControl) { /* control-end -> end of file */ startrow = ptab->hdr.nrows-1; } else { startrow = gtab_linetorow(hwnd, ptab, ptab->nlines - 1); if (startrow >= ptab->hdr.nrows) { startrow = ptab->hdr.nrows-1; } } startcell = 0; ncells = ptab->hdr.ncols; if (!(ptab->hdr.selectmode & TM_ROW)) { startcell = ptab->hdr.ncols-1; ncells = 1; } if (bShift) { gtab_extendsel(hwnd, ptab, startrow, startcell, TRUE); } else { gtab_select(hwnd, ptab, startrow, startcell, 1, ncells, TRUE); } /* we have selected the bottom line. We don't want to * move it up into the window, since the intended * effect is to select the lowest line. This doesn't * apply to the ctrl-end behaviour (move to bottom of * buffer. */ if (bControl) { /* move the selection to make it visible - but move it * to the bottom and not to the top of the window */ gtab_showsel(hwnd, ptab, TRUE); } return(0); case VK_RETURN: if (ptab->select.nrows != 0) { gtab_showsel(hwnd, ptab, FALSE); gtab_enter(hwnd, ptab, ptab->select.startrow, ptab->select.startcell, ptab->select.nrows, ptab->select.ncells); } return(0); case VK_SPACE: /* toggle the selection */ if (ptab->select.nrows == 0) { /* no selection - make one */ gtab_changesel(hwnd, ptab, 0, 0, TRUE, FALSE); } else { /* there is a selection - deselect it */ gtab_select(hwnd, ptab, 0, 0, 0, 0, TRUE); } return(0); case VK_PRIOR: /* page up */ if (ptab->nlines > 3) { gtab_dovscroll(hwnd, ptab, -(ptab->nlines - 3)); } gtab_selhome(hwnd, ptab, bShift); return(0); case VK_NEXT: /* page down */ /* scroll down one page */ if (ptab->nlines > 3) { gtab_dovscroll(hwnd, ptab, (ptab->nlines - 3)); } /* select new bottom line */ startrow = gtab_linetorow(hwnd, ptab, ptab->nlines - 1); if (startrow >= ptab->hdr.nrows) { startrow = ptab->hdr.nrows-1; } startcell = 0; ncells = ptab->hdr.ncols; if (!(ptab->hdr.selectmode & TM_ROW)) { startcell = ptab->hdr.ncols-1; ncells = 1; } /* select bottom line, but don't call showsel * since we don't want to adjust it's position - we * want it to remain at the bottom of the window */ if (bShift) { gtab_extendsel(hwnd, ptab, startrow, startcell, TRUE); } else { gtab_select(hwnd, ptab, startrow, startcell, 1, ncells, TRUE); } return(0); default: return(1); } }