home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / stevie / screen.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  17KB  |  710 lines

  1. /* $Header: /nw/tony/src/stevie/src/RCS/screen.c,v 1.8 89/08/02 09:26:33 tony Exp $
  2.  *
  3.  * Routines to manipulate the screen representations.
  4.  */
  5.  
  6. #include "stevie.h"
  7.  
  8. /*
  9.  * This gets set if we ignored an update request while input was pending.
  10.  * We check this when the input is drained to see if the screen should be
  11.  * updated.
  12.  */
  13. bool_t    need_redraw = FALSE;
  14.  
  15. /*
  16.  * The following variable is set (in filetonext) to the number of physical
  17.  * lines taken by the line the cursor is on. We use this to avoid extra
  18.  * calls to plines(). The optimized routines lfiletonext() and lnexttoscreen()
  19.  * make sure that the size of the cursor line hasn't changed. If so, lines
  20.  * below the cursor will move up or down and we need to call the routines
  21.  * filetonext() and nexttoscreen() to examine the entire screen.
  22.  */
  23. static    int    Cline_size;    /* size (in rows) of the cursor line */
  24. static    int    Cline_row;    /* starting row of the cursor line */
  25.  
  26. static    char    *mkline();    /* calculate line string for "number" mode */
  27.  
  28. /*
  29.  * filetonext()
  30.  *
  31.  * Based on the current value of Topchar, transfer a screenfull of
  32.  * stuff from Filemem to Nextscreen, and update Botchar.
  33.  */
  34.  
  35. static void
  36. filetonext()
  37. {
  38.     register int    row, col;
  39.     register char    *screenp = Nextscreen;
  40.     LPTR    memp;
  41.     LPTR    save;            /* save pos. in case line won't fit */
  42.     register char    *endscreen;
  43.     register char    *nextrow;
  44.     char    extra[16];
  45.     int    nextra = 0;
  46.     register int    c;
  47.     int    n;
  48.     bool_t    done;        /* if TRUE, we hit the end of the file */
  49.     bool_t    didline;    /* if TRUE, we finished the last line */
  50.     int    srow;        /* starting row of the current line */
  51.     int    lno;        /* number of the line we're doing */
  52.     int    coff;        /* column offset */
  53.  
  54.     coff = P(P_NU) ? 8 : 0;
  55.  
  56.     save = memp = *Topchar;
  57.  
  58.     if (P(P_NU))
  59.         lno = cntllines(Filemem, Topchar);
  60.  
  61.     /*
  62.      * The number of rows shown is Rows-1.
  63.      * The last line is the status/command line.
  64.      */
  65.     endscreen = &screenp[(Rows-1)*Columns];
  66.  
  67.     done = didline = FALSE;
  68.     srow = row = col = 0;
  69.     /*
  70.      * We go one past the end of the screen so we can find out if the
  71.      * last line fit on the screen or not.
  72.      */
  73.     while ( screenp <= endscreen && !done) {
  74.  
  75.  
  76.         if (P(P_NU) && col == 0 && memp.index == 0) {
  77.             strcpy(extra, mkline(lno++));
  78.             nextra = 8;
  79.         }
  80.  
  81.         /* Get the next character to put on the screen. */
  82.  
  83.         /* The 'extra' array contains the extra stuff that is */
  84.         /* inserted to represent special characters (tabs, and */
  85.         /* other non-printable stuff.  The order in the 'extra' */
  86.         /* array is reversed. */
  87.  
  88.         if ( nextra > 0 )
  89.             c = extra[--nextra];
  90.         else {
  91.             c = (unsigned)(0xff & gchar(&memp));
  92.             if (inc(&memp) == -1)
  93.                 done = 1;
  94.             /* when getting a character from the file, we */
  95.             /* may have to turn it into something else on */
  96.             /* the way to putting it into 'Nextscreen'. */
  97.             if ( c == TAB && !P(P_LS) ) {
  98.                 strcpy(extra,"        ");
  99.                 /* tab amount depends on current column */
  100.                 nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
  101.                 c = ' ';
  102.             }
  103.             else if ( c == NUL && P(P_LS) ) {
  104.                 extra[0] = NUL;
  105.                 nextra = 1;
  106.                 c = '$';
  107.             } else if ( (n = chars[c].ch_size) > 1 ) {
  108.                 char *p;
  109.                 nextra = 0;
  110.                 p = chars[c].ch_str;
  111.                 /* copy 'ch-str'ing into 'extra' in reverse */
  112.                 while ( n > 1 )
  113.                     extra[nextra++] = p[--n];
  114.                 c = p[0];
  115.             }
  116.         }
  117.  
  118.         if (screenp == endscreen) {
  119.             /*
  120.              * We're one past the end of the screen. If the
  121.              * current character is null, then we really did
  122.              * finish, so set didline = TRUE. In either case,
  123.              * break out because we're done.
  124.              */
  125.             dec(&memp);
  126.             if (memp.index != 0 && c == NUL) {
  127.                 didline = TRUE;
  128.                 inc(&memp);
  129.             }
  130.             break;
  131.         }
  132.  
  133.         if ( c == NUL ) {
  134.             srow = ++row;
  135.             /*
  136.              * Save this position in case the next line won't
  137.              * fit on the screen completely.
  138.              */
  139.             save = memp;
  140.             /* get pointer to start of next row */
  141.             nextrow = &Nextscreen[row*Columns];
  142.             /* blank out the rest of this row */
  143.             while ( screenp != nextrow )
  144.                 *screenp++ = ' ';
  145.             col = 0;
  146.             continue;
  147.         }
  148.         if ( col >= Columns ) {
  149.             row++;
  150.             col = 0;
  151.         }
  152.         /* store the character in Nextscreen */
  153.         *screenp++ = c;
  154.         col++;
  155.     }
  156.     /*
  157.      * If we didn't hit the end of the file, and we didn't finish
  158.      * the last line we were working on, then the line didn't fit.
  159.      */
  160.     if (!done && !didline) {
  161.         /*
  162.          * Clear the rest of the screen and mark the unused lines.
  163.          */
  164.         screenp = &Nextscreen[srow * Columns];
  165.         while (screenp < endscreen)
  166.             *screenp++ = ' ';
  167.         for (; srow < (Rows-1) ;srow++)
  168.             Nextscreen[srow * Columns] = '@';
  169.         *Botchar = save;
  170.         return;
  171.     }
  172.     /* make sure the rest of the screen is blank */
  173.     while ( screenp < endscreen )
  174.         *screenp++ = ' ';
  175.     /* put '~'s on rows that aren't part of the file. */
  176.     if ( col != 0 )
  177.         row++;
  178.     while ( row < Rows ) {
  179.         Nextscreen[row*Columns] = '~';
  180.         row++;
  181.     }
  182.     if (done)    /* we hit the end of the file */
  183.         *Botchar = *Fileend;
  184.     else
  185.         *Botchar = memp;    /* FIX - prev? */
  186. }
  187.  
  188. /*
  189.  * nexttoscreen
  190.  *
  191.  * Transfer the contents of Nextscreen to the screen, using Realscreen
  192.  * to avoid unnecessary output.
  193.  */
  194. static void
  195. nexttoscreen()
  196. {
  197.     register char    *np = Nextscreen;
  198.     register char    *rp = Realscreen;
  199.     register char    *endscreen;
  200.     register int    row = 0, col = 0;
  201.     int    gorow = -1, gocol = -1;
  202.  
  203.     if (anyinput()) {
  204.         need_redraw = TRUE;
  205.         return;
  206.     }
  207.  
  208.     endscreen = &np[(Rows-1)*Columns];
  209.  
  210.     CUROFF;        /* disable cursor */
  211.  
  212.     for ( ; np < endscreen ; np++,rp++ ) {
  213.         /* If desired screen (contents of Nextscreen) does not */
  214.         /* match what's really there, put it there. */
  215.         if ( *np != *rp ) {
  216.             /* if we are positioned at the right place, */
  217.             /* we don't have to use windgoto(). */
  218.             if (gocol != col || gorow != row) {
  219.                 /*
  220.                  * If we're just off by one, don't send
  221.                  * an entire esc. seq. (this happens a lot!)
  222.                  */
  223.                 if (gorow == row && gocol+1 == col) {
  224.                     outchar(*(np-1));
  225.                     gocol++;
  226.                 } else
  227.                     windgoto(gorow=row,gocol=col);
  228.             }
  229.             outchar(*rp = *np);
  230.             gocol++;
  231.         }
  232.         if ( ++col >= Columns ) {
  233.             col = 0;
  234.             row++;
  235.         }
  236.     }
  237.     CURON;        /* enable cursor again */
  238. }
  239.  
  240. /*
  241.  * lfiletonext() - like filetonext() but only for cursor line
  242.  *
  243.  * Returns true if the size of the cursor line (in rows) hasn't changed.
  244.  * This determines whether or not we need to call filetonext() to examine
  245.  * the entire screen for changes.
  246.  */
  247. static bool_t
  248. lfiletonext()
  249. {
  250.     register int    row, col;
  251.     register char    *screenp;
  252.     LPTR    memp;
  253.     register char    *nextrow;
  254.     char    extra[16];
  255.     int    nextra = 0;
  256.     register int    c;
  257.     int    n;
  258.     bool_t    eof;
  259.     int    lno;        /* number of the line we're doing */
  260.     int    coff;        /* column offset */
  261.  
  262.     coff = P(P_NU) ? 8 : 0;
  263.  
  264.     /*
  265.      * This should be done more efficiently.
  266.      */
  267.     if (P(P_NU))
  268.         lno = cntllines(Filemem, Curschar);
  269.  
  270.     screenp = Nextscreen + (Cline_row * Columns);
  271.  
  272.     memp = *Curschar;
  273.     memp.index = 0;
  274.  
  275.     eof = FALSE;
  276.     col = 0;
  277.     row = Cline_row;
  278.  
  279.     while (!eof) {
  280.  
  281.         if (P(P_NU) && col == 0 && memp.index == 0) {
  282.             strcpy(extra, mkline(lno));
  283.             nextra = 8;
  284.         }
  285.  
  286.         /* Get the next character to put on the screen. */
  287.  
  288.         /* The 'extra' array contains the extra stuff that is */
  289.         /* inserted to represent special characters (tabs, and */
  290.         /* other non-printable stuff.  The order in the 'extra' */
  291.         /* array is reversed. */
  292.  
  293.         if ( nextra > 0 )
  294.             c = extra[--nextra];
  295.         else {
  296.             c = (unsigned)(0xff & gchar(&memp));
  297.             if (inc(&memp) == -1)
  298.                 eof = TRUE;
  299.             /* when getting a character from the file, we */
  300.             /* may have to turn it into something else on */
  301.             /* the way to putting it into 'Nextscreen'. */
  302.             if ( c == TAB && !P(P_LS) ) {
  303.                 strcpy(extra,"        ");
  304.                 /* tab amount depends on current column */
  305.                 nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
  306.                 c = ' ';
  307.             } else if ( c == NUL && P(P_LS) ) {
  308.                 extra[0] = NUL;
  309.                 nextra = 1;
  310.                 c = '$';
  311.             } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
  312.                 char *p;
  313.                 nextra = 0;
  314.                 p = chars[c].ch_str;
  315.                 /* copy 'ch-str'ing into 'extra' in reverse */
  316.                 while ( n > 1 )
  317.                     extra[nextra++] = p[--n];
  318.                 c = p[0];
  319.             }
  320.         }
  321.  
  322.         if ( c == NUL ) {
  323.             row++;
  324.             /* get pointer to start of next row */
  325.             nextrow = &Nextscreen[row*Columns];
  326.             /* blank out the rest of this row */
  327.             while ( screenp != nextrow )
  328.                 *screenp++ = ' ';
  329.             col = 0;
  330.             break;
  331.         }
  332.  
  333.         if ( col >= Columns ) {
  334.             row++;
  335.             col = 0;
  336.         }
  337.         /* store the character in Nextscreen */
  338.         *screenp++ = c;
  339.         col++;
  340.     }
  341.     return ((row - Cline_row) == Cline_size);
  342. }
  343.  
  344. /*
  345.  * lnexttoscreen
  346.  *
  347.  * Like nexttoscreen() but only for the cursor line.
  348.  */
  349. static void
  350. lnexttoscreen()
  351. {
  352.     register char    *np = Nextscreen + (Cline_row * Columns);
  353.     register char    *rp = Realscreen + (Cline_row * Columns);
  354.     register char    *endline;
  355.     register int    row, col;
  356.     int    gorow = -1, gocol = -1;
  357.  
  358.     if (anyinput()) {
  359.         need_redraw = TRUE;
  360.         return;
  361.     }
  362.  
  363.     endline = np + (Cline_size * Columns);
  364.  
  365.     row = Cline_row;
  366.     col = 0;
  367.  
  368.     CUROFF;        /* disable cursor */
  369.  
  370.     for ( ; np < endline ; np++,rp++ ) {
  371.         /* If desired screen (contents of Nextscreen) does not */
  372.         /* match what's really there, put it there. */
  373.         if ( *np != *rp ) {
  374.             /* if we are positioned at the right place, */
  375.             /* we don't have to use windgoto(). */
  376.             if (gocol != col || gorow != row) {
  377.                 /*
  378.                  * If we're just off by one, don't send
  379.                  * an entire esc. seq. (this happens a lot!)
  380.                  */
  381.                 if (gorow == row && gocol+1 == col) {
  382.                     outchar(*(np-1));
  383.                     gocol++;
  384.                 } else
  385.                     windgoto(gorow=row,gocol=col);
  386.             }
  387.             outchar(*rp = *np);
  388.             gocol++;
  389.         }
  390.         if ( ++col >= Columns ) {
  391.             col = 0;
  392.             row++;
  393.         }
  394.     }
  395.     CURON;        /* enable cursor again */
  396. }
  397.  
  398. static char *
  399. mkline(n)
  400. register int    n;
  401. {
  402.     static    char    lbuf[9];
  403.     register int    i = 2;
  404.  
  405.     strcpy(lbuf, "        ");
  406.  
  407.     lbuf[i++] = (n % 10) + '0';
  408.     n /= 10;
  409.     if (n != 0) {
  410.         lbuf[i++] = (n % 10) + '0';
  411.         n /= 10;
  412.     }
  413.     if (n != 0) {
  414.         lbuf[i++] = (n % 10) + '0';
  415.         n /= 10;
  416.     }
  417.     if (n != 0) {
  418.         lbuf[i++] = (n % 10) + '0';
  419.         n /= 10;
  420.     }
  421.     if (n != 0) {
  422.         lbuf[i++] = (n % 10) + '0';
  423.         n /= 10;
  424.     }
  425.     return lbuf;
  426. }
  427.  
  428. /*
  429.  * updateline() - update the line the cursor is on
  430.  *
  431.  * Updateline() is called after changes that only affect the line that
  432.  * the cursor is on. This improves performance tremendously for normal
  433.  * insert mode operation. The only thing we have to watch for is when
  434.  * the cursor line grows or shrinks around a row boundary. This means
  435.  * we have to repaint other parts of the screen appropriately. If
  436.  * lfiletonext() returns FALSE, the size of the cursor line (in rows)
  437.  * has changed and we have to call updatescreen() to do a complete job.
  438.  */
  439. void
  440. updateline()
  441. {
  442.     if (!lfiletonext())
  443.         updatescreen();    /* bag it, do the whole screen */
  444.     else
  445.         lnexttoscreen();
  446. }
  447.  
  448. void
  449. updatescreen()
  450. {
  451.     extern    bool_t    interactive;
  452.  
  453.     if (interactive) {
  454.         filetonext();
  455.         nexttoscreen();
  456.     }
  457. }
  458.  
  459. /*
  460.  * prt_line() - print the given line
  461.  */
  462. void
  463. prt_line(s)
  464. char    *s;
  465. {
  466.     register int    si = 0;
  467.     register int    c;
  468.     register int    col = 0;
  469.  
  470.     char    extra[16];
  471.     int    nextra = 0;
  472.     int    n;
  473.  
  474.     for (;;) {
  475.  
  476.         if ( nextra > 0 )
  477.             c = extra[--nextra];
  478.         else {
  479.             c = s[si++];
  480.             if ( c == TAB && !P(P_LS) ) {
  481.                 strcpy(extra, "        ");
  482.                 /* tab amount depends on current column */
  483.                 nextra = (P(P_TS) - 1) - col%P(P_TS);
  484.                 c = ' ';
  485.             } else if ( c == NUL && P(P_LS) ) {
  486.                 extra[0] = NUL;
  487.                 nextra = 1;
  488.                 c = '$';
  489.             } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
  490.                 char    *p;
  491.  
  492.                 nextra = 0;
  493.                 p = chars[c].ch_str;
  494.                 /* copy 'ch-str'ing into 'extra' in reverse */
  495.                 while ( n > 1 )
  496.                     extra[nextra++] = p[--n];
  497.                 c = p[0];
  498.             }
  499.         }
  500.  
  501.         if ( c == NUL )
  502.             break;
  503.  
  504.         outchar(c);
  505.         col++;
  506.     }
  507. }
  508.  
  509. void
  510. screenclear()
  511. {
  512.     register char    *rp, *np;
  513.     register char    *end;
  514.  
  515.     CLS;        /* clear the display */
  516.  
  517.     rp  = Realscreen;
  518.     end = Realscreen + Rows * Columns;
  519.     np  = Nextscreen;
  520.  
  521.     /* blank out the stored screens */
  522.     while (rp != end)
  523.         *rp++ = *np++ = ' ';
  524. }
  525.  
  526. void
  527. cursupdate()
  528. {
  529.     register LPTR    *p;
  530.     register int    icnt, c, nlines;
  531.     register int    i;
  532.     int    didinc;
  533.  
  534.     if (bufempty()) {        /* special case - file is empty */
  535.         *Topchar  = *Filemem;
  536.         *Curschar = *Filemem;
  537.     } else if ( LINEOF(Curschar) < LINEOF(Topchar) ) {
  538.         nlines = cntllines(Curschar,Topchar);
  539.         /* if the cursor is above the top of */
  540.         /* the screen, put it at the top of the screen.. */
  541.         *Topchar = *Curschar;
  542.         Topchar->index = 0;
  543.         /* ... and, if we weren't very close to begin with, */
  544.         /* we scroll so that the line is close to the middle. */
  545.         if ( nlines > Rows/3 ) {
  546.             for (i=0, p = Topchar; i < Rows/3 ;i++, *Topchar = *p)
  547.                 if ((p = prevline(p)) == NULL)
  548.                     break;
  549.         } else
  550.             s_ins(0, nlines-1);
  551.         updatescreen();
  552.     }
  553.     else if (LINEOF(Curschar) >= LINEOF(Botchar)) {
  554.         nlines = cntllines(Botchar,Curschar);
  555.         /* If the cursor is off the bottom of the screen, */
  556.         /* put it at the top of the screen.. */
  557.         /* ... and back up */
  558.         if ( nlines > Rows/3 ) {
  559.             p = Curschar;
  560.             for (i=0; i < (2*Rows)/3 ;i++)
  561.                 if ((p = prevline(p)) == NULL)
  562.                     break;
  563.             *Topchar = *p;
  564.         } else {
  565.             scrollup(nlines);
  566.         }
  567.         updatescreen();
  568.     }
  569.  
  570.     Cursrow = Curscol = Cursvcol = 0;
  571.     for ( p=Topchar; p->linep != Curschar->linep ;p = nextline(p) )
  572.         Cursrow += plines(p);
  573.  
  574.     Cline_row = Cursrow;
  575.     Cline_size = plines(p);
  576.  
  577.     if (P(P_NU))
  578.         Curscol = 8;
  579.  
  580.     for (i=0; i <= Curschar->index ;i++) {
  581.         c = Curschar->linep->s[i];
  582.         /* A tab gets expanded, depending on the current column */
  583.         if ( c == TAB && !P(P_LS) )
  584.             icnt = P(P_TS) - (Cursvcol % P(P_TS));
  585.         else
  586.             icnt = chars[(unsigned)(c & 0xff)].ch_size;
  587.         Curscol += icnt;
  588.         Cursvcol += icnt;
  589.         if ( Curscol >= Columns ) {
  590.             Curscol -= Columns;
  591.             Cursrow++;
  592.             didinc = TRUE;
  593.         }
  594.         else
  595.             didinc = FALSE;
  596.     }
  597.     if (didinc)
  598.         Cursrow--;
  599.  
  600.     if (c == TAB && State == NORMAL && !P(P_LS)) {
  601.         Curscol--;
  602.         Cursvcol--;
  603.     } else {
  604.         Curscol -= icnt;
  605.         Cursvcol -= icnt;
  606.     }
  607.     if (Curscol < 0)
  608.         Curscol += Columns;
  609.  
  610.     if (set_want_col) {
  611.         Curswant = Cursvcol;
  612.         set_want_col = FALSE;
  613.     }
  614. }
  615.  
  616. /*
  617.  * The rest of the routines in this file perform screen manipulations.
  618.  * The given operation is performed physically on the screen. The
  619.  * corresponding change is also made to the internal screen image.
  620.  * In this way, the editor anticipates the effect of editing changes
  621.  * on the appearance of the screen. That way, when we call screenupdate
  622.  * a complete redraw isn't usually necessary. Another advantage is that
  623.  * we can keep adding code to anticipate screen changes, and in the
  624.  * meantime, everything still works.
  625.  */
  626.  
  627. /*
  628.  * s_ins(row, nlines) - insert 'nlines' lines at 'row'
  629.  */
  630. void
  631. s_ins(row, nlines)
  632. int    row;
  633. int    nlines;
  634. {
  635.     register char    *s, *d;        /* src & dest for block copy */
  636.     register char    *e;        /* end point for copy */
  637.     register int    i;
  638.  
  639.     if ( ! CANIL )        /* can't do it */
  640.         return;
  641.  
  642.     /*
  643.      * It "looks" better if we do all the inserts at once
  644.      */
  645.     SAVCUR;            /* save position */
  646.     windgoto(row, 0);
  647.  
  648.     CRTIL( row, nlines );
  649.  
  650.     windgoto(Rows-1, 0);    /* delete any garbage that may have */
  651.     CLEOL;            /* been shifted to the bottom line */
  652.     RESCUR;            /* restore the cursor position */
  653.  
  654.     /*
  655.      * Now do a block move to update the internal screen image
  656.      */
  657.     d = Realscreen + (Columns * (Rows - 1)) - 1;
  658.     s = d - (Columns * nlines);
  659.     e = Realscreen + (Columns * row);
  660.  
  661.     while (s >= e)
  662.         *d-- = *s--;
  663.  
  664.     /*
  665.      * Clear the inserted lines
  666.      */
  667.     s = Realscreen + (row * Columns);
  668.     e = s + (nlines * Columns);
  669.     while (s < e)
  670.         *s++ = ' ';
  671. }
  672.  
  673. /*
  674.  * s_del(row, nlines) - delete 'nlines' lines at 'row'
  675.  */
  676. void
  677. s_del(row, nlines)
  678. int    row;
  679. int    nlines;
  680. {
  681.     register char    *s, *d, *e;
  682.     register int    i;
  683.  
  684. #ifndef BIOS
  685.     if ( ! CANDL ) return;        /* can't do it */
  686. #endif
  687.  
  688.     /* delete the lines */
  689.     SAVCUR;                /* save position */
  690.  
  691.     windgoto (Rows-1, 0);        /* go to status line */
  692.     CLEOL;                /* Clear it */
  693.     windgoto (row, 0);        /* Go to 1st line-to-del */
  694.     CRTDL( row, nlines );        /* Delete the lines */
  695.     RESCUR;                /* Restore the cursor */
  696.  
  697.     /*
  698.      * do a block move to update the internal image
  699.      */
  700.     d = Realscreen + (row * Columns);
  701.     s = d + (nlines * Columns);
  702.     e = Realscreen + ((Rows - 1) * Columns);
  703.  
  704.     while (s < e)
  705.         *d++ = *s++;
  706.  
  707.     while (d < e)        /* clear the lines at the bottom */
  708.         *d++ = ' ';
  709. }
  710.