/* vi:ts=4:sw=4 * * VIM - Vi IMproved * * Code Contributions By: Bram Moolenaar mool@oce.nl * Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ /* * screen.c: code for displaying on the screen */ #include "vim.h" #include "globals.h" #include "proto.h" #include "param.h" #ifdef JP #include "jp.h" #endif #ifdef JP static int kanji; static char kcode = NUL, *kin = NULL, *kout = NULL; #endif #ifdef JPFEP static int uline; #endif /* Macros handling inverted lines */ #define ScreenAttr(screenp) ((screenp) - Nextscreen + Nextattrib) #define ScreenSetAttr(x) (*x = 0x0) #define ScreenClrAttr(x) (*x = 0x0) #define AM_None 0x00 #define AM_Inv 0x02 #ifdef JPFEP # define AM_UL 0x04 #endif /* JPFEP */ #ifdef JP # define AM_K1 0x01 #endif /* JP */ /* * Switch Attribute */ #ifdef JP # define KANJI_ON {if (!kanji) { if (kin ) outstr(kin ); kanji = TRUE; }} # define KANJI_OFF {if ( kanji) { if (kout) outstr(kout); kanji = FALSE; }} #endif #ifdef JPFEP # define ULINE_ON {if (!uline) { KANJI_OFF outstr(T_US); uline = TRUE; }} # define ULINE_OFF {if ( uline) { KANJI_OFF outstr(T_UE); uline = FALSE; }} #endif #ifdef JP # define INVERT_ON {if (!invert) { KANJI_OFF outstr(T_TI); invert = TRUE; }} # define INVERT_OFF {if ( invert) { KANJI_OFF outstr(T_TP); invert = FALSE; }} #else # define INVERT_ON {if (!invert) { outstr(T_TI); invert = TRUE; }} # define INVERT_OFF {if ( invert) { outstr(T_TP); invert = FALSE; }} #endif char *tgoto __PARMS((char *cm, int col, int line)); char *getvptr __PARMS((char *ptr, int vcol)); colnr_t vcol2col __PARMS((linenr_t lnum, int vcol)); static u_char *Nextscreen = NULL; /* What's to be put on the screen. */ static u_char *Nextattrib = NULL; /* Attributes for Nextscreen. */ static int NumLineSizes = 0; /* # of active LineSizes */ static linenr_t *LineNumbers = NULL; /* Pointer to the line for LineSizes */ static u_char *LineSizes = NULL; /* Number of rows the lines occupy */ static u_char **LinePointers = NULL; /* array of pointers into Netscreen */ /* * The following variable is set (in cursupdate) to the number of physical * lines taken by the line the cursor is on. We use this to avoid extra calls * to plines(). The optimized routine updateline() * makes sure that the size of the cursor line hasn't changed. If so, lines * below the cursor will move up or down and we need to call the routine * updateScreen() to examine the entire screen. */ static int Cline_size; /* size (in rows) of the cursor line */ static int Cline_row; /* starting row of the cursor line */ static int Leftcol = 0; /* starting column of the screen */ static FPOS oldCurpos = {0, 0}; /* last known end of visual part */ static int oldCurswant = 0; /* last known value of Curswant */ static int canopt; /* TRUE when cursor goto can be optimized */ static int screenline __ARGS((linenr_t, int, int)); static void screenchar __ARGS((u_char *, int, int)); static void screenfill __ARGS((int, int)); static void screenalloc __ARGS((int)); static void screenclear2 __ARGS((void)); /* * updateline() - like updateScreen() but only for cursor line * * This determines whether or not we need to call updateScreen() to examine * the entire screen for changes. This occurs if the size of the cursor line * (in rows) hasn't changed. */ void updateline() { int row; int n; if (must_redraw) /* must redraw whole screen */ { updateScreen(VALID); return; } screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL || RedrawingDisabled) return; screenchar(NULL, 0, 0); /* init cursor position of screenchar() */ cursor_off(); row = screenline(Curpos.lnum, Cline_row, (int)Rows - 1); cursor_on(); if (row == Rows) /* line too long for screen */ updateScreen(VALID_TO_CURSCHAR); else { n = row - Cline_row; if (n != Cline_size) /* line changed size */ { if (n < Cline_size) /* got smaller: delete lines */ s_del(row, Cline_size - n, FALSE); else /* got bigger: insert lines */ s_ins(Cline_row + Cline_size, n - Cline_size, FALSE); updateScreen(VALID_TO_CURSCHAR); } } } /* * updateScreen() * * Based on the current value of Topline, transfer a screenfull of stuff from * Filemem to Nextscreen, and update Botline. */ void updateScreen(type) int type; { register int row; register int endrow; linenr_t lnum; linenr_t lastline = 0; /* only valid if endrow != Rows -1 */ int done; /* if TRUE, we hit the end of the file */ int didline; /* if TRUE, we finished the last line */ int srow = 0; /* starting row of the current line */ int idx; int i; long j; static int postponed_not_valid = FALSE; register u_char *screenp; register u_char *attribp; #ifdef JP /* * Prepare kanji-shift-in / out character sequence */ if (kcode != JP_DISP) { kin = kanjiin(kcode = JP_DISP); kout= kanjiout(kcode); } #endif screen_msg(FALSE, NULL); screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return; cmdoffset = 0; /* after redraw command line has no offset */ if (must_redraw) { type = must_redraw; must_redraw = 0; } if (type == CLEAR) /* first clear screen */ { screenclear(); type = NOT_VALID; } if (type == CURSUPD) /* update cursor and then redraw */ { NumLineSizes = 0; cursupdate(); /* will call updateScreen(VALID) */ return; } if (NumLineSizes == 0) type = NOT_VALID; if (RedrawingDisabled) { if (type == NOT_VALID) postponed_not_valid = TRUE; /* use NOT_VALID next time */ return; } if (postponed_not_valid) { type = NOT_VALID; postponed_not_valid = FALSE; } /* return if there is nothing to do */ if ((type == VALID && Topline == LineNumbers[0]) || (type == INVERTED && oldCurpos.lnum == Curpos.lnum && oldCurpos.col == Curpos.col && Curswant == oldCurswant)) return; if (type == NOT_VALID) { redraw_msg = TRUE; NumLineSizes = 0; } idx = 0; row = 0; lnum = Topline; cursor_off(); /* The number of rows shown is Rows-1. */ /* The default last row is the status/command line. */ endrow = Rows - 1; if (type == VALID || type == VALID_TO_CURSCHAR) { /* * We handle two special cases: * 1: we are off the top of the screen by a few lines: scroll down * 2: Topline is below LineNumbers[0]: may scroll up */ if (Topline < LineNumbers[0]) /* may scroll down */ { j = LineNumbers[0] - Topline; if (j < Rows - 3) /* not too far off */ { lastline = LineNumbers[0] - 1; i = plines_m(Topline, lastline); if (i < Rows - 3) /* less than a screen off */ { /* * Try to insert the correct number of lines. * This may fail and the screen may have been cleared. */ if (s_ins(0, i, FALSE) && NumLineSizes) { endrow = i; if ((NumLineSizes += j) > Rows - 1) NumLineSizes = Rows - 1; for (idx = NumLineSizes; idx - j >= 0; idx--) { LineNumbers[idx] = LineNumbers[idx - j]; LineSizes[idx] = LineSizes[idx - j]; } idx = 0; } } else /* far off: clearing the screen is faster */ screenclear(); } else /* far off: clearing the screen is faster */ screenclear(); } else /* may scroll up */ { j = -1; for (i = 0; i < NumLineSizes; i++) /* try to find Topline in LineNumbers[] */ { if (LineNumbers[i] == Topline) { j = i; break; } row += LineSizes[i]; } if (j == -1) /* Topline is not in LineNumbers */ { row = 0; screenclear(); /* far off: clearing the screen is faster */ } else { /* * Try to delete the correct number of lines. * Topline is at LineNumbers[i]. */ if ((row == 0 || s_del(0, row, FALSE)) && NumLineSizes) { srow = row; row = 0; for (;;) { if (type == VALID_TO_CURSCHAR && lnum == Curpos.lnum) break; if (row + srow + (int)LineSizes[j] >= Rows - 1) break; LineSizes[idx] = LineSizes[j]; LineNumbers[idx] = lnum++; row += LineSizes[idx++]; if ((int)++j >= NumLineSizes) break; } NumLineSizes = idx; } else row = 0; /* update all lines */ } } if (endrow == Rows - 1 && idx == 0) /* no scrolling */ NumLineSizes = 0; } done = didline = FALSE; screenchar(NULL, 0, 0); /* init cursor position of screenchar() */ if (Visual.lnum) /* check if we are updating the inverted part */ { linenr_t from, to; /* find the line numbers that need to be updated */ if (Curpos.lnum < oldCurpos.lnum) { from = Curpos.lnum; to = oldCurpos.lnum; } else { from = oldCurpos.lnum; to = Curpos.lnum; } /* if in block mode and changed column or Curswant: update all lines */ #ifdef JP if (Visual_block) #else if (Visual_block && (Curpos.col != oldCurpos.col || Curswant != oldCurswant)) #endif { if (from > Visual.lnum) from = Visual.lnum; if (to < Visual.lnum) to = Visual.lnum; } if (from < Topline) from = Topline; if (to >= Botline) to = Botline - 1; /* find the minimal part to be updated */ if (type == INVERTED) { while (lnum < from) /* find start */ { row += LineSizes[idx++]; ++lnum; } srow = row; for (j = idx; j < NumLineSizes; ++j) /* find end */ { if (LineNumbers[j] == to + 1) { endrow = srow; break; } srow += LineSizes[j]; } oldCurpos = Curpos; oldCurswant = Curswant; } /* if we update the lines between from and to set oldCurpos */ else if (lnum <= from && (endrow == Rows - 1 || lastline >= to)) { oldCurpos = Curpos; oldCurswant = Curswant; } } /* * Update the screen rows from "row" to "endrow". * Start at line "lnum" which is at LineNumbers[idx]. */ for (;;) { if (lnum > line_count) /* hit the end of the file */ { done = TRUE; break; } srow = row; row = screenline(lnum, srow, endrow); if (row > endrow) /* past end of screen */ { LineSizes[idx] = plines(lnum); /* we may need the size of that */ LineNumbers[idx++] = lnum; /* too long line later on */ break; } LineSizes[idx] = row - srow; LineNumbers[idx++] = lnum; if (++lnum > line_count) { done = TRUE; break; } if (row == endrow) { didline = TRUE; break; } } if (idx > NumLineSizes) NumLineSizes = idx; /* Do we have to do off the top of the screen processing ? */ if (endrow != Rows - 1) { row = 0; for (idx = 0; idx < NumLineSizes && row < (Rows - 1); idx++) row += LineSizes[idx]; if (row < (Rows - 1)) { done = TRUE; } else if (row > (Rows - 1)) /* Need to blank out the last line */ { lnum = LineNumbers[idx - 1]; srow = row - LineSizes[idx - 1]; didline = FALSE; } else { lnum = LineNumbers[idx - 1] + 1; didline = TRUE; } } emptyrows = 0; /* * If we didn't hit the end of the file, and we didn't finish the last * line we were working on, then the line didn't fit. */ if (!done && !didline) { if (lnum == Topline) { /* * Single line that does not fit! * Fill last line with '@' characters. */ screenp = LinePointers[Rows - 2]; attribp = ScreenAttr(screenp); for (i = 0; i < Columns; ++i) { if (*screenp != '@' || *attribp) { *screenp = '@'; *attribp = AM_None; screenchar(screenp, (int)(Rows - 2), i); } ++screenp; ++attribp; } Botline = lnum + 1; } else { /* * Clear the rest of the screen and mark the unused lines. */ screenfill(srow, '@'); Botline = lnum; } } else { /* make sure the rest of the screen is blank */ /* put '~'s on rows that aren't part of the file. */ screenfill(row, '~'); emptyrows = Rows - row - 1; if (done) /* we hit the end of the file */ Botline = line_count + 1; else Botline = lnum; } if (redraw_msg) { showmode(); redraw_msg = FALSE; } cursor_on(); } static int invert; /* shared by screenline() and screenchar() */ /* * Move line "lnum" to the screen. * Start at row "startrow", stop when "endrow" is reached. * Return the number of last row the line occupies. */ static int screenline(lnum, startrow, endrow) linenr_t lnum; int startrow; int endrow; { register u_char *screenp; register u_char *attribp; register u_char c; register int col; /* visual column on screen */ register int vcol; /* visual column for tabs */ register int row; register u_char *ptr; char extra[16]; /* "%ld" must fit in here */ char *p_extra; int n_extra; int n_spaces = 0; int fromcol, tocol; /* start/end of inverting */ #ifdef JPFEP int kufrom, kuto; /* start/end of underilne for KANAKAN */ int krfrom, krto; /* start/end of inverting for KANAKAN */ #endif int noinvcur = FALSE; /* don't invert the cursor */ int temp; FPOS *top, *bot; row = startrow; col = 0; vcol = 0; invert = FALSE; fromcol = -10; tocol = MAXCOL; #ifdef JPFEP uline = FALSE; #endif ptr = (u_char *)nr2ptr(lnum); canopt = TRUE; if (Visual.lnum) /* visual active */ { if (ltoreq(Curpos, Visual)) /* Visual is after Curpos */ { top = &Curpos; bot = &Visual; } else /* Visual is before Curpos */ { top = &Visual; bot = &Curpos; } if (Visual_block) /* block mode */ { if (lnum >= top->lnum && lnum <= bot->lnum) { FPOS tpos; fromcol = getvcol(top, 2); temp = getvcol(bot, 2); if (temp < fromcol) fromcol = temp; #ifdef JP /* adjust for multibyte char. */ tpos.lnum = lnum; tpos.col = vcol2col(lnum, fromcol); if (*pos2ptr(&tpos) != '\t') fromcol = getvcol(&tpos, 2); #endif if (Curswant != MAXCOL) { tocol = getvcol(top, 3); temp = getvcol(bot, 3); if (temp > tocol) tocol = temp; #ifdef JP /* adjust for multibyte char. */ tpos.col = vcol2col(lnum, tocol); if (*pos2ptr(&tpos) != '\t') tocol = getvcol(&tpos, 3); #endif ++tocol; } } } else /* non-block mode */ { if (lnum > top->lnum && lnum <= bot->lnum) fromcol = 0; else if (lnum == top->lnum) fromcol = getvcol(top, 2); if (lnum == bot->lnum) tocol = getvcol(bot, 3) + 1; if (Visual.col == VISUALLINE) /* linewise */ { if (fromcol > 0) fromcol = 0; tocol = VISUALLINE; } } /* if the cursor can't be switched off, don't invert the character where the cursor is */ if ((T_CI == NULL || *T_CI == NUL) && lnum == Curpos.lnum) noinvcur = TRUE; /* if inverting in this line, can't optimize cursor positioning */ if (fromcol >= 0) canopt = FALSE; } if (!p_wrap) /* advance to first character to be displayed */ { while (vcol < Leftcol && *ptr) #ifdef JP if (IsKanji(*ptr)) { vcol += 2; ptr += 2; } else #endif vcol += chartabsize(*ptr++, vcol); if (vcol > Leftcol) { n_spaces = vcol - Leftcol; /* begin with some spaces */ vcol = Leftcol; } } #ifdef JPFEP if (Kconvlnum == lnum) { FPOS tmp; tmp.lnum = lnum; tmp.col = KconvAltStart; krfrom = getvcol(&tmp, 2); tmp.col = KconvAltEnd; krto = getvcol(&tmp, 2); tmp.col = KconvStart; kufrom = getvcol(&tmp, 2); tmp.col = KconvEnd; kuto = getvcol(&tmp, 2); canopt = FALSE; } else krfrom = kufrom = krto = kuto = -10; #endif /* JPFEP */ screenp = LinePointers[row]; attribp = ScreenAttr(screenp); if (p_nu) { sprintf(extra, "%7ld ", (long)lnum); p_extra = extra; n_extra = 8; vcol -= 8; /* so vcol is 0 when line number has been printed */ } else { p_extra = NULL; n_extra = 0; } #ifdef JP kanji = FALSE; #endif for (;;) { if (!canopt) /* Visual in this line */ { if (((vcol == fromcol && !(noinvcur && vcol == Cursvcol)) || (noinvcur && vcol >= fromcol && #ifdef JP (vcol == Cursvcol + 1 || vcol == Cursvcol + 2) #else vcol == Cursvcol + 1 #endif )) && vcol < tocol) INVERT_ON /* start inverting */ else if (invert && ( #ifdef JP (vcol == tocol || vcol == tocol + 1) #else vcol == tocol #endif || (noinvcur && vcol == Cursvcol))) INVERT_OFF /* stop inverting */ #ifdef JPFEP if (fromcol < 0) { /* invert for KANAKAN */ if (!invert) { if (((vcol == krfrom && !(noinvcur && vcol == Cursvcol)) || (noinvcur && vcol == Cursvcol + 1 && vcol >= krfrom)) && vcol < krto) /* start inverting */ INVERT_ON } else if (vcol == krto || (noinvcur && vcol == Cursvcol)) { /* stop inverting */ int ul; if ((ul = uline)) ULINE_OFF INVERT_OFF if (ul) ULINE_ON } /* underline for KANAKAN */ if (!uline) { if (vcol == kufrom && vcol < kuto) /* start underline */ ULINE_ON } else if (vcol == kuto) { int inv; if ((inv = invert)) INVERT_OFF; ULINE_OFF /* stop underline */ if (inv) INVERT_ON; } } #endif } /* Get the next character to put on the screen. */ /* * The 'extra' array contains the extra stuff that is inserted to * represent special characters (non-printable stuff). */ if (n_extra) { c = (u_char)*p_extra++; n_extra--; } else if (n_spaces) { c = ' '; n_spaces--; } else { if ((c = *ptr++) < ' ' || (c > '~' && c <= 0xa0)) { /* * when getting a character from the file, we may have to turn it * into something else on the way to putting it into 'Nextscreen'. */ if (c == TAB && !p_list) { /* tab amount depends on current column */ n_spaces = (int)p_ts - col % (int)p_ts - 1; c = ' '; } else if (c == NUL && p_list) { p_extra = ""; n_extra = 1; c = '$'; } else if (c != NUL) { p_extra = (char *)transchar(c); n_extra = charsize(c) - 1; c = (u_char)*p_extra++; } } } if (c == NUL) { #ifdef UNIX int ler; /* Line Erased */ ler= FALSE; #endif if (invert) { if (vcol == 0) /* invert first char of empty line */ { if (*screenp != ' ' || !(*attribp & AM_Inv)) { *screenp = ' '; *attribp = AM_Inv; screenchar(screenp, row, col); } ++screenp; ++attribp; ++col; } outstr(T_TP); invert = FALSE; } /* * could also use clear-to-end-of-line, but it is slower * on an Amiga */ while (col < Columns) { if (*screenp != ' ' || *attribp) { *screenp = ' '; *attribp = AM_None; #ifdef UNIX if (!ler) { ler = TRUE; windgoto(row, col); clear_line(); } #else screenchar(screenp, row, col); #endif } screenp++; attribp++; col++; } row++; #ifdef JP KANJI_OFF #endif /* JP */ break; } #ifdef JP if (col >= (IsKanji(c) ? Columns - 1 : Columns)) /* continuous line */ { int inv; #ifdef JPFEP int ul; #endif KANJI_OFF inv = invert; INVERT_OFF #ifdef JPFEP ul = uline; ULINE_OFF #endif if (col == Columns - 1 && (*screenp != '\\' || *attribp != AM_None)) { *screenp = '\\'; *attribp = AM_None; screenchar(screenp, row, col); } col = 0; if (!p_wrap || ++row == endrow) /* line got too long for screen */ { ++row; break; } screenp = LinePointers[row]; attribp = ScreenAttr(screenp); screen_msg(FALSE, NULL); if (inv) INVERT_ON #ifdef JPFEP if (ul) ULINE_ON #endif } #else /* JP */ if (col >= Columns) { col = 0; if (!p_wrap || ++row == endrow) /* line got too long for screen */ { ++row; break; } screenp = LinePointers[row]; attribp = ScreenAttr(screenp); screen_msg(FALSE, NULL); } #endif /* JP */ /* store the character in Nextscreen */ #ifdef JP if (IsKanji(c) && kcode != JP_NONE) { u_char c1, c2; int attr; c1 = c; c2 = *ptr++; attr = *attribp; if (*screenp != c1 || *(screenp + 1) != c2 || !(attr & AM_K1) || (invert ? !(attr & AM_Inv) : (attr & AM_Inv)) #ifdef JPFEP || (uline ? !(attr & AM_UL) : (attr & AM_UL)) #endif ) { if (invert) { *attribp++ = AM_Inv | AM_K1; *attribp++ = AM_Inv; } #ifdef JPFEP else if (uline) { *attribp++ = AM_UL | AM_K1; *attribp++ = AM_UL; } #endif else { *attribp++ = AM_K1; *attribp++ = AM_None; } *screenp++ = c1; *screenp++ = c2; screenchar(screenp - 2, row, col); col += 2; vcol += 2; } else { attribp += 2; screenp += 2; vcol += 2; col += 2; } } else #endif /* JP */ { int attr; attr = *attribp; if (*screenp != c || (invert ? !(attr & AM_Inv) : (attr & AM_Inv)) #ifdef JPFEP || (uline ? !(attr & AM_UL) : (attr & AM_UL)) #endif ) { *screenp = c; if (invert) *attribp = AM_Inv; #ifdef JPFEP else if (uline) *attribp = AM_UL; #endif else *attribp = AM_None; screenchar(screenp, row, col); } screenp ++; attribp ++; vcol ++; col ++; } } #ifdef JP KANJI_OFF #endif #ifdef JPFEP ULINE_OFF #endif INVERT_OFF return (row); } /* * put character '*p' on the screen at position 'row' and 'col' */ static void screenchar(p, row, col) u_char *p; int row; int col; { static int oldrow, oldcol; /* old cursor position */ if (p == NULL) /* initialize cursor position */ { oldrow = oldcol = -1; return; } if (oldcol != col || oldrow != row) { /* * If we're on the same row (which happens a lot!), try to * avoid a windgoto(). * If we are only a few characters off, output the * characters. That is faster than cursor positioning. * This can't be used when inverting (a part of) the line. */ if (oldrow == row && oldcol < col) { register int i; i = col - oldcol; #ifdef JP if (i <= 10 && canopt) { u_char k1, k2; u_char *ptr; ptr = p - i; while(ptr < p) if (IsKanji(k1 = *(ptr++))) { KANJI_ON k2 = *(ptr++); kanjito(&k1, &k2, kcode); outchar(k1); outchar(k2); } else { KANJI_OFF outchar(k1); } } #else /* JP */ if (i <= 4 && canopt) while (i) outchar( *(p - i--) ); #endif /* JP */ else { #ifdef JP KANJI_OFF #endif if (T_CRI && *T_CRI) /* use tgoto interface! jw */ outstr(tgoto(T_CRI, 0, i)); else windgoto(row, col); } oldcol = col; } else { #ifdef JP KANJI_OFF #endif windgoto(oldrow = row, oldcol = col); } } #ifdef JP if (IsKanji(*p)) { u_char k1, k2; k1 = *p++; k2 = *p; KANJI_ON; kanjito(&k1, &k2, kcode); outchar(k1); outchar(k2); oldcol++; } else { KANJI_OFF outchar(*p); } #else outchar(*p); #endif oldcol++; } /* * Fill the screen at 'srow' with character 'c' followed by blanks. */ static void screenfill(srow, c) int srow; int c; { register int row; register int col; register u_char *screenp; register u_char *attribp; for (row = srow; row < (Rows - 1); ++row) { screenp = LinePointers[row]; attribp = ScreenAttr(screenp); if (*screenp != c || *attribp) { *screenp = c; *attribp = AM_None; screenchar(screenp, row, 0); } ++attribp; ++screenp; for (col = 1; col < Columns; ++col) { if (*screenp != ' ' || *attribp) { *screenp = ' '; *attribp = AM_None; screenchar(screenp, row, col); } ++attribp; ++screenp; } } } /* * compute Botline. Can be called after Topline or Rows changed. */ void comp_Botline() { linenr_t lnum; int done = 0; for (lnum = Topline; lnum <= line_count; ++lnum) { if ((done += plines(lnum)) >= Rows) break; } Botline = lnum; /* Botline is the line that is just below the window */ } /* * prt_line() - print the given line * returns the number of characters written. */ int prt_line(s) char *s; { register int si = 0; #ifdef JP char c, k; #else register char c; #endif register int col = 0; int n_extra = 0; int n_spaces = 0; char *p = NULL; /* init to make SASC shut up */ int n; for (;;) { if (n_extra) { --n_extra; c = *p++; } else if (n_spaces) { --n_spaces; c = ' '; } else { c = s[si++]; #ifdef JP if (IsKanji(c)) k = s[si++]; else #endif if (c == TAB && !p_list) { /* tab amount depends on current column */ n_spaces = p_ts - col % p_ts - 1; c = ' '; } else if (c == NUL && p_list) { p = ""; n_extra = 1; c = '$'; } else if (c != NUL && (n = charsize(c)) > 1) { n_extra = n - 1; p = transchar(c); c = *p++; } } if (c == NUL) break; #ifdef JP if (IsKanji(c)) { if (k == NUL) break; KANJI_ON kanjito(&c, &k, kcode); outchar(c); outchar(k); col += 2; } else { KANJI_OFF outchar(c); col++; } #else outchar(c); col++; #endif } #ifdef JP KANJI_OFF #endif return col; } static void screenalloc(clear) int clear; { static int old_Rows = 0; static int old_Columns = 0; register int i; /* * Allocation of the sceen buffers is done only when the size changes */ if ((Nextscreen != NULL && Rows == old_Rows && Columns == old_Columns) || Rows == 0 || Columns == 0) return; comp_col(); /* recompute columns for shown command and ruler */ old_Rows = Rows; old_Columns = Columns; /* * If we're changing the size of the screen, free the old arrays */ if (Nextscreen != NULL) free((char *)Nextscreen); if (Nextattrib != NULL) free((char *)Nextattrib); if (LinePointers != NULL) free((char *)LinePointers); if (LineNumbers != NULL) free((char *) LineNumbers); if (LineSizes != NULL) free(LineSizes); Nextscreen = (u_char *)malloc((size_t) (Rows * Columns)); Nextattrib = (u_char *)malloc((size_t) (Rows * Columns)); LineNumbers = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t))); LineSizes = (u_char *)malloc((size_t) Rows); LinePointers = (u_char **)malloc(sizeof(u_char *) * Rows); if (Nextscreen == NULL || Nextattrib == NULL || LineNumbers == NULL || LineSizes == NULL || LinePointers == NULL) { emsg(e_outofmem); if (Nextscreen != NULL) { free((char *)Nextscreen); free((char *)Nextattrib); } Nextscreen = Nextattrib = NULL; } else { for (i = 0; i < Rows; ++i) LinePointers[i] = Nextscreen + i * Columns; } if (clear) screenclear2(); } void screenclear() { screenalloc(FALSE); /* allocate screen buffers if size changed */ screenclear2(); } static void screenclear2() { if (starting || Nextscreen == NULL) return; outstr(T_ED); /* clear the display */ /* blank out Nextscreen & Nextattrib */ memset((char *)Nextscreen, ' ', (size_t)(Rows * Columns)); memset((char *)Nextattrib, NUL, (size_t)(Rows * Columns)); NumLineSizes = 0; /* clear screen info */ redraw_msg = TRUE; /* refresh cmdline at next screen redraw */ } void cursupdate() { linenr_t p; long nlines; int i; int temp; screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return; if (Curpos.lnum > line_count) Curpos.lnum = line_count; if (bufempty()) /* special case - file is empty */ { Topline = 1; Curpos.lnum = 1; Curpos.col = 0; #ifdef JPFEP LineSizes[0] = 1; for (i = 1; i < Rows; i++) #else for (i = 0; i < Rows; i++) #endif LineSizes[i] = 0; if (NumLineSizes == 0) /* don't know about screen contents */ updateScreen(NOT_VALID); NumLineSizes = 1; } else if (Curpos.lnum < Topline) { /* * If the cursor is above the top of the screen, scroll the screen to * put it at the top of the screen. * If we weren't very close to begin with, we scroll more, so that * the line is close to the middle. */ temp = Rows / 2 - 1; if (Topline - Curpos.lnum >= temp) /* not very close */ { p = Curpos.lnum; i = plines(p); temp += i; /* count lines for 1/2 screenheight */ while (i < Rows && i < temp && p > 1) i += plines(--p); Topline = p; if (i >= Rows) /* cursor line won't fit, backup one line */ ++Topline; } else if (p_sj > 1) /* scroll at least p_sj lines */ { for (i = 0; i < p_sj && Topline > 1; i += plines(--Topline)) ; } if (Topline > Curpos.lnum) Topline = Curpos.lnum; updateScreen(VALID); } else if (Curpos.lnum >= Botline) { /* number of lines the cursor is below the bottom of the screen */ nlines = Curpos.lnum - Botline + 1; /* * If the cursor is less than a screenheight down * compute the number of lines at the top which have the same or more * rows than the rows of the lines below the bottom */ if (nlines <= Rows) { /* get the number or rows to scroll minus the number of free '~' rows */ temp = plines_m(Botline, Curpos.lnum) - emptyrows; if (temp <= 0) /* emptyrows is larger, no need to scroll */ nlines = 0; else if (temp >= Rows) /* more than a screenfull, don't scroll */ nlines = temp; else { /* scroll minimal number of lines */ if (temp < p_sj) temp = p_sj; for (i = 0, p = Topline; i < temp && p < Botline; ++p) i += plines(p); if (i >= temp) /* it's possible to scroll */ nlines = p - Topline; else /* below Botline, don't scroll */ nlines = 9999; } } /* * Scroll up if the cursor is off the bottom of the screen a bit. * Otherwise put it at 1/2 of the screen. */ if (nlines >= Rows / 2 && nlines > p_sj) { p = Curpos.lnum; temp = Rows / 2 + 1; nlines = 0; i = 0; do /* this loop could win a contest ... */ i += plines(p); while (i < temp && (nlines = 1) != 0 && --p != 0); Topline = p + nlines; } else scrollup(nlines); updateScreen(VALID); } else if (NumLineSizes == 0) /* don't know about screen contents */ updateScreen(NOT_VALID); Cursrow = Curscol = Cursvcol = i = 0; for (p = Topline; p != Curpos.lnum; ++p) if (RedrawingDisabled) /* LineSizes[] invalid */ Cursrow += plines(p); else Cursrow += LineSizes[i++]; Cline_row = Cursrow; if (!RedrawingDisabled && i > NumLineSizes) /* Should only happen with a line that is too */ /* long to fit on the last screen line. */ Cline_size = 0; else { if (RedrawingDisabled) /* LineSizes[] invalid */ Cline_size = plines(Curpos.lnum); else Cline_size = LineSizes[i]; curs_columns(!RedrawingDisabled); /* compute Cursvcol and Curscol */ if (must_redraw) updateScreen(VALID); } if (set_want_col) { if (Track) { if (Curswant < Cursvcol) Curswant = getvcol(&Curpos, 2); else if (Curswant > Cursvcol && Curswant != MAXCOL) Curswant = getvcol(&Curpos, 3); else Curswant = Cursvcol; } else Curswant = Cursvcol; set_want_col = FALSE; } } /* * compute Curscol and Cursvcol */ void curs_columns(scroll) int scroll; /* when TRUE, may scroll horizontally */ { int diff; #ifdef JP u_char *ptr, c; int col; Cursvcol = 0; if (p_nu) Curscol = 8; else Curscol = 0; Cursrow = Cline_row; ptr = (u_char *)nr2ptr(Curpos.lnum); col = 0; while(col < Curpos.col) { if (!(c = *ptr)) break; if (IsKanji(c)) { if (Curscol >= Columns - 1 && p_wrap) { /* last column wrapping for multi-byte char. */ Cursrow ++; Curscol = 0; } ptr += 2; col += 2; Cursvcol += 2; Curscol += 2; } else { int cw; ptr ++; col ++; cw = chartabsize(c, Curscol); Cursvcol += cw; Curscol += cw; } if (Curscol >= Columns && p_wrap) { /* long line wrapping, adjust Cursrow */ Cursrow ++; Curscol = 0; } } if (IsKanji(*ptr) && Curscol == Columns - 1) { /* last column kanji */ Cursrow ++; Curscol = 0; } if ((c = *ptr) == TAB && State == NORMAL && !p_list) { int cw; cw = chartabsize(c, Curscol) - 1; Cursvcol += cw; Curscol += cw; } if (p_wrap) { } #else Cursvcol = getvcol(&Curpos, 1); Curscol = Cursvcol; if (p_nu) Curscol += 8; Cursrow = Cline_row; if (p_wrap) /* long line wrapping, adjust Cursrow */ while (Curscol >= Columns) { Curscol -= Columns; Cursrow++; } #endif else if (scroll) /* no line wrapping, compute Leftcol if scrolling is on */ /* if scrolling is off, Leftcol is assumed to be 0 */ { /* If Cursor is left of the screen, scroll rightwards */ /* If Cursor is right of the screen, scroll leftwards */ if (((diff = Leftcol + (p_nu ? 8 : 0) - Curscol) > 0 || (diff = Curscol - (Leftcol + Columns) + 1) > 0)) { if (p_ss == 0 || diff >= Columns / 2) Leftcol = Curscol - Columns / 2; else { if (diff < p_ss) diff = p_ss; if (Curscol < Leftcol + 8) Leftcol -= diff; else Leftcol += diff; } if (Leftcol < 0) Leftcol = 0; must_redraw = NOT_VALID; /* screen has to be redrawn with new Leftcol */ } Curscol -= Leftcol; } if (Cursrow > Rows - 2) /* Cursor past end of screen */ Cursrow = Rows - 2; /* happens with line that does not fit on screen */ } /* * get virtual column number of pos * type = 1: where the cursor is on this character * type = 2: on the first position of this character (TAB) * type = 3: on the last position of this character (TAB) */ int getvcol(pos, type) FPOS *pos; int type; { int col; int vcol; u_char *ptr; int incr; u_char c; vcol = 0; ptr = (u_char *)nr2ptr(pos->lnum); for (col = pos->col; col >= 0; --col) { c = *ptr++; #ifdef JP if (IsKanji(c)) ptr++; #endif if (c == NUL) /* make sure we don't go past the end of the line */ break; /* A tab gets expanded, depending on the current column */ #ifdef JP if (IsKanji(c)) incr = 2; else #endif incr = chartabsize(c, vcol); if (col <= 0) /* character at pos.col */ { if (type == 3 || (type == 1 && c == TAB && State == NORMAL && !p_list)) --incr; else break; } #ifdef JP if (IsKanji(c)) col--; #endif vcol += incr; } return vcol; } /* * get pointer corresponding to vcol */ colnr_t vcol2col(lnum, dvcol) linenr_t lnum; int dvcol; { char *line, *ptr, c; int vcol; ptr = line = nr2ptr(lnum); vcol = 0; while((c = *ptr)) { #ifdef JP if (IsKanji(c)) { vcol += 2; if (vcol > dvcol) break; ptr += 2; continue; } #endif vcol += chartabsize(c, vcol); if (vcol > dvcol) break; ptr ++; } return (colnr_t)(ptr - line); } void scrolldown(nlines) long nlines; { register long done = 0; /* total # of physical lines done */ /* Scroll up 'nlines' lines. */ while (nlines--) { if (Topline == 1) break; done += plines(--Topline); } /* * Compute the row number of the last row of the cursor line * and move it onto the screen. */ Cursrow += done; if (p_wrap) Cursrow += plines(Curpos.lnum) - 1 - Cursvcol / Columns; while (Cursrow >= Rows - 1 && Curpos.lnum > 1) Cursrow -= plines(Curpos.lnum--); } void scrollup(nlines) long nlines; { #ifdef NEVER register long done = 0; /* total # of physical lines done */ /* Scroll down 'nlines' lines. */ while (nlines--) { if (Topline == line_count) break; done += plines(Topline); if (Curpos.lnum == Topline) ++Curpos.lnum; ++Topline; } s_del(0, done, TRUE); #endif Topline += nlines; if (Topline > line_count) Topline = line_count; if (Curpos.lnum < Topline) Curpos.lnum = Topline; } /* * The rest of the routines in this file perform screen manipulations. The * given operation is performed physically on the screen. The corresponding * change is also made to the internal screen image. In this way, the editor * anticipates the effect of editing changes on the appearance of the screen. * That way, when we call screenupdate a complete redraw isn't usually * necessary. Another advantage is that we can keep adding code to anticipate * screen changes, and in the meantime, everything still works. */ /* * s_ins(row, nlines, invalid) - insert 'nlines' lines at 'row' * if 'invalid' is TRUE the LineNumbers[] is invalidated. * Returns 0 if the lines are not inserted, 1 for success. */ int s_ins(row, nlines, invalid) int row; int nlines; int invalid; { int i; int j; u_char *temp; screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return 0; if (invalid) NumLineSizes = 0; if (nlines > (Rows - 1 - row)) nlines = Rows - 1 - row; if (RedrawingDisabled || nlines <= 0 || ((T_CIL == NULL || *T_CIL == NUL) && (T_IL == NULL || *T_IL == NUL) && (T_SR == NULL || *T_SR == NUL || row != 0))) return 0; if (Rows - nlines < 5) /* only a few lines left: redraw is faster */ { screenclear(); /* will set NumLineSizes to 0 */ return 0; } if (Rows != Rows_max) { windgoto((int)Rows - 1, 0); /* delete any garbage that may have */ clear_line(); /* been shifted to the bottom line */ } /* * It "looks" better if we do all the inserts at once */ if (T_CIL && *T_CIL) { windgoto(row, 0); if (nlines == 1 && T_IL && *T_IL) outstr(T_IL); else outstr(tgoto(T_CIL, 0, nlines)); } else { for (i = 0; i < nlines; i++) { if (i == 0 || row != 0) windgoto(row, 0); if (T_IL && *T_IL) outstr(T_IL); else outstr(T_SR); } } windgoto((int)Rows - 1, 0); /* delete any garbage that may have */ clear_line(); /* been shifted to the bottom line */ redraw_msg = TRUE; /* * Now shift LinePointers nlines down to reflect the inserted lines. * Clear the inserted lines. */ for (i = 0; i < nlines; ++i) { j = Rows - 2 - i; temp = LinePointers[j]; while ((j -= nlines) >= row) LinePointers[j + nlines] = LinePointers[j]; LinePointers[j + nlines] = temp; memset((char *)temp, ' ', (size_t)Columns); } return 1; } /* * s_del(row, nlines, invalid) - delete 'nlines' lines at 'row' * If 'invalid' is TRUE LineNumbers[] is invalidated. * Return 1 for success, 0 if the lines are not deleted. */ int s_del(row, nlines, invalid) int row; int nlines; int invalid; { int j; int i; u_char *temp; screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return 0; if (invalid) NumLineSizes = 0; if (nlines > (Rows - 1 - row)) nlines = Rows - 1 - row; if (RedrawingDisabled || nlines <= 0 || ((T_DL == NULL || *T_DL == NUL) && (T_CDL == NULL || *T_CDL == NUL) && row != 0)) return 0; if (Rows - nlines < 5) /* only a few lines left: redraw is faster */ { screenclear(); /* will set NumLineSizes to 0 */ return 0; } windgoto((int)Rows - 1, 0); /* delete any garbage that may be */ clear_line(); /* on the bottom line */ redraw_msg = TRUE; /* delete the lines */ if (T_CDL && *T_CDL) { windgoto(row, 0); if (nlines == 1 && T_DL && *T_DL) outstr(T_DL); else outstr(tgoto(T_CDL, 0, nlines)); } else { if (row == 0) { if (Rows != Rows_max) windgoto((int)Rows_max - 1, 0); for (i = 0; i < nlines; i++) outchar('\n'); } else { for (i = 0; i < nlines; i++) { windgoto(row, 0); outstr(T_DL); /* delete a line */ } } } /* * Now shift LinePointers nlines up to reflect the deleted lines. * Clear the deleted lines. */ for (i = 0; i < nlines; ++i) { j = row + i; temp = LinePointers[j]; while ((j += nlines) < Rows - 1) LinePointers[j - nlines] = LinePointers[j]; LinePointers[j - nlines] = temp; memset((char *)temp, ' ', (size_t)Columns); } return 1; } void showmode() { if ((p_smd && (State == INSERT || State == REPLACE)) || Recording) { gotocmdline(TRUE, NUL); if (p_smd) { #ifdef ONEW if (!KanjiInput && !p_oh && (State == INSERT || State == REPLACE)) { if (p_ri) outstrn("[<-"); else outstrn("["); if (State == INSERT) outstrn("INS"); else if (State == REPLACE) outstrn("REP"); outstrn("]"); if (p_ja) outstrn(p_ji); if (Recording) outstrn("...."); } #else if (State == INSERT || State == REPLACE) { outstrn("-- "); if (p_ri) outstrn("REVERSE "); if (State == INSERT) outstrn("INSERT --"); else outstrn("REPLACE --"); } #endif } #ifndef JP if (!p_fm && Recording) outstrn("recording"); #endif } showruler(1); } /* * delete mode message */ void delmode() { if (Recording) msg("recording"); else msg(""); } /* * if ruler option is set: show current cursor position * if always is FALSE, only print if position has changed */ void showruler(always) int always; { static linenr_t oldlnum = 0; static colnr_t oldcol = 0; static int oldlen = 0; int newlen; char buffer[20]; if (p_ru && p_fm) { sprintf(buffer, "%ld,%d", Curpos.lnum, (int)Curpos.col + 1); newlen = strlen(buffer); if (Curpos.col != Cursvcol) { sprintf(buffer + newlen, "-%d", Cursvcol + 1); newlen = strlen(buffer); } screen_msg(TRUE, buffer); return; } if (p_ru && (redraw_msg || always || Curpos.lnum != oldlnum || Cursvcol != oldcol)) { windgoto((int)Rows - 1, ru_col); /* * Some sprintfs return the lenght, some return a pointer. * To avoid portability problems we use strlen here. */ sprintf(buffer, "%ld,%d", Curpos.lnum, (int)Curpos.col + 1); newlen = strlen(buffer); if (Curpos.col != Cursvcol) { sprintf(buffer + newlen, "-%d", Cursvcol + 1); newlen = strlen(buffer); } outstrn(buffer); while (newlen < oldlen) { outchar(' '); --oldlen; } oldlen = newlen; oldlnum = Curpos.lnum; oldcol = Cursvcol; redraw_msg = FALSE; } } /* * Clear a line. The cursor must be at the first char of the line. */ void clear_line() { register int i; if (T_EL != NULL && *T_EL != NUL) outstr(T_EL); else for (i = 1; i < Columns; ++i) outchar(' '); } #define MAXMSG 80 void screen_msg(rev, msg) int rev; char *msg; { static int srow, scol, slen = 0; static u_char orgscr[MAXMSG + 1], orgatr[MAXMSG + 1]; u_char *mp, *screenp, *attribp, attr; u_char *osp, *oap; #ifdef JP KANJI_OFF #endif if (!LinePointers) { if (msg) { if (rev) outstr(T_TI); outstr((char *)msg); if (rev) outstr(T_TP); } return; } /* do not display the same message */ if (msg && slen) { screenp = LinePointers[srow] + scol; attribp = ScreenAttr(screenp); for(mp = (u_char *)msg; mp - (u_char *)msg < slen; mp++, screenp++) if (*mp != *screenp) break; if (mp - (u_char *)msg >= slen) return; } INVERT_OFF #ifdef JPFEP ULINE_OFF #endif /* remove old message */ if (slen) { screenp = LinePointers[srow]; attribp = ScreenAttr(screenp); screenp += scol; attribp += scol; osp = orgscr; oap = orgatr; windgoto(srow, scol); for(; slen > 0; slen--) { char c1, c2; c1 = *screenp++ = *osp++; *attribp++ = *oap++; #ifdef JP if (IsKanji(c1)) { slen --; c2 = *screenp++ = *osp++; *attribp++ = *oap++; KANJI_ON kanjito(&c1, &c2, kcode); outchar(c1); outchar(c2); } else { KANJI_OFF outchar(c1); } #else /* JP */ outchar(c1); #endif /* JP */ } #ifdef JP KANJI_OFF #endif /* JP */ } /* write new message */ if (msg) { u_char *cp, *msgend; curs_columns(TRUE); srow = Cursrow; scol = Curscol; slen = 0; cp = (u_char *)msg; while(*cp && slen < MAXMSG && slen < Columns) #ifdef JP if (IsKanji(*cp)) { cp += 2; slen+= 2; } else #endif { ++ cp; ++ slen; } msgend = cp; if (srow < Rows - (State == CMDLINE ? 2 : 1)) srow ++; else srow --; #ifdef ONEW { extern int onew_noredraw; if (State == CMDLINE && onew_noredraw) srow = 0; } #endif if (srow < 0) srow = 0; if (srow > Rows) srow = Rows - 1; screenp = LinePointers[srow]; attribp = ScreenAttr(screenp); attr = rev ? AM_None : AM_Inv; /* search white spaces */ { int fscol, bscol; u_char *cp, *sp, *startp; fscol = bscol = -1; /* search forward */ startp = screenp; if (scol > slen / 2) startp += scol - slen / 2; for(cp = startp; cp <= screenp + Columns - slen; cp++) { for(sp = cp; sp - cp != slen ; sp++) if (*sp != ' ') { cp = sp; break; } if (cp != sp) { fscol = cp - screenp; break; } } /* search backward */ cp = startp + slen - 1; if (cp - screenp > Columns) cp = screenp + Columns - 1; for(; cp >= screenp + slen; cp--) { for(sp = cp; cp - sp != slen; sp--) if (*sp != ' ') { cp = sp; break; } if (cp != sp) { bscol = sp - screenp + 1; break; } } if (fscol < 0) if (bscol < 0) /* when no enough space is found */ if (scol > Columns - slen / 2 - 1) scol = Columns - slen; else if (scol > slen / 2) scol -= slen / 2; else scol = 0; else /* when space is found backword */ scol = bscol; else if (bscol < 0) /* when space is found foreword */ scol = fscol; else scol = (scol - bscol -slen < fscol - scol) ? bscol : fscol; } #ifdef JP KANJI_OFF if (IsKanji(*(screenp + scol)) && !(*(attribp + scol) & AM_K1)) { if (scol > Columns / 2) scol--; else scol++; } #endif /* JP */ screenp += scol; attribp += scol; osp = orgscr; oap = orgatr; for(mp = (u_char *)msg; mp < msgend; mp++) { *osp ++ = *screenp; *oap ++ = *attribp; *screenp++ = *mp; *attribp++ = attr; } if (rev) outstr(T_TI); windgoto(srow, scol); for(mp = (u_char *)msg; mp < msgend; mp++) { u_char c1, c2; c1 = *mp; #ifdef JP if (IsKanji(c1)) { c2 = * ++mp; KANJI_ON kanjito(&c1, &c2, kcode); outchar(c1); outchar(c2); } else { KANJI_OFF outchar(c1); } #else /* JP */ outchar(c1); #endif /* JP */ } #ifdef JP KANJI_OFF if (IsKanji(*screenp) && !(*attribp & AM_K1)) { *osp++ = *screenp; *oap++ = *attribp; *screenp = ' '; *attribp = attr; outchar(' '); slen++; } #endif /* JP */ if (rev) outstr(T_TP); } flushbuf(); }