/*
 * 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);
    }
}
Esempio n. 4
0
/*
 * 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);
	}
}
Esempio n. 5
0
/*
 * 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);
    }
}
Esempio n. 10
0
/* 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);
}