Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

709 lines
23 KiB

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