home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 365_02 / redraw.c < prev    next >
C/C++ Source or Header  |  1992-04-06  |  27KB  |  1,296 lines

  1. /* redraw.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains functions that draw text on the screen.  The major entry
  12.  * points are:
  13.  *    redrawrange()    - called from modify.c to give hints about what parts
  14.  *              of the screen need to be redrawn.
  15.  *    redraw()    - redraws the screen (or part of it) and positions
  16.  *              the cursor where it belongs.
  17.  *    idx2col()    - converts a markidx() value to a logical column number.
  18.  */
  19.  
  20. #include "config.h"
  21. #include "vi.h"
  22.  
  23. /* This variable contains the line number that smartdrawtext() knows best */
  24. static long smartlno;
  25.  
  26. /* This function remembers where changes were made, so that the screen can be
  27.  * redraw in a more efficient manner.
  28.  */
  29. static long    redrawafter;    /* line# of first line that must be redrawn */
  30. static long    preredraw;    /* line# of last line changed, before change */
  31. static long    postredraw;    /* line# of last line changed, after change */
  32. static int    mustredraw;    /* boolean: anything forcing a screen update? */
  33. void redrawrange(after, pre, post)
  34.     long    after;    /* lower bound of redrawafter */
  35.     long    pre;    /* upper bound of preredraw */
  36.     long    post;    /* upper bound of postredraw */
  37. {
  38.     if (after == redrawafter)
  39.     {
  40.         /* multiple insertions/deletions at the same place -- combine
  41.          * them
  42.          */
  43.         preredraw -= (post - pre);
  44.         if (postredraw < post)
  45.         {
  46.             preredraw += (post - postredraw);
  47.             postredraw = post;
  48.         }
  49.         if (redrawafter > preredraw)
  50.         {
  51.             redrawafter = preredraw;
  52.         }
  53.         if (redrawafter < 1L)
  54.         {
  55.             redrawafter = 0L;
  56.             preredraw = postredraw = INFINITY;
  57.         }
  58.     }
  59.     else if (postredraw > 0L)
  60.     {
  61.         /* multiple changes in different places -- redraw everything
  62.          * after "after".
  63.          */
  64.         postredraw = preredraw = INFINITY;
  65.         if (after < redrawafter)
  66.             redrawafter = after;
  67.     }
  68.     else
  69.     {
  70.         /* first change */
  71.         redrawafter = after;
  72.         preredraw = pre;
  73.         postredraw = post;
  74.     }
  75.     mustredraw = TRUE;
  76. }
  77.  
  78.  
  79. #ifndef NO_CHARATTR
  80. /* see if a given line uses character attribute strings */
  81. static int hasattr(lno, text)
  82.     long        lno;    /* the line# of the cursor */
  83.     REG char    *text;    /* the text of the line, from fetchline */
  84. {
  85.     static long    plno;    /* previous line number */
  86.     static long    chgs;    /* previous value of changes counter */
  87.     static int    panswer;/* previous answer */
  88.     char        *scan;
  89.  
  90.     /* if charattr is off, then the answer is "no, it doesn't" */
  91.     if (!*o_charattr)
  92.     {
  93.         chgs = 0; /* <- forces us to check if charattr is later set */
  94.         return FALSE;
  95.     }
  96.  
  97.     /* if we already know the answer, return it... */
  98.     if (lno == plno && chgs == changes)
  99.     {
  100.         return panswer;
  101.     }
  102.  
  103.     /* get the line & look for "\fX" */
  104.     if (!text[0] || !text[1] || !text[2])
  105.     {
  106.         panswer = FALSE;
  107.     }
  108.     else
  109.     {
  110.         for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
  111.         {
  112.         }
  113.         panswer = (scan[2] != '\0');
  114.     }
  115.  
  116.     /* save the results */
  117.     plno = lno;
  118.     chgs = changes;
  119.  
  120.     /* return the results */
  121.     return panswer;
  122. }
  123. #endif
  124.  
  125.  
  126. #ifndef NO_VISIBLE
  127. /* This function checks to make sure that the correct lines are shown in
  128.  * reverse-video.  This is used to handle the "v" and "V" commands.
  129.  */
  130. static long    vizlow, vizhigh;    /* the starting and ending lines */
  131. static int    vizleft, vizright;    /* starting & ending indicies */
  132. static int    vizchange;        /* boolean: must use stupid drawtext? */
  133. static void setviz(curs)
  134.     MARK        curs;
  135. {
  136.     long        newlow, newhigh;
  137.     long        extra = 0L;
  138.  
  139.     /* for now, assume the worst... */
  140.     vizchange = TRUE;
  141.  
  142.     /* set newlow & newhigh according to V_from and cursor */
  143.     if (!V_from)
  144.     {
  145.         /* no lines should have reverse-video */
  146.         if (vizlow)
  147.         {
  148.             redrawrange(vizlow, vizhigh + 1L, vizhigh + 1L);
  149.             vizlow = vizhigh = 0L;
  150.         }
  151.         else
  152.         {
  153.             vizchange = FALSE;
  154.         }
  155.         return;
  156.     }
  157.  
  158.     /* figure out which lines *SHOULD* have hilites */
  159.     if (V_from < curs)
  160.     {
  161.         newlow = markline(V_from);
  162.         newhigh = markline(curs);
  163.         vizleft = markidx(V_from);
  164.         vizright = markidx(curs) + 1;
  165.     }
  166.     else
  167.     {
  168.         newlow = markline(curs);
  169.         newhigh = markline(V_from);
  170.         vizleft = markidx(curs);
  171.         vizright = markidx(V_from) + 1;
  172.     }
  173.  
  174.     /* adjust for line-mode hiliting */
  175.     if (V_linemd)
  176.     {
  177.         vizleft = 0;
  178.         vizright = BLKSIZE - 1;
  179.     }
  180.     else
  181.     {
  182.         extra = 1L;
  183.     }
  184.  
  185.     /* arrange for the necessary lines to be redrawn */
  186.     if (vizlow == 0L)
  187.     {
  188.         /* just starting to redraw */
  189.         redrawrange(newlow, newhigh, newhigh);
  190.     }
  191.     else
  192.     {
  193.         /* Were new lines added/removed at the front? */
  194.         if (newlow != vizlow)
  195.         {
  196.             if (newlow < vizlow)
  197.                 redrawrange(newlow, vizlow + extra, vizlow + extra);
  198.             else
  199.                 redrawrange(vizlow, newlow + extra, newlow + extra);
  200.         }
  201.  
  202.         /* Were new lines added/removed at the back? */
  203.         if (newhigh != vizhigh)
  204.         {
  205.             if (newhigh < vizhigh)
  206.                 redrawrange(newhigh + 1L - extra, vizhigh + 1L, vizhigh + 1L);
  207.             else
  208.                 redrawrange(vizhigh + 1L - extra, newhigh, newhigh);
  209.         }
  210.     }
  211.  
  212.     /* remember which lines will contain hilighted text now */
  213.     vizlow = newlow;
  214.     vizhigh = newhigh;
  215. }
  216. #endif /* !NO_VISIBLE */
  217.  
  218.  
  219. /* This function converts a MARK to a column number.  It doesn't automatically
  220.  * adjust for leftcol; that must be done by the calling function
  221.  */
  222. int idx2col(curs, text, inputting)
  223.     MARK        curs;    /* the line# & index# of the cursor */
  224.     REG char    *text;    /* the text of the line, from fetchline */
  225.     int        inputting;    /* boolean: called from input() ? */
  226. {
  227.     static MARK    pcursor;/* previous cursor, for possible shortcut */
  228.     static MARK    pcol;    /* column number for pcol */
  229.     static long    chgs;    /* previous value of changes counter */
  230.     REG int        col;    /* used to count column numbers */
  231.     REG int        idx;    /* used to count down the index */
  232.     REG int        i;
  233.  
  234.     /* for now, assume we have to start counting at the left edge */
  235.     col = 0;
  236.     idx = markidx(curs);
  237.  
  238.     /* if the file hasn't changed & line number is the same & it has no
  239.      * embedded character attribute strings, can we do shortcuts?
  240.      */
  241.     if (chgs == changes
  242.      && !((curs ^ pcursor) & ~(BLKSIZE - 1))
  243. #ifndef NO_CHARATTR
  244.      && !hasattr(markline(curs), text)
  245. #endif
  246.     )
  247.     {
  248.         /* no movement? */
  249.         if (curs == pcursor)
  250.         {
  251.             /* return the column of the char; for tabs, return its last column */
  252.             if (text[idx] == '\t' && !inputting && !*o_list)
  253.             {
  254.                 return pcol + *o_tabstop - (pcol % *o_tabstop) - 1;
  255.             }
  256.             else
  257.             {
  258.                 return pcol;
  259.             }
  260.         }
  261.  
  262.         /* movement to right? */
  263.         if (curs > pcursor)
  264.         {
  265.             /* start counting from previous place */
  266.             col = pcol;
  267.             idx = markidx(curs) - markidx(pcursor);
  268.             text += markidx(pcursor);
  269.         }
  270.     }
  271.  
  272.     /* count over to the char after the idx position */
  273.     while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */
  274.     {
  275.         if (i == '\t' && !*o_list)
  276.         {
  277.             col += *o_tabstop;
  278.             col -= col % *o_tabstop;
  279.         }
  280.         else if (i >= '\0' && i < ' ' || i == '\177')
  281.         {
  282.             col += 2;
  283.         }
  284. #ifndef NO_CHARATTR
  285.         else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
  286.         {
  287.             text += 2; /* plus one more at bottom of loop */
  288.             idx -= 2;
  289.         }            
  290. #endif
  291.         else
  292.         {
  293.             col++;
  294.         }
  295.         text++;
  296.         idx--;
  297.     }
  298.  
  299.     /* save stuff to speed next call */
  300.     pcursor = curs;
  301.     pcol = col;
  302.     chgs = changes;
  303.  
  304.     /* return the column of the char; for tabs, return its last column */
  305.     if (*text == '\t' && !inputting && !*o_list)
  306.     {
  307.         return col + *o_tabstop - (col % *o_tabstop) - 1;
  308.     }
  309.     else
  310.     {
  311.         return col;
  312.     }
  313. }
  314.  
  315.  
  316. /* This function is similar to idx2col except that it takes care of sideways
  317.  * scrolling - for the given line, at least.
  318.  */
  319. int mark2phys(m, text, inputting)
  320.     MARK    m;        /* a mark to convert */
  321.     char    *text;        /* the line that m refers to */
  322.     int    inputting;    /* boolean: caled from input() ? */
  323. {
  324.     int    i;
  325.  
  326.     i = idx2col(m, text, inputting);
  327.     while (i < leftcol)
  328.     {
  329.         leftcol -= *o_sidescroll;
  330.         mustredraw = TRUE;
  331.         redrawrange(1L, INFINITY, INFINITY);
  332.     }
  333.     while (i > rightcol)
  334.     {
  335.         leftcol += *o_sidescroll;
  336.         mustredraw = TRUE;
  337.         redrawrange(1L, INFINITY, INFINITY);
  338.     }
  339.     physrow = markline(m) - topline;
  340.     physcol = i - leftcol;
  341.     if (*o_number)
  342.         physcol += 8;
  343.  
  344.     return physcol;
  345. }
  346.  
  347. /* This function draws a single line of text on the screen.  The screen's
  348.  * cursor is assumed to be located at the leftmost column of the appropriate
  349.  * row.
  350.  */
  351. static void drawtext(text, lno, clr)
  352.     REG char    *text;    /* the text to draw */
  353.     long        lno;    /* the number of the line to draw */
  354.     int        clr;    /* boolean: do a clrtoeol? */
  355. {
  356.     REG int        col;    /* column number */
  357.     REG int        i;
  358.     REG int        tabstop;    /* *o_tabstop */
  359.     REG int        limitcol;    /* leftcol or leftcol + COLS */
  360.     int        abnormal;    /* boolean: charattr != A_NORMAL? */
  361. #ifndef NO_VISIBLE
  362.     int        rev;        /* boolean: standout mode, too? */
  363.     int        idx = 0;
  364. #endif
  365.     char        numstr[9];
  366.  
  367.     /* show the line number, if necessary */
  368.     if (*o_number)
  369.     {
  370.         sprintf(numstr, "%6ld |", lno);
  371.         qaddstr(numstr);
  372.     }
  373.  
  374. #ifndef NO_SENTENCE
  375.     /* if we're hiding format lines, and this is one of them, then hide it */
  376.     if (*o_hideformat && *text == '.')
  377.     {
  378.         clrtoeol();
  379. #if OSK
  380.         qaddch('\l');
  381. #else
  382.         qaddch('\n');
  383. #endif
  384.         return;
  385.     }
  386. #endif
  387.  
  388.     /* move some things into registers... */
  389.     limitcol = leftcol;
  390.     tabstop = *o_tabstop;
  391.     abnormal = FALSE;
  392.  
  393. #ifndef CRUNCH
  394.     if (clr)
  395.         clrtoeol();
  396. #endif
  397.  
  398.     /* skip stuff that was scrolled off left edge */
  399.     for (col = 0;
  400.          (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
  401.          text++)
  402.     {
  403. #ifndef NO_VISIBLE
  404.         idx++;
  405. #endif
  406.         if (i == '\t' && !*o_list)
  407.         {
  408.             col = col + tabstop - (col % tabstop);
  409.         }
  410.         else if (i >= 0 && i < ' ' || i == '\177')
  411.         {
  412.             col += 2;
  413.         }
  414. #ifndef NO_CHARATTR
  415.         else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
  416.         {
  417.             text += 2; /* plus one more as part of "for" loop */
  418.  
  419.             /* since this attribute might carry over, we need it */
  420.             switch (*text)
  421.             {
  422.               case 'R':
  423.               case 'P':
  424.                 attrset(A_NORMAL);
  425.                 abnormal = FALSE;
  426.                 break;
  427.  
  428.               case 'B':
  429.                 attrset(A_BOLD);
  430.                 abnormal = TRUE;
  431.                 break;
  432.  
  433.               case 'U':
  434.                 attrset(A_UNDERLINE);
  435.                 abnormal = TRUE;
  436.                 break;
  437.  
  438.               case 'I':
  439.                 attrset(A_ALTCHARSET);
  440.                 abnormal = TRUE;
  441.                 break;
  442.             }
  443.         }
  444. #endif
  445.         else
  446.         {
  447.             col++;
  448.         }
  449.     }
  450.  
  451. #ifndef NO_VISIBLE
  452.     /* Should we start hiliting at the first char of this line? */
  453.     if ((lno > vizlow && lno <= vizhigh
  454.         || lno == vizlow && vizleft < idx)
  455.        && !(lno == vizhigh && vizright < idx))
  456.     {
  457.         do_VISIBLE();
  458.         rev = TRUE;
  459.     }
  460. #endif
  461.  
  462.     /* adjust for control char that was partially visible */
  463.     while (col > limitcol)
  464.     {
  465.         qaddch(' ');
  466.         limitcol++;
  467.     }
  468.  
  469.     /* now for the visible characters */
  470.     limitcol = leftcol + COLS;
  471.     if (*o_number)
  472.         limitcol -= 8;
  473.     for (; (i = *text) && col < limitcol; text++)
  474.     {
  475. #ifndef NO_VISIBLE
  476.         /* maybe turn hilite on/off in the middle of the line */
  477.         if (lno == vizlow && vizleft == idx)
  478.         {
  479.             do_VISIBLE();
  480.             rev = TRUE;
  481.         }
  482.         if (lno == vizhigh && vizright == idx)
  483.         {
  484.             do_SE();
  485.             rev = FALSE;
  486.         }
  487.         idx++;
  488.  
  489.         /* if hiliting, never emit physical tabs */
  490.         if (rev && i == '\t' && !*o_list)
  491.         {
  492.             i = col + tabstop - (col % tabstop);
  493.             do
  494.             {
  495.                 qaddch(' ');
  496.                 col++;
  497.             } while (col < i);
  498.         }
  499.         else
  500. #endif /* !NO_VISIBLE */
  501.         if (i == '\t' && !*o_list)
  502.         {
  503.             i = col + tabstop - (col % tabstop);
  504.             if (i < limitcol)
  505.             {
  506. #ifdef CRUNCH
  507.                 if (!clr && has_PT && !((i - leftcol) & 7))
  508. #else
  509.                 if (has_PT && !((i - leftcol) & 7))
  510. #endif
  511.                 {
  512.                     do
  513.                     {
  514.                         qaddch('\t');
  515.                         col += 8; /* not exact! */
  516.                     } while (col < i);
  517.                     col = i; /* NOW it is exact */
  518.                 }
  519.                 else
  520.                 {
  521.                     do
  522.                     {
  523.                         qaddch(' ');
  524.                         col++;
  525.                     } while (col < i);
  526.                 }
  527.             }
  528.             else /* tab ending after screen? next line! */
  529.             {
  530.                 col = limitcol;
  531.                 if (has_AM)
  532.                 {
  533.                     addch('\n');    /* GB */
  534.                 }
  535.             }
  536.         }
  537.         else if (i >= 0 && i < ' ' || i == '\177')
  538.         {
  539.             col += 2;
  540.             qaddch('^');
  541.             if (col <= limitcol)
  542.             {
  543.                 qaddch(i ^ '@');
  544.             }
  545.         }
  546. #ifndef NO_CHARATTR
  547.         else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
  548.         {
  549.             text += 2; /* plus one more as part of "for" loop */
  550.             switch (*text)
  551.             {
  552.               case 'R':
  553.               case 'P':
  554.                 attrset(A_NORMAL);
  555.                 abnormal = FALSE;
  556.                 break;
  557.  
  558.               case 'B':
  559.                 attrset(A_BOLD);
  560.                 abnormal = TRUE;
  561.                 break;
  562.  
  563.               case 'U':
  564.                 attrset(A_UNDERLINE);
  565.                 abnormal = TRUE;
  566.                 break;
  567.  
  568.               case 'I':
  569.                 attrset(A_ALTCHARSET);
  570.                 abnormal = TRUE;
  571.                 break;
  572.             }
  573.         }
  574. #endif
  575.         else
  576.         {
  577.             col++;
  578.             qaddch(i);
  579.         }
  580.     }
  581.  
  582.     /* get ready for the next line */
  583. #ifndef NO_CHARATTR
  584.     if (abnormal)
  585.     {
  586.         attrset(A_NORMAL);
  587.     }
  588. #endif
  589.     if (*o_list && col < limitcol)
  590.     {
  591.         qaddch('$');
  592.         col++;
  593.     }
  594.  
  595. #ifndef NO_VISIBLE
  596.     /* did we hilite this whole line?  If so, STOP! */
  597.     if (rev)
  598.     {
  599.         do_SE();
  600.     }
  601. #endif
  602.  
  603. #ifdef CRUNCH
  604.     if (clr && col < limitcol)
  605.     {
  606.         clrtoeol();
  607.     }
  608. #endif
  609.     if (!has_AM || col < limitcol)
  610.     {
  611.         addch('\n');
  612.     }
  613.  
  614.     wqrefresh();
  615. }
  616.  
  617.  
  618. #ifndef CRUNCH
  619. static void nudgecursor(same, scan, new, lno)
  620.     int    same;    /* number of chars to be skipped over */
  621.     char    *scan;    /* where the same chars end */
  622.     char    *new;    /* where the visible part of the line starts */
  623.     long    lno;    /* line number of this line */
  624. {
  625.     int    col;
  626.  
  627.     if (same > 0)
  628.     {
  629.         if (same < 5)
  630.         {
  631.             /* move the cursor by overwriting */
  632.             while (same > 0)
  633.             {
  634.                 qaddch(scan[-same]);
  635.                 same--;
  636.             }
  637.         }
  638.         else
  639.         {
  640.             /* move the cursor by calling move() */
  641.             col = (int)(scan - new);
  642.             if (*o_number)
  643.                 col += 8;
  644.             move((int)(lno - topline), col);
  645.         }
  646.     }
  647. }
  648. #endif /* not CRUNCH */
  649.  
  650. /* This function draws a single line of text on the screen, possibly with
  651.  * some cursor optimization.  The cursor is repositioned before drawing
  652.  * begins, so its position before doesn't really matter.
  653.  */
  654. static void smartdrawtext(text, lno, showit)
  655.     REG char    *text;    /* the text to draw */
  656.     long        lno;    /* line number of the text */
  657.     int        showit;    /* boolean: output line? (else just remember it) */
  658. {
  659. #ifdef CRUNCH
  660.     move((int)(lno - topline), 0);
  661.     if (showit)
  662.     {
  663.         drawtext(text, lno, TRUE);
  664.     }
  665. #else /* not CRUNCH */
  666.     static char    old[256];    /* how the line looked last time */
  667.     char        new[256];    /* how it looks now */
  668.     char        *build;        /* used to put chars into new[] */
  669.     char        *scan;        /* used for moving thru new[] or old[] */
  670.     char        *end;        /* last non-blank changed char */
  671.     char        *shift;        /* used to insert/delete chars */
  672.     int        same;        /* length of a run of unchanged chars */
  673.     int        limitcol;
  674.     int        col;
  675.     int        i;
  676.     char        numstr[9];
  677.  
  678. # ifndef NO_CHARATTR
  679.     /* if this line has attributes, do it the dumb way instead */
  680.     if (hasattr(lno, text))
  681.     {
  682.         move((int)(lno - topline), 0);
  683.         drawtext(text, lno, TRUE);
  684.         return;
  685.     }
  686. # endif
  687. # ifndef NO_SENTENCE
  688.     /* if this line is a format line, & we're hiding format lines, then
  689.      * let the dumb drawtext() function handle it
  690.      */
  691.     if (*o_hideformat && *text == '.')
  692.     {
  693.         move((int)(lno - topline), 0);
  694.         drawtext(text, lno, TRUE);
  695.         return;
  696.     }
  697. # endif
  698. # ifndef NO_VISIBLE
  699.     if (vizchange)
  700.     {
  701.         move((int)(lno - topline), 0);
  702.         drawtext(text, lno, TRUE);
  703.         smartlno = 0L;
  704.         return;
  705.     }
  706. # endif
  707.  
  708.     /* skip stuff that was scrolled off left edge */
  709.     limitcol = leftcol;
  710.     for (col = 0;
  711.          (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
  712.          text++)
  713.     {
  714.         if (i == '\t' && !*o_list)
  715.         {
  716.             col = col + *o_tabstop - (col % *o_tabstop);
  717.         }
  718.         else if (i >= 0 && i < ' ' || i == '\177')
  719.         {
  720.             col += 2;
  721.         }
  722.         else
  723.         {
  724.             col++;
  725.         }
  726.     }
  727.  
  728.     /* adjust for control char that was partially visible */
  729.     build = new;
  730.     while (col > limitcol)
  731.     {
  732.         *build++ = ' ';
  733.         limitcol++;
  734.     }
  735.  
  736.     /* now for the visible characters */
  737.     limitcol = leftcol + COLS;
  738.     if (*o_number)
  739.         limitcol -= 8;
  740.     for (; (i = *text) && col < limitcol; text++)
  741.     {
  742.         if (i == '\t' && !*o_list)
  743.         {
  744.             i = col + *o_tabstop - (col % *o_tabstop);
  745.             while (col < i && col < limitcol)
  746.             {
  747.                 *build++ = ' ';
  748.                 col++;
  749.             }
  750.         }
  751.         else if (i >= 0 && i < ' ' || i == '\177')
  752.         {
  753.             col += 2;
  754.             *build++ = '^';
  755.             if (col <= limitcol)
  756.             {
  757.                 *build++ = (i ^ '@');
  758.             }
  759.         }
  760.         else
  761.         {
  762.             col++;
  763.             *build++ = i;
  764.         }
  765.     }
  766.     if (col < limitcol && *o_list)
  767.     {
  768.         *build++ = '$';
  769.         col++;
  770.     }
  771.     end = build;
  772.     while (col < limitcol)
  773.     {
  774.         *build++ = ' ';
  775.         col++;
  776.     }
  777.  
  778.     /* if we're just supposed to remember this line, then remember it */
  779.     if (!showit)
  780.     {
  781.         smartlno = lno;
  782.         strncpy(old, new, COLS);
  783.         return;
  784.     }
  785.  
  786.     /* locate the last non-blank character */
  787.     while (end > new && end[-1] == ' ')
  788.     {
  789.         end--;
  790.     }
  791.  
  792.     /* can we optimize the displaying of this line? */
  793.     if (lno != smartlno)
  794.     {
  795.         /* nope, can't optimize - different line */
  796.         move((int)(lno - topline), 0);
  797.  
  798.         /* show the line number, if necessary */
  799.         if (*o_number)
  800.         {
  801.             sprintf(numstr, "%6ld |", lno);
  802.             qaddstr(numstr);
  803.         }
  804.  
  805.         /* show the new line */
  806.         for (scan = new, build = old; scan < end; )
  807.         {
  808.             qaddch(*scan);
  809.             *build++ = *scan++;
  810.         }
  811.         if (end < new + COLS - (*o_number ? 8 : 0))
  812.         {
  813.             clrtoeol();
  814.             while (build < old + COLS)
  815.             {
  816.                 *build++ = ' ';
  817.             }
  818.         }
  819.         smartlno = lno;
  820.         return;
  821.     }
  822.  
  823.     /* skip any initial unchanged characters */
  824.     for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
  825.     {
  826.     }
  827.     i = (scan - new);
  828.     if (*o_number)
  829.         i += 8;
  830.     move((int)(lno - topline), i);
  831.  
  832.     /* The in-between characters must be changed */
  833.     same = 0;
  834.     while (scan < end)
  835.     {
  836.         /* is this character a match? */
  837.         if (scan[0] == build[0])
  838.         {
  839.             same++;
  840.         }
  841.         else /* do we want to insert? */
  842.         if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
  843.         {
  844.             nudgecursor(same, scan, new, lno);
  845.             same = 0;
  846.  
  847.             insch(*scan);
  848.             for (shift = old + COLS; --shift > build; )
  849.             {
  850.                 shift[0] = shift[-1];
  851.             }
  852.             *build = *scan;
  853.         }
  854.         else /* do we want to delete? */
  855.         if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
  856.         {
  857.             nudgecursor(same, scan, new, lno);
  858.             same = 0;
  859.  
  860.             delch();
  861.             same++;
  862.             for (shift = build; shift < old + COLS - 1; shift++)
  863.             {
  864.                 shift[0] = shift[1];
  865.             }
  866.             if (*o_number)
  867.                 shift -= 8;
  868.             *shift = ' ';
  869.         }
  870.         else /* we must overwrite */
  871.         {
  872.             nudgecursor(same, scan, new, lno);
  873.             same = 0;
  874.  
  875.             addch(*scan);
  876.             *build = *scan;
  877.         }
  878.  
  879.         build++;
  880.         scan++;
  881.     }
  882.  
  883.     /* maybe clear to EOL */
  884.     end = old + COLS - (*o_number ? 8 : 0);
  885.     while (build < end && *build == ' ')
  886.     {
  887.         build++;
  888.     }
  889.     if (build < end)
  890.     {
  891.         nudgecursor(same, scan, new, lno);
  892.         same = 0;
  893.  
  894.         clrtoeol();
  895.         while (build < old + COLS)
  896.         {
  897.             *build++ = ' ';
  898.         }
  899.     }
  900. #endif /* not CRUNCH */
  901. }
  902.  
  903.  
  904. /* This function is used in visual mode for drawing the screen (or just parts
  905.  * of the screen, if that's all thats needed).  It also takes care of
  906.  * scrolling.
  907.  */
  908. void redraw(curs, inputting)
  909.     MARK    curs;        /* where to leave the screen's cursor */
  910.     int    inputting;    /* boolean: being called from input() ? */
  911. {
  912.     char        *text;        /* a line of text to display */
  913.     static long    chgs;        /* previous changes level */
  914.     long        l;
  915.     int        i;
  916. #ifndef CRUNCH
  917.     static long    showtop;    /* top line in window */
  918.     static long    showbottom;    /* bottom line in window */
  919. #endif
  920.  
  921.     /* if curs == MARK_UNSET, then we should reset internal vars */
  922.     if (curs == MARK_UNSET)
  923.     {
  924.         if (topline < 1 || topline > nlines)
  925.         {
  926.             topline = 1L;
  927.         }
  928.         else
  929.         {
  930.             move(LINES - 1, 0);
  931.             clrtoeol();
  932.         }
  933.         leftcol = 0;
  934.         mustredraw = TRUE;
  935.         redrawafter = INFINITY;
  936.         preredraw = 0L;
  937.         postredraw = 0L;
  938.         chgs = 0;
  939.         smartlno = 0L;
  940. #ifndef NO_VISIBLE
  941.         vizlow = vizhigh = 0L;
  942.         vizchange = FALSE;
  943. #endif
  944. #ifndef CRUNCH
  945.         showtop = 0;
  946.         showbottom = INFINITY;
  947. #endif
  948.         return;
  949.     }
  950.  
  951. #ifndef NO_VISIBLE
  952.     /* adjustments to hilited area may force extra lines to be redrawn. */
  953.     setviz(curs);
  954. #endif
  955.  
  956.     /* figure out which column the cursor will be in */
  957.     l = markline(curs);
  958.     text = fetchline(l);
  959.     mark2phys(curs, text, inputting);
  960.  
  961. #ifndef NO_COLOR
  962.     fixcolor();
  963. #endif
  964.  
  965.     /* adjust topline, if necessary, to get the cursor on the screen */
  966.     if (l >= topline && l <= botline)
  967.     {
  968.         /* it is on the screen already */
  969.  
  970.         /* if the file was changed but !mustredraw, then redraw line */
  971.         if (!mustredraw && (chgs != changes
  972. #ifndef NO_VISIBLE
  973.             || V_from
  974. #endif
  975. #ifndef CRUNCH
  976.             || l < showtop || l > showbottom
  977. #endif
  978.                             ))
  979.         {
  980.             smartdrawtext(text, l, (chgs != changes));
  981.         }
  982.     }
  983.     else if (l < topline && l > topline - LINES && (has_SR || has_AL))
  984.     {
  985.         /* near top - scroll down */
  986.         if (!mustredraw)
  987.         {
  988.             move(0,0);
  989.             while (l < topline)
  990.             {
  991.                 topline--;
  992.                 if (has_SR)
  993.                 {
  994.                     do_SR();
  995.                 }
  996.                 else
  997.                 {
  998.                     insertln();
  999.                 }
  1000.                 text = fetchline(topline);
  1001.                 drawtext(text, topline, FALSE);
  1002.                 do_UP();
  1003.             }
  1004.  
  1005.             /* blank out the last line */
  1006.             move(LINES - 1, 0);
  1007.             clrtoeol();
  1008.         }
  1009.         else
  1010.         {
  1011.             topline = l;
  1012.             redrawrange(0L, INFINITY, INFINITY);
  1013.         }
  1014.     }
  1015.     else if (l > topline && l < botline + LINES)
  1016.     {
  1017.         /* near bottom -- scroll up */
  1018.         if (!mustredraw)
  1019.         {
  1020.             move(LINES - 1,0);
  1021.             clrtoeol();
  1022.             while (l > botline)
  1023.             {
  1024.                 topline++; /* <-- also adjusts botline */
  1025.                 text = fetchline(botline);
  1026.                 drawtext(text, botline, FALSE);
  1027.             }
  1028. #ifndef CRUNCH
  1029.             showbottom = l;
  1030. #endif
  1031.         }
  1032.         else
  1033.         {
  1034.             topline = l - (LINES - 2);
  1035.             redrawrange(0L, INFINITY, INFINITY);
  1036.         }
  1037.     }
  1038.     else
  1039.     {
  1040.         /* distant line - center it & force a redraw */
  1041.         topline = l - (LINES / 2) - 1;
  1042.         if (topline < 1)
  1043.         {
  1044.             topline = 1;
  1045.         }
  1046.         redrawrange(0L, INFINITY, INFINITY);
  1047.         changes++;
  1048.     }
  1049.  
  1050. #ifndef CRUNCH
  1051.     /* make sure the current line is included in the "window" */
  1052.     if (l < showtop)
  1053.     {
  1054.         redrawrange(l, showtop, showtop);
  1055.         showtop = l;
  1056.     }
  1057.     if (l > showbottom)
  1058.     {
  1059.         redrawrange(showbottom, l, l);
  1060.         showbottom = l;
  1061.     }
  1062. #endif
  1063.  
  1064.  
  1065.     /* Now... do we really have to redraw? */
  1066.     if (mustredraw)
  1067.     {
  1068.         /* If redrawfter (and friends) aren't set, assume we should
  1069.          * redraw everything.
  1070.          */
  1071.         if (redrawafter == INFINITY)
  1072.         {
  1073.             redrawafter = 0L;
  1074.             preredraw = postredraw = INFINITY;
  1075.         }
  1076.  
  1077. #ifndef CRUNCH
  1078.         /* shrink the window, if possible */
  1079.         if (showtop < topline)
  1080.         {
  1081.             showtop = topline;
  1082.         }
  1083.         if (showbottom > botline)
  1084.         {
  1085.             showbottom = botline;
  1086.         }
  1087.         if (postredraw == INFINITY)
  1088.         {
  1089.             /* these will be set to more reasonable values later */
  1090.             showtop = INFINITY;
  1091.             showbottom = 0L;
  1092.         }
  1093. #endif
  1094.  
  1095.         /* adjust smartlno to correspond with inserted/deleted lines */
  1096.         if (smartlno >= redrawafter)
  1097.         {
  1098.             if (smartlno < preredraw && postredraw != preredraw) /*!!!*/
  1099.             {
  1100.                 smartlno = 0L;
  1101.             }
  1102.             else
  1103.             {
  1104.                 smartlno += (postredraw - preredraw);
  1105.             }
  1106.         }
  1107.  
  1108.         /* should we insert some lines into the screen? */
  1109.         if (preredraw < postredraw && preredraw <= botline)
  1110.         {
  1111.             /* lines were inserted into the file */
  1112.  
  1113.             /* decide where insertion should start */
  1114.             if (preredraw < topline)
  1115.             {
  1116.                 l = topline;
  1117.             }
  1118.             else
  1119.             {
  1120.                 l = preredraw;
  1121.             }
  1122.  
  1123.             /* insert the lines... maybe */
  1124.             if (l + postredraw - preredraw > botline || !has_AL || *o_number)
  1125.             {
  1126.                 /* Whoa!  a whole screen full - just redraw */
  1127.                 preredraw = postredraw = INFINITY;
  1128.             }
  1129.             else
  1130.             {
  1131.                 /* really insert 'em */
  1132.                 move((int)(l - topline), 0);
  1133.                 for (i = postredraw - preredraw; i > 0; i--)
  1134.                 {
  1135.                     insertln();
  1136.                 }
  1137.  
  1138.                 /* NOTE: the contents of those lines will be
  1139.                  * drawn as part of the regular redraw loop.
  1140.                  */
  1141.  
  1142.                 /* clear the last line */
  1143.                 move(LINES - 1, 0);
  1144.                 clrtoeol();
  1145.             }
  1146.         }
  1147.  
  1148.         /* do we want to delete some lines from the screen? */
  1149.         if (preredraw > postredraw && postredraw <= botline)
  1150.         {
  1151.             if (preredraw > botline || !has_DL || *o_number)
  1152.             {
  1153.                 postredraw = preredraw = INFINITY;
  1154.             }
  1155.             else /* we'd best delete some lines from the screen */
  1156.             {
  1157.                 /* clear the last line, so it doesn't look
  1158.                  * ugly as it gets pulled up into the screen
  1159.                  */
  1160.                 move(LINES - 1, 0);
  1161.                 clrtoeol();
  1162.  
  1163.                 /* delete the lines */
  1164.                 move((int)(postredraw - topline), 0);
  1165.                  for (l = postredraw;
  1166.                      l < preredraw && l <= botline;
  1167.                      l++)
  1168.                 {
  1169.                     deleteln();
  1170.                 }
  1171.  
  1172.                 /* draw the lines that are now newly visible
  1173.                  * at the bottom of the screen
  1174.                  */
  1175.                 i = LINES - 1 + (postredraw - preredraw);
  1176.                 move(i, 0);
  1177.                 for (l = topline + i; l <= botline; l++)
  1178.                 {
  1179.                     /* clear this line */
  1180.                     clrtoeol();
  1181.  
  1182.                     /* draw the line, or ~ for non-lines */
  1183.                     if (l <= nlines)
  1184.                     {
  1185.                         text = fetchline(l);
  1186.                         drawtext(text, l, FALSE);
  1187.                     }
  1188.                     else
  1189.                     {
  1190.                         addstr("~\n");
  1191.                     }
  1192.                 }
  1193.             }
  1194.         }
  1195.  
  1196.         /* redraw the current line */
  1197.         l = markline(curs);
  1198.         pfetch(l);
  1199.         smartdrawtext(ptext, l, TRUE);
  1200.  
  1201. #ifndef CRUNCH
  1202.         /* decide which lines must be in the "window" around the cursor */
  1203.         l = markline(curs);
  1204.         if ((*o_window & 0xff) + 1 == LINES)
  1205.         {
  1206.             showtop = 1;
  1207.             showbottom = INFINITY;
  1208.         }
  1209.         else if (l < showtop || l > showbottom)
  1210.         {
  1211.             l -= (*o_window & 0xff) / 2;
  1212.             if (l < topline)
  1213.             {
  1214.                 l = topline;
  1215.             }
  1216.             if (l < showtop)
  1217.             {
  1218.                 showtop = l;
  1219.             }
  1220.             l += (*o_window & 0xff) - 1;
  1221.             if (l > botline)
  1222.             {
  1223.                 showtop = showtop - l + botline;
  1224.                 l = botline;
  1225.             }
  1226.             if (l > showbottom)
  1227.             {
  1228.                 showbottom = l;
  1229.             }
  1230.         }
  1231. #endif
  1232.  
  1233.         /* decide where we should start redrawing from */
  1234.         if (redrawafter < topline)
  1235.         {
  1236.             l = topline;
  1237.         }
  1238.         else
  1239.         {
  1240.             l = redrawafter;
  1241.         }
  1242.         if (l <= botline && l < postredraw && (l != smartlno || botline != smartlno))
  1243.         {
  1244.             /* draw the other lines */
  1245.             move((int)(l - topline), 0);
  1246.             for (; l <= botline && l < postredraw; l++)
  1247.             {
  1248.                 /* we already drew the current line, so skip it now */
  1249.                 if (l == smartlno)
  1250.                 {
  1251. #if OSK
  1252.                     qaddch('\l');
  1253. #else
  1254.                     qaddch('\n');
  1255. #endif
  1256.                     continue;
  1257.                 }
  1258.  
  1259.                 /* draw the line, or ~ for non-lines */
  1260.                 if (l > nlines)
  1261.                 {
  1262.                     qaddch('~');
  1263.                     clrtoeol();
  1264.                     addch('\n');
  1265.                 }
  1266. #ifndef CRUNCH
  1267.                 else if (l < showtop || l > showbottom)
  1268.                 {
  1269.                     qaddch('@');
  1270.                     clrtoeol();
  1271.                     addch('\n');
  1272.                 }
  1273. #endif
  1274.                 else
  1275.                 {
  1276.                     text = fetchline(l);
  1277.                     drawtext(text, l, TRUE);
  1278.                 }
  1279.             }
  1280.         }
  1281.  
  1282.         mustredraw = FALSE;
  1283.     }
  1284.  
  1285.     /* force total (non-partial) redraw next time if not set */
  1286.     redrawafter = INFINITY;
  1287.     preredraw = 0L;
  1288.     postredraw = 0L;
  1289.  
  1290.     /* move the cursor to where it belongs */
  1291.     move((int)(markline(curs) - topline), physcol);
  1292.     wqrefresh();
  1293.  
  1294.     chgs = changes;
  1295. }
  1296.