home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c070 / 4.ddi / TOOLS.4 / TCTSRC1.EXE / MNREAD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-31  |  35.2 KB  |  1,292 lines

  1. /**
  2. *
  3. * Name        MNREAD - Read from any menu using a highlight
  4. *              bar, given a starting row and column.
  5. *
  6. * Synopsis    ret = mnread (pmenu, srow, scol, prow, pcol,
  7. *                   pch, pscan, option);
  8. *
  9. *        int ret     WN_NO_ERROR (0) if selection was
  10. *                without error, otherwise ret is
  11. *                the MN/WN error code.
  12. *        BMENU *pmenu    Pointer to menu to read from.
  13. *        int    srow,    Starting row and column of item to
  14. *               scol    highlight first.
  15. *        int   *prow,    Pointers to returned row and column
  16. *              *pcol    of highlight bar when a transmit
  17. *                key is pressed.
  18. *        int   *pch,    Pointers to returned character code
  19. *              *pscan,    and scan code of key pressed that
  20. *                terminated MNREAD.
  21. *        int    option    A bitwise OR-ing of the following
  22. *                values:
  23. *
  24. *            MN_USE_DESCRIPTION
  25. *            Display Lotus-style descriptions, if any,
  26. *            for highlighted items.
  27. *            MN_ALL_TRANSMIT
  28. *            Transmit on any key pressed,
  29. *            whether it is a transmit key
  30. *            or not.
  31. *            MN_DESTROY
  32. *            Both remove the menu from
  33. *            the screen and destroy the
  34. *            menu's data structures upon
  35. *            termination of MNREAD.
  36. *            MN_HOLD_BEEP
  37. *            Don't beep if the key pressed
  38. *            is a transmit key, even if
  39. *            the key's action specifies a
  40. *            beep.
  41. *            MN_KEEP_DESCRIPTION
  42. *            Leave the "Lotus" description
  43. *            on the menu on termination.
  44. *            MN_KEEP_HIGHLIGHT
  45. *            Leave the highlight bar on
  46. *            the menu on termination.
  47. *            MN_REMOVE
  48. *            Remove the menu from the
  49. *            display screen when MNREAD
  50. *            terminates.
  51. *            MN_UNKNOWN_BEEP
  52. *            Beep when the user types an
  53. *            unknown key.
  54. *            MN_UNKNOWN_TRANSMIT
  55. *            Transmit when user types an
  56. *            unknown key.
  57. *            MN_PREV_BAR
  58. *            Use previous highlight bar.  This effect
  59. *            may be modified by MN_SHOW_BAR.
  60. *            MN_SHOW_BAR
  61. *            If MN_PREV_BAR is used, force display of
  62. *            highlight bar even if it was not shown
  63. *            formerly.  (Error WN_NONE_SELECT if no
  64. *            selectable item can be found.)
  65. *            MN_HIDE_BAR
  66. *            Highlight no item at first.  Overrides
  67. *            MN_SHOW_BAR.
  68. *
  69. * Description    MNREAD accepts keystrokes from the user and moves
  70. *        a highlight bar on the specified menu.
  71. *
  72. *        An error code is returned if:
  73. *            There are no selectable items on the menu.
  74. *            The menu is not displayed (via MNDSPLAY).
  75. *            WNREMOVE fails (option includes MN_REMOVE
  76. *            or MN_DESTROY).
  77. *            MNDSTROY fails (option includes MN_DESTROY).
  78. *            The key pressed is unknown (and option includes
  79. *            MN_UNKNOWN_TRANSMIT).
  80. *
  81. * Returns    ret    WN_NO_ERROR (if no error), or a MN/WN error
  82. *            code if failure.
  83. *        *prow    The row and column where the highlight bar was
  84. *        *pcol    when a transmit key was pressed, or the
  85. *            item most recently highlighted if no item
  86. *            is currently highlighted.
  87. *        *pch    The character code and scan code of the
  88. *        *pscan    last key pressed, or 0xff if mouse event
  89. *            caused transmission.
  90. *        b_mnmoevent
  91. *            Most recent mouse event, or OL if none.
  92. *        b_mnmovert, b_mnmohoriz
  93. *            Row and column where most recent mouse event
  94. *            took place, or 0xffff if none.
  95. *
  96. * Version    6.00 (C)Copyright Blaise Computing Inc.  1987-1989
  97. *
  98. **/
  99.  
  100. #include <bkeybrd.h>
  101. #include <bmenu.h>
  102. #include <bmouse.h>
  103.  
  104.         /* Most recent mouse menu event.            */
  105. unsigned long b_mnmoevent;      /* Event, or OL if none.        */
  106. unsigned      b_mnmovert;      /* Row and column where event took*/
  107. unsigned      b_mnmohoriz;      /*  place, or 0xffff if none.     */
  108.  
  109.  
  110. typedef struct              /* CLEANUP_ITEMS:  Items to        */
  111. {                  /*  restore on exit.            */
  112.                   /*                    */
  113.     int      MOCATCH_was_off;      /* Must remove MOCATCH.        */
  114.     unsigned delayed_flag;      /* Window was previously delayed. */
  115. } CLEANUP_ITEMS;
  116.  
  117. typedef struct              /* USER_RESPONSE:  Mouse or        */
  118. {                  /* keyboard event.            */
  119.  
  120.     enum {MOUSE, KEYSTROKE, INTERNAL_ERROR} mouse_or_key;
  121.     union
  122.     {
  123.     struct                 /* Details of mouse event: */
  124.     {                 /*                */
  125.         unsigned long   event;     /* Actual mouse event.     */
  126.         unsigned        vert,horiz;  /* Mouse cursor location   */
  127.                      /*  (in characters).        */
  128.         const BMNMOUSE *pmouse;     /* Mouse event node.        */
  129.         BITEM       *pitem;     /* Menu item where mouse   */
  130.     } mouse;             /*  event took place.        */
  131.     struct
  132.     {                 /* Details of keystroke:   */
  133.         KEY_SEQUENCE kseq;         /*  Character and key codes*/
  134.         const BKEYMAP *pkey;     /*  Key map entry.        */
  135.     } keystroke;
  136.     } data;
  137. } USER_RESPONSE;
  138.  
  139. #define TRUE  1
  140. #define FALSE 0
  141.  
  142.     /* Internal functions.                        */
  143.  
  144. static void clean_up(const BMENU *,const CLEANUP_ITEMS *);
  145. static int mnchkxit(const BKEYMAP *,const BMNMOUSE *,int);
  146. static int in_window(const BWINDOW *,unsigned,unsigned);
  147. static BITEM *pfind_item(const BMENU *,unsigned,unsigned);
  148. static const BMNMOUSE *pfind_mouse_event(const BMNMOUSE *,unsigned long,int *);
  149. static int wait_operator(const BMENU *,USER_RESPONSE *,
  150.              const BKEYMAP *,int);
  151. static int interp_resp(BMENU *,const USER_RESPONSE *,int *,BITEM **,
  152.                int *,int *);
  153. static int jump(BWINDOW *,int);
  154.  
  155. int    mnread  (pmenu, srow, scol, prow, pcol, pch, pscan, option)
  156. BMENU *pmenu;
  157. int    srow, scol;
  158. int   *prow, *pcol;
  159. int   *pch,  *pscan;
  160. int    option;
  161. {
  162.     int      code,        /* Returned error code from MNFINDSL*/
  163.          done = FALSE,    /* Becomes true when MNREAD should  */
  164.                 /*   exit at next opportunity.        */
  165.          result,        /* Return code from interp_resp().  */
  166.          shouldbeep = FALSE;/* Whether we should beep or not.   */
  167.     BITEM *pitem;        /* Pointer to current menu item.    */
  168.     const BKEYMAP *pkey;    /* Pointer to current key found.    */
  169.     USER_RESPONSE  resp;
  170.     CLEANUP_ITEMS  dirt;
  171.  
  172.         /* Validate menu data structures.            */
  173.     if (mnvalmnu (pmenu) == NIL)
  174.     wnretern (MN_BAD_MENU);
  175.  
  176.         /* Exit if menu not displayed.                */
  177.     if (   pmenu->pwin->where_shown.dev != SC_MONO
  178.     && pmenu->pwin->where_shown.dev != SC_COLOR)
  179.     wnretern (WN_NOT_SHOWN);
  180.  
  181.         /* Exit if window is covered or frozen.         */
  182.     if (pmenu->pwin->internals.frozen)
  183.     wnretern (WN_COVERED);
  184.  
  185.         /* Disable keys which do not point either at items  */
  186.         /* or at (-1,-1).                    */
  187.     if (mndisabl (pmenu) == NIL)
  188.     return (b_wnerr);
  189.  
  190.         /* Check to make sure there is an exit route.        */
  191.     if (mnchkxit(pmenu->pkeys,pmenu->pmouse,option))
  192.     return (b_wnerr);
  193.  
  194.         /* Set pitem to highlight bar location (and set     */
  195.         /* previous location if no bar shown).            */
  196.  
  197.     if (option & MN_PREV_BAR)
  198.     {
  199.     pitem = pmenu->pbar_item;    /* Use former location        */
  200.                     /* or none if no bar shown. */
  201.     if (   (pitem == NIL)
  202.         && (option & MN_SHOW_BAR))    /* We require a bar,        */
  203.         pitem = pmenu->pbar_prev;    /* so try harder.        */
  204.     }
  205.     else
  206.     {                    /* If not using previous,   */
  207.     option |= MN_SHOW_BAR;        /* then we must show a bar. */
  208.     pitem    = NIL;
  209.     }
  210.  
  211.     if (   (pitem == NIL)
  212.     && (option & MN_SHOW_BAR))    /* We still need a bar.     */
  213.     {
  214.         /* Find the first selectable menu item.         */
  215.     pitem = mnfindsl(pmenu,NIL,srow,scol,&code);
  216.     if (pitem == NIL)
  217.     {
  218.         if (   code == WN_NO_ERROR    /* Error if none selectable */
  219.         && !(option & MN_HIDE_BAR))  /* unless none wanted. */
  220.         wnretern (MN_NONE_SELECT);
  221.         else
  222.         return (b_wnerr);         /* Internal error.     */
  223.     }
  224.     }
  225.  
  226.     if (   (option & MN_HIDE_BAR)    /* Must hide the bar and    */
  227.     && (pitem != NIL))        /* there is a bar to hide.  */
  228.     {
  229.     pmenu->pbar_prev = pitem;    /* Save previous bar site.  */
  230.     pitem         = NIL;     /* Show no bar.         */
  231.     }
  232.  
  233.         /* Point pkey at menu's keylist.                    */
  234.     pkey = pmenu->pkeys;
  235.  
  236.         /* Save the starting row and column coordinates.    */
  237.     if (pitem)
  238.     {
  239.     *prow = pitem->row;
  240.     *pcol = pitem->col;
  241.     }
  242.  
  243.         /* Save items that may need restoration on exit.    */
  244.     dirt.MOCATCH_was_off = !b_mocatch;
  245.     dirt.delayed_flag     = pmenu->pwin->options.delayed;
  246.  
  247.         /* Miscellaneous setup.                 */
  248.     pmenu->pwin->options.delayed   = 0;  /* Allow immediate output. */
  249.  
  250.         /* Check whether we should always transmit.        */
  251.     if (option & MN_ALL_TRANSMIT)
  252.     done = TRUE;
  253.  
  254.     do
  255.     {
  256.         /* Show highlight bar and (perhaps) description.    */
  257.     if (NIL == mnhilit0(pmenu,pitem,
  258.            (option & MN_USE_DESCRIPTION) | WN_UPDATE | MN_HIGHLIGHT))
  259.     {
  260.         clean_up(pmenu,&dirt);
  261.         return b_wnerr;
  262.     }
  263.  
  264.     /* Do a beep if the last keycode told us to do so....        */
  265.     if (shouldbeep)
  266.     {
  267.         scttywrt ('\a', 0);
  268.         shouldbeep = FALSE;
  269.     }
  270.  
  271.     /* Get a keystroke or mouse event from the user.        */
  272.  
  273.     if (WN_NO_ERROR != wait_operator(pmenu,&resp,pkey,option))
  274.     {
  275.         clean_up(pmenu,&dirt);
  276.         return b_wnerr;
  277.     }
  278.     switch (resp.mouse_or_key)
  279.     {
  280.         case KEYSTROKE:
  281.         *pch   = resp.data.keystroke.kseq.character_code;
  282.         *pscan = resp.data.keystroke.kseq.key_code;
  283.         if (resp.data.keystroke.pkey != NIL)
  284.             pkey = resp.data.keystroke.pkey;
  285.         b_mnmoevent = 0L;
  286.         b_mnmovert  = 0xffff;
  287.         b_mnmohoriz = 0xffff;
  288.         break;
  289.  
  290.         case MOUSE:
  291.         *pch        = 0xff;
  292.         *pscan        = 0xff;
  293.         b_mnmoevent = resp.data.mouse.event;
  294.         b_mnmovert  = resp.data.mouse.vert;
  295.         b_mnmohoriz = resp.data.mouse.horiz;
  296.         break;
  297.     }
  298.  
  299.     /* Interpret user response in terms of menu results.        */
  300.  
  301.     switch (result = interp_resp(pmenu,&resp,&option,&pitem,
  302.                      &shouldbeep,&done))
  303.     {
  304.         case WN_NO_ERROR:
  305.         case MN_READ_AB:
  306.         case MN_XMIT_NOBAR:
  307.         case MN_UNKNOWN_AB:
  308.         break;
  309.  
  310.         default:
  311.         clean_up(pmenu,&dirt);
  312.         return result;             /* Internal error. */
  313.     }
  314.  
  315.     } while (!done);
  316.  
  317.     clean_up(pmenu,&dirt);
  318.  
  319.         /* Show highlight bar and (perhaps) description.    */
  320.     if (NIL == mnhilit0(pmenu,pitem,
  321.            (option & MN_USE_DESCRIPTION) | WN_UPDATE | MN_HIGHLIGHT))
  322.     return b_wnerr;
  323.  
  324.         /* Check whether we should beep.            */
  325.     if (shouldbeep && ( !(option & MN_HOLD_BEEP)))
  326.     scttywrt ('\a', 0);
  327.  
  328.         /* Perhaps remove highlight bar and description.    */
  329.     if (NIL == mnhilit0(pmenu,pitem,
  330.          WN_UPDATE
  331.        | ((option & MN_KEEP_HIGHLIGHT)   ? MN_HIGHLIGHT : MN_UNHIGHLIGHT)
  332.        | ((option & MN_KEEP_DESCRIPTION) ? MN_USE_DESCRIPTION : 0)))
  333.     return b_wnerr;
  334.  
  335.         /* Return the final row and column of the highlight */
  336.         /* bar.                         */
  337.     if (pitem == NIL)
  338.     pitem = pmenu->pbar_prev;
  339.     if (pitem == NIL)
  340.     {
  341.     *pcol = -1;
  342.     *prow = -1;
  343.     }
  344.     else
  345.     {
  346.     *pcol = pitem->col;
  347.     *prow = pitem->row;
  348.     }
  349.  
  350.         /* Remove menu's window from display if told to.    */
  351.     if (option & MN_REMOVE)
  352.     if (wnremove (pmenu->pwin) == NIL)
  353.         return (b_wnerr);
  354.  
  355.         /* Destroy menu's data structures if told to do so. */
  356.     if ((option & MN_DESTROY) == MN_DESTROY)
  357.     if (mndstroy (pmenu) != WN_NO_ERROR)
  358.         return (b_wnerr);
  359.  
  360.     return (result);
  361. }
  362.  
  363. /**
  364. *
  365. * Name        clean_up -- Restore miscellaneous aspects of
  366. *                environment.
  367. *
  368. * Synopsis    clean_up(pmenu,pdirt);
  369. *
  370. *        const CLEANUP_ITEMS *pmenu
  371. *                Address of the menu structure.
  372. *        const CLEANUP_ITEMS *pdirt
  373. *                Address of record structure listing
  374. *                items to restore on exit from
  375. *                MNREAD.
  376. *
  377. * Description    This function restores miscellaneous items:
  378. *        specifically, if MOCATCH was not installed before MNREAD
  379. *        was invoked, this function removes it; also restores the
  380. *        menu's window's "delayed" state.
  381. *
  382. * Returns    b_mocatch    Restored to value before MNREAD was
  383. *                called.
  384. *
  385. **/
  386.  
  387. static void clean_up(pmenu,pdirt)
  388. const BMENU        *pmenu;
  389. const CLEANUP_ITEMS *pdirt;
  390. {
  391.     if (pdirt->MOCATCH_was_off)
  392.     mopreclk(MO_REMOVE);
  393.     pmenu->pwin->options.delayed = pdirt->delayed_flag;
  394. }
  395.  
  396.  
  397. /**
  398. *
  399. * Name        mnchkxit -- Check whether there is any way to
  400. *                exit the menu.
  401. *
  402. * Synopsis    ret = mnchkxit(pkey,pmouse,option);
  403. *
  404. *        int     ret    Zero if there is an exit route (i.e. no
  405. *                  error);
  406. *                MN_BAD_KEY if a bad keymap
  407. *                  entry encountered;
  408. *                MN_BAD_MOUSE if a bad mouse
  409. *                  entry encountered;
  410. *                MN_NO_EXIT if there is no exit key.
  411. *        const BKEYMAP *pkey
  412. *                Pointer to the start of the menu's
  413. *                key list.
  414. *        const BMNMOUSE *pmouse
  415. *                Address of first item in list of menu's
  416. *                mouse actions.
  417. *        int option    MNREAD options including the following
  418. *                bits:
  419. *
  420. *                  MN_KBIGNORE
  421. *                  MN_ALL_TRANSMIT
  422. *                  MN_UNKNOWN_TRANSMIT
  423. *
  424. * Description    MNCHKXIT checks the option value and the menu's key and
  425. *        mouse lists to see if there is any way for the menu to
  426. *        be exited.
  427. *
  428. * Returns    ret        Zero if there is an exit route (i.e. no
  429. *                  error);
  430. *                MN_BAD_KEY if a bad keymap
  431. *                  entry encountered;
  432. *                MN_BAD_MOUSE if a bad mouse
  433. *                  entry encountered;
  434. *                MN_NO_EXIT if there is no exit key.
  435. *
  436. *        b_wnerr     Possible values:
  437. *                  (No change)    Success.
  438. *                  MN_NO_EXIT    No way to exit the
  439. *                        menu.
  440. *                  MN_BAD_KEY    Bad keymap entry
  441. *                        encountered.
  442. *                  MN_BAD_MOUSE    Bad mouse entry
  443. *                        encountered.
  444. **/
  445.  
  446. static int mnchkxit(pkey,pmouse,option)
  447. const BKEYMAP  *pkey;
  448. const BMNMOUSE *pmouse;
  449. int        option;
  450. {
  451.     if (option & (MN_ALL_TRANSMIT | MN_UNKNOWN_TRANSMIT))
  452.     return 0;
  453.  
  454.     if (! (option & MN_KBIGNORE))
  455.     {
  456.     for (;    pkey != NIL;  pkey = pkey->next)
  457.     {
  458.             /* Check key signature.                */
  459.         if (pkey->signature != MN_KEY_SIGN)
  460.         wnretern (MN_BAD_KEY);
  461.  
  462.         if (   !(pkey->action & (MN_DISABLE | MN_TEMP_DISABLE))
  463.         && (pkey->action & (MN_TRANSMIT | MN_ABORT)))
  464.         return (0);
  465.     }
  466.     }
  467.  
  468.     for (;  pmouse != NIL;  pmouse = pmouse->pnext)
  469.     {
  470.         /* Check mouse signature.                */
  471.     if (pmouse->signature != MN_MOU_SIGN)
  472.         wnretern (MN_BAD_MOUSE);
  473.  
  474.     if (   !(pmouse->action & (MN_DISABLE | MN_TEMP_DISABLE))
  475.         && (pmouse->action & (MN_TRANSMIT | MN_ABORT)))
  476.         return (0);
  477.     }
  478.  
  479.     wnretern (MN_NO_EXIT);
  480. }
  481.  
  482. /**
  483. *
  484. * Name        pfind_item -- Find menu item that covers a given
  485. *                  screen location.
  486. *
  487. * Synopsis    pitem = pfind_item(pmenu,row,col)
  488. *
  489. *        BITEM *pitem    Address of resulting menu item node,
  490. *                or NIL if error or if none found.
  491. *        const BMENU *pmenu
  492. *                Address of menu control structure.
  493. *        int row,col    Display location (relative to (0,0) at
  494. *                upper left corner of display).
  495. *
  496. * Description    This function finds a menu item (if any) that covers a
  497. *        given display location.
  498. *
  499. *        The result is meaningless if the menu is not displayed
  500. *        or (row,col) is outside the viewport.
  501. *
  502. * Returns    *pitem        Address of resulting menu item node,
  503. *                or NIL if error or if none found.
  504. *        b_wnerr     Unchanged if no error or if none found;
  505. *                MN_BAD_ITEM if corrupted item list.
  506. *
  507. **/
  508.  
  509. static BITEM *pfind_item(pmenu,row,col)
  510. const BMENU *pmenu;
  511. unsigned     row,col;
  512. {
  513.     BITEM *pitem;
  514.  
  515.     int target_row =   row
  516.              - pmenu->pwin->where_shown.corner.row
  517.              + pmenu->pwin->data_origin.row;
  518.     int target_col =   col
  519.              - pmenu->pwin->where_shown.corner.col
  520.              + pmenu->pwin->data_origin.col;
  521.  
  522.     for (pitem =  pmenu->pitems;
  523.      pitem != NIL;
  524.      pitem =  pitem->next)
  525.     {
  526.     if (pitem->signature != MN_ITEM_SIGN)
  527.     {
  528.         wnreterr(MN_BAD_ITEM);
  529.     }
  530.     if (   pitem->row == target_row
  531.         && pitem->col <= target_col
  532.         && target_col <  pitem->col + pitem->len)
  533.     {
  534.         return pitem;
  535.     }
  536.     }
  537.  
  538.     return NIL;
  539. }
  540.  
  541. /**
  542. *
  543. * Name        in_window -- Report whether window data area contains
  544. *                 a given display location.
  545. *
  546. * Synopsis    contains = in_window(pwin,row,col);
  547. *
  548. *        int contains    Nonzero if window contains the
  549. *                  given location,
  550. *                zero if not.
  551. *        const BWINDOW *pwin
  552. *                Address of window control structure.
  553. *        int row,col    Display location (relative to (0,0) at
  554. *                upper left corner of display).
  555. *
  556. * Description    This function tells whether a given display location
  557. *        lies within a window's data area.  The window must be
  558. *        displayed.
  559. *
  560. * Returns    contains    Nonzero if window contains the
  561. *                  given location,
  562. *                zero if not.
  563. *
  564. **/
  565.  
  566. static int in_window(pwin,row,col)
  567. const BWINDOW *pwin;
  568. unsigned       row,col;
  569. {
  570.  
  571.     /* Macros to return edge of data area (plus 1).            */
  572.  
  573. #define BOT_ROW_P1 (pwin->where_shown.corner.row + wnview_h(pwin))
  574. #define RT_COL_P1  (pwin->where_shown.corner.col + wnview_w(pwin))
  575.  
  576.     return (   pwin->where_shown.corner.row <= row
  577.         && row  < BOT_ROW_P1
  578.         && pwin->where_shown.corner.col <= col
  579.         && col < RT_COL_P1);
  580.  
  581. #undef BOT_ROW_P1
  582. #undef RT_COL_P1
  583. }
  584.  
  585. /**
  586. *
  587. * Name        pfind_mouse_event -- Find mouse event in list.
  588. *
  589. * Synopsis    pfound = pfind_mouse_event(pmouse,event,perror)
  590. *
  591. *        const BMNMOUSE *pfound
  592. *                Returned address of found mouse event
  593. *                record.
  594. *        const BMNMOUSE *pmouse
  595. *                Address of first mouse node to check.
  596. *        unsigned long event
  597. *                Actual mouse event to search for.
  598. *        int *perror    Address into which to return success
  599. *                code.
  600. *
  601. * Description    This function searches for a matching mouse event
  602. *        in a list of mouse events, taking into account
  603. *        what aspects should be ignored.
  604. *
  605. * Returns    pfound        Address of found mouse event record,
  606. *                  or NIL if error or if none found.
  607. *        *perror     WN_NO_ERROR or MN_BAD_MOUSE.
  608. *
  609. **/
  610.  
  611. static const BMNMOUSE *pfind_mouse_event(pmouse,event,perror)
  612. const BMNMOUSE *pmouse;
  613. unsigned long    event;
  614. int           *perror;
  615. {
  616.     for ( ;
  617.      pmouse != NIL;
  618.      pmouse  = pmouse->pnext)
  619.     {
  620.     if (pmouse->signature != MN_MOU_SIGN)
  621.     {
  622.         wnreterr(*perror = MN_BAD_MOUSE);
  623.     }
  624.     if (   0 == (pmouse->action & (MN_DISABLE | MN_TEMP_DISABLE))
  625.         && (pmouse->ignore | event) == (pmouse->ignore | pmouse->event))
  626.     {
  627.         *perror = WN_NO_ERROR;
  628.         return pmouse;
  629.     }
  630.     }
  631.     *perror = WN_NO_ERROR;
  632.     return NIL;
  633. }
  634.  
  635. /**
  636. *
  637. * Name        wait_operator -- Wait for mouse or keyboard command
  638. *                 from operator.
  639. *
  640. * Synopsis    ercode = wait_operator(pmenu,presp,pkey,option);
  641. *
  642. *        int ercode    Error code:
  643. *                  WN_NO_ERROR if okay;
  644. *                  other if internal error.
  645. *        const BMENU *pmenu
  646. *                Address of menu structure.
  647. *        USER_RESPONSE *presp
  648. *                Address of structure containing the
  649. *                returned user response.
  650. *        const BKEYMAP *pkey
  651. *                Address of keymap entry for most
  652. *                recent keystroke.
  653. *        int option    Option flag.  Relevant bits:
  654. *                  MN_KBIGNORE
  655. *                  MN_KBDISCARD (both have same effect).
  656. *
  657. * Description    This function polls the mouse and keyboard for a response
  658. *        from the user.
  659. *
  660. *        Mouse events are reported only if they are listed
  661. *        in the menu's list of mouse events.
  662. *
  663. * Returns    ercode        Error code:
  664. *                  WN_NO_ERROR if okay;
  665. *                  other if internal error.
  666. *        b_wnerr     Unchanged if no error;
  667. *                same as "ercode" if error detected.
  668. *        *presp        User response.
  669. *
  670. **/
  671.  
  672. static int wait_operator(pmenu,presp,pkey,option)
  673. const BMENU     *pmenu;
  674. USER_RESPONSE     *presp;
  675. const BKEYMAP     *pkey;
  676. int          option;
  677. {
  678.     unsigned long event;
  679.     BITEM     *pitem;
  680.     unsigned      vert,horiz;
  681.     int       ercode;
  682.  
  683.     const BMNMOUSE *pmouse;
  684.  
  685.     for (;;)        /* Infinite loop:  First poll mouse, then        */
  686.     {            /* keyboard.  Exit when anything happens.        */
  687.  
  688.             /* Poll mouse history separately for each        */
  689.             /* listed mouse event.                */
  690.     for (pmouse  = pmenu->pmouse;
  691.          pmouse != NIL;
  692.          pmouse  = pmouse->pnext)
  693.     {
  694.         if (pmouse->signature != MN_MOU_SIGN)
  695.         {
  696.         wnretern(MN_BAD_MOUSE);
  697.         }
  698.  
  699.         if (   0     == (pmouse->action & (MN_DISABLE | MN_TEMP_DISABLE))
  700.         && MO_OK == mocheck(pmouse->event,pmouse->ignore,
  701.                     MO_CLEAR,&event,&vert,&horiz)
  702.         && event)
  703.         {
  704.         /* Analyze location of mouse event:  whether on a   */
  705.         /* menu item and whether outside the menu.        */
  706.  
  707.         vert  >>= 3;  /* Convert from pixels to characters. */
  708.         horiz >>= 3;
  709.  
  710.         if (in_window(pmenu->pwin,vert,horiz))
  711.         {
  712.             if (NIL == (pitem = pfind_item(pmenu,vert,horiz)))
  713.             event =   (event | MN_OFF_ITEMS)
  714.                 & ~(unsigned long) (MN_ITEMS | MN_OUT_MENU);
  715.             else
  716.             event = (event | MN_ITEMS)
  717.                 & ~(unsigned long) (MN_OFF_ITEMS | MN_OUT_MENU);
  718.         }
  719.         else
  720.             event =   (event | MN_OUT_MENU)
  721.                 & ~(unsigned long) (MN_OFF_ITEMS | MN_ITEMS);
  722.  
  723.         /* Mouse event is now completely characterized, so  */
  724.         /* scan list of menu events for this menu.  Ignore  */
  725.         /* event if not found in list.                */
  726.  
  727.         if (NIL != (presp->data.mouse.pmouse
  728.               = pfind_mouse_event(pmenu->pmouse,event,&ercode)))
  729.         {                  /* Found mouse event. */
  730.             presp->mouse_or_key      = MOUSE;
  731.             presp->data.mouse.event  = event;
  732.             presp->data.mouse.vert   = vert;
  733.             presp->data.mouse.horiz  = horiz;
  734.             presp->data.mouse.pitem  = pitem;
  735.             return ercode;
  736.         }
  737.         }
  738.     }
  739.  
  740.         /* No relevant mouse event, so poll keyboard.        */
  741.  
  742.     if (kbpoll(b_key_ctrl,pmenu,
  743.            &presp->data.keystroke.kseq,
  744.            KB_REMOVE_KEY))
  745.         if (0 == (option & (MN_KBIGNORE | MN_KBDISCARD)))
  746.         {
  747.         presp->mouse_or_key       = KEYSTROKE;
  748.         presp->data.keystroke.pkey =
  749.               mnmchkey(pmenu,pkey,
  750.                    presp->data.keystroke.kseq.character_code,
  751.                    presp->data.keystroke.kseq.key_code,
  752.                    &ercode);
  753.         return ercode;
  754.         }
  755.     }
  756. }
  757.  
  758. /**
  759. *
  760. * Name        interp_resp -- Interpret user response and decide
  761. *                   actions to take.
  762. *
  763. * Synopsis    ercode = interp_resp(pmenu,presp,poption,ppitem,
  764. *                     pshouldbeep,pdone);
  765. *
  766. *        int ercode    Error code:
  767. *
  768. *             WN_NO_ERROR if okay;
  769. *             MN_READ_AB if user requested abort;
  770. *             MN_XMIT_NOBAR if user requested transmit while
  771. *               no bar shown;
  772. *             MN_UNKNOWN_AB if user pressed an unknown key
  773. *               the MN_UNKNOWN_TRANSMIT option in *poption
  774. *               was set;
  775. *             other if internal error.
  776. *
  777. *        BMENU *pmenu    Address of menu structure.
  778. *        const USER_RESPONSE *presp
  779. *                Address of structure containing the
  780. *                actual user response.
  781. *        int *poption    Address of option flag.
  782. *        BITEM **ppitem    Address of pointer to highlighted menu
  783. *                item (*pitem == NIL if none).
  784. *        int *pshouldbeep
  785. *                Address of flag indicating whether
  786. *                a beep was requested.
  787. *        int *pdone    Address of flag indicating whether
  788. *                calling function should return without
  789. *                further user input.
  790. *
  791. * Description    This function interprets the user's response (in *presp)
  792. *        and determines what action to take.
  793. *
  794. * Returns    ercode        Error code:
  795. *
  796. *             WN_NO_ERROR if okay;
  797. *             MN_READ_AB if user requested abort;
  798. *             MN_XMIT_NOBAR if user requested transmit while
  799. *               no bar shown;
  800. *             MN_UNKNOWN_AB if user pressed an unknown key
  801. *               the MN_UNKNOWN_TRANSMIT option in *poption
  802. *               was set;
  803. *             other if internal error.
  804. *
  805. *        b_wnerr     Unchanged if no error;
  806. *                same as "ercode" if error detected.
  807. *        *poption    The MN_KB_DISCARD bit may be set or
  808. *                cleared.
  809. *        *ppitem     Menu item to highlight (NIL if no
  810. *                highlight bar).
  811. *        *pshouldbeep    TRUE if beep requested;
  812. *                unchanged if no beep requested.
  813. *        *pdone        TRUE if caller should return;
  814. *                unchanged if further input okay.
  815. *
  816. **/
  817.  
  818.         /* Macro to do a for loop which makes the variable  */
  819.         /* qitem take on all of the pointer values from     */
  820.         /* pitem to the end of the item list in the menu's  */
  821.         /* item list.  ritem is initialized to be pitem.    */
  822.  
  823. #define FORALL(pitem)                             \
  824.             for (qitem = pmenu->pitems,  ritem = (pitem);    \
  825.              qitem != NIL;                     \
  826.              qitem = qitem->next)                 \
  827.             {                             \
  828.             if (qitem->signature != MN_ITEM_SIGN)         \
  829.                 wnretern (MN_BAD_ITEM);
  830.  
  831. #define FOREND()    }
  832.  
  833.         /* Macro which maximizes a "rangefunc".  This is    */
  834.         /* used by MN_UP, DOWN, RIGHT, and LEFT to determine*/
  835.         /* the correct place to move the highlight bar.     */
  836.         /*     Arguments:                    */
  837.         /*       matchrc -- Either "row" or "col"; the    */
  838.         /*              component of the item's       */
  839.         /*              position which is supposed to */
  840.         /*              match that of the other item. */
  841.         /*       diffrc --  Either "row" or "col"; the    */
  842.         /*              opposite of "matchrc".        */
  843.         /*       size --    Either height (if matchrc is  */
  844.         /*              "col") or width (if matchrc is*/
  845.         /*              "row) of menu's window data   */
  846.         /*              area.                */
  847.         /*       rangefunc- Either ULRANGE or DRRANGE.    */
  848.  
  849. #define IFMATCH(matchrc, diffrc, size, rangefunc, pitem)         \
  850.              if (( !(qitem->option & MN_PROTECT)) &&         \
  851.              (qitem->matchrc == (pitem)->matchrc) &&     \
  852.              (rangefunc ((pitem), qitem, diffrc, size) > \
  853.                rangefunc ((pitem), ritem, diffrc, size)))
  854.  
  855.  
  856.  
  857. #define DRRANGE(pitem, qitem, rowcol, size)                 \
  858.            ((qitem->rowcol <= (pitem)->rowcol)             \
  859.          ? ((pitem)->rowcol - qitem->rowcol) & wrap_mask     \
  860.          : ((size - qitem->rowcol) + (pitem)->rowcol))
  861.  
  862. #define ULRANGE(pitem, qitem, rowcol, size)                 \
  863.            ((qitem->rowcol >= (pitem)->rowcol)             \
  864.          ? (qitem->rowcol - (pitem)->rowcol) & wrap_mask     \
  865.          : ((size - (pitem)->rowcol) + qitem->rowcol))
  866.  
  867.     /* BEYOND:    Macro that maximizes a "beyondfunc":  returns       */
  868.     /* nonzero value if *qitem is a better candidate than *ritem.   */
  869.     /* Use is similar to IFMATCH.                    */
  870.     /*        size - height or width of data area.            */
  871.     /*        floor - preferred amount of motion (negative for upward */
  872.     /*            or leftward.                    */
  873.  
  874. #define BEYOND(matchrc,diffrc,size,beyondfunc,pitem,floor)         \
  875.         (    (!(qitem->option & MN_PROTECT))              \
  876.          && (qitem->matchrc == (pitem)->matchrc)             \
  877.          && (beyondfunc((pitem),qitem,diffrc,size,floor) >         \
  878.          beyondfunc((pitem),ritem,diffrc,size,floor)))
  879.  
  880. #define DRBEYOND(pitem,qitem,rowcol,size,floor)              \
  881.     (  (qitem->rowcol >= (pitem)->rowcol + (floor))          \
  882.      ? size - qitem->rowcol                      \
  883.      : qitem->rowcol - size)
  884.  
  885. #define ULBEYOND(pitem,qitem,rowcol,size,ceiling)             \
  886.     (  (qitem->rowcol <= (pitem)->rowcol + (ceiling))         \
  887.      ? qitem->rowcol                         \
  888.      : -1 - qitem->rowcol)
  889.  
  890.         /* Height of menu's data area.                      */
  891. #define H (pmenu->pwin->img.dim.h)
  892.  
  893.         /* Width of menu's data area.                       */
  894. #define W (pmenu->pwin->img.dim.w)
  895.  
  896.  
  897. static int interp_resp(pmenu,presp,poption,ppitem,
  898.                pshouldbeep,pdone)
  899. BMENU            *pmenu;
  900. const USER_RESPONSE *presp;
  901. int            *poption;
  902. BITEM           **ppitem;
  903. int            *pshouldbeep,*pdone;
  904. {
  905.     int    code;
  906.     BITEM *pitem,*qitem,*ritem;
  907.     int    result = WN_NO_ERROR;
  908.     int    action;
  909.     int    disp;
  910.     int    wrap_mask;
  911.  
  912.     pitem = *ppitem;
  913.  
  914.         /* Do operations that are different for mouse and   */
  915.         /* keystroke events:  in particular, get action code*/
  916.     switch (presp->mouse_or_key)
  917.     {
  918.     case KEYSTROKE:
  919.         if (presp->data.keystroke.pkey != NIL)
  920.         {
  921.         action = presp->data.keystroke.pkey->action;
  922.  
  923.         if (MNMOVE(action) == MN_SELECT)
  924.         {
  925.             const BKEYMAP *pkey;
  926.  
  927.         /* Move highlight bar to specified row/column of a  */
  928.         /* menu item.                        */
  929.             pkey = presp->data.keystroke.pkey;
  930.             if ((qitem =
  931.              mnmchitm (pmenu, pitem, pkey->row,
  932.                    pkey->col, 0, &code)) != NIL)
  933.             pitem = qitem;
  934.  
  935.             else if (code != WN_NO_ERROR)
  936.             {
  937.             *pdone = TRUE;
  938.             return (b_wnerr);
  939.             }
  940.         }
  941.         }
  942.         else    /* Unknown (or disabled) key pressed.        */
  943.         {
  944.         action = 0;
  945.  
  946.         /* Check whether we should beep when unknown key    */
  947.         /* pressed.                        */
  948.         if (*poption & MN_UNKNOWN_BEEP)
  949.             *pshouldbeep = TRUE;
  950.  
  951.         /* Check whether we should transmit when unknown    */
  952.         /* key pressed.                     */
  953.         if (*poption & MN_UNKNOWN_TRANSMIT)
  954.         {
  955.             result = wnerror(MN_UNKNOWN_AB);
  956.             *pdone = TRUE;
  957.         }
  958.         }
  959.         break;
  960.  
  961.     case MOUSE:
  962.         action = presp->data.mouse.pmouse->action;
  963.         if (MNMOVE(action) == MN_SELECT)
  964.         pitem = presp->data.mouse.pitem;
  965.         break;
  966.     }
  967.  
  968.     switch (MNMOVE(action))
  969.     {
  970.     case MN_UP:
  971.     case MN_DOWN:
  972.     case MN_RIGHT:
  973.     case MN_LEFT:
  974.     case MN_NEXT:
  975.     case MN_PREVIOUS:
  976.     case MN_PGDN:
  977.     case MN_PGUP:
  978.     case MN_PGRIGHT:
  979.     case MN_PGLEFT:
  980.         /* Handle relative movements specially:  if bar     */
  981.         /* not shown, use previously highlighted item.        */
  982.         /* This has the effect of showing the bar unless    */
  983.         /* MN_HIDE_BAR is also specified in the action code.*/
  984.         if (pitem == NIL)
  985.         pitem = pmenu->pbar_prev;
  986.         if (pitem != NIL)
  987.         {
  988.         /* wrap_mask is used by the DRRANGE and ULRANGE     */
  989.         /* macros for the MN_UP, MN_DOWN, MN_RIGHT, and     */
  990.         /* MN_LEFT motions.  When MN_NOWRAP is specified,   */
  991.         /* then wrap_mask is 0, thus causing DRRANGE and    */
  992.         /* ULRANGE to return 0 for coordinates in the        */
  993.         /* opposite direction.                    */
  994.         wrap_mask = ((action & MN_NOWRAP) ? 0 : ~0);
  995.  
  996.         switch (MNMOVE(action))
  997.         {
  998.         /* Move highlight bar up.  Wrap to bottom if at top.*/
  999.             case MN_UP:
  1000.             FORALL(pitem)
  1001.                 IFMATCH (col, row, H, ULRANGE, pitem)
  1002.                 ritem = qitem;
  1003.             FOREND ()
  1004.  
  1005.             pitem = ritem;
  1006.             break;
  1007.  
  1008.  
  1009.             case MN_DOWN:
  1010.         /* Move highlight bar down.  Wrap to top if at        */
  1011.         /* bottom.                        */
  1012.             FORALL(pitem)
  1013.                 IFMATCH (col, row, H, DRRANGE, pitem)
  1014.                 ritem = qitem;
  1015.             FOREND ()
  1016.  
  1017.             pitem = ritem;
  1018.             break;
  1019.  
  1020.  
  1021.             case MN_RIGHT:
  1022.         /* Move highlight bar right.  Wrap to left if at    */
  1023.         /* rightmost item.                    */
  1024.             FORALL(pitem)
  1025.                 IFMATCH (row, col, W, DRRANGE, pitem)
  1026.                 ritem = qitem;
  1027.             FOREND ()
  1028.  
  1029.             pitem = ritem;
  1030.             break;
  1031.  
  1032.  
  1033.             case MN_LEFT:
  1034.         /* Move highlight bar left.  Wrap to right if at    */
  1035.         /* leftmost item.                    */
  1036.             FORALL(pitem)
  1037.                 IFMATCH (row, col, W, ULRANGE, pitem)
  1038.                 ritem = qitem;
  1039.             FOREND ()
  1040.  
  1041.             pitem = ritem;
  1042.             break;
  1043.  
  1044.  
  1045.             case MN_NEXT:
  1046.         /* Move highlight bar to "logical-next" menu        */
  1047.         /* position.  This is the next item in the order    */
  1048.         /* that items were entered by MNITEM and/or        */
  1049.         /* MNITMKEY.  Wrap to beginning if at end unless    */
  1050.         /* MN_NOWRAP specified.                 */
  1051.             if (action & MN_NOWRAP)
  1052.             {
  1053.                 for (qitem =  pitem->next;
  1054.                  qitem != NIL;
  1055.                  qitem =  qitem->next)
  1056.                 {
  1057.                 if (!(qitem->option & MN_PROTECT))
  1058.                 {
  1059.                     pitem = qitem;
  1060.                     break;
  1061.                 }
  1062.                 }
  1063.             }
  1064.             else
  1065.             {       /* MNMCHITM automatically wraps. */
  1066.                 pitem = mnmchitm (pmenu, (*ppitem)->next,
  1067.                           -1, -1, 0, &code);
  1068.                 if (code != WN_NO_ERROR)
  1069.                 {
  1070.                 *pdone = TRUE;
  1071.                 return (b_wnerr);
  1072.                 }
  1073.             }
  1074.             break;
  1075.  
  1076.  
  1077.             case MN_PREVIOUS:
  1078.         /* Move highlight bar to "logical-previous" menu    */
  1079.         /* position.  This is the previous item in the        */
  1080.         /* order that items were entered by MNITEM and/or   */
  1081.         /* MNITMKEY.  Wrap to end if at beginning.        */
  1082.             if (pitem == mnmchitm (pmenu, pmenu->pitems,
  1083.                            -1, -1, 0, &code))
  1084.             {         /* pitem is first in list. */
  1085.                 if (action & MN_NOWRAP)
  1086.                 ritem = pitem;
  1087.                 else
  1088.                 {         /* Find last in list.        */
  1089.                 FORALL(pitem)
  1090.                     if (! (qitem->option & MN_PROTECT))
  1091.                     ritem = qitem;
  1092.                 FOREND ()
  1093.                 }
  1094.             }
  1095.             else
  1096.             {
  1097.                 if (code != WN_NO_ERROR)
  1098.                 {
  1099.                 *pdone = TRUE;
  1100.                 return (b_wnerr);
  1101.                 }
  1102.  
  1103.                 for (ritem = pitem,  qitem = pmenu->pitems;
  1104.                  qitem != pitem;
  1105.                  qitem = qitem->next)
  1106.  
  1107.                 if ( !(qitem->option & MN_PROTECT))
  1108.                     ritem = qitem;
  1109.             }
  1110.             pitem = ritem;
  1111.             break;
  1112.  
  1113.             case MN_PGDN:
  1114.             disp = jump(pmenu->pwin,MN_PGDN);
  1115.             FORALL(pitem)
  1116.                 if (BEYOND(col,row,H,DRBEYOND,pitem,disp))
  1117.                 ritem = qitem;
  1118.             FOREND()
  1119.             pitem = ritem;
  1120.             break;
  1121.  
  1122.             case MN_PGUP:
  1123.             disp = jump(pmenu->pwin,MN_PGUP);
  1124.             FORALL(pitem)
  1125.                 if (BEYOND(col,row,H,ULBEYOND,pitem,disp))
  1126.                 ritem = qitem;
  1127.             FOREND()
  1128.             pitem = ritem;
  1129.             break;
  1130.  
  1131.             case MN_PGRIGHT:
  1132.             disp = jump(pmenu->pwin,MN_PGRIGHT);
  1133.             FORALL(pitem)
  1134.                 if (BEYOND(row,col,W,DRBEYOND,pitem,disp))
  1135.                 ritem = qitem;
  1136.             FOREND()
  1137.             pitem = ritem;
  1138.             break;
  1139.  
  1140.             case MN_PGLEFT:
  1141.             disp = jump(pmenu->pwin,MN_PGLEFT);
  1142.             FORALL(pitem)
  1143.                 if (BEYOND(row,col,W,ULBEYOND,pitem,disp))
  1144.                 ritem = qitem;
  1145.             FOREND()
  1146.             pitem = ritem;
  1147.             break;
  1148.         }
  1149.         }
  1150.         break;
  1151.  
  1152.  
  1153.     case MN_FIRST:
  1154.         /* Move highlight bar to first item entered by        */
  1155.         /* MNITEM and/or MNITMKEY.                */
  1156.         pitem = mnmchitm (pmenu, pmenu->pitems, -1, -1, 0, &code);
  1157.         if (code != WN_NO_ERROR)
  1158.         {
  1159.         *pdone = TRUE;
  1160.         return (b_wnerr);
  1161.         }
  1162.         break;
  1163.  
  1164.  
  1165.     case MN_LAST:
  1166.         /* Move highlight bar to last item entered by        */
  1167.         /* MNITEM and/or MNITMKEY.                */
  1168.         FORALL(pitem)
  1169.         if ( !(qitem->option & MN_PROTECT))
  1170.             ritem = qitem;
  1171.         FOREND ()
  1172.  
  1173.         pitem = ritem;
  1174.         break;
  1175.     }
  1176.  
  1177.     if (action & MN_SHOW_BAR)
  1178.     {
  1179.     if (pitem == NIL)        /* If no menu item lit,     */
  1180.         pitem = pmenu->pbar_prev;    /* find an item to highlight*/
  1181.     }
  1182.     else if (action & MN_HIDE_BAR)
  1183.     {
  1184.     if (pitem != NIL)        /* If an item is lit,        */
  1185.     {
  1186.         pmenu->pbar_prev = pitem;    /* save location        */
  1187.         pitem         = NIL;    /* and unlight the bar.     */
  1188.     }
  1189.     }
  1190.  
  1191.     if (action & MN_BEEP)
  1192.     *pshouldbeep = TRUE;        /* Forward beep request.    */
  1193.  
  1194.     if (action & MN_KBIGNORE)
  1195.     *poption |= MN_KBDISCARD;    /* Ignore keystrokes.        */
  1196.     else
  1197.     *poption &= ~MN_KBDISCARD;    /* Begin watching keys.     */
  1198.  
  1199.     if (action & MN_ABORT)
  1200.     {                    /* User requested abort.    */
  1201.     result     = wnerror(MN_READ_AB);
  1202.     *pdone     = TRUE;
  1203.     }
  1204.     else if (action & MN_TRANSMIT)
  1205.     {                    /* User requested transmit. */
  1206.     if (pitem == NIL)
  1207.         result = wnerror(MN_XMIT_NOBAR);   /* Error if transmit */
  1208.                            /* while no item lit.*/
  1209.     *pdone     = TRUE;
  1210.     }
  1211.  
  1212.     *ppitem = pitem;
  1213.     return result;
  1214. }
  1215.  
  1216. /**
  1217. *
  1218. * Name        jump -- Adjust window origin for MN_PGUP, MN_PGDN,
  1219. *            MN_PGRIGHT, or MN_PGLEFT motion.
  1220. *
  1221. * Synopsis    displacement = jump(pwin,dir);
  1222. *
  1223. *        int displacement  Preferred amount of motion by
  1224. *                  highlight bar (negative for
  1225. *                  upward or toward left edge).
  1226. *        BWINDOW *pwin      Address of menu's window.
  1227. *        int dir       MN_PGUP, MN_PGDN, MN_PGRIGHT, or
  1228. *                  MN_PGLEFT.
  1229. *
  1230. * Description    This function moves the window's origin and
  1231. *        computes the distance the highlight bar should move.
  1232. *
  1233. * Returns    displacement      Preferred amount of motion by
  1234. *                  highlight bar (negative for
  1235. *                  upward or toward left edge).
  1236. *        b_wnerr       Window error if any.
  1237. *
  1238. **/
  1239.  
  1240. static int jump(pwin,dir)
  1241. BWINDOW *pwin;
  1242. int     dir;
  1243. {
  1244.     int new_origin_row,old_origin_row,new_origin_col,old_origin_col;
  1245.     int displacement;
  1246.  
  1247.     new_origin_row = old_origin_row = pwin->data_origin.row;
  1248.     new_origin_col = old_origin_col = pwin->data_origin.col;
  1249.  
  1250.     switch (dir)
  1251.     {
  1252.     case MN_PGDN:
  1253.         new_origin_row = utmin(old_origin_row + wnview_h(pwin) - 1,
  1254.                    wndata_h(pwin) - wnview_h(pwin));
  1255.         if (new_origin_row == old_origin_row)
  1256.         displacement = wnview_h(pwin) - 1;
  1257.         else
  1258.         displacement = new_origin_row - old_origin_row;
  1259.         break;
  1260.  
  1261.     case MN_PGUP:
  1262.         new_origin_row = utmax(old_origin_row - (wnview_h(pwin) - 1),
  1263.                    0);
  1264.         if (new_origin_row == old_origin_row)
  1265.         displacement = 1 - wnview_h(pwin);
  1266.         else
  1267.         displacement = new_origin_row - old_origin_row;
  1268.         break;
  1269.  
  1270.     case MN_PGRIGHT:
  1271.         new_origin_col = utmin(old_origin_col + wnview_w(pwin) - 1,
  1272.                    wndata_w(pwin) - wnview_w(pwin));
  1273.         if (new_origin_col == old_origin_col)
  1274.         displacement = wnview_w(pwin) - 1;
  1275.         else
  1276.         displacement = new_origin_col - old_origin_col;
  1277.         break;
  1278.  
  1279.     case MN_PGLEFT:
  1280.         new_origin_col = utmax(old_origin_col - (wnview_w(pwin) - 1),
  1281.                    0);
  1282.         if (new_origin_col == old_origin_col)
  1283.         displacement = 1 - wnview_w(pwin);
  1284.         else
  1285.         displacement = new_origin_col - old_origin_col;
  1286.         break;
  1287.     }
  1288.  
  1289.     wnorigin(pwin,new_origin_row,new_origin_col,WN_NO_UPDATE);
  1290.     return displacement;
  1291. }
  1292.