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);
}
Example #5
0
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);
}
Example #6
0
/* 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);
    }
}