home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1707 / redraw.c < prev   
Encoding:
C/C++ Source or Header  |  1990-12-28  |  18.3 KB  |  943 lines

  1. /* redraw.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    16820 SW Tallac Way
  6.  *    Beaverton, OR 97006
  7.  *    kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
  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 remebers where changes were made, so that the screen can be
  27.  * redraw in a more efficient manner.
  28.  */
  29. redrawrange(after, pre, post)
  30.     long    after;    /* lower bound of redrawafter */
  31.     long    pre;    /* upper bound of preredraw */
  32.     long    post;    /* upper bound of postredraw */
  33. {
  34.     long    l;
  35.  
  36.     if (after < redrawafter)
  37.         redrawafter = after;
  38.  
  39.     l = preredraw - postredraw + pre - post;
  40.     if (post > postredraw)
  41.         postredraw = post;
  42.     preredraw = postredraw + l;
  43. }
  44.  
  45.  
  46. /* This function is used in visual mode for drawing the screen (or just parts
  47.  * of the screen, if that's all thats needed).  It also takes care of
  48.  * scrolling.
  49.  */
  50. redraw(curs, inputting)
  51.     MARK    curs;        /* where to leave the screen's cursor */
  52.     int    inputting;    /* boolean: being called from input() ? */
  53. {
  54.     char        *text;        /* a line of text to display */
  55.     static long    chgs;        /* previous changes level */
  56.     long        l;
  57.     int        i;
  58.  
  59.     /* if curs == MARK_UNSET, then we should reset internal vars */
  60.     if (curs == MARK_UNSET)
  61.     {
  62.         if (topline < 1 || topline > nlines)
  63.         {
  64.             topline = 1L;
  65.         }
  66.         else
  67.         {
  68.             move(LINES - 1, 0);
  69.             clrtoeol();
  70.         }
  71.         leftcol = 0;
  72.         mustredraw = TRUE;
  73.         redrawafter = INFINITY;
  74.         preredraw = 0L;
  75.         postredraw = 0L;
  76.         chgs = 0;
  77.         smartlno = 0L;
  78.         return;
  79.     }
  80.  
  81.     /* figure out which column the cursor will be in */
  82.     l = markline(curs);
  83.     text = fetchline(l);
  84.     mark2phys(curs, text, inputting);
  85.  
  86.     /* adjust topline, if necessary, to get the cursor on the screen */
  87.     if (l >= topline && l <= botline)
  88.     {
  89.         /* it is on the screen already */
  90.  
  91.         /* if the file was changed but !mustredraw, then redraw line */
  92.         if (chgs != changes && !mustredraw)
  93.         {
  94.             smartdrawtext(text, l);
  95.         }
  96.     }
  97.     else if (l < topline && l > topline - LINES && (has_SR || has_AL))
  98.     {
  99.         /* near top - scroll down */
  100.         if (!mustredraw)
  101.         {
  102.             move(0,0);
  103.             while (l < topline)
  104.             {
  105.                 topline--;
  106.                 if (has_SR)
  107.                 {
  108.                     do_SR();
  109.                 }
  110.                 else
  111.                 {
  112.                     insertln();
  113.                 }
  114.                 text = fetchline(topline);
  115.                 drawtext(text);
  116.                 do_UP();
  117.             }
  118.  
  119.             /* blank out the last line */
  120.             move(LINES - 1, 0);
  121.             clrtoeol();
  122.         }
  123.         else
  124.         {
  125.             topline = l;
  126.             redrawafter = INFINITY;
  127.             preredraw = 0L;
  128.             postredraw = 0L;
  129.         }
  130.     }
  131.     else if (l > topline && l < botline + LINES)
  132.     {
  133.         /* near bottom -- scroll up */
  134.         if (!mustredraw
  135. #if 1
  136.          || redrawafter == preredraw && preredraw == botline && postredraw == l
  137. #endif
  138.         )
  139.         {
  140.             move(LINES - 1,0);
  141.             clrtoeol();
  142.             while (l > botline)
  143.             {
  144.                 topline++; /* <-- also adjusts botline */
  145.                 text = fetchline(botline);
  146.                 drawtext(text);
  147.             }
  148.             mustredraw = FALSE;
  149.         }
  150.         else
  151.         {
  152.             topline = l - (LINES - 2);
  153.             redrawafter = INFINITY;
  154.             preredraw = 0L;
  155.             postredraw = 0L;
  156.         }
  157.     }
  158.     else
  159.     {
  160.         /* distant line - center it & force a redraw */
  161.         topline = l - (LINES / 2) - 1;
  162.         if (topline < 1)
  163.         {
  164.             topline = 1;
  165.         }
  166.         mustredraw = TRUE;
  167.         redrawafter = INFINITY;
  168.         preredraw = 0L;
  169.         postredraw = 0L;
  170.     }
  171.  
  172.     /* Now... do we really have to redraw? */
  173.     if (mustredraw)
  174.     {
  175.         /* If redrawfter (and friends) aren't set, assume we should
  176.          * redraw everything.
  177.          */
  178.         if (redrawafter == INFINITY)
  179.         {
  180.             redrawafter = 0L;
  181.             preredraw = postredraw = INFINITY;
  182.         }
  183.  
  184.         /* adjust smartlno to correspond with inserted/deleted lines */
  185.         if (smartlno >= redrawafter)
  186.         {
  187.             if (smartlno < preredraw)
  188.             {
  189.                 smartlno = 0L;
  190.             }
  191.             else
  192.             {
  193.                 smartlno += (postredraw - preredraw);
  194.             }
  195.         }
  196.  
  197.         /* should we insert some lines into the screen? */
  198.         if (preredraw < postredraw && preredraw <= botline)
  199.         {
  200.             /* lines were inserted into the file */
  201.  
  202.             /* decide where insertion should start */
  203.             if (preredraw < topline)
  204.             {
  205.                 l = topline;
  206.             }
  207.             else
  208.             {
  209.                 l = preredraw;
  210.             }
  211.  
  212.             /* insert the lines... maybe */
  213.             if (l + postredraw - preredraw > botline || !has_AL)
  214.             {
  215.                 /* Whoa!  a whole screen full - just redraw */
  216.                 preredraw = postredraw = INFINITY;
  217.             }
  218.             else
  219.             {
  220.                 /* really insert 'em */
  221.                 move((int)(l - topline), 0);
  222.                 for (i = postredraw - preredraw; i > 0; i--)
  223.                 {
  224.                     insertln();
  225.                 }
  226.  
  227.                 /* NOTE: the contents of those lines will be
  228.                  * drawn as part of the regular redraw loop.
  229.                  */
  230.  
  231.                 /* clear the last line */
  232.                 move(LINES - 1, 0);
  233.                 clrtoeol();
  234.             }
  235.         }
  236.  
  237.         /* do we want to delete some lines from the screen? */
  238.         if (preredraw > postredraw && postredraw <= botline)
  239.         {
  240.             if (preredraw > botline || !has_DL)
  241.             {
  242.                 postredraw = preredraw = INFINITY;
  243.             }
  244.             else /* we'd best delete some lines from the screen */
  245.             {
  246.                 /* clear the last line, so it doesn't look
  247.                  * ugly as it gets pulled up into the screen
  248.                  */
  249.                 move(LINES - 1, 0);
  250.                 clrtoeol();
  251.  
  252.                 /* delete the lines */
  253.                 move((int)(postredraw - topline), 0);
  254.                  for (l = postredraw;
  255.                      l < preredraw && l <= botline;
  256.                      l++)
  257.                 {
  258.                     deleteln();
  259.                 }
  260.  
  261.                 /* draw the lines that are now newly visible
  262.                  * at the bottom of the screen
  263.                  */
  264.                 i = LINES - 1 + (postredraw - preredraw);
  265.                 move(i, 0);
  266.                 for (l = topline + i; l <= botline; l++)
  267.                 {
  268.                     /* clear this line */
  269.                     clrtoeol();
  270.  
  271.                     /* draw the line, or ~ for non-lines */
  272.                     if (l <= nlines)
  273.                     {
  274.                         text = fetchline(l);
  275.                         drawtext(text);
  276.                     }
  277.                     else
  278.                     {
  279.                         addstr("~\n");
  280.                     }
  281.                 }
  282.             }
  283.         }
  284.  
  285.         /* redraw the current line */
  286.         l = markline(curs);
  287.         pfetch(l);
  288.         smartdrawtext(ptext, l);
  289.  
  290.         /* decide where we should start redrawing from */
  291.         if (redrawafter < topline)
  292.         {
  293.             l = topline;
  294.         }
  295.         else
  296.         {
  297.             l = redrawafter;
  298.         }
  299.         move((int)(l - topline), 0);
  300.  
  301.         /* draw the other lines */
  302.         for (; l <= botline && l < postredraw; l++)
  303.         {
  304.             /* we already drew the current line, so skip it now */
  305.             if (l == smartlno)
  306.             {
  307.                 qaddch('\n');
  308.                 continue;
  309.             }
  310.  
  311.             /* clear this line */
  312.             clrtoeol();
  313.  
  314.             /* draw the line, or ~ for non-lines */
  315.             if (l <= nlines)
  316.             {
  317.                 text = fetchline(l);
  318.                 drawtext(text);
  319.             }
  320.             else
  321.             {
  322.                 addstr("~\n");
  323.             }
  324.         }
  325.  
  326.         mustredraw = FALSE;
  327.     }
  328.  
  329.     /* force total (non-partial) redraw next time if not set */
  330.     redrawafter = INFINITY;
  331.     preredraw = 0L;
  332.     postredraw = 0L;
  333.  
  334.     /* move the cursor to where it belongs */
  335.     move((int)(markline(curs) - topline), physcol);
  336.     wqrefresh(stdscr);
  337.  
  338.     chgs = changes;
  339. }
  340.  
  341.  
  342. /* This function converts a MARK to a column number.  It doesn't automatically
  343.  * adjust for leftcol; that must be done by the calling function
  344.  */
  345. int idx2col(curs, text, inputting)
  346.     MARK        curs;    /* the line# & index# of the cursor */
  347.     register char    *text;    /* the text of the line, from fetchline */
  348.     int        inputting;    /* boolean: called from input() ? */
  349. {
  350.     static MARK    pcursor;/* previous cursor, for possible shortcut */
  351.     static MARK    pcol;    /* column number for pcol */
  352.     static long    chgs;    /* previous value of changes counter */
  353.     register int    col;    /* used to count column numbers */
  354.     register int    idx;    /* used to count down the index */
  355.     register int    i;
  356.  
  357.     /* for now, assume we have to start counting at the left edge */
  358.     col = 0;
  359.     idx = markidx(curs);
  360.  
  361.     /* if the file hasn't changed & line number is the same & it has no
  362.      * embedded character attribute strings, can we do shortcuts?
  363.      */
  364.     if (chgs == changes
  365.      && !((curs ^ pcursor) & ~(BLKSIZE - 1))
  366. #ifndef NO_CHARATTR
  367.      && !hasattr(markline(curs), text)
  368. #endif
  369.     )
  370.     {
  371.         /* no movement? */
  372.         if (curs == pcursor)
  373.         {
  374.             /* return the column of the char; for tabs, return its last column */
  375.             if (text[idx] == '\t' && !inputting && !*o_list)
  376.             {
  377.                 return pcol + *o_tabstop - (pcol % *o_tabstop) - 1;
  378.             }
  379.             else
  380.             {
  381.                 return pcol;
  382.             }
  383.         }
  384.  
  385.         /* movement to right? */
  386.         if (curs > pcursor)
  387.         {
  388.             /* start counting from previous place */
  389.             col = pcol;
  390.             idx = markidx(curs) - markidx(pcursor);
  391.             text += markidx(pcursor);
  392.         }
  393.     }
  394.  
  395.     /* count over to the char after the idx position */
  396.     while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */
  397.     {
  398.         if (i == '\t' && !*o_list)
  399.         {
  400.             col += *o_tabstop;
  401.             col -= col % *o_tabstop;
  402.         }
  403.         else if (i >= '\0' && i < ' ' || i == '\177')
  404.         {
  405.             col += 2;
  406.         }
  407. #ifndef NO_CHARATTR
  408.         else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
  409.         {
  410.             text += 2; /* plus one more at bottom of loop */
  411.             idx -= 2;
  412.         }            
  413. #endif
  414.         else
  415.         {
  416.             col++;
  417.         }
  418.         text++;
  419.         idx--;
  420.     }
  421.  
  422.     /* save stuff to speed next call */
  423.     pcursor = curs;
  424.     pcol = col;
  425.     chgs = changes;
  426.  
  427.     /* return the column of the char; for tabs, return its last column */
  428.     if (*text == '\t' && !inputting && !*o_list)
  429.     {
  430.         return col + *o_tabstop - (col % *o_tabstop) - 1;
  431.     }
  432.     else
  433.     {
  434.         return col;
  435.     }
  436. }
  437.  
  438.  
  439. /* This function is similar to idx2col except that it takes care of sideways
  440.  * scrolling - for the given line, at least.
  441.  */
  442. mark2phys(m, text, inputting)
  443.     MARK    m;        /* a mark to convert */
  444.     char    *text;        /* the line that m refers to */
  445.     int    inputting;    /* boolean: caled from input() ? */
  446. {
  447.     int    i;
  448.  
  449.     i = idx2col(cursor, text, inputting);
  450.     while (i < leftcol)
  451.     {
  452.         leftcol -= *o_sidescroll;
  453.         mustredraw = TRUE;
  454.         redrawrange(1L, INFINITY, INFINITY);
  455.         qaddch('\r');
  456.         /* drawtext(text); */
  457.     }
  458.     while (i > rightcol)
  459.     {
  460.         leftcol += *o_sidescroll;
  461.         mustredraw = TRUE;
  462.         redrawrange(1L, INFINITY, INFINITY);
  463.         qaddch('\r');
  464.         /* drawtext(text); */
  465.     }
  466.     physcol = i - leftcol;
  467.     physrow = markline(m) - topline;
  468.  
  469.     return physcol;
  470. }
  471.  
  472. /* This function draws a single line of text on the screen.  The screen's
  473.  * cursor is assumed to be located at the leftmost column of the appropriate
  474.  * row.
  475.  */
  476. drawtext(text)
  477.     register char    *text;    /* the text to draw */
  478. {
  479.     register int    col;    /* column number */
  480.     register int    i;
  481.     register int    tabstop;    /* *o_tabstop */
  482.     register int    limitcol;    /* leftcol or leftcol + COLS */
  483.     int        abnormal;    /* boolean: charattr != A_NORMAL? */
  484.  
  485. #ifndef NO_SENTENCE
  486.     /* if we're hiding format lines, and this is one of them, then hide it */
  487.     if (*o_hideformat && *text == '.')
  488.     {
  489.         clrtoeol();
  490.         qaddch('\n');
  491.         return;
  492.     }
  493. #endif
  494.  
  495.     /* move some things into registers... */
  496.     limitcol = leftcol;
  497.     tabstop = *o_tabstop;
  498.     abnormal = FALSE;
  499.  
  500.     /* skip stuff that was scrolled off left edge */
  501.     for (col = 0;
  502.          (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
  503.          text++)
  504.     {
  505.         if (i == '\t' && !*o_list)
  506.         {
  507.             col = col + tabstop - (col % tabstop);
  508.         }
  509.         else if (i >= 0 && i < ' ' || i == '\177')
  510.         {
  511.             col += 2;
  512.         }
  513. #ifndef NO_CHARATTR
  514.         else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
  515.         {
  516.             text += 2; /* plus one more as part of "for" loop */
  517.  
  518.             /* since this attribute might carry over, we need it */
  519.             switch (*text)
  520.             {
  521.               case 'R':
  522.               case 'P':
  523.                 attrset(A_NORMAL);
  524.                 abnormal = FALSE;
  525.                 break;
  526.  
  527.               case 'B':
  528.                 attrset(A_BOLD);
  529.                 abnormal = TRUE;
  530.                 break;
  531.  
  532.               case 'U':
  533.                 attrset(A_UNDERLINE);
  534.                 abnormal = TRUE;
  535.                 break;
  536.  
  537.               case 'I':
  538.                 attrset(A_ALTCHARSET);
  539.                 abnormal = TRUE;
  540.                 break;
  541.             }
  542.         }
  543. #endif
  544.         else
  545.         {
  546.             col++;
  547.         }
  548.     }
  549.  
  550.     /* adjust for control char that was partially visible */
  551.     while (col > limitcol)
  552.     {
  553.         qaddch(' ');
  554.         limitcol++;
  555.     }
  556.  
  557.     /* now for the visible characters */
  558.     for (limitcol = leftcol + COLS;
  559.          (i = *text) && col < limitcol;
  560.          text++)
  561.     {
  562.         if (i == '\t' && !*o_list)
  563.         {
  564.             i = col + tabstop - (col % tabstop);
  565.             if (i < limitcol)
  566.             {
  567.                 if (has_PT && !((i - leftcol) & 7))
  568.                 {
  569.                     do
  570.                     {
  571.                         qaddch('\t');
  572.                         col += 8; /* not exact! */
  573.                     } while (col < i);
  574.                     col = i; /* NOW it is exact */
  575.                 }
  576.                 else
  577.                 {
  578.                     do
  579.                     {
  580.                         qaddch(' ');
  581.                         col++;
  582.                     } while (col < i);
  583.                 }
  584.             }
  585.             else /* tab ending after screen? next line! */
  586.             {
  587.                 col = limitcol;
  588.                 if (has_AM)
  589.                 {
  590.                     qaddch('\r');    /* GB */
  591.                     qaddch('\n');    /* GB */
  592.                 }
  593.             }
  594.         }
  595.         else if (i >= 0 && i < ' ' || i == '\177')
  596.         {
  597.             col += 2;
  598.             if (col < limitcol)
  599.             {
  600.                 qaddch('^');
  601.                 qaddch(i ^ '@');
  602.             }
  603.         }
  604. #ifndef NO_CHARATTR
  605.         else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
  606.         {
  607.             text += 2; /* plus one more as part of "for" loop */
  608.             switch (*text)
  609.             {
  610.               case 'R':
  611.               case 'P':
  612.                 attrset(A_NORMAL);
  613.                 abnormal = FALSE;
  614.                 break;
  615.  
  616.               case 'B':
  617.                 attrset(A_BOLD);
  618.                 abnormal = TRUE;
  619.                 break;
  620.  
  621.               case 'U':
  622.                 attrset(A_UNDERLINE);
  623.                 abnormal = TRUE;
  624.                 break;
  625.  
  626.               case 'I':
  627.                 attrset(A_ALTCHARSET);
  628.                 abnormal = TRUE;
  629.                 break;
  630.             }
  631.         }
  632. #endif
  633.         else
  634.         {
  635.             col++;
  636.             qaddch(i);
  637.         }
  638.     }
  639.  
  640.     /* get ready for the next line */
  641. #ifndef NO_CHARATTR
  642.     if (abnormal)
  643.     {
  644.         attrset(A_NORMAL);
  645.     }
  646. #endif
  647.     if (*o_list && col < limitcol)
  648.     {
  649.         qaddch('$');
  650.         col++;
  651.     }
  652.     if (!has_AM || col < limitcol)
  653.     {
  654.         qaddch('\r');
  655.         qaddch('\n');
  656.     }
  657. }
  658.  
  659.  
  660. static nudgecursor(same, scan, new, lno)
  661.     int    same;    /* number of chars to be skipped over */
  662.     char    *scan;    /* where the same chars end */
  663.     char    *new;    /* where the visible part of the line starts */
  664.     long    lno;    /* line number of this line */
  665. {
  666.     if (same > 0)
  667.     {
  668.         if (same < 5)
  669.         {
  670.             /* move the cursor by overwriting */
  671.             while (same > 0)
  672.             {
  673.                 qaddch(scan[-same]);
  674.                 same--;
  675.             }
  676.         }
  677.         else
  678.         {
  679.             /* move the cursor by calling move() */
  680.             move((int)(lno - topline), (int)(scan - new));
  681.         }
  682.     }
  683. }
  684.  
  685. /* This function draws a single line of text on the screen, possibly with
  686.  * some cursor optimization.  The cursor is repositioned before drawing
  687.  * begins, so its position before doesn't really matter.
  688.  */
  689. smartdrawtext(text, lno)
  690.     register char    *text;    /* the text to draw */
  691.     long        lno;    /* line number of the text */
  692. {
  693.     static char    old[256];    /* how the line looked last time */
  694.     char        new[256];    /* how it looks now */
  695.     char        *build;        /* used to put chars into new[] */
  696.     char        *scan;        /* used for moving thru new[] or old[] */
  697.     char        *end;        /* last non-blank changed char */
  698.     char        *shift;        /* used to insert/delete chars */
  699.     int        same;        /* length of a run of unchanged chars */
  700.     int        limitcol;
  701.     int        col;
  702.     int        i;
  703.  
  704. #ifndef NO_CHARATTR
  705.     /* if this line has attributes, do it the dumb way instead */
  706.     if (hasattr(lno, text))
  707.     {
  708.         move((int)(lno - topline), 0);
  709.         clrtoeol();
  710.         drawtext(text);
  711.         return;
  712.     }
  713. #endif
  714. #ifndef NO_SENTENCE
  715.     /* if this line is a format line, & we're hiding format lines, then
  716.      * let the dumb drawtext() function handle it
  717.      */
  718.     if (*o_hideformat && *text == '.')
  719.     {
  720.         move((int)(lno - topline), 0);
  721.         clrtoeol();
  722.         drawtext(text);
  723.         return;
  724.     }
  725. #endif
  726.  
  727.     /* skip stuff that was scrolled off left edge */
  728.     limitcol = leftcol;
  729.     for (col = 0;
  730.          (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
  731.          text++)
  732.     {
  733.         if (i == '\t' && !*o_list)
  734.         {
  735.             col = col + *o_tabstop - (col % *o_tabstop);
  736.         }
  737.         else if (i >= 0 && i < ' ' || i == '\177')
  738.         {
  739.             col += 2;
  740.         }
  741.         else
  742.         {
  743.             col++;
  744.         }
  745.     }
  746.  
  747.     /* adjust for control char that was partially visible */
  748.     build = new;
  749.     while (col > limitcol)
  750.     {
  751.         *build++ = ' ';
  752.         limitcol++;
  753.     }
  754.  
  755.     /* now for the visible characters */
  756.     for (limitcol = leftcol + COLS;
  757.          (i = *text) && col < limitcol;
  758.          text++)
  759.     {
  760.         if (i == '\t' && !*o_list)
  761.         {
  762.             i = col + *o_tabstop - (col % *o_tabstop);
  763.             while (col < i && col < limitcol)
  764.             {
  765.                 *build++ = ' ';
  766.                 col++;
  767.             }
  768.         }
  769.         else if (i >= 0 && i < ' ' || i == '\177')
  770.         {
  771.             col += 2;
  772.             *build++ = '^';
  773.             if (col < limitcol)
  774.             {
  775.                 *build++ = (i ^ '@');
  776.             }
  777.         }
  778.         else
  779.         {
  780.             col++;
  781.             *build++ = i;
  782.         }
  783.     }
  784.     if (col < limitcol && *o_list)
  785.     {
  786.         *build++ = '$';
  787.         col++;
  788.     }
  789.     end = build;
  790.     while (col < limitcol)
  791.     {
  792.         *build++ = ' ';
  793.         col++;
  794.     }
  795.  
  796.     /* locate the last non-blank character */
  797.     while (end > new && end[-1] == ' ')
  798.     {
  799.         end--;
  800.     }
  801.  
  802.     /* can we optimize the displaying of this line? */
  803.     if (lno != smartlno)
  804.     {
  805.         /* nope, can't optimize - different line */
  806.         move((int)(lno - topline), 0);
  807.         for (scan = new, build = old; scan < end; )
  808.         {
  809.             qaddch(*scan);
  810.             *build++ = *scan++;
  811.         }
  812.         if (end < new + COLS)
  813.         {
  814.             clrtoeol();
  815.             while (build < old + COLS)
  816.             {
  817.                 *build++ = ' ';
  818.             }
  819.         }
  820.         smartlno = lno;
  821.         return;
  822.     }
  823.  
  824.     /* skip any initial unchanged characters */
  825.     for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
  826.     {
  827.     }
  828.     move((int)(lno - topline), (int)(scan - new));
  829.  
  830.     /* The in-between characters must be changed */
  831.     same = 0;
  832.     while (scan < end)
  833.     {
  834.         /* is this character a match? */
  835.         if (scan[0] == build[0])
  836.         {
  837.             same++;
  838.         }
  839.         else /* do we want to insert? */
  840.         if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
  841.         {
  842.             nudgecursor(same, scan, new, lno);
  843.             same = 0;
  844.  
  845.             insch(*scan);
  846.             for (shift = old + COLS; --shift > build; )
  847.             {
  848.                 shift[0] = shift[-1];
  849.             }
  850.             *build = *scan;
  851.         }
  852.         else /* do we want to delete? */
  853.         if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
  854.         {
  855.             nudgecursor(same, scan, new, lno);
  856.             same = 0;
  857.  
  858.             delch();
  859.             same++;
  860.             for (shift = build; shift < old + COLS - 1; shift++)
  861.             {
  862.                 shift[0] = shift[1];
  863.             }
  864.             *shift = ' ';
  865.         }
  866.         else /* we must overwrite */
  867.         {
  868.             nudgecursor(same, scan, new, lno);
  869.             same = 0;
  870.  
  871.             addch(*scan);
  872.             *build = *scan;
  873.         }
  874.  
  875.         build++;
  876.         scan++;
  877.     }
  878.  
  879.     /* maybe clear to EOL */
  880.     while (build < old + COLS && *build == ' ')
  881.     {
  882.         build++;
  883.     }
  884.     if (build < old + COLS)
  885.     {
  886.         nudgecursor(same, scan, new, lno);
  887.         same = 0;
  888.  
  889.         clrtoeol();
  890.         while (build < old + COLS)
  891.         {
  892.             *build++ = ' ';
  893.         }
  894.     }
  895. }
  896.  
  897.  
  898. #ifndef NO_CHARATTR
  899. /* see if a given line uses character attribute strings */
  900. int hasattr(lno, text)
  901.     long        lno;    /* the line# of the cursor */
  902.     register char    *text;    /* the text of the line, from fetchline */
  903. {
  904.     static long    plno;    /* previous line number */
  905.     static long    chgs;    /* previous value of changes counter */
  906.     static int    panswer;/* previous answer */
  907.     char        *scan;
  908.  
  909.     /* if charattr is off, then the answer is "no, it doesn't" */
  910.     if (!*o_charattr)
  911.     {
  912.         chgs = 0; /* <- forces us to check if charattr is later set */
  913.         return FALSE;
  914.     }
  915.  
  916.     /* if we already know the answer, return it... */
  917.     if (lno == plno && chgs == changes)
  918.     {
  919.         return panswer;
  920.     }
  921.  
  922.     /* get the line & look for "\fX" */
  923.     if (!text[0] || !text[1] || !text[2])
  924.     {
  925.         panswer = FALSE;
  926.     }
  927.     else
  928.     {
  929.         for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
  930.         {
  931.         }
  932.         panswer = (scan[2] != '\0');
  933.     }
  934.  
  935.     /* save the results */
  936.     plno = lno;
  937.     chgs = changes;
  938.  
  939.     /* return the results */
  940.     return panswer;
  941. }
  942. #endif
  943.