home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / screen.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  23KB  |  1,013 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "fp.h"
  10. #include "chars.h"
  11. #include "jctype.h"
  12. #include "disp.h"
  13. #include "extend.h"
  14. #include "fmt.h"
  15. #include "term.h"
  16. #include "mac.h"
  17. #include "screen.h"
  18. #include "wind.h"
  19.  
  20. int    AbortCnt,
  21.     tabstop = 8;    /* VAR: expand tabs to this number of spaces */
  22.  
  23. struct scrimage
  24.     *DesiredScreen = NULL,
  25.     *PhysScreen = NULL;
  26.  
  27. private struct screenline   *Savelines = NULL;    /* scratch entries (LI of them) */
  28.  
  29. private void LEclear proto((struct screenline *));    /* free s_effects component */
  30.  
  31. private char    *cursor;            /* offset into current Line */
  32.  
  33. char    *cursend;
  34.  
  35. int    CapCol,
  36.     CapLine;
  37.  
  38. private int
  39.     i_line,
  40.     i_col;
  41.  
  42. void
  43. make_scr()
  44. {
  45.     register int    i;
  46.     register struct screenline    *ns;
  47.     register char    *nsp;
  48.     static char    *screenchars = NULL;
  49.     static volatile int    oldLI = 0;
  50.  
  51.     /* In case we are RESHAPING the window! */
  52.     if (DesiredScreen != NULL)
  53.         free((UnivPtr) DesiredScreen);
  54.     if (PhysScreen != NULL)
  55.         free((UnivPtr) PhysScreen);
  56.     i = oldLI;
  57.     oldLI = 0;
  58.     if (Savelines != NULL) {
  59.         /* Note: each screenline in Savelines has a null s_effects
  60.          * (or is uninitialized).  LEclear must not be applied.
  61.          */
  62.         free((UnivPtr) Savelines);
  63.     }
  64.     if (Screen != NULL) {
  65. #ifdef HIGHLIGHTING
  66.         for (ns = Screen; ns != &Screen[i]; ns++)
  67.             LEclear(ns);
  68. #endif
  69.         free((UnivPtr) Screen);
  70.     }
  71.     if (screenchars != NULL)
  72.         free((UnivPtr) screenchars);    /* free all the screen data */
  73.  
  74.     DesiredScreen = (struct scrimage *) malloc((unsigned) LI * sizeof (struct scrimage));
  75.     PhysScreen = (struct scrimage *) malloc((unsigned) LI * sizeof (struct scrimage));
  76.  
  77.     Savelines = (struct screenline *)
  78.             malloc((unsigned) LI * sizeof(struct screenline));
  79.     ns = Screen = (struct screenline *)
  80.             malloc((unsigned) LI * sizeof(struct screenline));
  81.  
  82.     nsp = screenchars = (char *) malloc((unsigned)CO * LI);
  83.  
  84.     if (DesiredScreen == NULL
  85.     || PhysScreen == NULL
  86.     || Savelines == NULL
  87.     || ns == NULL
  88.     || nsp == NULL)
  89.     {
  90.         writef("\n\rCannot malloc screen!\n");
  91.         finish(-1);    /* die! */
  92.     }
  93.  
  94.     for (i = 0; i < LI; i++) {
  95.         ns->s_line = nsp;
  96.         /* End of Line (nsp[CO-1] is never used) */
  97.         ns->s_roof = nsp + CO - 1;
  98.         ns->s_effects = NOEFFECT;
  99.         nsp += CO;
  100.         ns += 1;
  101.  
  102.         /* ??? The following is a fudge to placate Purify.
  103.          * There is a real bug here, so we squash it with
  104.          * a sledge hammer.  What is the correct fix?
  105.          */
  106.         {
  107.             register struct scrimage    *p;
  108.  
  109.             p = &PhysScreen[i];
  110.             p->s_offset = 0;
  111.             p->s_flags = 0;
  112.             p->s_vln = 0;
  113.             p->s_id = NULL_DADDR;
  114.             p->s_lp = NULL;
  115.             p->s_window = NULL;
  116.  
  117.             p = &DesiredScreen[i];
  118.             p->s_offset = 0;
  119.             p->s_flags = 0;
  120.             p->s_vln = 0;
  121.             p->s_id = NULL_DADDR;
  122.             p->s_lp = NULL;
  123.             p->s_window = NULL;
  124.         }
  125.     }
  126.     oldLI = LI;
  127.     SO_off();
  128. #ifdef HIGHLIGHTING
  129.     US_effect(NO);
  130. #endif
  131.     cl_scr(NO);
  132. }
  133.  
  134. void
  135. clrline(cp1, cp2)
  136. register char    *cp1,
  137.         *cp2;
  138. {
  139.     while (cp1 < cp2)
  140.         *cp1++ = ' ';
  141. }
  142.  
  143.  
  144. /* Output one character (if necessary) at the current position */
  145.  
  146. #ifdef MAC
  147.  
  148. /* Character output to bit-mapped screen is very expensive. It makes
  149.    much more sense to write the entire line at once. So, we print all
  150.    the characters, whether already there or not, once the line is
  151.    complete. */
  152.  
  153. private unsigned char sput_buf[255];
  154. private size_t sput_len = 0;
  155.  
  156. private void
  157. sput_start()
  158. {
  159. /*    if (i_line != CapLine || i_col != CapCol) */
  160.         NPlacur(i_line, i_col);
  161.     sput_len = 0;
  162. }
  163.  
  164. private void
  165. sput_end()
  166. {
  167.     if (sput_len != 0) {
  168.         writetext(sput_buf, sput_len);
  169.         sput_len = 0;
  170.     }
  171. }
  172.  
  173. private void
  174. sputc(c)
  175. register char c;
  176. {
  177.     /* if line gets too long for sput_buf, ignore subsequent chars */
  178.     if (sput_len < sizeof(sput_buf)) {
  179.         *cursor++ = c;
  180.         sput_buf[sput_len++] = (c == '0')? 0xAF /* slashed zero */ : c;
  181.         CapCol++;
  182.         i_col++;
  183.     }
  184. }
  185.  
  186. #else /* !MAC */
  187.  
  188. # ifdef HIGHLIGHTING
  189. #  define    CharChanged(c)    (*cursor != (char) (c))
  190. # else /* !HIGHLIGHTING */
  191. private bool    ChangeEffect = NO;
  192. #  define    CharChanged(c)    (ChangeEffect || *cursor != (char) (c))
  193. # endif /* !HIGHLIGHTING */
  194.  
  195. # ifdef IBMPCDOS
  196. /* On PC, we think that trying to avoid painting the character
  197.  * is slower than just doing it.  I wonder if this is true.
  198.  */
  199. #  define    sputc(c)    do_sputc(c)
  200. # else /* !IBMPCDOS */
  201. #  define sputc(c)    { \
  202.     if (CharChanged(c)) { \
  203.         do_sputc(c); \
  204.     } else { \
  205.         cursor += 1; \
  206.         i_col += 1; \
  207.     } \
  208. }
  209. # endif /* !IBMPCDOS */
  210.  
  211. private void
  212. do_sputc(c)
  213. register char    c;
  214. {
  215.     if (CharChanged(c)) {
  216. # ifdef ID_CHAR
  217.         INSmode(NO);
  218. # endif
  219.         if (i_line != CapLine || i_col != CapCol)
  220.             Placur(i_line, i_col);
  221.         *cursor++ = c;
  222. # ifdef TERMCAP
  223.         if (UL && c == '_' && *cursor != ' ')
  224.             putstr(" \b");        /* Erase so '_' looks right. */
  225. # endif
  226.         scr_putchar(c);
  227.         AbortCnt -= 1;
  228.         CapCol += 1;
  229.     } else {
  230.         cursor += 1;
  231.     }
  232.     i_col += 1;
  233. }
  234. #endif /* !MAC */
  235.  
  236. #ifdef HIGHLIGHTING
  237.  
  238. private void    (*real_effect) ptrproto((bool));
  239.  
  240. private void
  241. do_hlsputc(hl, oldhl, c)
  242. register const struct LErange *hl;    /* desired highlighting */
  243. register const struct LErange *oldhl;    /* previous highlighting */
  244. char c;
  245. {
  246.     /* assert: hl != NULL && oldhl != NULL
  247.      * In other words, hl and oldhl must point to real LErange structs.
  248.      */
  249.  
  250.     /* The following two initializing expressions use the peculiar
  251.      * properties of unsigneds to make an efficient range test.
  252.      */
  253.     void
  254.         (*virtual_effect) ptrproto((bool)) =
  255.             (unsigned)i_col - hl->start < hl->width? hl->high : hl->norm,
  256.         (*underlying_effect) ptrproto((bool)) =
  257.             (unsigned)i_col - oldhl->start < oldhl->width? oldhl->high : oldhl->norm;
  258.  
  259.     if (*cursor != c || virtual_effect != underlying_effect) {
  260. # ifdef ID_CHAR
  261.         INSmode(NO);
  262. # endif
  263.         if (i_line != CapLine || i_col != CapCol)
  264.             Placur(i_line, i_col);
  265.         if (virtual_effect != real_effect) {
  266.             if (real_effect != NULL)
  267.                 real_effect(NO);
  268.             /* instantaneously in neutral state */
  269.             if (virtual_effect != NULL)
  270.                 virtual_effect(YES);
  271.             real_effect = virtual_effect;
  272.         }
  273. # ifdef TERMCAP
  274.         if (UL && c == '_' && *cursor != ' ')
  275.             putstr(" \b");        /* Erase so '_' looks right. */
  276. # endif
  277.         *cursor++ = c;
  278.         scr_putchar(c);
  279.         AbortCnt -= 1;
  280.         CapCol += 1;
  281.     } else {
  282.         cursor += 1;
  283.     }
  284.     i_col += 1;
  285. }
  286.  
  287. #endif /* HIGHLIGHTING */
  288.  
  289. void
  290. cl_eol()
  291. {
  292.     if (cursor == Curline->s_line)
  293.         LEclear(Curline);    /* in case swrite was not called (hack!) */
  294.  
  295.     if (cursor < Curline->s_roof) {
  296. #ifdef TERMCAP
  297.         if (CE) {
  298.             Placur(i_line, i_col);
  299.             putpad(CE, 1);
  300.             clrline(cursor, Curline->s_roof);
  301.         } else {
  302.             /* Ugh.  The slow way for dumb terminals. */
  303.             register char *savecp = cursor;
  304.  
  305.             while (cursor < Curline->s_roof)
  306.                 sputc(' ');
  307.             cursor = savecp;
  308.         }
  309. #else /* !TERMCAP */
  310.         Placur(i_line, i_col);
  311.         clr_eoln();    /* MAC and PCSCR define this */
  312.         clrline(cursor, Curline->s_roof);
  313. #endif /* !TERMCAP */
  314.         Curline->s_roof = cursor;
  315.     }
  316. }
  317.  
  318. void
  319. cl_scr(doit)
  320. bool doit;
  321. {
  322.     register int    i;
  323.     register struct screenline    *sp = Screen;
  324.  
  325.     for (i = 0; i < LI; i++, sp++) {
  326.         LEclear(sp);
  327.         clrline(sp->s_line, sp->s_roof);
  328.         sp->s_roof = sp->s_line;
  329.         PhysScreen[i].s_id = NULL_DADDR;
  330.     }
  331.     if (doit) {
  332.         clr_page();
  333.         CapCol = CapLine = 0;
  334.         UpdMesg = YES;
  335.     }
  336. }
  337.  
  338. /* routines to manage a pool of LErange structs */
  339.  
  340. #ifdef HIGHLIGHTING
  341.  
  342. union LEspace {
  343.     struct LErange    le;
  344.     union LEspace    *next;
  345. };
  346.  
  347. private union LEspace *LEfreeHead = NULL;
  348.  
  349. private struct LErange *
  350. LEnew()
  351. {
  352.     struct LErange    *ret;
  353.  
  354.     if (LEfreeHead == NULL) {
  355.         LEfreeHead = (union LEspace *) emalloc(sizeof(union LEspace));
  356.         LEfreeHead->next = NULL;
  357.     }
  358.     ret = &LEfreeHead->le;
  359.     LEfreeHead = LEfreeHead->next;
  360.     return ret;
  361. }
  362.  
  363. #endif /* HIGHLIGHTING */
  364.  
  365. private void
  366. LEclear(sl)
  367. struct screenline *sl;
  368. {
  369. #ifdef HIGHLIGHTING
  370.     if (sl->s_effects != NOEFFECT) {
  371.         ((union LEspace *) sl->s_effects)->next = LEfreeHead;
  372.         LEfreeHead = (union LEspace *) sl->s_effects;
  373.     }
  374. #endif /* HIGHLIGHTING */
  375.     sl->s_effects = NOEFFECT;
  376. }
  377.  
  378. /* Write `line' at the current position of `cursor'.  Stop when we
  379.  * reach the end of the screen.  Aborts if there is a character
  380.  * waiting.
  381.  *
  382.  * Note: All callers must have "DeTabed" "line", or processed
  383.  * it equivalently -- it is presumed that line contains only
  384.  * displayable characters.
  385.  */
  386.  
  387. bool
  388. swrite(line, hl, abortable)
  389. register char    *line;
  390. LineEffects    hl;
  391. bool    abortable;
  392. {
  393.     register int    n = cursend - cursor;
  394.     bool    aborted = NO;
  395.     /* Unfortunately, neither of our LineEffects representation
  396.      * is suitable for representing the state of a partially
  397.      * updated line.  Consequently, this routine unconditionally
  398.      * replaces the old hl with the new.  To ensure that the new
  399.      * hl is correct, we compute MinCol to indicate how far in
  400.      * the line we must get, and will not abort until we have
  401.      * reached at least that column.
  402.      *
  403.      * This is unacceptably ugly.  We really must switch to a better
  404.      * representation
  405.      */
  406.     int    MinCol = 0;
  407. #ifdef HIGHLIGHTING
  408.     /* If either the old line or the new line has effects,
  409.      * we know that some effects processing is necessary.
  410.      * If so, we ensure that the old line has an effect
  411.      * by adding a no-op effect if necessary: this is needed
  412.      * to ensure Placur does not get into trouble.  (UGLY!)
  413.      */
  414.     struct LErange    *oldhl = Curline->s_effects;
  415.     static const struct LErange    nohl = { 0, 0, NULL, NULL };
  416.  
  417.     if (oldhl != NOEFFECT) {
  418.         int    w = Curline->s_roof - Curline->s_line;
  419.  
  420.         if (oldhl->norm != NULL)
  421.             MinCol = w;
  422.         if (oldhl->high != NULL && w > (int)oldhl->start)
  423.             MinCol = max(MinCol, min(w, (int) (oldhl->start + oldhl->width)));
  424.     }
  425.     /* If either the old line or the new line has effects,
  426.      * we know that some effects processing is necessary.
  427.      * If so, we ensure that the old line has an effect
  428.      * by adding a no-op effect if necessary: this is needed
  429.      * to ensure Placur does not get into trouble.  (UGLY!)
  430.      */
  431.     if (hl != NOEFFECT) {
  432.         if (hl->high != NULL)
  433.             MinCol = max(MinCol, (int) (hl->start + hl->width));
  434.         if (oldhl == NOEFFECT) {
  435.             oldhl = Curline->s_effects = LEnew();    /* keep Placur on-track */
  436.             *oldhl = nohl;
  437.         }
  438.     }
  439.     real_effect = NULL;
  440. #else /* !HIGHLIGHTING */
  441.     if (Curline->s_effects != hl)
  442.         MinCol = Curline->s_roof - Curline->s_line;    /* must obliterate old */
  443. #endif /* !HIGHLIGHTING */
  444.  
  445.     if (n > 0) {
  446.         register ZXchar    c;
  447.         int    col = i_col;
  448.  
  449. #ifdef HIGHLIGHTING
  450.         /* nnhl: non-NULL version of hl (possibly
  451.          * a no-op) to reduce the cases handled.
  452.          */
  453.         const struct LErange    *nnhl = hl == NOEFFECT? &nohl : hl;
  454. #        define    spit(c)    { if (oldhl != NULL) do_hlsputc(nnhl,oldhl,c); else sputc(c); }
  455.  
  456. #else /* !HIGHLIGHTING */
  457.  
  458. #        define    spit(c)    sputc(c)
  459.  
  460. # ifdef MAC
  461.         sput_start();    /* Okay, because no interruption possible */
  462. # else /* !MAC */
  463.         if (hl != Curline->s_effects)
  464.             ChangeEffect = YES;
  465. # endif /* !MAC */
  466.  
  467.         if (hl != NOEFFECT)
  468.             SO_effect(YES);
  469. #endif /* !HIGHLIGHTING */
  470.  
  471.         while ((c = ZXC(*line++)) != '\0') {
  472.             if (abortable && i_col >= MinCol && AbortCnt < 0) {
  473.                 AbortCnt = ScrBufSize;
  474.                 if (PreEmptOutput()) {
  475.                     aborted = YES;
  476.                     break;
  477.                 }
  478.             }
  479. #ifdef TERMCAP
  480.             if (Hazeltine && c == '~')
  481.                 c = '`';
  482. #endif
  483. #ifdef CODEPAGE437
  484.             /* ??? Some archane mapping of IBM PC characters.
  485.              * According to the appendix of the Microsoft MSDOS
  486.              * Operating System 5.0 User's Guide and Reference,
  487.              * in Code Page 437 (USA English) ' ', 0x00, and 0xFF are
  488.              * blank and 0x01 is a face.
  489.              */
  490.             if (c == 0xFF)
  491.                 c = 1;
  492.             else if (c == ' ' && hl != NOEFFECT)
  493.                 c = 0xFF;
  494. #endif /* CODEPAGE437 */
  495.             if (--n <= 0) {
  496.                 /* We've got one more column -- how will we spend it?
  497.                  * ??? This is probably redundant -- callers do truncation.
  498.                  */
  499.                 if (*line != '\0')
  500.                     c = '!';
  501.                 spit(c);
  502.                 break;
  503.             }
  504.             spit(c);
  505.             col += 1;
  506.         }
  507. #ifdef HIGHLIGHTING
  508.         if (real_effect != NULL)
  509.             real_effect(NO);
  510. #else /* !HIGHLIGHTING */
  511. # ifdef MAC
  512.         sput_end();    /* flush before reverting SO */
  513. # else /* !MAC */
  514.         ChangeEffect = NO;
  515. # endif /* !MAC */
  516.         if (hl != NOEFFECT)
  517.             SO_off();
  518. #endif /* !HIGHLIGHTING */
  519.         if (cursor > Curline->s_roof)
  520.             Curline->s_roof = cursor;
  521. #        undef    spit
  522.     }
  523. #ifdef HIGHLIGHTING
  524.     if (hl == NOEFFECT)
  525.         LEclear(Curline);
  526.     else
  527.         *(Curline->s_effects) = *hl;
  528. #else /* !HIGHLIGHTING */
  529.     Curline->s_effects = hl;
  530. #endif /* !HIGHLIGHTING */
  531.     return !aborted;
  532. }
  533.  
  534. void
  535. i_set(nline, ncol)
  536. register int    nline,
  537.         ncol;
  538. {
  539.     Curline = &Screen[nline];
  540.     cursor = Curline->s_line + ncol;
  541.     cursend = &Curline->s_line[CO - 1];
  542.     i_line = nline;
  543.     i_col = ncol;
  544. }
  545.  
  546. void
  547. SO_off()
  548. {
  549.     SO_effect(NO);
  550. }
  551.  
  552. #ifdef TERMCAP
  553.  
  554. void
  555. SO_effect(on)
  556. bool    on;
  557. {
  558.     /* If there are magic cookies, then WHERE the SO string is
  559.        printed decides where the SO actually starts on the screen.
  560.        So it's important to make sure the cursor is positioned there
  561.        anyway.  I think this is right. */
  562.     if (SG != 0) {
  563.         Placur(i_line, i_col);
  564.         i_col += SG;
  565.         CapCol += SG;
  566.         cursor += SG;
  567.     }
  568.     putpad(on? SO : SE, 1);
  569. }
  570.  
  571. # ifdef HIGHLIGHTING
  572. void
  573. US_effect(on)
  574. bool    on;
  575. {
  576.     if (UG == 0)    /* not used if magic cookies */
  577.         putpad(on? US : UE, 1);
  578. }
  579. # endif /* HIGHLIGHTING */
  580.  
  581. #endif /* TERMCAP */
  582.  
  583. /* Insert `num' lines at top, but leave all the lines BELOW `bottom'
  584.    alone (at least they won't look any different when we are done).
  585.    This changes the screen array AND does the physical changes. */
  586.  
  587. void
  588. v_ins_line(num, top, bottom)
  589. int num,
  590.     top,
  591.     bottom;
  592. {
  593.     register int    i;
  594.  
  595.     /* assert(num <= bottom-top+1) */
  596.  
  597.     /* Blank and save the screen pointers that will fall off the end. */
  598.  
  599.     for(i = 0; i < num; i++) {
  600.         struct screenline    *sp = &Screen[bottom - i];
  601.  
  602.         clrline(sp->s_line, sp->s_roof);
  603.         sp->s_roof = sp->s_line;
  604.         LEclear(sp);
  605.         Savelines[i] = *sp;
  606.     }
  607.  
  608.     /* Num number of bottom lines will be lost.
  609.        Copy everything down num number of times. */
  610.  
  611.     for (i = bottom-num; i >= top; i--)
  612.         Screen[i + num] = Screen[i];
  613.  
  614.     /* Insert the now-blank saved ones at the top. */
  615.  
  616.     for (i = 0; i < num; i++)
  617.         Screen[top + i] = Savelines[i];
  618.     i_lines(top, bottom, num);
  619. }
  620.  
  621. /* Delete `num' lines starting at `top' leaving the lines below `bottom'
  622.    alone.  This updates the internal image as well as the physical image.  */
  623.  
  624. void
  625. v_del_line(num, top, bottom)
  626. int num,
  627.     top,
  628.     bottom;
  629. {
  630.     register int    i;
  631.  
  632.     /* assert(num <= bottom-top+1) */
  633.  
  634.     /* Blank and save the lines to be deleted from the top. */
  635.  
  636.     for (i = 0; i < num; i++) {
  637.         struct screenline    *sp = &Screen[top + i];
  638.  
  639.         clrline(sp->s_line, sp->s_roof);
  640.         sp->s_roof = sp->s_line;
  641.         LEclear(sp);
  642.         Savelines[i] = *sp;
  643.     }
  644.  
  645.     /* Copy everything up num number of lines. */
  646.  
  647.     for (i = top; i + num <= bottom; i++)
  648.         Screen[i] = Screen[i + num];
  649.  
  650.     /* Restore the now-blank lost lines */
  651.  
  652.     for (i = 0; i < num; i++)
  653.         Screen[bottom - i] = Savelines[i];
  654.     d_lines(top, bottom, num);
  655. }
  656.  
  657. #ifdef TERMCAP    /* remainder of this file */
  658.  
  659. /* The cursor optimization happens here.  You may decide that this
  660.    is going too far with cursor optimization, or perhaps it should
  661.    limit the amount of checking to when the output speed is slow.
  662.    What ever turns you on ...   */
  663.  
  664. struct cursaddr {
  665.     int    cm_numchars;
  666.     void    (*cm_proc) ();
  667. };
  668.  
  669. private char    *Cmstr;
  670. private struct cursaddr    *HorMin,
  671.             *VertMin,
  672.             *DirectMin;
  673.  
  674. private void
  675.     ForTab proto((int)),
  676.     RetTab proto((int)),
  677.     DownMotion proto((int)),
  678.     UpMotion proto((int)),
  679.     GoDirect proto((int, int)),
  680.     HomeGo proto((int, int)),
  681.     BottomUp proto((int, int));
  682.  
  683.  
  684. private struct cursaddr    WarpHor[] = {
  685.     { 0,    ForTab },
  686.     { 0,    RetTab }
  687. };
  688.  
  689. private struct cursaddr    WarpVert[] = {
  690.     { 0,    DownMotion },
  691.     { 0,    UpMotion }
  692. };
  693.  
  694. private struct cursaddr    WarpDirect[] = {
  695.     { 0,    GoDirect },
  696.     { 0,    HomeGo },
  697.     { 0,    BottomUp }
  698. };
  699.  
  700. # define FORTAB        0    /* Forward using tabs */
  701. # define RETFORTAB    1    /* Beginning of line and then tabs */
  702. # define NUMHOR        2
  703.  
  704. # define DOWN        0    /* Move down */
  705. # define UPMOVE        1    /* Move up */
  706. # define NUMVERT        2
  707.  
  708. # define DIRECT        0    /* Using CM */
  709. # define HOME        1    /* HOME    */
  710. # define LOWER        2    /* Lower Line */
  711. # define NUMDIRECT    3
  712.  
  713. # define    home()        Placur(0, 0)
  714. # define LowLine()    { putpad(LL, 1); CapLine = ILI; CapCol = 0; }
  715. # define PrintHo()    { putpad(HO, 1); CapLine = CapCol = 0; }
  716.  
  717. private void
  718. GoDirect(line, col)
  719. register int    line,
  720.         col;
  721. {
  722.     putpad(Cmstr, 1);
  723.     CapLine = line;
  724.     CapCol = col;
  725. }
  726.  
  727. private void
  728. RetTab(col)
  729. register int    col;
  730. {
  731.     scr_putchar('\r');
  732.     CapCol = 0;
  733.     ForTab(col);
  734. }
  735.  
  736. private void
  737. HomeGo(line, col)
  738. int line,
  739.     col;
  740. {
  741.     PrintHo();
  742.     DownMotion(line);
  743.     ForTab(col);
  744. }
  745.  
  746. private void
  747. BottomUp(line, col)
  748. register int    line,
  749.         col;
  750. {
  751.     LowLine();
  752.     UpMotion(line);
  753.     ForTab(col);
  754. }
  755.  
  756. /* Tries to move forward using tabs (if possible).  It tabs to the
  757.    closest tabstop which means it may go past 'destcol' and backspace
  758.    to it.
  759.    Note: changes to this routine must be matched by changes in ForNum. */
  760.  
  761. private void
  762. ForTab(to)
  763. int    to;
  764. {
  765.     if ((to > CapCol+1) && TABS && (phystab > 0)) {
  766.         register int    tabgoal,
  767.                 ntabs,
  768.                 pts = phystab;
  769.  
  770.         tabgoal = to + (pts / 2);
  771.         tabgoal -= (tabgoal % pts);
  772.  
  773.         /* Don't tab to last place or else it is likely to screw up. */
  774.         if (tabgoal >= CO)
  775.             tabgoal -= pts;
  776.  
  777.         ntabs = (tabgoal / pts) - (CapCol / pts);
  778.         /* If tabbing moves past goal, and goal is more cols back
  779.          * than we would have had to move forward from our original
  780.          * position, tab is counterproductive.  Notice that if our
  781.          * original motion would have been backwards, tab loses too,
  782.          * so we need not write abs(to-CapCol).
  783.          */
  784.         if (tabgoal > to && tabgoal-to >= to-CapCol)
  785.             ntabs = 0;
  786.         while (--ntabs >= 0) {
  787.             scr_putchar('\t');
  788.             CapCol = tabgoal;    /* idempotent */
  789.         }
  790.     }
  791.  
  792.     if (to > CapCol) {
  793.         register char    *cp = &Screen[CapLine].s_line[CapCol];
  794.  
  795. # ifdef ID_CHAR
  796.         INSmode(NO);    /* we're not just a motion */
  797. # endif
  798.         while (to > CapCol) {
  799.             scr_putchar(*cp++);
  800.             CapCol++;
  801.         }
  802.     }
  803.  
  804.     while (to < CapCol) {
  805.         putpad(BC, 1);
  806.         CapCol--;
  807.     }
  808. }
  809.  
  810. private void
  811. DownMotion(destline)
  812. register int    destline;
  813. {
  814.     register int    nlines = destline - CapLine;
  815.  
  816.     while (--nlines >= 0) {
  817.         putpad(DO, 1);
  818.         CapLine = destline;    /* idempotent */
  819.     }
  820. }
  821.  
  822. private void
  823. UpMotion(destline)
  824. register int    destline;
  825. {
  826.     register int    nchars = CapLine - destline;
  827.  
  828.     while (--nchars >= 0) {
  829.         putpad(UP, 1);
  830.         CapLine = destline;    /* idempotent */
  831.     }
  832. }
  833.  
  834. private int ForNum proto((int from, int to));
  835.  
  836. void
  837. Placur(line, col)
  838. int line,
  839.     col;
  840. {
  841.     int    dline,        /* Number of lines to move */
  842.         dcol;        /* Number of columns to move */
  843.     register int    best,
  844.             i;
  845.     register struct cursaddr    *cp;
  846.     int    xtracost = 0;    /* Misc addition to cost. */
  847.  
  848. # define CursMin(which,addrs,max)    { \
  849.     for (best = 0, cp = &(addrs)[1], i = 1; i < (max); i++, cp++) \
  850.         if (cp->cm_numchars < (addrs)[best].cm_numchars) \
  851.             best = i; \
  852.     (which) = &(addrs)[best]; \
  853. }
  854.  
  855.     if (line == CapLine && col == CapCol)
  856.         return;        /* We are already there. */
  857.  
  858.     dline = line - CapLine;
  859.     dcol = col - CapCol;
  860. # ifdef ID_CHAR
  861.     if (IN_INSmode && MI)
  862.         xtracost = EIlen + IMlen;
  863.     /* If we're already in insert mode, it is likely that we will
  864.        want to be in insert mode again, after the insert. */
  865. # endif
  866.  
  867.     /* Number of characters to move horizontally for each case.
  868.        1: Try tabbing to the correct place.
  869.        2: Try going to the beginning of the line, and then tab. */
  870.  
  871.     if (dcol == 1 || dcol == 0) {        /* Most common case. */
  872.         HorMin = &WarpHor[FORTAB];
  873.         HorMin->cm_numchars = dcol + xtracost;
  874.     } else {
  875.         WarpHor[FORTAB].cm_numchars = xtracost + ForNum(CapCol, col);
  876.         WarpHor[RETFORTAB].cm_numchars = xtracost + 1 + ForNum(0, col);
  877.  
  878.         /* Which is the shortest of the bunch */
  879.  
  880.         CursMin(HorMin, WarpHor, NUMHOR);
  881.     }
  882.  
  883.     /* Moving vertically is more simple. */
  884.  
  885.     WarpVert[DOWN].cm_numchars = dline >= 0 ? dline : INFINITY;
  886.     WarpVert[UPMOVE].cm_numchars = dline < 0 ? ((-dline) * UPlen) : INFINITY;
  887.  
  888.     /* Which of these is simpler */
  889.     CursMin(VertMin, WarpVert, NUMVERT);
  890.  
  891.     /* Homing first and lowering first are considered
  892.        direct motions.
  893.        Homing first's total is the sum of the cost of homing
  894.        and the sum of tabbing (if possible) to the right. */
  895.  
  896.     if (Screen[line].s_effects != NOEFFECT && CM != NULL) {
  897.         /* We are going to a line with inversion or underlining;
  898.            Don't try any clever stuff */
  899.         DirectMin = &WarpDirect[DIRECT];
  900.         DirectMin->cm_numchars = 0;
  901.         Cmstr = targ2(CM, col, line);
  902.     } else if (VertMin->cm_numchars + HorMin->cm_numchars <= 3) {
  903.         /* Since no direct method is ever shorter than 3 chars, don't try it. */
  904.         DirectMin = &WarpDirect[DIRECT];    /* A dummy ... */
  905.         DirectMin->cm_numchars = INFINITY;
  906.     } else {
  907.         WarpDirect[DIRECT].cm_numchars = CM != NULL ?
  908.                 strlen(Cmstr = targ2(CM, col, line)) : INFINITY;
  909.         WarpDirect[HOME].cm_numchars = HOlen + line +
  910.                 WarpHor[RETFORTAB].cm_numchars;
  911.         WarpDirect[LOWER].cm_numchars = LLlen + ((ILI - line) * UPlen) +
  912.                 WarpHor[RETFORTAB].cm_numchars;
  913.         CursMin(DirectMin, WarpDirect, NUMDIRECT);
  914.     }
  915.  
  916.     if (HorMin->cm_numchars + VertMin->cm_numchars < DirectMin->cm_numchars) {
  917.         if (line != CapLine)
  918.             (*(void (*)ptrproto((int)))VertMin->cm_proc)(line);
  919.         if (col != CapCol) {
  920. # ifdef ID_CHAR
  921.             INSmode(NO);    /* We may use real characters ... */
  922. # endif
  923.             (*(void (*)ptrproto((int)))HorMin->cm_proc)(col);
  924.         }
  925.     } else {
  926. # ifdef ID_CHAR
  927.         if (IN_INSmode && !MI)
  928.             INSmode(NO);
  929. # endif
  930.         (*(void (*)ptrproto((int, int)))DirectMin->cm_proc)(line, col);
  931.     }
  932. }
  933.  
  934.  
  935. /* Figures out how many characters ForTab() would use to move forward
  936.    using tabs (if possible).
  937.    Note: changes to this routine must be matched by changes in ForTab.
  938.    An exception is that any cost for leaving insert mode has been
  939.    accounted for by our caller. */
  940.  
  941. private int
  942. ForNum(from, to)
  943. register int    from;
  944. int to;
  945. {
  946.     register int    tabgoal,
  947.             pts = phystab;
  948.     int        ntabs = 0;
  949.  
  950.     if ((to > from+1) && TABS && (pts > 0)) {
  951.         tabgoal = to + (pts / 2);
  952.         tabgoal -= (tabgoal % pts);
  953.         if (tabgoal >= CO)
  954.             tabgoal -= pts;
  955.         ntabs = (tabgoal / pts) - (from / pts);
  956.         /* If tabbing moves past goal, and goal is more cols back
  957.          * than we would have had to move forward from our original
  958.          * position, tab is counterproductive.  Notice that if our
  959.          * original motion would have been backwards, tab loses too,
  960.          * so we need not write abs(to-from).
  961.          */
  962.         if (tabgoal > to && tabgoal-to >= to-from)
  963.             ntabs = 0;
  964.         if (ntabs != 0)
  965.             from = tabgoal;
  966.     }
  967.     return ntabs + (from>to? from-to : to-from);
  968. }
  969.  
  970. void
  971. i_lines(top, bottom, num)
  972. int top,
  973.     bottom,
  974.     num;
  975. {
  976.     if (CS) {
  977.         putpad(targ2(CS, bottom, top), 1);
  978.         CapCol = CapLine = 0;
  979.         Placur(top, 0);
  980.         putmulti(SR, M_SR, num, bottom - top);
  981.         putpad(targ2(CS, ILI, 0), 1);
  982.         CapCol = CapLine = 0;
  983.     } else {
  984.         Placur(bottom - num + 1, 0);
  985.         putmulti(DL, M_DL, num, ILI - CapLine);
  986.         Placur(top, 0);
  987.         putmulti(AL, M_AL, num, ILI - CapLine);
  988.     }
  989. }
  990.  
  991. void
  992. d_lines(top, bottom, num)
  993. int top,
  994.     bottom,
  995.     num;
  996. {
  997.     if (CS) {
  998.         putpad(targ2(CS, bottom, top), 1);
  999.         CapCol = CapLine = 0;
  1000.         Placur(bottom, 0);
  1001.         putmulti(SF, M_SF, num, bottom - top);
  1002.         putpad(targ2(CS, ILI, 0), 1);
  1003.         CapCol = CapLine = 0;
  1004.     } else {
  1005.         Placur(top, 0);
  1006.         putmulti(DL, M_DL, num, ILI - top);
  1007.         Placur(bottom + 1 - num, 0);
  1008.         putmulti(AL, M_AL, num, ILI - CapLine);
  1009.     }
  1010. }
  1011.  
  1012. #endif /* TERMCAP */
  1013.