| 1 | /* Copyright (c) 1980 Regents of the University of California */ |
| 2 | static char *sccsid = "@(#)ex_vadj.c 4.2 %G%"; |
| 3 | #include "ex.h" |
| 4 | #include "ex_tty.h" |
| 5 | #include "ex_vis.h" |
| 6 | |
| 7 | /* |
| 8 | * Routines to deal with management of logical versus physical |
| 9 | * display, opening and redisplaying lines on the screen, and |
| 10 | * use of intelligent terminal operations. Routines to deal with |
| 11 | * screen cleanup after a change. |
| 12 | */ |
| 13 | |
| 14 | /* |
| 15 | * Display a new line at physical line p, returning |
| 16 | * the depth of the newly displayed line. We may decide |
| 17 | * to expand the window on an intelligent terminal if it is |
| 18 | * less than a full screen by deleting a line above the top of the |
| 19 | * window before doing an insert line to keep all the good text |
| 20 | * on the screen in which case the line may actually end up |
| 21 | * somewhere other than line p. |
| 22 | */ |
| 23 | vopen(tp, p) |
| 24 | line *tp; |
| 25 | int p; |
| 26 | { |
| 27 | register int cnt; |
| 28 | register struct vlinfo *vp, *vpc; |
| 29 | |
| 30 | #ifdef ADEBUG |
| 31 | if (trace != NULL) |
| 32 | tfixnl(), fprintf(trace, "vopen(%d, %d)\n", lineno(tp), p); |
| 33 | #endif |
| 34 | if (state != VISUAL) { |
| 35 | if (vcnt) |
| 36 | if (hold & HOLDROL) |
| 37 | vup1(); |
| 38 | else |
| 39 | vclean(); |
| 40 | |
| 41 | /* |
| 42 | * Forget all that we once knew. |
| 43 | */ |
| 44 | vcnt = vcline = 0; |
| 45 | p = WBOT; LASTLINE = WBOT + 1; |
| 46 | state = bastate; |
| 47 | WTOP = basWTOP; |
| 48 | WLINES = basWLINES; |
| 49 | } |
| 50 | vpc = &vlinfo[vcline]; |
| 51 | for (vp = &vlinfo[vcnt]; vp >= vpc; vp--) |
| 52 | vlcopy(vp[1], vp[0]); |
| 53 | vcnt++; |
| 54 | if (Pline == numbline) |
| 55 | /* |
| 56 | * Dirtying all the lines is rather inefficient |
| 57 | * internally, but number mode is used rarely |
| 58 | * and so its not worth optimizing. |
| 59 | */ |
| 60 | vdirty(vcline+1, WECHO); |
| 61 | getline(*tp); |
| 62 | |
| 63 | /* |
| 64 | * If we are opening at the top of the window, can try a window |
| 65 | * expansion at the top. |
| 66 | */ |
| 67 | if (state == VISUAL && vcline == 0 && vcnt > 1 && p > ZERO) { |
| 68 | cnt = p + vdepth() - LINE(1); |
| 69 | if (cnt > 0) { |
| 70 | p -= cnt; |
| 71 | if (p < ZERO) |
| 72 | p = ZERO; |
| 73 | WTOP = p; |
| 74 | WLINES = WBOT - WTOP + 1; |
| 75 | } |
| 76 | } |
| 77 | vpc->vliny = p, vpc->vdepth = 0, vpc->vflags = 0; |
| 78 | cnt = vreopen(p, lineno(tp), vcline); |
| 79 | if (vcline + 1 == vcnt) |
| 80 | LINE(vcnt) = LINE(vcline) + cnt; |
| 81 | } |
| 82 | |
| 83 | /* |
| 84 | * Redisplay logical line l at physical line p with line number lineno. |
| 85 | */ |
| 86 | vreopen(p, lineno, l) |
| 87 | int p, lineno, l; |
| 88 | { |
| 89 | register int d; |
| 90 | register struct vlinfo *vp = &vlinfo[l]; |
| 91 | |
| 92 | #ifdef ADEBUG |
| 93 | if (trace) |
| 94 | tfixnl(), fprintf(trace, "vreopen(%d, %d, %d)\n", p, lineno, l); |
| 95 | #endif |
| 96 | d = vp->vdepth; |
| 97 | if (d == 0 || (vp->vflags & VDIRT)) |
| 98 | vp->vdepth = d = vdepth(); |
| 99 | vp->vliny = p, vp->vflags &= ~VDIRT; |
| 100 | |
| 101 | /* |
| 102 | * Try to win by making the screen larger rather than inserting |
| 103 | * a line and driving text off the bottom. |
| 104 | */ |
| 105 | p = vglitchup(l, 0); |
| 106 | |
| 107 | /* |
| 108 | * BUG: Should consider using CE here to clear to end of line. |
| 109 | * As it stands we always strike over the current text. |
| 110 | * Since often the current text is the same as what |
| 111 | * we are overstriking with, it tends not to show. |
| 112 | * On the other hand if it is different and we end up |
| 113 | * spacing out a lot of text, we could have won with |
| 114 | * a CE. This is probably worthwhile at low speed |
| 115 | * only however, since clearly computation will be |
| 116 | * necessary to determine which way to go. |
| 117 | */ |
| 118 | vigoto(p, 0); |
| 119 | #ifdef TRACE |
| 120 | if (trace) |
| 121 | fprintf(trace, "before pline in vreopen\n"); |
| 122 | #endif |
| 123 | pline(lineno); |
| 124 | #ifdef TRACE |
| 125 | if (trace) |
| 126 | fprintf(trace, "after pline in vreopen\n"); |
| 127 | #endif |
| 128 | |
| 129 | /* |
| 130 | * When we are typing part of a line for hardcopy open, don't |
| 131 | * want to type the '$' marking an end of line if in list mode. |
| 132 | */ |
| 133 | if (hold & HOLDDOL) |
| 134 | return (d); |
| 135 | if (Putchar == listchar) |
| 136 | putchar('$'); |
| 137 | |
| 138 | /* |
| 139 | * Optimization of cursor motion may prevent screen rollup if the |
| 140 | * line has blanks/tabs at the end unless we force the cursor to appear |
| 141 | * on the last line segment. |
| 142 | */ |
| 143 | if (vp->vliny + d - 1 > WBOT) |
| 144 | vcsync(); |
| 145 | |
| 146 | /* |
| 147 | * Switch into hardcopy open mode if we are in one line (adm3) |
| 148 | * open mode and this line is now too long. If in hardcopy |
| 149 | * open mode, then call sethard to move onto the next line |
| 150 | * with appropriate positioning. |
| 151 | */ |
| 152 | if (state == ONEOPEN) { |
| 153 | WCOLS = OCOLUMNS; |
| 154 | if (vdepth() > 1) { |
| 155 | WCOLS = TUBECOLS; |
| 156 | sethard(); |
| 157 | } else |
| 158 | WCOLS = TUBECOLS; |
| 159 | } else if (state == HARDOPEN) |
| 160 | sethard(); |
| 161 | |
| 162 | /* |
| 163 | * Unless we filled (completely) the last line we typed on, |
| 164 | * we have to clear to the end of the line |
| 165 | * in case stuff is left from before. |
| 166 | */ |
| 167 | if (vp->vliny + d > destline) { |
| 168 | if (IN && destcol == WCOLS) |
| 169 | vigoto(vp->vliny + d - 1, 0); |
| 170 | vclreol(); |
| 171 | } |
| 172 | return (d); |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | * Real work for winning growing of window at top |
| 177 | * when inserting in the middle of a partially full |
| 178 | * screen on an intelligent terminal. We have as argument |
| 179 | * the logical line number to be inserted after, and the offset |
| 180 | * from that line where the insert will go. |
| 181 | * We look at the picture of depths and positions, and if we can |
| 182 | * delete some (blank) lines from the top of the screen so that |
| 183 | * later inserts will not push stuff off the bottom. |
| 184 | */ |
| 185 | vglitchup(l, o) |
| 186 | int l, o; |
| 187 | { |
| 188 | register struct vlinfo *vp = &vlinfo[l]; |
| 189 | register int need; |
| 190 | register int p = vp->vliny; |
| 191 | short oldhold, oldheldech; |
| 192 | bool glitched = 0; |
| 193 | |
| 194 | if (l < vcnt - 1) { |
| 195 | need = p + vp->vdepth - (vp+1)->vliny; |
| 196 | if (need > 0) { |
| 197 | if (state == VISUAL && WTOP - ZERO >= need && AL && DL) { |
| 198 | glitched++; |
| 199 | WTOP -= need; |
| 200 | WLINES = WBOT - WTOP + 1; |
| 201 | p -= need; |
| 202 | if (p + o == WTOP) { |
| 203 | vp->vliny = WTOP; |
| 204 | return (WTOP + o); |
| 205 | } |
| 206 | vdellin(WTOP, need, -1); |
| 207 | oldheldech = heldech; |
| 208 | oldhold = hold; |
| 209 | hold |= HOLDECH; |
| 210 | } |
| 211 | vinslin((vp+1)->vliny, need, l); |
| 212 | if (glitched) { |
| 213 | hold = oldhold; |
| 214 | heldech = oldheldech; |
| 215 | } |
| 216 | } |
| 217 | } else |
| 218 | vp[1].vliny = vp[0].vliny + vp->vdepth; |
| 219 | return (p + o); |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | * Insert cnt blank lines before line p, |
| 224 | * logically and (if supported) physically. |
| 225 | */ |
| 226 | vinslin(p, cnt, l) |
| 227 | register int p, cnt; |
| 228 | int l; |
| 229 | { |
| 230 | register int i; |
| 231 | bool could = 1; |
| 232 | |
| 233 | #ifdef ADEBUG |
| 234 | if (trace) |
| 235 | tfixnl(), fprintf(trace, "vinslin(%d, %d, %d)\n", p, cnt, l); |
| 236 | #endif |
| 237 | if (p + cnt > WBOT && CD) { |
| 238 | /* |
| 239 | * Really quick -- clear to end of screen. |
| 240 | */ |
| 241 | cnt = WECHO + 1 - p; |
| 242 | vgoto(p, 0), vputp(CD, cnt); |
| 243 | vclrech(1); |
| 244 | vadjAL(p, cnt); |
| 245 | } else if (AL) { |
| 246 | /* |
| 247 | * Use insert line. |
| 248 | */ |
| 249 | vgoto(p, 0), vputp(AL, WECHO + 1 - p); |
| 250 | for (i = cnt - 1; i > 0; i--) { |
| 251 | vgoto(outline+1, 0), vputp(AL, WECHO + 1 - outline); |
| 252 | if ((hold & HOLDAT) == 0) |
| 253 | putchar('@'); |
| 254 | } |
| 255 | vadjAL(p, cnt); |
| 256 | } else if (SR && p == WTOP) { |
| 257 | /* |
| 258 | * Use reverse scroll mode of the terminal, at |
| 259 | * the top of the window. |
| 260 | */ |
| 261 | for (i = cnt; i > 0; i--) { |
| 262 | vgoto(p, 0), vputp(SR, 0); |
| 263 | if (i > 1 && (hold & HOLDAT) == 0) |
| 264 | putchar('@'); |
| 265 | /* |
| 266 | * If we are at the top of the screen, and the |
| 267 | * terminal retains display above, then we |
| 268 | * should try to clear to end of line. |
| 269 | * Have to use CE since we don't remember what is |
| 270 | * actually on the line. |
| 271 | */ |
| 272 | if (CE && (DA || p != 0)) |
| 273 | vputp(CE, 1); |
| 274 | } |
| 275 | vadjAL(p, cnt); |
| 276 | } else |
| 277 | could = 0; |
| 278 | vopenup(cnt, could, l); |
| 279 | } |
| 280 | |
| 281 | /* |
| 282 | * Logically open up after line l, cnt of them. |
| 283 | * We need to know if it was done ``physically'' since in this |
| 284 | * case we accept what the hardware gives us. If we have to do |
| 285 | * it ourselves (brute force) we will squish out @ lines in the process |
| 286 | * if this will save us work. |
| 287 | */ |
| 288 | vopenup(cnt, could, l) |
| 289 | int cnt; |
| 290 | bool could; |
| 291 | { |
| 292 | register struct vlinfo *vc = &vlinfo[l + 1]; |
| 293 | register struct vlinfo *ve = &vlinfo[vcnt]; |
| 294 | |
| 295 | #ifdef ADEBUG |
| 296 | if (trace) |
| 297 | tfixnl(), fprintf(trace, "vopenup(%d, %d, %d)\n", cnt, could, l); |
| 298 | #endif |
| 299 | if (could) |
| 300 | /* |
| 301 | * This will push @ lines down the screen, |
| 302 | * just as the hardware did. Since the default |
| 303 | * for intelligent terminals is to never have @ |
| 304 | * lines on the screen, this should never happen, |
| 305 | * and the code makes no special effort to be nice in this |
| 306 | * case, e.g. squishing out the @ lines by delete lines |
| 307 | * before doing append lines. |
| 308 | */ |
| 309 | for (; vc <= ve; vc++) |
| 310 | vc->vliny += cnt; |
| 311 | else { |
| 312 | /* |
| 313 | * Will have to clean up brute force eventually, |
| 314 | * so push the line data around as little as possible. |
| 315 | */ |
| 316 | vc->vliny += cnt, vc->vflags |= VDIRT; |
| 317 | while (vc < ve) { |
| 318 | register int i = vc->vliny + vc->vdepth; |
| 319 | |
| 320 | vc++; |
| 321 | if (i <= vc->vliny) |
| 322 | break; |
| 323 | vc->vliny = i, vc->vflags |= VDIRT; |
| 324 | } |
| 325 | } |
| 326 | vscrap(); |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | * Adjust data structure internally to account for insertion of |
| 331 | * blank lines on the screen. |
| 332 | */ |
| 333 | vadjAL(p, cnt) |
| 334 | int p, cnt; |
| 335 | { |
| 336 | char *tlines[TUBELINES]; |
| 337 | register int from, to; |
| 338 | |
| 339 | #ifdef ADEBUG |
| 340 | if (trace) |
| 341 | tfixnl(), fprintf(trace, "vadjal(%d, %d)\n", p, cnt); |
| 342 | #endif |
| 343 | copy(tlines, vtube, sizeof vtube); /*SASSIGN*/ |
| 344 | for (from = p, to = p + cnt; to <= WECHO; from++, to++) |
| 345 | vtube[to] = tlines[from]; |
| 346 | for (to = p; from <= WECHO; from++, to++) { |
| 347 | vtube[to] = tlines[from]; |
| 348 | vclrbyte(vtube[to], WCOLS); |
| 349 | } |
| 350 | /* |
| 351 | * Have to clear the echo area since its contents aren't |
| 352 | * necessarily consistent with the rest of the display. |
| 353 | */ |
| 354 | vclrech(0); |
| 355 | } |
| 356 | |
| 357 | /* |
| 358 | * Roll the screen up logically and physically |
| 359 | * so that line dl is the bottom line on the screen. |
| 360 | */ |
| 361 | vrollup(dl) |
| 362 | int dl; |
| 363 | { |
| 364 | register int cnt; |
| 365 | register int dc = destcol; |
| 366 | |
| 367 | #ifdef ADEBUG |
| 368 | if (trace) |
| 369 | tfixnl(), fprintf(trace, "vrollup(%d)\n", dl); |
| 370 | #endif |
| 371 | cnt = dl - (splitw ? WECHO : WBOT); |
| 372 | if (splitw && (state == VISUAL || state == CRTOPEN)) |
| 373 | holdupd = 1; |
| 374 | vmoveitup(cnt, 1); |
| 375 | vscroll(cnt); |
| 376 | destline = dl - cnt, destcol = dc; |
| 377 | } |
| 378 | |
| 379 | vup1() |
| 380 | { |
| 381 | |
| 382 | vrollup(WBOT + 1); |
| 383 | } |
| 384 | |
| 385 | /* |
| 386 | * Scroll the screen up cnt lines physically. |
| 387 | * If doclr is true, do a clear eol if the terminal |
| 388 | * has standout (to prevent it from scrolling up) |
| 389 | */ |
| 390 | vmoveitup(cnt, doclr) |
| 391 | register int cnt; |
| 392 | bool doclr; |
| 393 | { |
| 394 | |
| 395 | if (cnt == 0) |
| 396 | return; |
| 397 | #ifdef ADEBUG |
| 398 | if (trace) |
| 399 | tfixnl(), fprintf(trace, "vmoveitup(%d)\n", cnt); |
| 400 | #endif |
| 401 | if (doclr && (SO || SE)) |
| 402 | vclrech(0); |
| 403 | if (SF) { |
| 404 | while (cnt > 0) |
| 405 | vputp(SF, 0), cnt--; |
| 406 | return; |
| 407 | } |
| 408 | destline = WECHO + cnt; |
| 409 | destcol = (NONL ? 0 : outcol % WCOLS); |
| 410 | fgoto(); |
| 411 | if (state == ONEOPEN || state == HARDOPEN) { |
| 412 | outline = destline = 0; |
| 413 | vclrbyte(vtube[0], WCOLS); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | * Scroll the screen up cnt lines logically. |
| 419 | */ |
| 420 | vscroll(cnt) |
| 421 | register int cnt; |
| 422 | { |
| 423 | register int from, to; |
| 424 | char *tlines[TUBELINES]; |
| 425 | |
| 426 | #ifdef ADEBUG |
| 427 | if (trace) |
| 428 | fprintf(trace, "vscroll(%d)\n", cnt); |
| 429 | #endif |
| 430 | if (cnt < 0 || cnt > TUBELINES) |
| 431 | error("Internal error: vscroll"); |
| 432 | if (cnt == 0) |
| 433 | return; |
| 434 | copy(tlines, vtube, sizeof vtube); |
| 435 | for (to = ZERO, from = ZERO + cnt; to <= WECHO - cnt; to++, from++) |
| 436 | vtube[to] = tlines[from]; |
| 437 | for (from = ZERO; to <= WECHO; to++, from++) { |
| 438 | vtube[to] = tlines[from]; |
| 439 | vclrbyte(vtube[to], WCOLS); |
| 440 | } |
| 441 | for (from = 0; from <= vcnt; from++) |
| 442 | LINE(from) -= cnt; |
| 443 | } |
| 444 | |
| 445 | /* |
| 446 | * Discard logical lines due to physical wandering off the screen. |
| 447 | */ |
| 448 | vscrap() |
| 449 | { |
| 450 | register int i, j; |
| 451 | |
| 452 | #ifdef ADEBUG |
| 453 | if (trace) |
| 454 | tfixnl(), fprintf(trace, "vscrap\n"), tvliny(); |
| 455 | #endif |
| 456 | if (splitw) |
| 457 | return; |
| 458 | if (vcnt && WBOT != WECHO && LINE(0) < WTOP && LINE(0) >= ZERO) { |
| 459 | WTOP = LINE(0); |
| 460 | WLINES = WBOT - WTOP + 1; |
| 461 | } |
| 462 | for (j = 0; j < vcnt; j++) |
| 463 | if (LINE(j) >= WTOP) { |
| 464 | if (j == 0) |
| 465 | break; |
| 466 | /* |
| 467 | * Discard the first j physical lines off the top. |
| 468 | */ |
| 469 | vcnt -= j, vcline -= j; |
| 470 | for (i = 0; i <= vcnt; i++) |
| 471 | vlcopy(vlinfo[i], vlinfo[i + j]); |
| 472 | break; |
| 473 | } |
| 474 | /* |
| 475 | * Discard lines off the bottom. |
| 476 | */ |
| 477 | if (vcnt) { |
| 478 | for (j = 0; j <= vcnt; j++) |
| 479 | if (LINE(j) > WBOT || LINE(j) + DEPTH(j) - 1 > WBOT) { |
| 480 | vcnt = j; |
| 481 | break; |
| 482 | } |
| 483 | LASTLINE = LINE(vcnt-1) + DEPTH(vcnt-1); |
| 484 | } |
| 485 | #ifdef ADEBUG |
| 486 | if (trace) |
| 487 | tvliny(); |
| 488 | #endif |
| 489 | /* |
| 490 | * May have no lines! |
| 491 | */ |
| 492 | } |
| 493 | |
| 494 | /* |
| 495 | * Repaint the screen, with cursor at curs, aftern an arbitrary change. |
| 496 | * Handle notification on large changes. |
| 497 | */ |
| 498 | vrepaint(curs) |
| 499 | char *curs; |
| 500 | { |
| 501 | |
| 502 | wdot = NOLINE; |
| 503 | /* |
| 504 | * In open want to notify first. |
| 505 | */ |
| 506 | noteit(0); |
| 507 | vscrap(); |
| 508 | |
| 509 | /* |
| 510 | * Deal with a totally useless display. |
| 511 | */ |
| 512 | if (vcnt == 0 || vcline < 0 || vcline > vcnt || holdupd && state != VISUAL) { |
| 513 | register line *odol = dol; |
| 514 | |
| 515 | vcnt = 0; |
| 516 | if (holdupd) |
| 517 | if (state == VISUAL) |
| 518 | ignore(peekkey()); |
| 519 | else |
| 520 | vup1(); |
| 521 | holdupd = 0; |
| 522 | if (odol == zero) |
| 523 | fixzero(); |
| 524 | vcontext(dot, '.'); |
| 525 | noteit(1); |
| 526 | if (noteit(1) == 0 && odol == zero) { |
| 527 | CATCH |
| 528 | error("No lines in buffer"); |
| 529 | ENDCATCH |
| 530 | linebuf[0] = 0; |
| 531 | splitw = 0; |
| 532 | } |
| 533 | vnline(curs); |
| 534 | return; |
| 535 | } |
| 536 | |
| 537 | /* |
| 538 | * Have some useful displayed text; refresh it. |
| 539 | */ |
| 540 | getDOT(); |
| 541 | |
| 542 | /* |
| 543 | * This is for boundary conditions in open mode. |
| 544 | */ |
| 545 | if (FLAGS(0) & VDIRT) |
| 546 | vsync(WTOP); |
| 547 | |
| 548 | /* |
| 549 | * If the current line is after the last displayed line |
| 550 | * or the bottom of the screen, then special effort is needed |
| 551 | * to get it on the screen. We first try a redraw at the |
| 552 | * last line on the screen, hoping it will fill in where @ |
| 553 | * lines are now. If this doesn't work, then roll it onto |
| 554 | * the screen. |
| 555 | */ |
| 556 | if (vcline >= vcnt || LINE(vcline) > WBOT) { |
| 557 | short oldhold = hold; |
| 558 | hold |= HOLDAT, vredraw(LASTLINE), hold = oldhold; |
| 559 | if (vcline >= vcnt) { |
| 560 | register int i = vcline - vcnt + 1; |
| 561 | |
| 562 | dot -= i; |
| 563 | vcline -= i; |
| 564 | vroll(i); |
| 565 | } else |
| 566 | vsyncCL(); |
| 567 | } else |
| 568 | vsync(vcline > 0 ? LINE(vcline - 1) : WTOP); |
| 569 | |
| 570 | /* |
| 571 | * Notification on large change for visual |
| 572 | * has to be done last or we may lose |
| 573 | * the echo area with redisplay. |
| 574 | */ |
| 575 | noteit(1); |
| 576 | |
| 577 | /* |
| 578 | * Finally. Move the cursor onto the current line. |
| 579 | */ |
| 580 | vnline(curs); |
| 581 | } |
| 582 | |
| 583 | /* |
| 584 | * Fully cleanup the screen, leaving no @ lines except at end when |
| 585 | * line after last won't completely fit. The routine vsync is |
| 586 | * more conservative and much less work on dumb terminals. |
| 587 | */ |
| 588 | vredraw(p) |
| 589 | register int p; |
| 590 | { |
| 591 | register int l; |
| 592 | register line *tp; |
| 593 | char temp[LBSIZE]; |
| 594 | bool anydl = 0; |
| 595 | short oldhold = hold; |
| 596 | |
| 597 | #ifdef ADEBUG |
| 598 | if (trace) |
| 599 | tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny(); |
| 600 | #endif |
| 601 | if (holdupd) { |
| 602 | holdupd = 3; |
| 603 | return; |
| 604 | } |
| 605 | if (state == HARDOPEN || splitw) |
| 606 | return; |
| 607 | if (p < 0 /* || p > WECHO */) |
| 608 | error("Internal error: vredraw"); |
| 609 | |
| 610 | /* |
| 611 | * Trim the ragged edges (lines which are off the screen but |
| 612 | * not yet logically discarded), save the current line, and |
| 613 | * search for first logical line affected by the redraw. |
| 614 | */ |
| 615 | vscrap(); |
| 616 | CP(temp, linebuf); |
| 617 | l = 0; |
| 618 | tp = dot - vcline; |
| 619 | if (vcnt == 0) |
| 620 | LINE(0) = WTOP; |
| 621 | while (l < vcnt && LINE(l) < p) |
| 622 | l++, tp++; |
| 623 | |
| 624 | /* |
| 625 | * We hold off echo area clearing during the redraw in deference |
| 626 | * to a final clear of the echo area at the end if appropriate. |
| 627 | */ |
| 628 | heldech = 0; |
| 629 | hold |= HOLDECH; |
| 630 | for (; l < vcnt && Peekkey != ATTN; l++) { |
| 631 | if (l == vcline) |
| 632 | strcLIN(temp); |
| 633 | else |
| 634 | getline(*tp); |
| 635 | |
| 636 | /* |
| 637 | * Delete junk between displayed lines. |
| 638 | */ |
| 639 | if (LINE(l) != LINE(l + 1) && LINE(l) != p) { |
| 640 | if (anydl == 0 && DB && CD) { |
| 641 | hold = oldhold; |
| 642 | vclrech(0); |
| 643 | anydl = 1; |
| 644 | hold |= HOLDECH; |
| 645 | heldech = 0; |
| 646 | } |
| 647 | vdellin(p, LINE(l) - p, l); |
| 648 | } |
| 649 | |
| 650 | /* |
| 651 | * If line image is not know to be up to date, then |
| 652 | * redisplay it; else just skip onward. |
| 653 | */ |
| 654 | LINE(l) = p; |
| 655 | if (FLAGS(l) & VDIRT) { |
| 656 | DEPTH(l) = vdepth(); |
| 657 | if (l != vcline && p + DEPTH(l) - 1 > WBOT) { |
| 658 | vscrap(); |
| 659 | break; |
| 660 | } |
| 661 | FLAGS(l) &= ~VDIRT; |
| 662 | vreopen(p, lineno(tp), l); |
| 663 | p = LINE(l) + DEPTH(l); |
| 664 | } else |
| 665 | p += DEPTH(l); |
| 666 | tp++; |
| 667 | } |
| 668 | |
| 669 | /* |
| 670 | * That takes care of lines which were already partially displayed. |
| 671 | * Now try to fill the rest of the screen with text. |
| 672 | */ |
| 673 | if (state == VISUAL && p <= WBOT) { |
| 674 | int ovcline = vcline; |
| 675 | |
| 676 | vcline = l; |
| 677 | for (; tp <= dol && Peekkey != ATTN; tp++) { |
| 678 | getline(*tp); |
| 679 | if (p + vdepth() - 1 > WBOT) |
| 680 | break; |
| 681 | vopen(tp, p); |
| 682 | p += DEPTH(vcline); |
| 683 | vcline++; |
| 684 | } |
| 685 | vcline = ovcline; |
| 686 | } |
| 687 | |
| 688 | /* |
| 689 | * Thats all the text we can get on. |
| 690 | * Now rest of lines (if any) get either a ~ if they |
| 691 | * are past end of file, or an @ if the next line won't fit. |
| 692 | */ |
| 693 | for (; p <= WBOT && Peekkey != ATTN; p++) |
| 694 | vclrlin(p, tp); |
| 695 | strcLIN(temp); |
| 696 | hold = oldhold; |
| 697 | if (heldech) |
| 698 | vclrech(0); |
| 699 | #ifdef ADEBUG |
| 700 | if (trace) |
| 701 | tvliny(); |
| 702 | #endif |
| 703 | } |
| 704 | |
| 705 | /* |
| 706 | * Do the real work in deleting cnt lines starting at line p from |
| 707 | * the display. First affected line is line l. |
| 708 | */ |
| 709 | vdellin(p, cnt, l) |
| 710 | int p, cnt, l; |
| 711 | { |
| 712 | register int i; |
| 713 | |
| 714 | if (cnt == 0) |
| 715 | return; |
| 716 | if (DL == NOSTR || cnt < 0) { |
| 717 | /* |
| 718 | * Can't do it; just remember that line l is munged. |
| 719 | */ |
| 720 | FLAGS(l) |= VDIRT; |
| 721 | return; |
| 722 | } |
| 723 | #ifdef ADEBUG |
| 724 | if (trace) |
| 725 | tfixnl(), fprintf(trace, "vdellin(%d, %d, %d)\n", p, cnt, l); |
| 726 | #endif |
| 727 | /* |
| 728 | * Send the deletes to the screen and then adjust logical |
| 729 | * and physical internal data structures. |
| 730 | */ |
| 731 | vgoto(p, 0); |
| 732 | for (i = 0; i < cnt; i++) |
| 733 | vputp(DL, WECHO - p); |
| 734 | vadjDL(p, cnt); |
| 735 | vcloseup(l, cnt); |
| 736 | } |
| 737 | /* |
| 738 | * Adjust internal physical screen image to account for deleted lines. |
| 739 | */ |
| 740 | vadjDL(p, cnt) |
| 741 | int p, cnt; |
| 742 | { |
| 743 | char *tlines[TUBELINES]; |
| 744 | register int from, to; |
| 745 | |
| 746 | #ifdef ADEBUG |
| 747 | if (trace) |
| 748 | tfixnl(), fprintf(trace, "vadjDL(%d, %d)\n", p, cnt); |
| 749 | #endif |
| 750 | /* |
| 751 | * Would like to use structured assignment but early |
| 752 | * v7 compiler (released with phototypesetter for v6) |
| 753 | * can't hack it. |
| 754 | */ |
| 755 | copy(tlines, vtube, sizeof vtube); /*SASSIGN*/ |
| 756 | for (from = p + cnt, to = p; from <= WECHO; from++, to++) |
| 757 | vtube[to] = tlines[from]; |
| 758 | for (from = p; to <= WECHO; from++, to++) { |
| 759 | vtube[to] = tlines[from]; |
| 760 | vclrbyte(vtube[to], WCOLS); |
| 761 | } |
| 762 | } |
| 763 | /* |
| 764 | * Sync the screen, like redraw but more lazy and willing to leave |
| 765 | * @ lines on the screen. VsyncCL syncs starting at the current line. |
| 766 | * In any case, if the redraw option is set then all syncs map to redraws |
| 767 | * as if vsync didn't exist. |
| 768 | */ |
| 769 | vsyncCL() |
| 770 | { |
| 771 | |
| 772 | vsync(LINE(vcline)); |
| 773 | } |
| 774 | |
| 775 | vsync(p) |
| 776 | register int p; |
| 777 | { |
| 778 | |
| 779 | if (value(REDRAW)) |
| 780 | vredraw(p); |
| 781 | else |
| 782 | vsync1(p); |
| 783 | } |
| 784 | |
| 785 | /* |
| 786 | * The guts of a sync. Similar to redraw but |
| 787 | * just less ambitous. |
| 788 | */ |
| 789 | vsync1(p) |
| 790 | register int p; |
| 791 | { |
| 792 | register int l; |
| 793 | char temp[LBSIZE]; |
| 794 | register struct vlinfo *vp = &vlinfo[0]; |
| 795 | short oldhold = hold; |
| 796 | |
| 797 | #ifdef ADEBUG |
| 798 | if (trace) |
| 799 | tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny(); |
| 800 | #endif |
| 801 | if (holdupd) { |
| 802 | if (holdupd < 3) |
| 803 | holdupd = 2; |
| 804 | return; |
| 805 | } |
| 806 | if (state == HARDOPEN || splitw) |
| 807 | return; |
| 808 | vscrap(); |
| 809 | CP(temp, linebuf); |
| 810 | if (vcnt == 0) |
| 811 | LINE(0) = WTOP; |
| 812 | l = 0; |
| 813 | while (l < vcnt && vp->vliny < p) |
| 814 | l++, vp++; |
| 815 | heldech = 0; |
| 816 | hold |= HOLDECH; |
| 817 | while (p <= WBOT && Peekkey != ATTN) { |
| 818 | /* |
| 819 | * Want to put a line here if not in visual and first line |
| 820 | * or if there are lies left and this line starts before |
| 821 | * the current line, or if this line is piled under the |
| 822 | * next line (vreplace does this and we undo it). |
| 823 | */ |
| 824 | if (l == 0 && state != VISUAL || |
| 825 | (l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) { |
| 826 | if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) { |
| 827 | if (l == vcline) |
| 828 | strcLIN(temp); |
| 829 | else |
| 830 | getline(dot[l - vcline]); |
| 831 | /* |
| 832 | * Be careful that a long line doesn't cause the |
| 833 | * screen to shoot up. |
| 834 | */ |
| 835 | if (l != vcline && (vp->vflags & VDIRT)) { |
| 836 | vp->vdepth = vdepth(); |
| 837 | vp->vflags &= ~VDIRT; |
| 838 | if (p + vp->vdepth - 1 > WBOT) |
| 839 | break; |
| 840 | } |
| 841 | vreopen(p, lineDOT() + (l - vcline), l); |
| 842 | } |
| 843 | p = vp->vliny + vp->vdepth; |
| 844 | vp++; |
| 845 | l++; |
| 846 | } else |
| 847 | /* |
| 848 | * A physical line between logical lines, |
| 849 | * so we settle for an @ at the beginning. |
| 850 | */ |
| 851 | vclrlin(p, dot + (l - vcline)), p++; |
| 852 | } |
| 853 | strcLIN(temp); |
| 854 | hold = oldhold; |
| 855 | if (heldech) |
| 856 | vclrech(0); |
| 857 | } |
| 858 | |
| 859 | /* |
| 860 | * Subtract (logically) cnt physical lines from the |
| 861 | * displayed position of lines starting with line l. |
| 862 | */ |
| 863 | vcloseup(l, cnt) |
| 864 | int l; |
| 865 | register int cnt; |
| 866 | { |
| 867 | register int i; |
| 868 | |
| 869 | #ifdef ADEBUG |
| 870 | if (trace) |
| 871 | tfixnl(), fprintf(trace, "vcloseup(%d, %d)\n", l, cnt); |
| 872 | #endif |
| 873 | for (i = l + 1; i <= vcnt; i++) |
| 874 | LINE(i) -= cnt; |
| 875 | } |
| 876 | |
| 877 | /* |
| 878 | * Workhorse for rearranging line descriptors on changes. |
| 879 | * The idea here is that, starting with line l, cnt lines |
| 880 | * have been replaced with newcnt lines. All of these may |
| 881 | * be ridiculous, i.e. l may be -1000, cnt 50 and newcnt 0, |
| 882 | * since we may be called from an undo after the screen has |
| 883 | * moved a lot. Thus we have to be careful. |
| 884 | * |
| 885 | * Many boundary conditions here. |
| 886 | */ |
| 887 | vreplace(l, cnt, newcnt) |
| 888 | int l, cnt, newcnt; |
| 889 | { |
| 890 | register int from, to, i; |
| 891 | bool savenote = 0; |
| 892 | |
| 893 | #ifdef ADEBUG |
| 894 | if (trace) { |
| 895 | tfixnl(), fprintf(trace, "vreplace(%d, %d, %d)\n", l, cnt, newcnt); |
| 896 | tvliny(); |
| 897 | } |
| 898 | #endif |
| 899 | if (l >= vcnt) |
| 900 | return; |
| 901 | if (l < 0) { |
| 902 | if (l + cnt < 0) { |
| 903 | /* |
| 904 | * Nothing on the screen is relevant. |
| 905 | * Settle for redrawing from scratch (later). |
| 906 | */ |
| 907 | vcnt = 0; |
| 908 | return; |
| 909 | } |
| 910 | /* |
| 911 | * Normalize l to top of screen; the add is |
| 912 | * really a subtract from cnt since l is negative. |
| 913 | */ |
| 914 | cnt += l; |
| 915 | l = 0; |
| 916 | |
| 917 | /* |
| 918 | * Unseen lines were affect so notify (later). |
| 919 | */ |
| 920 | savenote++; |
| 921 | } |
| 922 | |
| 923 | /* |
| 924 | * These shouldn't happen |
| 925 | * but would cause great havoc. |
| 926 | */ |
| 927 | if (cnt < 0) |
| 928 | cnt = 0; |
| 929 | if (newcnt < 0) |
| 930 | newcnt = 0; |
| 931 | |
| 932 | /* |
| 933 | * Surely worthy of note if more than report |
| 934 | * lines were changed. |
| 935 | */ |
| 936 | if (cnt > value(REPORT) || newcnt > value(REPORT)) |
| 937 | savenote++; |
| 938 | |
| 939 | /* |
| 940 | * Same number of lines affeted as on screen, and we |
| 941 | * can insert and delete lines. Thus we just type |
| 942 | * over them, since otherwise we will push them |
| 943 | * slowly off the screen, a clear lose. |
| 944 | */ |
| 945 | if (cnt == newcnt || vcnt - l == newcnt && AL && DL) { |
| 946 | if (cnt > 1 && l + cnt > vcnt) |
| 947 | savenote++; |
| 948 | vdirty(l, newcnt); |
| 949 | } else { |
| 950 | /* |
| 951 | * Lines are going away, squish them out. |
| 952 | */ |
| 953 | if (cnt > 0) { |
| 954 | /* |
| 955 | * If non-displayed lines went away, |
| 956 | * always notify. |
| 957 | */ |
| 958 | if (cnt > 1 && l + cnt > vcnt) |
| 959 | savenote++; |
| 960 | if (l + cnt >= vcnt) |
| 961 | cnt = vcnt - l; |
| 962 | else |
| 963 | for (from = l + cnt, to = l; from <= vcnt; to++, from++) |
| 964 | vlcopy(vlinfo[to], vlinfo[from]); |
| 965 | vcnt -= cnt; |
| 966 | } |
| 967 | /* |
| 968 | * Open up space for new lines appearing. |
| 969 | * All new lines are piled in the same place, |
| 970 | * and will be unpiled by vredraw/vsync, which |
| 971 | * inserts lines in front as it unpiles. |
| 972 | */ |
| 973 | if (newcnt > 0) { |
| 974 | /* |
| 975 | * Newlines are appearing which may not show, |
| 976 | * so notify (this is only approximately correct |
| 977 | * when long lines are present). |
| 978 | */ |
| 979 | if (newcnt > 1 && l + newcnt > vcnt + 1) |
| 980 | savenote++; |
| 981 | |
| 982 | /* |
| 983 | * If there will be more lines than fit, then |
| 984 | * just throw way the rest of the stuff on the screen. |
| 985 | */ |
| 986 | if (l + newcnt > WBOT && AL && DL) { |
| 987 | vcnt = l; |
| 988 | goto skip; |
| 989 | } |
| 990 | from = vcnt, to = vcnt + newcnt; |
| 991 | i = TUBELINES - to; |
| 992 | if (i < 0) |
| 993 | from += i, to += i; |
| 994 | vcnt = to; |
| 995 | for (; from >= l; from--, to--) |
| 996 | vlcopy(vlinfo[to], vlinfo[from]); |
| 997 | for (from = to + 1, to = l; to < l + newcnt && to <= WBOT + 1; to++) { |
| 998 | LINE(to) = LINE(from); |
| 999 | DEPTH(to) = 0; |
| 1000 | FLAGS(to) = VDIRT; |
| 1001 | } |
| 1002 | } |
| 1003 | } |
| 1004 | skip: |
| 1005 | if (Pline == numbline && cnt != newcnt) |
| 1006 | /* |
| 1007 | * When lines positions are shifted, the numbers |
| 1008 | * will be wrong. |
| 1009 | */ |
| 1010 | vdirty(l, WECHO); |
| 1011 | if (!savenote) |
| 1012 | notecnt = 0; |
| 1013 | #ifdef ADEBUG |
| 1014 | if (trace) |
| 1015 | tvliny(); |
| 1016 | #endif |
| 1017 | } |
| 1018 | |
| 1019 | /* |
| 1020 | * Start harcopy open. |
| 1021 | * Print an image of the line to the left of the cursor |
| 1022 | * under the full print of the line and position the cursor. |
| 1023 | * If we are in a scroll ^D within hardcopy open then all this |
| 1024 | * is suppressed. |
| 1025 | */ |
| 1026 | sethard() |
| 1027 | { |
| 1028 | |
| 1029 | if (state == VISUAL) |
| 1030 | return; |
| 1031 | rubble = 0; |
| 1032 | state = HARDOPEN; |
| 1033 | if (hold & HOLDROL) |
| 1034 | return; |
| 1035 | vup1(); |
| 1036 | LINE(0) = WBOT; |
| 1037 | if (Pline == numbline) |
| 1038 | vgoto(WBOT, 0), printf("%6d ", lineDOT()); |
| 1039 | } |
| 1040 | |
| 1041 | /* |
| 1042 | * Mark the lines starting at base for i lines |
| 1043 | * as dirty so that they will be checked for correct |
| 1044 | * display at next sync/redraw. |
| 1045 | */ |
| 1046 | vdirty(base, i) |
| 1047 | register int base, i; |
| 1048 | { |
| 1049 | register int l; |
| 1050 | |
| 1051 | for (l = base; l < vcnt; l++) { |
| 1052 | if (--i < 0) |
| 1053 | return; |
| 1054 | FLAGS(l) |= VDIRT; |
| 1055 | } |
| 1056 | } |