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
make_scr()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
clrline(cp1,cp2)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
sput_start()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
sput_end()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
sputc(c)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
do_sputc(c)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
do_hlsputc(hl,oldhl,c)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
cl_eol()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
cl_scr(doit)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 *
LEnew()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
LEclear(sl)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
swrite(line,hl,abortable)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
i_set(nline,ncol)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
SO_off()547 SO_off()
548 {
549 SO_effect(NO);
550 }
551
552 #ifdef TERMCAP
553
554 void
SO_effect(on)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
US_effect(on)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
v_ins_line(num,top,bottom)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
v_del_line(num,top,bottom)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
GoDirect(line,col)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
RetTab(col)728 RetTab(col)
729 register int col;
730 {
731 scr_putchar('\r');
732 CapCol = 0;
733 ForTab(col);
734 }
735
736 private void
HomeGo(line,col)737 HomeGo(line, col)
738 int line,
739 col;
740 {
741 PrintHo();
742 DownMotion(line);
743 ForTab(col);
744 }
745
746 private void
BottomUp(line,col)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
ForTab(to)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
DownMotion(destline)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
UpMotion(destline)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
Placur(line,col)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
ForNum(from,to)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
i_lines(top,bottom,num)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
d_lines(top,bottom,num)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