/* * set the topmost selectable unit in window as the selection * * if bExtend is TRUE, then extend the selection to include this, rather * than replacing the existing selection. Note that (startrow, startcell) * is always the anchor point - ie most recently selected end, and the * (nrows, ncells) can be + or - to extend the selection downwards or upwards. */ void gtab_selhome(HWND hwnd, lpTable ptab, BOOL bExtend) { long startrow, startcell, ncells; if (ptab->hdr.selectmode & TM_ROW) { ncells = ptab->hdr.ncols; } else { ncells = 1; } startcell = 0; if (ptab->hdr.fixedselectable) { startrow = gtab_linetorow(hwnd, ptab, 0); } else { startrow = gtab_linetorow(hwnd, ptab, ptab->hdr.fixedrows); if (!(ptab->hdr.selectmode & TM_ROW)) { startcell = ptab->hdr.fixedcols; } } if (bExtend) { gtab_extendsel(hwnd, ptab, startrow, startcell, TRUE); } else { gtab_select(hwnd, ptab, startrow, startcell, 1, ncells, TRUE); } }
/* * called on right-click events. Select the cell clicked on, and if * valid, send on to owner for any context-menu type operation */ void gtab_rightclick(HWND hwnd, lpTable ptab, int x, int y) { long cell, ncells; long row; HWND hOwner; /* find which col, row he selected */ cell = gtab_xtocol(hwnd, ptab, x); if (cell == -1) { return; } row = gtab_linetorow(hwnd, ptab, gtab_ytoline(hwnd, ptab, y)); /* is he selecting a disabled fixed area ? */ if ( (row < ptab->hdr.fixedrows) || (cell < ptab->hdr.fixedcols)) { if (ptab->hdr.fixedselectable == FALSE) { return; } } // ignore if beyond data if ((row >= ptab->hdr.nrows) || (cell >= ptab->hdr.ncols)) { return; } /* is this within the already-selected area? */ if (!gtab_insideselection(ptab, row, cell)) { // no selection, or clicked outside the selection - make new selection // before sending the right-click // if shift is down, extend selection if (GetKeyState(VK_SHIFT) & 0x8000) { gtab_extendsel(hwnd, ptab, row, cell, TRUE); } else { /* record and paint new selection */ if (ptab->hdr.selectmode & TM_ROW) { cell = 0; ncells = ptab->hdr.ncols; } else { ncells = 1; } gtab_select(hwnd, ptab, row, cell, 1, ncells, TRUE); } } // now we have sent the selection, pass the message onto him hOwner = (HWND) GetWindowLongPtr(hwnd, WW_OWNER); SendMessage(hOwner, WM_RBUTTONDOWN, 0, MAKELONG( (short)x, (short)y)); }
/* dbl-click - send an TQ_ENTER event to the owner (if valid) */ void gtab_dblclick(HWND hwnd, lpTable ptab, int x, int y) { int cell, line; long row; line = gtab_ytoline(hwnd, ptab, y); cell = gtab_xtocol(hwnd, ptab, x); if ( (line < ptab->hdr.fixedrows) || (cell < ptab->hdr.fixedcols) ) { if (!ptab->hdr.fixedselectable) { return; } } row = gtab_linetorow(hwnd, ptab, line); if (ptab->hdr.selectmode & TM_ROW) { gtab_enter(hwnd, ptab, row, 0, 1, ptab->hdr.ncols); } else { gtab_enter(hwnd, ptab, row, cell, 1, 1); } }
/* * mark the selected line, if visible, in the style chosen by the * client app. This can be TM_SOLID, meaning an inversion of * the whole selected area or TM_FOCUS, meaning, inversion of the first * cell, and then a dotted focus rectangle for the rest. * * this function inverts either style, and so will turn the selection * both on and off. */ void gtab_invertsel(HWND hwnd, lpTable ptab, HDC hdc_in) { HDC hdc; int firstline, lastline; long startrow, lastrow, toprow, bottomrow; RECT rc; int lastcell; /* get the selection start and end rows ordered vertically */ if (ptab->select.nrows == 0) { return; } else if (ptab->select.nrows < 0) { startrow = ptab->select.startrow + ptab->select.nrows + 1; lastrow = ptab->select.startrow; } else { startrow = ptab->select.startrow; lastrow = ptab->select.startrow + ptab->select.nrows -1; } /* is selected area (or part of it) visible on screen ? */ firstline = gtab_rowtoline(hwnd, ptab, startrow); lastline = gtab_rowtoline(hwnd, ptab, lastrow); if (firstline < 0) { toprow = gtab_linetorow(hwnd, ptab, ptab->hdr.fixedselectable ? 0: ptab->hdr.fixedrows); if ((toprow >= startrow) && (toprow <= lastrow)) { firstline = gtab_rowtoline(hwnd, ptab, toprow); } else { return; } } else { toprow = 0; } if (lastline < 0) { bottomrow = gtab_linetorow(hwnd, ptab, ptab->nlines-1); if ((bottomrow <= lastrow) && (bottomrow >=startrow)) { lastline = gtab_rowtoline(hwnd, ptab, bottomrow); } else { return; } } rc.top = ptab->pdata[firstline].linepos.clipstart; rc.bottom = ptab->pdata[lastline].linepos.clipend; /* selection mode includes a flag TM_FOCUS indicating we should * use a focus rect instead of the traditional inversion for * selections in this table. This interferes with multiple backgrnd * colours less. However we still do inversion for fixedcols. */ lastcell = (int)(ptab->select.startcell + ptab->select.ncells - 1); /* * invert the whole area for TM_SOLID or just the first * cell for TM_FOCUS */ rc.left = ptab->pcellpos[ptab->select.startcell].clipstart; if (ptab->hdr.selectmode & TM_FOCUS) { rc.right = ptab->pcellpos[ptab->select.startcell].clipend; }else { rc.right = ptab->pcellpos[lastcell].clipend; } if (hdc_in == NULL) { hdc = GetDC(hwnd); } else { hdc = hdc_in; } InvertRect(hdc, &rc); /* * draw focus rectangle around remaining cells on this line, if there * are any */ if (ptab->hdr.selectmode & TM_FOCUS) { /* * now this is a real fudge. if we are drawing TM_FOCUS * selection, and the real top line is off the top of the * window, then the top of the focus rect will be drawn at * the top of our window. If we then scroll up one line, * a new focus rect will be drawn, but the old top of focus * rect line will still be there as junk on the * screen. To fix this, we have 2 choices: we undo the selection * before every scroll (too slow) or we set the focus rect a little * bigger if the real top line is off-window, so that the top line * is clipped (as it should be). This latter is what we do here */ if (toprow > startrow) { rc.top--; } if (ptab->select.ncells > 1) { rc.left = ptab->pcellpos[ptab->select.startcell+1].clipstart; rc.right = ptab->pcellpos[lastcell].clipend; DrawFocusRect(hdc, &rc); } } if (hdc_in == NULL) { ReleaseDC(hwnd, hdc); } }
/* * 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. */ } }
/* 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; } }
/* * called on mouse-up. complete any tracking that was happening */ void gtab_release(HWND hwnd, lpTable ptab, int x, int y) { lpCellPos ppos; lpProps pprop; long row, cell; int cx; switch(ptab->trackmode) { case TRACK_NONE: return; case TRACK_COLUMN: /* erase marker lines */ gtab_drawvertline(hwnd, ptab); ReleaseCapture(); ptab->trackmode = TRACK_NONE; /* adjust cell width */ ppos = &ptab->pcellpos[ptab->tracknr]; cx = ptab->trackline1 - ppos->start; pprop = &ptab->pcolhdr[ptab->tracknr].props; pprop->valid |= P_WIDTH; pprop->width = cx; gtab_calcwidths(hwnd, ptab); gtab_setsize(hwnd, ptab); InvalidateRect(hwnd, NULL, FALSE); return; case TRACK_CELL: row = gtab_linetorow(hwnd, ptab, gtab_ytoline(hwnd, ptab, y)); cell = gtab_xtocol(hwnd, ptab, x); ReleaseCapture(); ptab->trackmode = TRACK_NONE; // ignore if before or beyond data if ( (row < ptab->hdr.fixedrows) || (cell < ptab->hdr.fixedcols)) { if (ptab->hdr.fixedselectable == FALSE) { gtab_select( hwnd, ptab, ptab->select.startrow, ptab->select.startcell, ptab->select.nrows, ptab->select.ncells, TRUE); return; } } if ((row >= ptab->hdr.nrows) || (cell >= ptab->hdr.ncols)) { gtab_select( hwnd, ptab, ptab->select.startrow, ptab->select.startcell, ptab->select.nrows, ptab->select.ncells, TRUE); return; } /* * Extend to this new selection end point * we used to only do this if shift key pressed, but that * is not a good UI. */ gtab_extendsel(hwnd, ptab, row, cell, TRUE); return; } }
/* * called on mouse-down events. decide what to start tracking. */ void gtab_press(HWND hwnd, lpTable ptab, int x, int y) { long cell, ncells; long row; if (ptab->trackmode != TRACK_NONE) { return; } /* has he grabbed a cell-edge to resize ? */ cell = gtab_xtocol(hwnd, ptab, x); if (cell == -1) { return; } if (gtab_isborder(hwnd, ptab, x, cell)) { gtab_trackcol(hwnd, ptab, cell, x); return; } if ( (cell > 0) && gtab_isborder(hwnd, ptab, x, cell-1)) { gtab_trackcol(hwnd, ptab, cell, x); return; } /* find which line he selected */ row = gtab_linetorow(hwnd, ptab, gtab_ytoline(hwnd, ptab, y)); /* is he selecting a disabled fixed area ? */ if ( (row < ptab->hdr.fixedrows) || (cell < ptab->hdr.fixedcols)) { if (ptab->hdr.fixedselectable == FALSE) { return; } } // ignore if beyond data if ((row >= ptab->hdr.nrows) || (cell >= ptab->hdr.ncols)) { return; } /* ok, start cell selection */ ptab->trackmode = TRACK_CELL; SetCapture(hwnd); /* record and paint new selection */ if (ptab->hdr.selectmode & TM_ROW) { cell = 0; ncells = ptab->hdr.ncols; } else { ncells = 1; } /* * if the shift key is down, then extend the selection to this * new anchor point, rather than create a new selection */ if (GetKeyState(VK_SHIFT) & 0x8000) { gtab_extendsel(hwnd, ptab, row, cell, FALSE); } else { gtab_select(hwnd, ptab, row, cell, 1, ncells, FALSE); } 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); } }
/* move the selection a specified nr of rows or cells * if no selection, select first visible unit * * if bExtend is true and there is a current selection, then extend it rather than * replace it. Note that (startrow, startcell) will always be set to the newly * selected position - this is the anchor point. nrows or ncells may be negative * if the selection extends upwards above the anchor. nrows == -1 is the same * as nrows == 1, meaning only the current row is visible. Similarly * (in TM_CELL mode), ncells may be negative. * * Move the selection (ie anchor point) to make it visible. bToBottom * indicates whether it should be moved to the bottom or the top * of the window. */ VOID gtab_changesel( HWND hwnd, lpTable ptab, long rowincr, int cellincr, BOOL bToBottom, BOOL bExtend ) { long row, col, ncols; /* is there a selection ? */ if (ptab->select.nrows == 0) { /* no selection - force a selection * at the first visible unit */ if (ptab->hdr.fixedselectable) { row = 0; col = 0; } else { row = gtab_linetorow(hwnd, ptab, ptab->hdr.fixedrows); /* should really check for first visible cell */ col = ptab->hdr.fixedcols; } ncols = 1; if (ptab->hdr.selectmode & TM_ROW) { col = 0; ncols = ptab->hdr.ncols; } gtab_select(hwnd, ptab, row, col, 1, ncols, TRUE); } else { /* move the anchor point by rowincr, cellincr */ row = ptab->select.startrow + rowincr; col = ptab->select.startcell + cellincr; /* * ensure that new anchor point is in a valid position */ while (col >= ptab->hdr.ncols) { col -= ptab->hdr.ncols; row++; } while (col < 0) { col += ptab->hdr.ncols; row--; } if (row < 0) { row = 0; } if (row >= ptab->hdr.nrows) { row = ptab->hdr.nrows-1; } /* check we haven't moved into non-selectable region */ if ((row < ptab->hdr.fixedrows) && (!ptab->hdr.fixedselectable)) { row = ptab->hdr.fixedrows; } if (bExtend) { gtab_extendsel(hwnd, ptab, row, col, TRUE); } else { gtab_select( hwnd, ptab, row, col, 1, (ptab->hdr.selectmode & TM_ROW) ? ptab->hdr.ncols : 1, TRUE); } } /* ensure selection visible */ gtab_showsel(hwnd, ptab, bToBottom); }