Comment PFIL_HOOKS since it should not be needed in GENERIC.
[dragonfly.git] / contrib / nvi / vi / vs_refresh.c
1 /*-
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5  *      Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  *
9  * @(#)vs_refresh.c     10.44 (Berkeley) 10/13/96
10  * $FreeBSD: src/contrib/nvi/vi/vs_refresh.c,v 1.2.6.1 2001/07/31 00:03:41 dd Exp $
11  * $DragonFly: src/contrib/nvi/vi/vs_refresh.c,v 1.2 2003/06/17 04:24:04 dillon Exp $
12  */
13
14 #include "config.h"
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/time.h>
19
20 #include <bitstring.h>
21 #include <ctype.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "../common/common.h"
28 #include "vi.h"
29
30 #define UPDATE_CURSOR   0x01                    /* Update the cursor. */
31 #define UPDATE_SCREEN   0x02                    /* Flush to screen. */
32
33 static void     vs_modeline __P((SCR *));
34 static int      vs_paint __P((SCR *, u_int));
35
36 /*
37  * v_repaint --
38  *      Repaint selected lines from the screen.
39  *
40  * PUBLIC: int vs_repaint __P((SCR *, EVENT *));
41  */
42 int
43 vs_repaint(sp, evp)
44         SCR *sp;
45         EVENT *evp;
46 {
47         SMAP *smp;
48
49         for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) {
50                 smp = HMAP + evp->e_flno - 1;
51                 SMAP_FLUSH(smp);
52                 if (vs_line(sp, smp, NULL, NULL))
53                         return (1);
54         }
55         return (0);
56 }
57
58 /*
59  * vs_refresh --
60  *      Refresh all screens.
61  *
62  * PUBLIC: int vs_refresh __P((SCR *, int));
63  */
64 int
65 vs_refresh(sp, forcepaint)
66         SCR *sp;
67         int forcepaint;
68 {
69         GS *gp;
70         SCR *tsp;
71         int need_refresh;
72         u_int priv_paint, pub_paint;
73
74         gp = sp->gp;
75
76         /*
77          * 1: Refresh the screen.
78          *
79          * If SC_SCR_REDRAW is set in the current screen, repaint everything
80          * that we can find, including status lines.
81          */
82         if (F_ISSET(sp, SC_SCR_REDRAW))
83                 for (tsp = gp->dq.cqh_first;
84                     tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
85                         if (tsp != sp)
86                                 F_SET(tsp, SC_SCR_REDRAW | SC_STATUS);
87
88         /*
89          * 2: Related or dirtied screens, or screens with messages.
90          *
91          * If related screens share a view into a file, they may have been
92          * modified as well.  Refresh any screens that aren't exiting that
93          * have paint or dirty bits set.  Always update their screens, we
94          * are not likely to get another chance.  Finally, if we refresh any
95          * screens other than the current one, the cursor will be trashed.
96          */
97         pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW;
98         priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH;
99         if (O_ISSET(sp, O_NUMBER))
100                 priv_paint |= VIP_N_RENUMBER;
101         for (tsp = gp->dq.cqh_first;
102             tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
103                 if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) &&
104                     (F_ISSET(tsp, pub_paint) ||
105                     F_ISSET(VIP(tsp), priv_paint))) {
106                         (void)vs_paint(tsp,
107                             (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ?
108                             UPDATE_CURSOR : 0) | UPDATE_SCREEN);
109                         F_SET(VIP(sp), VIP_CUR_INVALID);
110                 }
111
112         /*
113          * 3: Refresh the current screen.
114          *
115          * Always refresh the current screen, it may be a cursor movement.
116          * Also, always do it last -- that way, SC_SCR_REDRAW can be set
117          * in the current screen only, and the screen won't flash.
118          */
119         if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint &&
120             F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN)))
121                 return (1);
122
123         /*
124          * 4: Paint any missing status lines.
125          *
126          * XXX
127          * This is fairly evil.  Status lines are written using the vi message
128          * mechanism, since we have no idea how long they are.  Since we may be
129          * painting screens other than the current one, we don't want to make
130          * the user wait.  We depend heavily on there not being any other lines
131          * currently waiting to be displayed and the message truncation code in
132          * the msgq_status routine working.
133          *
134          * And, finally, if we updated any status lines, make sure the cursor
135          * gets back to where it belongs.
136          */
137         for (need_refresh = 0, tsp = gp->dq.cqh_first;
138             tsp != (void *)&gp->dq; tsp = tsp->q.cqe_next)
139                 if (F_ISSET(tsp, SC_STATUS)) {
140                         need_refresh = 1;
141                         vs_resolve(tsp, sp, 0);
142                 }
143         if (need_refresh)
144                 (void)gp->scr_refresh(sp, 0);
145
146         /*
147          * A side-effect of refreshing the screen is that it's now ready
148          * for everything else, i.e. messages.
149          */
150         F_SET(sp, SC_SCR_VI);
151         return (0);
152 }
153
154 /*
155  * vs_paint --
156  *      This is the guts of the vi curses screen code.  The idea is that
157  *      the SCR structure passed in contains the new coordinates of the
158  *      screen.  What makes this hard is that we don't know how big
159  *      characters are, doing input can put the cursor in illegal places,
160  *      and we're frantically trying to avoid repainting unless it's
161  *      absolutely necessary.  If you change this code, you'd better know
162  *      what you're doing.  It's subtle and quick to anger.
163  */
164 static int
165 vs_paint(sp, flags)
166         SCR *sp;
167         u_int flags;
168 {
169         GS *gp;
170         SMAP *smp, tmp;
171         VI_PRIVATE *vip;
172         recno_t lastline, lcnt;
173         size_t cwtotal, cnt, len, notused, off, y;
174         int ch, didpaint, isempty, leftright_warp;
175         char *p;
176
177 #define  LNO    sp->lno                 /* Current file line. */
178 #define OLNO    vip->olno               /* Remembered file line. */
179 #define  CNO    sp->cno                 /* Current file column. */
180 #define OCNO    vip->ocno               /* Remembered file column. */
181 #define SCNO    vip->sc_col             /* Current screen column. */
182
183         gp = sp->gp;
184         vip = VIP(sp);
185         didpaint = leftright_warp = 0;
186
187         /*
188          * 5: Reformat the lines.
189          *
190          * If the lines themselves have changed (:set list, for example),
191          * fill in the map from scratch.  Adjust the screen that's being
192          * displayed if the leftright flag is set.
193          */
194         if (F_ISSET(sp, SC_SCR_REFORMAT)) {
195                 /* Invalidate the line size cache. */
196                 VI_SCR_CFLUSH(vip);
197
198                 /* Toss vs_line() cached information. */
199                 if (F_ISSET(sp, SC_SCR_TOP)) {
200                         if (vs_sm_fill(sp, LNO, P_TOP))
201                                 return (1);
202                 }
203                 else if (F_ISSET(sp, SC_SCR_CENTER)) {
204                         if (vs_sm_fill(sp, LNO, P_MIDDLE))
205                                 return (1);
206                 } else
207                         if (vs_sm_fill(sp, OOBLNO, P_TOP))
208                                 return (1);
209                 F_SET(sp, SC_SCR_REDRAW);
210         }
211
212         /*
213          * 6: Line movement.
214          *
215          * Line changes can cause the top line to change as well.  As
216          * before, if the movement is large, the screen is repainted.
217          *
218          * 6a: Small screens.
219          *
220          * Users can use the window, w300, w1200 and w9600 options to make
221          * the screen artificially small.  The behavior of these options
222          * in the historic vi wasn't all that consistent, and, in fact, it
223          * was never documented how various screen movements affected the
224          * screen size.  Generally, one of three things would happen:
225          *      1: The screen would expand in size, showing the line
226          *      2: The screen would scroll, showing the line
227          *      3: The screen would compress to its smallest size and
228          *              repaint.
229          * In general, scrolling didn't cause compression (200^D was handled
230          * the same as ^D), movement to a specific line would (:N where N
231          * was 1 line below the screen caused a screen compress), and cursor
232          * movement would scroll if it was 11 lines or less, and compress if
233          * it was more than 11 lines.  (And, no, I have no idea where the 11
234          * comes from.)
235          *
236          * What we do is try and figure out if the line is less than half of
237          * a full screen away.  If it is, we expand the screen if there's
238          * room, and then scroll as necessary.  The alternative is to compress
239          * and repaint.
240          *
241          * !!!
242          * This code is a special case from beginning to end.  Unfortunately,
243          * home modems are still slow enough that it's worth having.
244          *
245          * XXX
246          * If the line a really long one, i.e. part of the line is on the
247          * screen but the column offset is not, we'll end up in the adjust
248          * code, when we should probably have compressed the screen.
249          */
250         if (IS_SMALL(sp))
251                 if (LNO < HMAP->lno) {
252                         lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows);
253                         if (lcnt <= HALFSCREEN(sp))
254                                 for (; lcnt && sp->t_rows != sp->t_maxrows;
255                                      --lcnt, ++sp->t_rows) {
256                                         ++TMAP;
257                                         if (vs_sm_1down(sp))
258                                                 return (1);
259                                 }
260                         else
261                                 goto small_fill;
262                 } else if (LNO > TMAP->lno) {
263                         lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows);
264                         if (lcnt <= HALFSCREEN(sp))
265                                 for (; lcnt && sp->t_rows != sp->t_maxrows;
266                                      --lcnt, ++sp->t_rows) {
267                                         if (vs_sm_next(sp, TMAP, TMAP + 1))
268                                                 return (1);
269                                         ++TMAP;
270                                         if (vs_line(sp, TMAP, NULL, NULL))
271                                                 return (1);
272                                 }
273                         else {
274 small_fill:                     (void)gp->scr_move(sp, LASTLINE(sp), 0);
275                                 (void)gp->scr_clrtoeol(sp);
276                                 for (; sp->t_rows > sp->t_minrows;
277                                     --sp->t_rows, --TMAP) {
278                                         (void)gp->scr_move(sp, TMAP - HMAP, 0);
279                                         (void)gp->scr_clrtoeol(sp);
280                                 }
281                                 if (vs_sm_fill(sp, LNO, P_FILL))
282                                         return (1);
283                                 F_SET(sp, SC_SCR_REDRAW);
284                                 goto adjust;
285                         }
286                 }
287
288         /*
289          * 6b: Line down, or current screen.
290          */
291         if (LNO >= HMAP->lno) {
292                 /* Current screen. */
293                 if (LNO <= TMAP->lno)
294                         goto adjust;
295                 if (F_ISSET(sp, SC_SCR_TOP))
296                         goto top;
297                 if (F_ISSET(sp, SC_SCR_CENTER))
298                         goto middle;
299
300                 /*
301                  * If less than half a screen above the line, scroll down
302                  * until the line is on the screen.
303                  */
304                 lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp));
305                 if (lcnt < HALFTEXT(sp)) {
306                         while (lcnt--)
307                                 if (vs_sm_1up(sp))
308                                         return (1);
309                         goto adjust;
310                 }
311                 goto bottom;
312         }
313
314         /*
315          * 6c: If not on the current screen, may request center or top.
316          */
317         if (F_ISSET(sp, SC_SCR_TOP))
318                 goto top;
319         if (F_ISSET(sp, SC_SCR_CENTER))
320                 goto middle;
321
322         /*
323          * 6d: Line up.
324          */
325         lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp));
326         if (lcnt < HALFTEXT(sp)) {
327                 /*
328                  * If less than half a screen below the line, scroll up until
329                  * the line is the first line on the screen.  Special check so
330                  * that if the screen has been emptied, we refill it.
331                  */
332                 if (db_exist(sp, HMAP->lno)) {
333                         while (lcnt--)
334                                 if (vs_sm_1down(sp))
335                                         return (1);
336                         goto adjust;
337                 }
338
339                 /*
340                  * If less than a half screen from the bottom of the file,
341                  * put the last line of the file on the bottom of the screen.
342                  */
343 bottom:         if (db_last(sp, &lastline))
344                         return (1);
345                 tmp.lno = LNO;
346                 tmp.coff = HMAP->coff;
347                 tmp.soff = 1;
348                 lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows);
349                 if (lcnt < HALFTEXT(sp)) {
350                         if (vs_sm_fill(sp, lastline, P_BOTTOM))
351                                 return (1);
352                         F_SET(sp, SC_SCR_REDRAW);
353                         goto adjust;
354                 }
355                 /* It's not close, just put the line in the middle. */
356                 goto middle;
357         }
358
359         /*
360          * If less than half a screen from the top of the file, put the first
361          * line of the file at the top of the screen.  Otherwise, put the line
362          * in the middle of the screen.
363          */
364         tmp.lno = 1;
365         tmp.coff = HMAP->coff;
366         tmp.soff = 1;
367         lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp));
368         if (lcnt < HALFTEXT(sp)) {
369                 if (vs_sm_fill(sp, 1, P_TOP))
370                         return (1);
371         } else
372 middle:         if (vs_sm_fill(sp, LNO, P_MIDDLE))
373                         return (1);
374         if (0) {
375 top:            if (vs_sm_fill(sp, LNO, P_TOP))
376                         return (1);
377         }
378         F_SET(sp, SC_SCR_REDRAW);
379
380         /*
381          * At this point we know part of the line is on the screen.  Since
382          * scrolling is done using logical lines, not physical, all of the
383          * line may not be on the screen.  While that's not necessarily bad,
384          * if the part the cursor is on isn't there, we're going to lose.
385          * This can be tricky; if the line covers the entire screen, lno
386          * may be the same as both ends of the map, that's why we test BOTH
387          * the top and the bottom of the map.  This isn't a problem for
388          * left-right scrolling, the cursor movement code handles the problem.
389          *
390          * There's a performance issue here if editing *really* long lines.
391          * This gets to the right spot by scrolling, and, in a binary, by
392          * scrolling hundreds of lines.  If the adjustment looks like it's
393          * going to be a serious problem, refill the screen and repaint.
394          */
395 adjust: if (!O_ISSET(sp, O_LEFTRIGHT) &&
396             (LNO == HMAP->lno || LNO == TMAP->lno)) {
397                 cnt = vs_screens(sp, LNO, &CNO);
398                 if (LNO == HMAP->lno && cnt < HMAP->soff)
399                         if ((HMAP->soff - cnt) > HALFTEXT(sp)) {
400                                 HMAP->soff = cnt;
401                                 vs_sm_fill(sp, OOBLNO, P_TOP);
402                                 F_SET(sp, SC_SCR_REDRAW);
403                         } else
404                                 while (cnt < HMAP->soff)
405                                         if (vs_sm_1down(sp))
406                                                 return (1);
407                 if (LNO == TMAP->lno && cnt > TMAP->soff)
408                         if ((cnt - TMAP->soff) > HALFTEXT(sp)) {
409                                 TMAP->soff = cnt;
410                                 vs_sm_fill(sp, OOBLNO, P_BOTTOM);
411                                 F_SET(sp, SC_SCR_REDRAW);
412                         } else
413                                 while (cnt > TMAP->soff)
414                                         if (vs_sm_1up(sp))
415                                                 return (1);
416         }
417
418         /*
419          * If the screen needs to be repainted, skip cursor optimization.
420          * However, in the code above we skipped leftright scrolling on
421          * the grounds that the cursor code would handle it.  Make sure
422          * the right screen is up.
423          */
424         if (F_ISSET(sp, SC_SCR_REDRAW)) {
425                 if (O_ISSET(sp, O_LEFTRIGHT))
426                         goto slow;
427                 goto paint;
428         }
429
430         /*
431          * 7: Cursor movements (current screen only).
432          */
433         if (!LF_ISSET(UPDATE_CURSOR))
434                 goto number;
435
436         /*
437          * Decide cursor position.  If the line has changed, the cursor has
438          * moved over a tab, or don't know where the cursor was, reparse the
439          * line.  Otherwise, we've just moved over fixed-width characters,
440          * and can calculate the left/right scrolling and cursor movement
441          * without reparsing the line.  Note that we don't know which (if any)
442          * of the characters between the old and new cursor positions changed.
443          *
444          * XXX
445          * With some work, it should be possible to handle tabs quickly, at
446          * least in obvious situations, like moving right and encountering
447          * a tab, without reparsing the whole line.
448          *
449          * If the line we're working with has changed, reread it..
450          */
451         if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO)
452                 goto slow;
453
454         /* Otherwise, if nothing's changed, ignore the cursor. */
455         if (CNO == OCNO)
456                 goto fast;
457
458         /*
459          * Get the current line.  If this fails, we either have an empty
460          * file and can just repaint, or there's a real problem.  This
461          * isn't a performance issue because there aren't any ways to get
462          * here repeatedly.
463          */
464         if (db_eget(sp, LNO, &p, &len, &isempty)) {
465                 if (isempty)
466                         goto slow;
467                 return (1);
468         }
469
470 #ifdef DEBUG
471         /* Sanity checking. */
472         if (CNO >= len && len != 0) {
473                 msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)",
474                      tail(__FILE__), __LINE__, CNO, len);
475                 return (1);
476         }
477 #endif
478         /*
479          * The basic scheme here is to look at the characters in between
480          * the old and new positions and decide how big they are on the
481          * screen, and therefore, how many screen positions to move.
482          */
483         if (CNO < OCNO) {
484                 /*
485                  * 7a: Cursor moved left.
486                  *
487                  * Point to the old character.  The old cursor position can
488                  * be past EOL if, for example, we just deleted the rest of
489                  * the line.  In this case, since we don't know the width of
490                  * the characters we traversed, we have to do it slowly.
491                  */
492                 p += OCNO;
493                 cnt = (OCNO - CNO) + 1;
494                 if (OCNO >= len)
495                         goto slow;
496
497                 /*
498                  * Quick sanity check -- it's hard to figure out exactly when
499                  * we cross a screen boundary as we do in the cursor right
500                  * movement.  If cnt is so large that we're going to cross the
501                  * boundary no matter what, stop now.
502                  */
503                 if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
504                         goto slow;
505
506                 /*
507                  * Count up the widths of the characters.  If it's a tab
508                  * character, go do it the the slow way.
509                  */
510                 for (cwtotal = 0; cnt--; cwtotal += KEY_LEN(sp, ch))
511                         if ((ch = *(u_char *)p--) == '\t')
512                                 goto slow;
513
514                 /*
515                  * Decrement the screen cursor by the total width of the
516                  * characters minus 1.
517                  */
518                 cwtotal -= 1;
519
520                 /*
521                  * If we're moving left, and there's a wide character in the
522                  * current position, go to the end of the character.
523                  */
524                 if (KEY_LEN(sp, ch) > 1)
525                         cwtotal -= KEY_LEN(sp, ch) - 1;
526
527                 /*
528                  * If the new column moved us off of the current logical line,
529                  * calculate a new one.  If doing leftright scrolling, we've
530                  * moved off of the current screen, as well.
531                  */
532                 if (SCNO < cwtotal)
533                         goto slow;
534                 SCNO -= cwtotal;
535         } else {
536                 /*
537                  * 7b: Cursor moved right.
538                  *
539                  * Point to the first character to the right.
540                  */
541                 p += OCNO + 1;
542                 cnt = CNO - OCNO;
543
544                 /*
545                  * Count up the widths of the characters.  If it's a tab
546                  * character, go do it the the slow way.  If we cross a
547                  * screen boundary, we can quit.
548                  */
549                 for (cwtotal = SCNO; cnt--;) {
550                         if ((ch = *(u_char *)p++) == '\t')
551                                 goto slow;
552                         if ((cwtotal += KEY_LEN(sp, ch)) >= SCREEN_COLS(sp))
553                                 break;
554                 }
555
556                 /*
557                  * Increment the screen cursor by the total width of the
558                  * characters.
559                  */
560                 SCNO = cwtotal;
561
562                 /* See screen change comment in section 6a. */
563                 if (SCNO >= SCREEN_COLS(sp))
564                         goto slow;
565         }
566
567         /*
568          * 7c: Fast cursor update.
569          *
570          * We have the current column, retrieve the current row.
571          */
572 fast:   (void)gp->scr_cursor(sp, &y, &notused);
573         goto done_cursor;
574
575         /*
576          * 7d: Slow cursor update.
577          *
578          * Walk through the map and find the current line.
579          */
580 slow:   for (smp = HMAP; smp->lno != LNO; ++smp);
581
582         /*
583          * 7e: Leftright scrolling adjustment.
584          *
585          * If doing left-right scrolling and the cursor movement has changed
586          * the displayed screen, scroll the screen left or right, unless we're
587          * updating the info line in which case we just scroll that one line.
588          * We adjust the offset up or down until we have a window that covers
589          * the current column, making sure that we adjust differently for the
590          * first screen as compared to subsequent ones.
591          */
592         if (O_ISSET(sp, O_LEFTRIGHT)) {
593                 /*
594                  * Get the screen column for this character, and correct
595                  * for the number option offset.
596                  */
597                 cnt = vs_columns(sp, NULL, LNO, &CNO, NULL);
598                 if (O_ISSET(sp, O_NUMBER) && cnt >= O_NUMBER_LENGTH)
599                         cnt -= O_NUMBER_LENGTH;
600
601                 /* Adjust the window towards the beginning of the line. */
602                 off = smp->coff;
603                 if (off >= cnt) {
604                         do {
605                                 if (off >= O_VAL(sp, O_SIDESCROLL))
606                                         off -= O_VAL(sp, O_SIDESCROLL);
607                                 else {
608                                         off = 0;
609                                         break;
610                                 }
611                         } while (off >= cnt);
612                         goto shifted;
613                 }
614
615                 /* Adjust the window towards the end of the line. */
616                 if (off == 0 && off + SCREEN_COLS(sp) < cnt ||
617                     off != 0 && off + sp->cols < cnt) {
618                         do {
619                                 off += O_VAL(sp, O_SIDESCROLL);
620                         } while (off + sp->cols < cnt);
621
622 shifted:                /* Fill in screen map with the new offset. */
623                         if (F_ISSET(sp, SC_TINPUT_INFO))
624                                 smp->coff = off;
625                         else {
626                                 for (smp = HMAP; smp <= TMAP; ++smp)
627                                         smp->coff = off;
628                                 leftright_warp = 1;
629                         }
630                         goto paint;
631                 }
632
633                 /*
634                  * We may have jumped here to adjust a leftright screen because
635                  * redraw was set.  If so, we have to paint the entire screen.
636                  */
637                 if (F_ISSET(sp, SC_SCR_REDRAW))
638                         goto paint;
639         }
640
641         /*
642          * Update the screen lines for this particular file line until we
643          * have a new screen cursor position.
644          */
645         for (y = -1,
646             vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) {
647                 if (vs_line(sp, smp, &y, &SCNO))
648                         return (1);
649                 if (y != -1) {
650                         vip->sc_smap = smp;
651                         break;
652                 }
653         }
654         goto done_cursor;
655
656         /*
657          * 8: Repaint the entire screen.
658          *
659          * Lost big, do what you have to do.  We flush the cache, since
660          * SC_SCR_REDRAW gets set when the screen isn't worth fixing, and
661          * it's simpler to repaint.  So, don't trust anything that we
662          * think we know about it.
663          */
664 paint:  for (smp = HMAP; smp <= TMAP; ++smp)
665                 SMAP_FLUSH(smp);
666         for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) {
667                 if (vs_line(sp, smp, &y, &SCNO))
668                         return (1);
669                 if (y != -1 && vip->sc_smap == NULL)
670                         vip->sc_smap = smp;
671         }
672         /*
673          * If it's a small screen and we're redrawing, clear the unused lines,
674          * ex may have overwritten them.
675          */
676         if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp))
677                 for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
678                         (void)gp->scr_move(sp, cnt, 0);
679                         (void)gp->scr_clrtoeol(sp);
680                 }
681
682         didpaint = 1;
683
684 done_cursor:
685         /*
686          * Sanity checking.  When the repainting code messes up, the usual
687          * result is we don't repaint the cursor and so sc_smap will be
688          * NULL.  If we're debugging, die, otherwise restart from scratch.
689          */
690 #ifdef DEBUG
691         if (vip->sc_smap == NULL)
692                 abort();
693 #else
694         if (vip->sc_smap == NULL) {
695                 F_SET(sp, SC_SCR_REFORMAT);
696                 return (vs_paint(sp, flags));
697         }
698 #endif
699
700         /*
701          * 9: Set the remembered cursor values.
702          */
703         OCNO = CNO;
704         OLNO = LNO;
705
706         /*
707          * 10: Repaint the line numbers.
708          *
709          * If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we
710          * didn't repaint the screen, repaint all of the line numbers,
711          * they've changed.
712          */
713 number: if (O_ISSET(sp, O_NUMBER) &&
714             F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp))
715                 return (1);
716
717         /*
718          * 11: Update the mode line, position the cursor, and flush changes.
719          *
720          * If we warped the screen, we have to refresh everything.
721          */
722         if (leftright_warp)
723                 LF_SET(UPDATE_CURSOR | UPDATE_SCREEN);
724
725         if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) &&
726             !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO))
727                 vs_modeline(sp);
728
729         if (LF_ISSET(UPDATE_CURSOR)) {
730                 (void)gp->scr_move(sp, y, SCNO);
731
732                 /*
733                  * XXX
734                  * If the screen shifted, we recalculate the "most favorite"
735                  * cursor position.  Vi won't know that we've warped the
736                  * screen, so it's going to have a wrong idea about where the
737                  * cursor should be.  This is vi's problem, and fixing it here
738                  * is a gross layering violation.
739                  */
740                 if (leftright_warp)
741                         (void)vs_column(sp, &sp->rcm);
742         }
743
744         if (LF_ISSET(UPDATE_SCREEN))
745                 (void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT));
746
747         /* 12: Clear the flags that are handled by this routine. */
748         F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP);
749         F_CLR(vip, VIP_CUR_INVALID |
750             VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE);
751
752         return (0);
753
754 #undef   LNO
755 #undef  OLNO
756 #undef   CNO
757 #undef  OCNO
758 #undef  SCNO
759 }
760
761 /*
762  * vs_modeline --
763  *      Update the mode line.
764  */
765 static void
766 vs_modeline(sp)
767         SCR *sp;
768 {
769         static char * const modes[] = {
770                 "215|Append",                   /* SM_APPEND */
771                 "216|Change",                   /* SM_CHANGE */
772                 "217|Command",                  /* SM_COMMAND */
773                 "218|Insert",                   /* SM_INSERT */
774                 "219|Replace",                  /* SM_REPLACE */
775         };
776         GS *gp;
777         size_t cols, curcol, curlen, endpoint, len, midpoint;
778         const char *t;
779         int ellipsis;
780         char *p, buf[20];
781
782         gp = sp->gp;
783
784         /*
785          * We put down the file name, the ruler, the mode and the dirty flag.
786          * If there's not enough room, there's not enough room, we don't play
787          * any special games.  We try to put the ruler in the middle and the
788          * mode and dirty flag at the end.
789          *
790          * !!!
791          * Leave the last character blank, in case it's a really dumb terminal
792          * with hardware scroll.  Second, don't paint the last character in the
793          * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
794          *
795          * Move to the last line on the screen.
796          */
797         (void)gp->scr_move(sp, LASTLINE(sp), 0);
798
799         /* If more than one screen in the display, show the file name. */
800         curlen = 0;
801         if (IS_SPLIT(sp)) {
802                 for (p = sp->frp->name; *p != '\0'; ++p);
803                 for (ellipsis = 0, cols = sp->cols / 2; --p > sp->frp->name;) {
804                         if (*p == '/') {
805                                 ++p;
806                                 break;
807                         }
808                         if ((curlen += KEY_LEN(sp, *p)) > cols) {
809                                 ellipsis = 3;
810                                 curlen +=
811                                     KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' ');
812                                 while (curlen > cols) {
813                                         ++p;
814                                         curlen -= KEY_LEN(sp, *p);
815                                 }
816                                 break;
817                         }
818                 }
819                 if (ellipsis) {
820                         while (ellipsis--)
821                                 (void)gp->scr_addstr(sp,
822                                     KEY_NAME(sp, '.'), KEY_LEN(sp, '.'));
823                         (void)gp->scr_addstr(sp,
824                             KEY_NAME(sp, ' '), KEY_LEN(sp, ' '));
825                 }
826                 for (; *p != '\0'; ++p)
827                         (void)gp->scr_addstr(sp,
828                             KEY_NAME(sp, *p), KEY_LEN(sp, *p));
829         }
830
831         /* Clear the rest of the line. */
832         (void)gp->scr_clrtoeol(sp);
833
834         /*
835          * Display the ruler.  If we're not at the midpoint yet, move there.
836          * Otherwise, add in two extra spaces.
837          *
838          * Adjust the current column for the fact that the editor uses it as
839          * a zero-based number.
840          *
841          * XXX
842          * Assume that numbers, commas, and spaces only take up a single
843          * column on the screen.
844          */
845         cols = sp->cols - 1;
846         if (O_ISSET(sp, O_RULER)) {
847                 vs_column(sp, &curcol);
848                 len = snprintf(buf, sizeof(buf), "%lu,%lu",
849                     (u_long)sp->lno, (u_long)(curcol + 1));
850
851                 midpoint = (cols - ((len + 1) / 2)) / 2;
852                 if (curlen < midpoint) {
853                         (void)gp->scr_move(sp, LASTLINE(sp), midpoint);
854                         curlen += len;
855                 } else if (curlen + 2 + len < cols) {
856                         (void)gp->scr_addstr(sp, "  ", 2);
857                         curlen += 2 + len;
858                 }
859                 (void)gp->scr_addstr(sp, buf, len);
860         }
861
862         /*
863          * Display the mode and the modified flag, as close to the end of the
864          * line as possible, but guaranteeing at least two spaces between the
865          * ruler and the modified flag.
866          */
867 #define MODESIZE        9
868         endpoint = cols;
869         if (O_ISSET(sp, O_SHOWMODE)) {
870                 if (F_ISSET(sp->ep, F_MODIFIED))
871                         --endpoint;
872                 t = msg_cat(sp, modes[sp->showmode], &len);
873                 endpoint -= len;
874         }
875
876         if (endpoint > curlen + 2) {
877                 (void)gp->scr_move(sp, LASTLINE(sp), endpoint);
878                 if (O_ISSET(sp, O_SHOWMODE)) {
879                         if (F_ISSET(sp->ep, F_MODIFIED))
880                                 (void)gp->scr_addstr(sp,
881                                     KEY_NAME(sp, '*'), KEY_LEN(sp, '*'));
882                         (void)gp->scr_addstr(sp, t, len);
883                 }
884         }
885 }