This is screen.c in view mode; [Download] [Up]
/* vi:ts=4:sw=4 * * VIM - Vi IMproved by Bram Moolenaar * * Read the file "credits.txt" for a list of people who contributed. * Read the file "uganda.txt" for copying and usage conditions. */ /* * screen.c: code for displaying on the screen */ #include "vim.h" #include "globals.h" #include "proto.h" #include "param.h" char *tgoto __PARMS((char *cm, int col, int line)); static char_u *Nextscreen = NULL; /* What is currently on the screen. */ static char_u **LinePointers = NULL; /* array of pointers into Nextscreen */ /* * Cline_height 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_height; /* current size of cursor line */ static int Cline_row; /* starting row of the cursor line on screen */ static FPOS old_cursor = {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 invert = 0; /* set to INVERTCODE when inverting */ #define INVERTCODE 0x80 static int win_line __ARGS((WIN *, linenr_t, int, int)); static void screen_char __ARGS((char_u *, int, int)); static void screenalloc __ARGS((int)); static void screenclear2 __ARGS((void)); static int screen_ins_lines __ARGS((int, int, int, int)); /* * 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(must_redraw); return; } screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL || RedrawingDisabled) return; screen_start(); /* init cursor position of screen_char() */ cursor_off(); (void)set_highlight('v'); row = win_line(curwin, curwin->w_cursor.lnum, Cline_row, curwin->w_height); if (row == curwin->w_height + 1) /* line too long for window */ updateScreen(VALID_TO_CURSCHAR); else { n = row - Cline_row; if (n != Cline_height) /* line changed size */ { if (n < Cline_height) /* got smaller: delete lines */ win_del_lines(curwin, row, Cline_height - n, FALSE, TRUE); else /* got bigger: insert lines */ win_ins_lines(curwin, Cline_row + Cline_height, n - Cline_height, FALSE, TRUE); updateScreen(VALID_TO_CURSCHAR); } } } /* * updateScreen() * * Based on the current value of curwin->w_topline, transfer a screenfull * of stuff from Filemem to Nextscreen, and update curwin->w_botline. */ void updateScreen(type) int type; { WIN *wp; screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return; if (must_redraw) { if (type < must_redraw) /* use maximal type */ type = must_redraw; must_redraw = 0; } if (type == CURSUPD) /* update cursor and then redraw NOT_VALID*/ { curwin->w_lsize_valid = 0; cursupdate(); /* will call updateScreen() */ return; } if (curwin->w_lsize_valid == 0 && type != CLEAR) type = NOT_VALID; if (RedrawingDisabled) { must_redraw = type; /* remember type for next time */ return; } /* * if the screen was scrolled up when displaying a message, scroll it down */ if (msg_scrolled) { clear_cmdline = TRUE; if (msg_scrolled > Rows - 5) /* clearing is faster */ type = CLEAR; else if (type != CLEAR) { if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows) == FAIL) type = CLEAR; win_rest_invalid(firstwin); /* should do only first/last few */ } msg_scrolled = 0; } /* * reset cmdline_row now (may have been changed temporarily) */ compute_cmdrow(); if (type == CLEAR) /* first clear screen */ { screenclear(); /* will reset clear_cmdline */ type = NOT_VALID; } if (clear_cmdline) gotocmdline(TRUE, NUL); /* first clear cmdline */ /* return if there is nothing to do */ if ((type == VALID && curwin->w_topline == curwin->w_lsize_lnum[0]) || (type == INVERTED && old_cursor.lnum == curwin->w_cursor.lnum && old_cursor.col == curwin->w_cursor.col && curwin->w_curswant == oldCurswant)) return; curwin->w_redr_type = type; /* * go from top to bottom through the windows, redrawing the ones that need it */ cursor_off(); for (wp = firstwin; wp; wp = wp->w_next) { if (wp->w_redr_type) win_update(wp); if (wp->w_redr_status) win_redr_status(wp); } if (redraw_cmdline) showmode(); } /* * update a single window * * This may cause the windows below it also to be redrawn */ void win_update(wp) WIN *wp; { int type = wp->w_redr_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; if (type == NOT_VALID) { wp->w_redr_status = TRUE; wp->w_lsize_valid = 0; } idx = 0; row = 0; lnum = wp->w_topline; /* The number of rows shown is w_height. */ /* The default last row is the status/command line. */ endrow = wp->w_height; 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: wp->w_topline is below wp->w_lsize_lnum[0]: may scroll up */ if (wp->w_topline < wp->w_lsize_lnum[0]) /* may scroll down */ { j = wp->w_lsize_lnum[0] - wp->w_topline; if (j < wp->w_height - 2) /* not too far off */ { lastline = wp->w_lsize_lnum[0] - 1; i = plines_m_win(wp, wp->w_topline, lastline); if (i < wp->w_height - 2) /* less than a screen off */ { /* * Try to insert the correct number of lines. * If not the last window, delete the lines at the bottom. * win_ins_lines may fail. */ if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK && wp->w_lsize_valid) { endrow = i; if ((wp->w_lsize_valid += j) > wp->w_height) wp->w_lsize_valid = wp->w_height; for (idx = wp->w_lsize_valid; idx - j >= 0; idx--) { wp->w_lsize_lnum[idx] = wp->w_lsize_lnum[idx - j]; wp->w_lsize[idx] = wp->w_lsize[idx - j]; } idx = 0; } } else if (lastwin == firstwin) /* far off: clearing the screen is faster */ screenclear(); } else if (lastwin == firstwin) /* far off: clearing the screen is faster */ screenclear(); } else /* may scroll up */ { j = -1; for (i = 0; i < wp->w_lsize_valid; i++) /* try to find wp->w_topline in wp->w_lsize_lnum[] */ { if (wp->w_lsize_lnum[i] == wp->w_topline) { j = i; break; } row += wp->w_lsize[i]; } if (j == -1) /* wp->w_topline is not in wp->w_lsize_lnum */ { row = 0; if (lastwin == firstwin) screenclear(); /* far off: clearing the screen is faster */ } else { /* * Try to delete the correct number of lines. * wp->w_topline is at wp->w_lsize_lnum[i]. */ if ((row == 0 || win_del_lines(wp, 0, row, FALSE, wp == firstwin) == OK) && wp->w_lsize_valid) { srow = row; row = 0; for (;;) { if (type == VALID_TO_CURSCHAR && lnum == wp->w_cursor.lnum) break; if (row + srow + (int)wp->w_lsize[j] >= wp->w_height) break; wp->w_lsize[idx] = wp->w_lsize[j]; wp->w_lsize_lnum[idx] = lnum++; row += wp->w_lsize[idx++]; if ((int)++j >= wp->w_lsize_valid) break; } wp->w_lsize_valid = idx; } else row = 0; /* update all lines */ } } if (endrow == wp->w_height && idx == 0) /* no scrolling */ wp->w_lsize_valid = 0; } done = didline = FALSE; screen_start(); /* init cursor position of screen_char() */ 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 (wp->w_cursor.lnum < old_cursor.lnum) { from = wp->w_cursor.lnum; to = old_cursor.lnum; } else { from = old_cursor.lnum; to = wp->w_cursor.lnum; } /* if in block mode and changed column or wp->w_curswant: update all lines */ if (Visual_block && (wp->w_cursor.col != old_cursor.col || wp->w_curswant != oldCurswant)) { if (from > VIsual.lnum) from = VIsual.lnum; if (to < VIsual.lnum) to = VIsual.lnum; } if (from < wp->w_topline) from = wp->w_topline; if (to >= wp->w_botline) to = wp->w_botline - 1; /* find the minimal part to be updated */ if (type == INVERTED) { while (lnum < from) /* find start */ { row += wp->w_lsize[idx++]; ++lnum; } srow = row; for (j = idx; j < wp->w_lsize_valid; ++j) /* find end */ { if (wp->w_lsize_lnum[j] == to + 1) { endrow = srow; break; } srow += wp->w_lsize[j]; } old_cursor = wp->w_cursor; oldCurswant = wp->w_curswant; } /* if we update the lines between from and to set old_cursor */ else if (lnum <= from && (endrow == wp->w_height || lastline >= to)) { old_cursor = wp->w_cursor; oldCurswant = wp->w_curswant; } } (void)set_highlight('v'); /* * Update the screen rows from "row" to "endrow". * Start at line "lnum" which is at wp->w_lsize_lnum[idx]. */ for (;;) { if (lnum > wp->w_buffer->b_ml.ml_line_count) /* hit the end of the file */ { done = TRUE; break; } srow = row; row = win_line(wp, lnum, srow, endrow); if (row > endrow) /* past end of screen */ { wp->w_lsize[idx] = plines_win(wp, lnum); /* we may need the size of that */ wp->w_lsize_lnum[idx++] = lnum; /* too long line later on */ break; } wp->w_lsize[idx] = row - srow; wp->w_lsize_lnum[idx++] = lnum; if (++lnum > wp->w_buffer->b_ml.ml_line_count) { done = TRUE; break; } if (row == endrow) { didline = TRUE; break; } } if (idx > wp->w_lsize_valid) wp->w_lsize_valid = idx; /* Do we have to do off the top of the screen processing ? */ if (endrow != wp->w_height) { row = 0; for (idx = 0; idx < wp->w_lsize_valid && row < wp->w_height; idx++) row += wp->w_lsize[idx]; if (row < wp->w_height) { done = TRUE; } else if (row > wp->w_height) /* Need to blank out the last line */ { lnum = wp->w_lsize_lnum[idx - 1]; srow = row - wp->w_lsize[idx - 1]; didline = FALSE; } else { lnum = wp->w_lsize_lnum[idx - 1] + 1; didline = TRUE; } } wp->w_empty_rows = 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 == wp->w_topline) { /* * Single line that does not fit! * Fill last line with '@' characters. */ screen_fill(wp->w_winpos + wp->w_height - 1, wp->w_winpos + wp->w_height, 0, (int)Columns, '@', '@'); wp->w_botline = lnum + 1; } else { /* * Clear the rest of the screen and mark the unused lines. */ screen_fill(wp->w_winpos + srow, wp->w_winpos + wp->w_height, 0, (int)Columns, '@', ' '); wp->w_botline = lnum; wp->w_empty_rows = wp->w_height - srow; } } else { /* make sure the rest of the screen is blank */ /* put '~'s on rows that aren't part of the file. */ screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height, 0, (int)Columns, '~', ' '); wp->w_empty_rows = wp->w_height - row; if (done) /* we hit the end of the file */ wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1; else wp->w_botline = lnum; } wp->w_redr_type = 0; } /* * mark all status lines for redraw; used after first :cd */ void status_redraw_all() { WIN *wp; for (wp = firstwin; wp; wp = wp->w_next) wp->w_redr_status = TRUE; updateScreen(NOT_VALID); } /* * Redraw the status line of window wp. * * If inversion is possible we use it. Else '=' characters are used. */ void win_redr_status(wp) WIN *wp; { int row; int col; char_u *p; int len; int fillchar; if (wp->w_status_height) /* if there is a status line */ { if (set_highlight('s') == OK) /* can highlight */ { fillchar = ' '; start_highlight(); } else /* can't highlight, use '=' */ fillchar = '='; screen_start(); /* init cursor position */ row = wp->w_winpos + wp->w_height; col = 0; p = wp->w_buffer->b_xfilename; if (p == NULL) p = (char_u *)"[No File]"; else { home_replace(p, NameBuff, MAXPATHL); p = NameBuff; } len = STRLEN(p); if (wp->w_buffer->b_changed) len += 4; if (len > ru_col - 1) { screen_outchar('<', row, 0); p += len - (ru_col - 1) + 1; len = (ru_col - 1); col = 1; } screen_msg(p, row, col); if (wp->w_buffer->b_changed) screen_msg((char_u *)" [+]", row, len - 4); screen_fill(row, row + 1, len, ru_col, fillchar, fillchar); stop_highlight(); win_redr_ruler(wp, TRUE); } else /* no status line, can only be last window */ redraw_cmdline = TRUE; wp->w_redr_status = FALSE; } /* * display line "lnum" of window 'wp' on the screen * Start at row "startrow", stop when "endrow" is reached. * Return the number of last row the line occupies. */ static int win_line(wp, lnum, startrow, endrow) WIN *wp; linenr_t lnum; int startrow; int endrow; { char_u *screenp; int c; int col; /* visual column on screen */ long vcol; /* visual column for tabs */ int row; /* row in the window, excluding w_winpos */ int screen_row; /* row on the screen, including w_winpos */ char_u *ptr; char_u extra[16]; /* "%ld" must fit in here */ char_u *p_extra; int n_extra; int n_spaces = 0; int fromcol, tocol; /* start/end of inverting */ int noinvcur = FALSE; /* don't invert the cursor */ int temp; FPOS *top, *bot; row = startrow; screen_row = row + wp->w_winpos; col = 0; vcol = 0; fromcol = -10; tocol = MAXCOL; canopt = TRUE; if (VIsual.lnum && wp == curwin) /* visual active in this window */ { if (ltoreq(wp->w_cursor, VIsual)) /* Visual is after wp->w_cursor */ { top = &wp->w_cursor; bot = &VIsual; } else /* Visual is before wp->w_cursor */ { top = &VIsual; bot = &wp->w_cursor; } if (Visual_block) /* block mode */ { if (lnum >= top->lnum && lnum <= bot->lnum) { fromcol = getvcol(wp, top, 2); temp = getvcol(wp, bot, 2); if (temp < fromcol) fromcol = temp; if (wp->w_curswant != MAXCOL) { tocol = getvcol(wp, top, 3); temp = getvcol(wp, bot, 3); if (temp > tocol) tocol = temp; ++tocol; } } } else /* non-block mode */ { if (lnum > top->lnum && lnum <= bot->lnum) fromcol = 0; else if (lnum == top->lnum) fromcol = getvcol(wp, top, 2); if (lnum == bot->lnum) tocol = getvcol(wp, 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 == wp->w_cursor.lnum) noinvcur = TRUE; if (tocol <= wp->w_leftcol) /* inverting is left of screen */ fromcol = 0; else if (fromcol >= 0 && fromcol < wp->w_leftcol) /* start of invert is left of screen */ fromcol = wp->w_leftcol; /* if inverting in this line, can't optimize cursor positioning */ if (fromcol >= 0) canopt = FALSE; } ptr = ml_get_buf(wp->w_buffer, lnum, FALSE); if (!wp->w_p_wrap) /* advance to first character to be displayed */ { while (vcol < wp->w_leftcol && *ptr) vcol += chartabsize(*ptr++, vcol); if (vcol > wp->w_leftcol) { n_spaces = vcol - wp->w_leftcol; /* begin with some spaces */ vcol = wp->w_leftcol; } } screenp = LinePointers[screen_row]; if (wp->w_p_nu) { sprintf((char *)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; } for (;;) { if (!canopt) /* Visual in this line */ { if (((vcol == fromcol && !(noinvcur && vcol == wp->w_virtcol)) || (noinvcur && vcol == wp->w_virtcol + 1 && vcol >= fromcol)) && vcol < tocol) start_highlight(); /* start highlighting */ else if (invert && (vcol == tocol || (noinvcur && vcol == wp->w_virtcol))) stop_highlight(); /* stop highlighting */ } /* 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 = *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 && !wp->w_p_list) { /* tab amount depends on current column */ n_spaces = (int)wp->w_buffer->b_p_ts - vcol % (int)wp->w_buffer->b_p_ts - 1; c = ' '; } else if (c == NUL && wp->w_p_list) { p_extra = (char_u *)""; n_extra = 1; c = '$'; } else if (c != NUL) { p_extra = transchar(c); n_extra = charsize(c) - 1; c = *p_extra++; } } } if (c == NUL) { if (invert) { if (vcol == 0) /* invert first char of empty line */ { if (*screenp != (' ' ^ INVERTCODE)) { *screenp = (' ' ^ INVERTCODE); screen_char(screenp, screen_row, col); } ++screenp; ++col; } stop_highlight(); } /* * blank out the rest of this row, if necessary */ while (col < Columns && *screenp == ' ') { ++screenp; ++col; } if (col < Columns) { screen_fill(screen_row, screen_row + 1, col, (int)Columns, ' ', ' '); col = Columns; } row++; screen_row++; break; } if (col >= Columns) { col = 0; ++row; ++screen_row; if (!wp->w_p_wrap) break; if (row == endrow) /* line got too long for screen */ { ++row; break; } screenp = LinePointers[screen_row]; } /* * Store the character in Nextscreen. * Be careful with characters where (c ^ INVERTCODE == ' '), they may be * confused with spaces inserted by scrolling. */ if (*screenp != (c ^ invert) || c == (' ' ^ INVERTCODE)) { *screenp = (c ^ invert); screen_char(screenp, screen_row, col); } ++screenp; col++; vcol++; } if (invert) stop_highlight(); return (row); } /* * output a single character directly to the screen * update NextScreen * Note: must do screen_start() before this! */ void screen_outchar(c, row, col) int c; int row, col; { char_u buf[2]; buf[0] = c; buf[1] = NUL; screen_msg(buf, row, col); } /* * put string '*msg' on the screen at position 'row' and 'col' * update NextScreen * Note: only outputs within one row, message is truncated at screen boundary! * Note: must do screen_start() before this! * Note: caller must make sure that row is valid! */ void screen_msg(msg, row, col) char_u *msg; int row; int col; { char_u *screenp; screenp = LinePointers[row] + col; while (*msg && col < Columns) { if (*screenp != (*msg ^ invert) || *msg == (' ' ^ INVERTCODE)) { *screenp = (*msg ^ invert); screen_char(screenp, row, col); } ++screenp; ++col; ++msg; } } /* * last cursor position known by screen_char */ static int oldrow, oldcol; /* old cursor position */ /* * reset cursor position. Use whenever cursor moved before calling screen_char. */ void screen_start() { oldcol = 9999; } /* * set_highlight - set highlight depending on 'highlight' option and context. * * return FAIL if highlighting is not possible, OK otherwise */ int set_highlight(context) int context; { int len; int i; int mode; len = STRLEN(p_hl); for (i = 0; i < len; i += 3) if (p_hl[i] == context) break; if (i < len) mode = p_hl[i + 1]; else mode = 'i'; switch (mode) { case 'b': highlight = T_TB; /* bold */ unhighlight = T_TP; break; case 's': highlight = T_SO; /* standout */ unhighlight = T_SE; break; case 'n': highlight = NULL; /* no highlighting */ unhighlight = NULL; break; default: highlight = T_TI; /* invert/reverse */ unhighlight = T_TP; break; } if (highlight == NULL || *highlight == NUL || unhighlight == NULL || *unhighlight == NUL) { highlight = NULL; return FAIL; } return OK; } void start_highlight() { if (highlight != NULL) { outstr(highlight); invert = INVERTCODE; } } void stop_highlight() { if (invert) { outstr(unhighlight); invert = 0; } } /* * put character '*p' on the screen at position 'row' and 'col' */ static void screen_char(p, row, col) char_u *p; int row; int col; { int c; int noinvcurs; /* * Outputting the last character on the screen may scrollup the screen. * Don't to it! */ if (row == Rows - 1 && col == Columns - 1) return; if (oldcol != col || oldrow != row) { /* check if no cursor movement is allowed in standout mode */ if (invert && !p_wi && (T_MS == NULL || *T_MS == NUL)) noinvcurs = 7; else noinvcurs = 0; /* * 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; if (i <= 4 + noinvcurs && canopt) { while (i) { c = *(p - i--); outchar(c ^ invert); } } else { if (noinvcurs) stop_highlight(); if (T_CRI && *T_CRI) /* use tgoto interface! jw */ OUTSTR(tgoto((char *)T_CRI, 0, i)); else windgoto(row, col); if (noinvcurs) start_highlight(); } oldcol = col; } else { if (noinvcurs) stop_highlight(); windgoto(oldrow = row, oldcol = col); if (noinvcurs) start_highlight(); } } /* * For weird invert mechanism: output (un)highlight before every char * Lots of extra output, but works. */ if (p_wi) { if (invert) outstr(highlight); else outstr(unhighlight); } outchar(*p ^ invert); oldcol++; } /* * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' * with character 'c1' in first column followed by 'c2' in the other columns. */ void screen_fill(start_row, end_row, start_col, end_col, c1, c2) int start_row, end_row; int start_col, end_col; int c1, c2; { int row; int col; char_u *screenp; int did_delete = FALSE; int c; if (start_row >= end_row || start_col >= end_col) /* nothing to do */ return; c1 ^= invert; c2 ^= invert; for (row = start_row; row < end_row; ++row) { /* try to use delete-line termcap code */ if (c2 == ' ' && end_col == Columns && T_EL != NULL && *T_EL != NUL) { /* * check if we really need to clear something */ col = start_col; screenp = LinePointers[row] + start_col; if (c1 != ' ') /* don't clear first char */ { ++col; ++screenp; } while (col < end_col && *screenp == ' ') /* skip blanks */ { ++col; ++screenp; } if (col < end_col) /* something to be cleared */ { windgoto(row, col); outstr(T_EL); } did_delete = TRUE; } screen_start(); /* init cursor position of screen_char() */ screenp = LinePointers[row] + start_col; c = c1; for (col = start_col; col < end_col; ++col) { if (*screenp != c) { *screenp = c; if (!did_delete || c != ' ') screen_char(screenp, row, col); } ++screenp; c = c2; } if (row == Rows - 1) { redraw_cmdline = TRUE; if (c1 == ' ' && c2 == ' ') clear_cmdline = FALSE; } } } /* * recompute all w_botline's. Called after Rows changed. */ void comp_Botline_all() { WIN *wp; for (wp = firstwin; wp; wp = wp->w_next) comp_Botline(wp); } /* * compute wp->w_botline. Can be called after wp->w_topline changed. */ void comp_Botline(wp) WIN *wp; { linenr_t lnum; int done = 0; for (lnum = wp->w_topline; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum) { if ((done += plines_win(wp, lnum)) > wp->w_height) break; } wp->w_botline = lnum; /* wp->w_botline is the line that is just below the window */ } static void screenalloc(clear) int clear; { static int old_Rows = 0; static int old_Columns = 0; register int i; WIN *wp; int outofmem = FALSE; /* * Allocation of the screen buffers is done only when the size changes * and when Rows and Columns have been set. */ 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 */ free(Nextscreen); free(LinePointers); for (wp = firstwin; wp; wp = wp->w_next) win_free_lsize(wp); Nextscreen = (char_u *)malloc((size_t) (Rows * Columns)); LinePointers = (char_u **)malloc(sizeof(char_u *) * Rows); for (wp = firstwin; wp; wp = wp->w_next) { if (win_alloc_lsize(wp) == FAIL) { outofmem = TRUE; break; } } if (Nextscreen == NULL || LinePointers == NULL || outofmem) { emsg(e_outofmem); free(Nextscreen); Nextscreen = 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 */ memset((char *)Nextscreen, ' ', (size_t)(Rows * Columns)); win_rest_invalid(firstwin); clear_cmdline = FALSE; if (must_redraw == CLEAR) /* no need to clear again */ must_redraw = NOT_VALID; } /* * check cursor for a valid lnum */ void check_cursor() { if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; if (curwin->w_cursor.lnum <= 0) curwin->w_cursor.lnum = 1; } void cursupdate() { linenr_t p; long nlines; int i; int temp; screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return; check_cursor(); if (bufempty()) /* special case - file is empty */ { curwin->w_topline = 1; curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; curwin->w_lsize[0] = 0; if (curwin->w_lsize_valid == 0) /* don't know about screen contents */ updateScreen(NOT_VALID); curwin->w_lsize_valid = 1; } else if (curwin->w_cursor.lnum < curwin->w_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 = curwin->w_height / 2 - 1; if (temp < 2) temp = 2; if (curwin->w_topline - curwin->w_cursor.lnum >= temp) /* not very close */ { p = curwin->w_cursor.lnum; i = plines(p); temp += i; /* count lines for 1/2 screenheight */ while (i < curwin->w_height + 1 && i < temp && p > 1) i += plines(--p); curwin->w_topline = p; if (i > curwin->w_height) /* cursor line won't fit, backup one line */ ++curwin->w_topline; } else if (p_sj > 1) /* scroll at least p_sj lines */ { for (i = 0; i < p_sj && curwin->w_topline > 1; i += plines(--curwin->w_topline)) ; } if (curwin->w_topline > curwin->w_cursor.lnum) curwin->w_topline = curwin->w_cursor.lnum; updateScreen(VALID); } else if (curwin->w_cursor.lnum >= curwin->w_botline) { /* * If the cursor is below the bottom of the screen, scroll the screen to * put the cursor on the screen. * 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 */ nlines = curwin->w_cursor.lnum - curwin->w_botline + 1; if (nlines <= curwin->w_height + 1) { /* get the number or rows to scroll minus the number of free '~' rows */ temp = plines_m(curwin->w_botline, curwin->w_cursor.lnum) - curwin->w_empty_rows; if (temp <= 0) /* curwin->w_empty_rows is larger, no need to scroll */ nlines = 0; else if (temp > curwin->w_height) /* 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 = curwin->w_topline; i < temp && p < curwin->w_botline; ++p) i += plines(p); if (i >= temp) /* it's possible to scroll */ nlines = p - curwin->w_topline; else /* below curwin->w_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 >= curwin->w_height / 2 && nlines > p_sj) { p = curwin->w_cursor.lnum; temp = curwin->w_height / 2 + 1; nlines = 0; i = 0; do /* this loop could win a contest ... */ i += plines(p); while (i < temp && (nlines = 1) != 0 && --p != 0); curwin->w_topline = p + nlines; } else scrollup(nlines); updateScreen(VALID); } else if (curwin->w_lsize_valid == 0) /* don't know about screen contents */ updateScreen(NOT_VALID); curwin->w_row = curwin->w_col = curwin->w_virtcol = i = 0; for (p = curwin->w_topline; p != curwin->w_cursor.lnum; ++p) if (RedrawingDisabled) /* curwin->w_lsize[] invalid */ curwin->w_row += plines(p); else curwin->w_row += curwin->w_lsize[i++]; Cline_row = curwin->w_row; if (!RedrawingDisabled && i > curwin->w_lsize_valid) /* Should only happen with a line that is too */ /* long to fit on the last screen line. */ Cline_height = 0; else { if (RedrawingDisabled) /* curwin->w_lsize[] invalid */ Cline_height = plines(curwin->w_cursor.lnum); else Cline_height = curwin->w_lsize[i]; curs_columns(!RedrawingDisabled); /* compute curwin->w_virtcol and curwin->w_col */ if (must_redraw) updateScreen(must_redraw); } if (curwin->w_set_curswant) { curwin->w_curswant = curwin->w_virtcol; curwin->w_set_curswant = FALSE; } } /* * compute curwin->w_col and curwin->w_virtcol */ void curs_columns(scroll) int scroll; /* when TRUE, may scroll horizontally */ { int diff; curwin->w_virtcol = getvcol(curwin, &curwin->w_cursor, 1); curwin->w_col = curwin->w_virtcol; if (curwin->w_p_nu) curwin->w_col += 8; curwin->w_row = Cline_row; if (curwin->w_p_wrap) /* long line wrapping, adjust curwin->w_row */ while (curwin->w_col >= Columns) { curwin->w_col -= Columns; curwin->w_row++; } else if (scroll) /* no line wrapping, compute curwin->w_leftcol if scrolling is on */ /* if scrolling is off, curwin->w_leftcol is assumed to be 0 */ { /* If Cursor is in columns 0, start in column 0 */ /* If Cursor is left of the screen, scroll rightwards */ /* If Cursor is right of the screen, scroll leftwards */ if (curwin->w_cursor.col == 0) { /* screen has to be redrawn with new curwin->w_leftcol */ if (curwin->w_leftcol != 0 && must_redraw < NOT_VALID) must_redraw = NOT_VALID; curwin->w_leftcol = 0; } else if (((diff = curwin->w_leftcol + (curwin->w_p_nu ? 8 : 0) - curwin->w_col) > 0 || (diff = curwin->w_col - (curwin->w_leftcol + Columns) + 1) > 0)) { if (p_ss == 0 || diff >= Columns / 2) curwin->w_leftcol = curwin->w_col - Columns / 2; else { if (diff < p_ss) diff = p_ss; if (curwin->w_col < curwin->w_leftcol + 8) curwin->w_leftcol -= diff; else curwin->w_leftcol += diff; } if (curwin->w_leftcol < 0) curwin->w_leftcol = 0; if (must_redraw < NOT_VALID) must_redraw = NOT_VALID; /* screen has to be redrawn with new curwin->w_leftcol */ } curwin->w_col -= curwin->w_leftcol; } if (curwin->w_row > curwin->w_height - 1) /* Cursor past end of screen */ curwin->w_row = curwin->w_height - 1; /* 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(wp, pos, type) WIN *wp; FPOS *pos; int type; { int col; int vcol; char_u *ptr; int incr; int c; vcol = 0; ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); for (col = pos->col; col >= 0; --col) { c = *ptr++; 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 */ incr = chartabsize(c, (long)vcol); if (col == 0) /* character at pos.col */ { if (type == 3 || (type == 1 && c == TAB && State == NORMAL && !wp->w_p_list)) --incr; else break; } vcol += incr; } return vcol; } void scrolldown(nlines) long nlines; { register long done = 0; /* total # of physical lines done */ /* Scroll up 'nlines' lines. */ while (nlines--) { if (curwin->w_topline == 1) break; done += plines(--curwin->w_topline); } /* * Compute the row number of the last row of the cursor line * and move it onto the screen. */ curwin->w_row += done; if (curwin->w_p_wrap) curwin->w_row += plines(curwin->w_cursor.lnum) - 1 - curwin->w_virtcol / Columns; while (curwin->w_row >= curwin->w_height && curwin->w_cursor.lnum > 1) curwin->w_row -= plines(curwin->w_cursor.lnum--); } void scrollup(nlines) long nlines; { #ifdef NEVER register long done = 0; /* total # of physical lines done */ /* Scroll down 'nlines' lines. */ while (nlines--) { if (curwin->w_topline == curbuf->b_ml.ml_line_count) break; done += plines(curwin->w_topline); if (curwin->w_cursor.lnum == curwin->w_topline) ++curwin->w_cursor.lnum; ++curwin->w_topline; } win_del_lines(curwin, 0, done, TRUE, TRUE); #endif curwin->w_topline += nlines; if (curwin->w_topline > curbuf->b_ml.ml_line_count) curwin->w_topline = curbuf->b_ml.ml_line_count; if (curwin->w_cursor.lnum < curwin->w_topline) curwin->w_cursor.lnum = curwin->w_topline; } /* * insert 'nlines' lines at 'row' in window 'wp' * if 'invalid' is TRUE the wp->w_lsize_lnum[] is invalidated. * if 'mayclear' is TRUE the screen will be cleared if it is faster than scrolling * Returns FAIL if the lines are not inserted, OK for success. */ int win_ins_lines(wp, row, nlines, invalid, mayclear) WIN *wp; int row; int nlines; int invalid; int mayclear; { int did_delete; int nextrow; int lastrow; int retval; if (invalid) wp->w_lsize_valid = 0; if (RedrawingDisabled || nlines <= 0 || wp->w_height < 5) return FAIL; if (nlines > wp->w_height - row) nlines = wp->w_height - row; if (mayclear && Rows - nlines < 5) /* only a few lines left: redraw is faster */ { screenclear(); /* will set wp->w_lsize_valid to 0 */ return FAIL; } if (nlines == wp->w_height) /* will delete all lines */ return FAIL; /* * when scrolling, the message on the command line should be cleared, * otherwise it will stay there forever. */ clear_cmdline = TRUE; /* * if the terminal can set a scroll region, use that */ if (scroll_region) { scroll_region_set(wp); retval = screen_ins_lines(wp->w_winpos, row, nlines, wp->w_height); scroll_region_reset(); return retval; } if (wp->w_next && p_tf) /* don't delete/insert on fast terminal */ return FAIL; /* * If there is a next window or a status line, we first try to delete the * lines at the bottom to avoid messing what is after the window. * If this fails and there are following windows, don't do anything to avoid * messing up those windows, better just redraw. */ did_delete = FALSE; if (wp->w_next || wp->w_status_height) { if (screen_del_lines(0, wp->w_winpos + wp->w_height - nlines, nlines, (int)Rows) == OK) did_delete = TRUE; else if (wp->w_next) return FAIL; } /* * if no lines deleted, blank the lines that will end up below the window */ if (!did_delete) { wp->w_redr_status = TRUE; redraw_cmdline = TRUE; nextrow = wp->w_winpos + wp->w_height + wp->w_status_height; lastrow = nextrow + nlines; if (lastrow > Rows) lastrow = Rows; screen_fill(nextrow - nlines, lastrow - nlines, 0, (int)Columns, ' ', ' '); } if (screen_ins_lines(0, wp->w_winpos + row, nlines, (int)Rows) == FAIL) { /* deletion will have messed up other windows */ if (did_delete) { wp->w_redr_status = TRUE; win_rest_invalid(wp->w_next); } return FAIL; } return OK; } /* * delete 'nlines' lines at 'row' in window 'wp' * If 'invalid' is TRUE curwin->w_lsize_lnum[] is invalidated. * If 'mayclear' is TRUE the screen will be cleared if it is faster than scrolling * Return OK for success, FAIL if the lines are not deleted. */ int win_del_lines(wp, row, nlines, invalid, mayclear) WIN *wp; int row; int nlines; int invalid; int mayclear; { int retval; if (invalid) wp->w_lsize_valid = 0; if (RedrawingDisabled || nlines <= 0) return FAIL; if (nlines > wp->w_height - row) nlines = wp->w_height - row; if (mayclear && Rows - nlines < 5) /* only a few lines left: redraw is faster */ { screenclear(); /* will set wp->w_lsize_valid to 0 */ return FAIL; } if (nlines == wp->w_height) /* will delete all lines */ return FAIL; /* * when scrolling, the message on the command line should be cleared, * otherwise it will stay there forever. */ clear_cmdline = TRUE; /* * if the terminal can set a scroll region, use that */ if (scroll_region) { scroll_region_set(wp); retval = screen_del_lines(wp->w_winpos, row, nlines, wp->w_height); scroll_region_reset(); return retval; } if (wp->w_next && p_tf) /* don't delete/insert on fast terminal */ return FAIL; if (screen_del_lines(0, wp->w_winpos + row, nlines, (int)Rows) == FAIL) return FAIL; /* * If there are windows or status lines below, try to put them at the * correct place. If we can't do that, they have to be redrawn. */ if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1) { if (screen_ins_lines(0, wp->w_winpos + wp->w_height - nlines, nlines, (int)Rows) == FAIL) { wp->w_redr_status = TRUE; win_rest_invalid(wp->w_next); } } /* * If this is the last window and there is no status line, redraw the * command line later. */ else redraw_cmdline = TRUE; return OK; } /* * window 'wp' and everything after it is messed up, mark it for redraw */ void win_rest_invalid(wp) WIN *wp; { while (wp) { wp->w_lsize_valid = 0; wp->w_redr_type = NOT_VALID; wp->w_redr_status = TRUE; wp = wp->w_next; } redraw_cmdline = TRUE; } /* * 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. */ /* * insert lines on the screen and update Nextscreen * 'end' is the line after the scrolled part. Normally it is Rows. * When scrolling region used 'off' is the offset from the top for the region. * 'row' and 'end' are relative to the start of the region. * * return FAIL for failure, OK for success. */ static int screen_ins_lines(off, row, nlines, end) int off; int row; int nlines; int end; { int i; int j; char_u *temp; int cursor_row; if (T_CSC != NULL && *T_CSC != NUL) /* cursor relative to region */ cursor_row = row; else cursor_row = row + off; screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return FAIL; if (nlines <= 0 || ((T_CIL == NULL || *T_CIL == NUL) && (T_IL == NULL || *T_IL == NUL) && (T_SR == NULL || *T_SR == NUL || row != 0))) return FAIL; /* * It "looks" better if we do all the inserts at once */ if (T_CIL && *T_CIL) { windgoto(cursor_row, 0); if (nlines == 1 && T_IL && *T_IL) outstr(T_IL); else OUTSTR(tgoto((char *)T_CIL, 0, nlines)); } else { for (i = 0; i < nlines; i++) { if (i == 0 || cursor_row != 0) windgoto(cursor_row, 0); if (T_IL && *T_IL) outstr(T_IL); else outstr(T_SR); } } /* * Now shift LinePointers nlines down to reflect the inserted lines. * Clear the inserted lines. */ row += off; end += off; for (i = 0; i < nlines; ++i) { j = end - 1 - i; temp = LinePointers[j]; while ((j -= nlines) >= row) LinePointers[j + nlines] = LinePointers[j]; LinePointers[j + nlines] = temp; memset((char *)temp, ' ', (size_t)Columns); } return OK; } /* * delete lines on the screen and update Nextscreen * 'end' is the line after the scrolled part. Normally it is Rows. * When scrolling region used 'off' is the offset from the top for the region. * 'row' and 'end' are relative to the start of the region. * * Return OK for success, FAIL if the lines are not deleted. */ int screen_del_lines(off, row, nlines, end) int off; int row; int nlines; int end; { int j; int i; char_u *temp; int cursor_row; int cursor_end; if (T_CSC != NULL && *T_CSC != NUL) /* cursor relative to region */ { cursor_row = row; cursor_end = end; } else { cursor_row = row + off; cursor_end = end + off; } screenalloc(TRUE); /* allocate screen buffers if size changed */ if (Nextscreen == NULL) return FAIL; if (nlines <= 0 || ((T_DL == NULL || *T_DL == NUL) && (T_CDL == NULL || *T_CDL == NUL) && row != 0)) return FAIL; /* delete the lines */ if (T_CDL && *T_CDL) { windgoto(cursor_row, 0); if (nlines == 1 && T_DL && *T_DL) outstr(T_DL); else OUTSTR(tgoto((char *)T_CDL, 0, nlines)); } else { if (row == 0) { windgoto(cursor_end - 1, 0); for (i = 0; i < nlines; i++) outchar('\n'); } else { for (i = 0; i < nlines; i++) { windgoto(cursor_row, 0); outstr(T_DL); /* delete a line */ } } } /* * Now shift LinePointers nlines up to reflect the deleted lines. * Clear the deleted lines. */ row += off; end += off; for (i = 0; i < nlines; ++i) { j = row + i; temp = LinePointers[j]; while ((j += nlines) <= end - 1) LinePointers[j - nlines] = LinePointers[j]; LinePointers[j - nlines] = temp; memset((char *)temp, ' ', (size_t)Columns); } return OK; } /* * show the current mode and ruler * * If clear_cmdline is TRUE, clear it first. * If clear_cmdline is FALSE there may be a message there that needs to be * cleared only if a mode is shown. */ void showmode() { int did_clear = clear_cmdline; int need_clear = FALSE; if ((p_smd && (State & INSERT)) || Recording) { gotocmdline(clear_cmdline, NUL); if (p_smd) { if (State & INSERT) { msg_outstr((char_u *)"-- "); if (p_ri) msg_outstr((char_u *)"REVERSE "); if (State == INSERT) msg_outstr((char_u *)"INSERT --"); else msg_outstr((char_u *)"REPLACE --"); need_clear = TRUE; } } if (Recording) { msg_outstr((char_u *)"recording"); need_clear = TRUE; } if (need_clear && !did_clear) msg_ceol(); } win_redr_ruler(lastwin, TRUE); redraw_cmdline = FALSE; } /* * 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; { win_redr_ruler(curwin, always); } void win_redr_ruler(wp, always) WIN *wp; int always; { static linenr_t oldlnum = 0; static colnr_t oldcol = 0; char_u buffer[30]; int row; int fillchar; if (p_ru && (redraw_cmdline || always || wp->w_cursor.lnum != oldlnum || wp->w_virtcol != oldcol)) { cursor_off(); if (wp->w_status_height) { row = wp->w_winpos + wp->w_height; if (set_highlight('s') == OK) /* can use highlighting */ { fillchar = ' '; start_highlight(); } else fillchar = '='; } else { row = Rows - 1; fillchar = ' '; } /* * Some sprintfs return the lenght, some return a pointer. * To avoid portability problems we use strlen here. */ sprintf((char *)buffer, "%ld,%d", wp->w_cursor.lnum, (int)wp->w_cursor.col + 1); if (wp->w_cursor.col != wp->w_virtcol) sprintf((char *)buffer + STRLEN(buffer), "-%d", wp->w_virtcol + 1); screen_start(); /* init cursor position */ screen_msg(buffer, row, ru_col); screen_fill(row, row + 1, ru_col + (int)STRLEN(buffer), (int)Columns, fillchar, fillchar); oldlnum = wp->w_cursor.lnum; oldcol = wp->w_virtcol; stop_highlight(); } } /* * screen_valid: Returns TRUE if there is a valid screen to write to. * Returns FALSE when starting up and screen not initialized yet. * Used by msg() to decide to use either screen_msg() or printf(). */ int screen_valid() { screenalloc(FALSE); /* allocate screen buffers if size changed */ return (Nextscreen != NULL); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.