home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / v_scroll.c < prev    next >
C/C++ Source or Header  |  1996-04-27  |  12KB  |  475 lines

  1. /*-
  2.  * Copyright (c) 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)v_scroll.c    10.9 (Berkeley) 4/27/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24.  
  25. #include "../common/common.h"
  26. #include "vi.h"
  27.  
  28. static void goto_adjust __P((VICMD *));
  29.  
  30. /*
  31.  * The historic vi had a problem in that all movements were by physical
  32.  * lines, not by logical, or screen lines.  Arguments can be made that this
  33.  * is the right thing to do.  For example, single line movements, such as
  34.  * 'j' or 'k', should probably work on physical lines.  Commands like "dj",
  35.  * or "j.", where '.' is a change command, make more sense for physical lines
  36.  * than they do for logical lines.
  37.  *
  38.  * These arguments, however, don't apply to scrolling commands like ^D and
  39.  * ^F -- if the window is fairly small, using physical lines can result in
  40.  * a half-page scroll repainting the entire screen, which is not what the
  41.  * user wanted.  Second, if the line is larger than the screen, using physical
  42.  * lines can make it impossible to display parts of the line -- there aren't
  43.  * any commands that don't display the beginning of the line in historic vi,
  44.  * and if both the beginning and end of the line can't be on the screen at
  45.  * the same time, you lose.  This is even worse in the case of the H, L, and
  46.  * M commands -- for large lines, they may all refer to the same line and
  47.  * will result in no movement at all.
  48.  *
  49.  * Another issue is that page and half-page scrolling commands historically
  50.  * moved to the first non-blank character in the new line.  If the line is
  51.  * approximately the same size as the screen, this loses because the cursor
  52.  * before and after a ^D, may refer to the same location on the screen.  In
  53.  * this implementation, scrolling commands set the cursor to the first non-
  54.  * blank character if the line changes because of the scroll.  Otherwise,
  55.  * the cursor is left alone.
  56.  *
  57.  * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the
  58.  * cursor positioning commands (H, L, M) commands using logical lines, not
  59.  * physical.
  60.  */
  61.  
  62. /*
  63.  * v_lgoto -- [count]G
  64.  *    Go to first non-blank character of the line count, the last line
  65.  *    of the file by default.
  66.  *
  67.  * PUBLIC: int v_lgoto __P((SCR *, VICMD *));
  68.  */
  69. int
  70. v_lgoto(sp, vp)
  71.     SCR *sp;
  72.     VICMD *vp;
  73. {
  74.     recno_t nlines;
  75.  
  76.     if (F_ISSET(vp, VC_C1SET)) {
  77.         if (!db_exist(sp, vp->count)) {
  78.             /*
  79.              * !!!
  80.              * Historically, 1G was legal in an empty file.
  81.              */
  82.             if (vp->count == 1) {
  83.                 if (db_last(sp, &nlines))
  84.                     return (1);
  85.                 if (nlines == 0)
  86.                     return (0);
  87.             }
  88.             v_eof(sp, &vp->m_start);
  89.             return (1);
  90.         }
  91.         vp->m_stop.lno = vp->count;
  92.     } else {
  93.         if (db_last(sp, &nlines))
  94.             return (1);
  95.         vp->m_stop.lno = nlines ? nlines : 1;
  96.     }
  97.     goto_adjust(vp);
  98.     return (0);
  99. }
  100.  
  101. /*
  102.  * v_home -- [count]H
  103.  *    Move to the first non-blank character of the logical line
  104.  *    count - 1 from the top of the screen, 0 by default.
  105.  *
  106.  * PUBLIC: int v_home __P((SCR *, VICMD *));
  107.  */
  108. int
  109. v_home(sp, vp)
  110.     SCR *sp;
  111.     VICMD *vp;
  112. {
  113.     if (vs_sm_position(sp, &vp->m_stop,
  114.         F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_TOP))
  115.         return (1);
  116.     goto_adjust(vp);
  117.     return (0);
  118. }
  119.  
  120. /*
  121.  * v_middle -- M
  122.  *    Move to the first non-blank character of the logical line
  123.  *    in the middle of the screen.
  124.  *
  125.  * PUBLIC: int v_middle __P((SCR *, VICMD *));
  126.  */
  127. int
  128. v_middle(sp, vp)
  129.     SCR *sp;
  130.     VICMD *vp;
  131. {
  132.     /*
  133.      * Yielding to none in our quest for compatibility with every
  134.      * historical blemish of vi, no matter how strange it might be,
  135.      * we permit the user to enter a count and then ignore it.
  136.      */
  137.     if (vs_sm_position(sp, &vp->m_stop, 0, P_MIDDLE))
  138.         return (1);
  139.     goto_adjust(vp);
  140.     return (0);
  141. }
  142.  
  143. /*
  144.  * v_bottom -- [count]L
  145.  *    Move to the first non-blank character of the logical line
  146.  *    count - 1 from the bottom of the screen, 0 by default.
  147.  *
  148.  * PUBLIC: int v_bottom __P((SCR *, VICMD *));
  149.  */
  150. int
  151. v_bottom(sp, vp)
  152.     SCR *sp;
  153.     VICMD *vp;
  154. {
  155.     if (vs_sm_position(sp, &vp->m_stop,
  156.         F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_BOTTOM))
  157.         return (1);
  158.     goto_adjust(vp);
  159.     return (0);
  160. }
  161.  
  162. static void
  163. goto_adjust(vp)
  164.     VICMD *vp;
  165. {
  166.     /* Guess that it's the end of the range. */
  167.     vp->m_final = vp->m_stop;
  168.  
  169.     /*
  170.      * Non-motion commands move the cursor to the end of the range, and
  171.      * then to the NEXT nonblank of the line.  Historic vi always moved
  172.      * to the first nonblank in the line; since the H, M, and L commands
  173.      * are logical motions in this implementation, we do the next nonblank
  174.      * so that it looks approximately the same to the user.  To make this
  175.      * happen, the VM_RCM_SETNNB flag is set in the vcmd.c command table.
  176.      *
  177.      * If it's a motion, it's more complicated.  The best possible solution
  178.      * is probably to display the first nonblank of the line the cursor
  179.      * will eventually rest on.  This is tricky, particularly given that if
  180.      * the associated command is a delete, we don't yet know what line that
  181.      * will be.  So, we clear the VM_RCM_SETNNB flag, and set the first
  182.      * nonblank flag (VM_RCM_SETFNB).  Note, if the lines are sufficiently
  183.      * long, this can cause the cursor to warp out of the screen.  It's too
  184.      * hard to fix.
  185.      *
  186.      * XXX
  187.      * The G command is always first nonblank, so it's okay to reset it.
  188.      */
  189.     if (ISMOTION(vp)) {
  190.         F_CLR(vp, VM_RCM_MASK);
  191.         F_SET(vp, VM_RCM_SETFNB);
  192.     } else
  193.         return;
  194.  
  195.     /*
  196.      * If moving backward in the file, delete and yank move to the end
  197.      * of the range, unless the line didn't change, in which case yank
  198.      * doesn't move.  If moving forward in the file, delete and yank
  199.      * stay at the start of the range.  Ignore others.
  200.      */
  201.     if (vp->m_stop.lno < vp->m_start.lno ||
  202.         vp->m_stop.lno == vp->m_start.lno &&
  203.         vp->m_stop.cno < vp->m_start.cno) {
  204.         if (ISCMD(vp->rkp, 'y') && vp->m_stop.lno == vp->m_start.lno)
  205.             vp->m_final = vp->m_start;
  206.     } else
  207.         vp->m_final = vp->m_start;
  208. }
  209.  
  210. /*
  211.  * v_up -- [count]^P, [count]k, [count]-
  212.  *    Move up by lines.
  213.  *
  214.  * PUBLIC: int v_up __P((SCR *, VICMD *));
  215.  */
  216. int
  217. v_up(sp, vp)
  218.     SCR *sp;
  219.     VICMD *vp;
  220. {
  221.     recno_t lno;
  222.  
  223.     lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
  224.     if (vp->m_start.lno <= lno) {
  225.         v_sof(sp, &vp->m_start);
  226.         return (1);
  227.     }
  228.     vp->m_stop.lno = vp->m_start.lno - lno;
  229.     vp->m_final = vp->m_stop;
  230.     return (0);
  231. }
  232.  
  233. /*
  234.  * v_cr -- [count]^M
  235.  *    In a script window, send the line to the shell.
  236.  *    In a regular window, move down by lines.
  237.  *
  238.  * PUBLIC: int v_cr __P((SCR *, VICMD *));
  239.  */
  240. int
  241. v_cr(sp, vp)
  242.     SCR *sp;
  243.     VICMD *vp;
  244. {
  245.     /* If it's a colon command-line edit window, it's an ex command. */
  246.     if (F_ISSET(sp, SC_COMEDIT))
  247.         return (v_ecl_exec(sp));
  248.  
  249.     /* If it's a script window, exec the line. */
  250.     if (F_ISSET(sp, SC_SCRIPT))
  251.         return (sscr_exec(sp, vp->m_start.lno));
  252.  
  253.     /* Otherwise, it's the same as v_down(). */
  254.     return (v_down(sp, vp));
  255. }
  256.  
  257. /*
  258.  * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
  259.  *    Move down by lines.
  260.  *
  261.  * PUBLIC: int v_down __P((SCR *, VICMD *));
  262.  */
  263. int
  264. v_down(sp, vp)
  265.     SCR *sp;
  266.     VICMD *vp;
  267. {
  268.     recno_t lno;
  269.  
  270.     lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
  271.     if (!db_exist(sp, lno)) {
  272.         v_eof(sp, &vp->m_start);
  273.         return (1);
  274.     }
  275.     vp->m_stop.lno = lno;
  276.     vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
  277.     return (0);
  278. }
  279.  
  280. /*
  281.  * v_hpageup -- [count]^U
  282.  *    Page up half screens.
  283.  *
  284.  * PUBLIC: int v_hpageup __P((SCR *, VICMD *));
  285.  */
  286. int
  287. v_hpageup(sp, vp)
  288.     SCR *sp;
  289.     VICMD *vp;
  290. {
  291.     /*
  292.      * Half screens always succeed unless already at SOF.
  293.      *
  294.      * !!!
  295.      * Half screens set the scroll value, even if the command
  296.      * ultimately failed, in historic vi.  Probably a don't care.
  297.      */
  298.     if (F_ISSET(vp, VC_C1SET))
  299.         sp->defscroll = vp->count;
  300.     if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_U))
  301.         return (1);
  302.     vp->m_final = vp->m_stop;
  303.     return (0);
  304. }
  305.  
  306. /*
  307.  * v_hpagedown -- [count]^D
  308.  *    Page down half screens.
  309.  *
  310.  * PUBLIC: int v_hpagedown __P((SCR *, VICMD *));
  311.  */
  312. int
  313. v_hpagedown(sp, vp)
  314.     SCR *sp;
  315.     VICMD *vp;
  316. {
  317.     /*
  318.      * Half screens always succeed unless already at EOF.
  319.      *
  320.      * !!!
  321.      * Half screens set the scroll value, even if the command
  322.      * ultimately failed, in historic vi.  Probably a don't care.
  323.      */
  324.     if (F_ISSET(vp, VC_C1SET))
  325.         sp->defscroll = vp->count;
  326.     if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_D))
  327.         return (1);
  328.     vp->m_final = vp->m_stop;
  329.     return (0);
  330. }
  331.  
  332. /*
  333.  * v_pagedown -- [count]^F
  334.  *    Page down full screens.
  335.  * !!!
  336.  * Historic vi did not move to the EOF if the screen couldn't move, i.e.
  337.  * if EOF was already displayed on the screen.  This implementation does
  338.  * move to EOF in that case, making ^F more like the the historic ^D.
  339.  *
  340.  * PUBLIC: int v_pagedown __P((SCR *, VICMD *));
  341.  */
  342. int
  343. v_pagedown(sp, vp)
  344.     SCR *sp;
  345.     VICMD *vp;
  346. {
  347.     recno_t offset;
  348.  
  349.     /*
  350.      * !!!
  351.      * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
  352.      *
  353.      *    top_line = top_line + count * (window - 2);
  354.      *
  355.      * which was historically wrong.  The correct one is:
  356.      *
  357.      *    top_line = top_line + count * window - 2;
  358.      *
  359.      * i.e. the two line "overlap" was only subtracted once.  Which
  360.      * makes no sense, but then again, an overlap makes no sense for
  361.      * any screen but the "next" one anyway.  We do it the historical
  362.      * way as there's no good reason to change it.
  363.      *
  364.      * If the screen has been split, use the smaller of the current
  365.      * window size and the window option value.
  366.      *
  367.      * It possible for this calculation to be less than 1; move at
  368.      * least one line.
  369.      */
  370.     offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ?
  371.         MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW));
  372.     offset = offset <= 2 ? 1 : offset - 2;
  373.     if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_F))
  374.         return (1);
  375.     vp->m_final = vp->m_stop;
  376.     return (0);
  377. }
  378.  
  379. /*
  380.  * v_pageup -- [count]^B
  381.  *    Page up full screens.
  382.  *
  383.  * !!!
  384.  * Historic vi did not move to the SOF if the screen couldn't move, i.e.
  385.  * if SOF was already displayed on the screen.  This implementation does
  386.  * move to SOF in that case, making ^B more like the the historic ^U.
  387.  *
  388.  * PUBLIC: int v_pageup __P((SCR *, VICMD *));
  389.  */
  390. int
  391. v_pageup(sp, vp)
  392.     SCR *sp;
  393.     VICMD *vp;
  394. {
  395.     recno_t offset;
  396.  
  397.     /*
  398.      * !!!
  399.      * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
  400.      *
  401.      *    top_line = top_line - count * (window - 2);
  402.      *
  403.      * which was historically wrong.  The correct one is:
  404.      *
  405.      *    top_line = (top_line - count * window) + 2;
  406.      *
  407.      * A simpler expression is that, as with ^F, we scroll exactly:
  408.      *
  409.      *    count * window - 2
  410.      *
  411.      * lines.
  412.      *
  413.      * Bizarre.  As with ^F, an overlap makes no sense for anything
  414.      * but the first screen.  We do it the historical way as there's
  415.      * no good reason to change it.
  416.      *
  417.      * If the screen has been split, use the smaller of the current
  418.      * window size and the window option value.
  419.      *
  420.      * It possible for this calculation to be less than 1; move at
  421.      * least one line.
  422.      */
  423.     offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ?
  424.         MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW));
  425.     offset = offset <= 2 ? 1 : offset - 2;
  426.     if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_B))
  427.         return (1);
  428.     vp->m_final = vp->m_stop;
  429.     return (0);
  430. }
  431.  
  432. /*
  433.  * v_lineup -- [count]^Y
  434.  *    Page up by lines.
  435.  *
  436.  * PUBLIC: int v_lineup __P((SCR *, VICMD *));
  437.  */
  438. int
  439. v_lineup(sp, vp)
  440.     SCR *sp;
  441.     VICMD *vp;
  442. {
  443.     /*
  444.      * The cursor moves down, staying with its original line, unless it
  445.      * reaches the bottom of the screen.
  446.      */
  447.     if (vs_sm_scroll(sp,
  448.         &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_Y))
  449.         return (1);
  450.     vp->m_final = vp->m_stop;
  451.     return (0);
  452. }
  453.  
  454. /*
  455.  * v_linedown -- [count]^E
  456.  *    Page down by lines.
  457.  *
  458.  * PUBLIC: int v_linedown __P((SCR *, VICMD *));
  459.  */
  460. int
  461. v_linedown(sp, vp)
  462.     SCR *sp;
  463.     VICMD *vp;
  464. {
  465.     /*
  466.      * The cursor moves up, staying with its original line, unless it
  467.      * reaches the top of the screen.
  468.      */
  469.     if (vs_sm_scroll(sp,
  470.         &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_E))
  471.         return (1);
  472.     vp->m_final = vp->m_stop;
  473.     return (0);
  474. }
  475.