File: [local] / src / usr.bin / vim / Attic / screen.c (download)
Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:24 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42 Changes since 1.1: +0 -0 lines
Initial import of vim 4.2.
This is meant to replace nvi in the tree. Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.
On the other hand, vim lacks a non-visual ex mode, in addition to open mode.
This includes the GUI (X11) code, but doesn't try to compile it.
|
/* $OpenBSD: screen.c,v 1.1.1.1 1996/09/07 21:40:24 downsj Exp $ */
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* screen.c: code for displaying on the screen
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#include "ops.h" /* For op_inclusive */
char *tgoto __PARMS((char *cm, int col, int line));
static int canopt; /* TRUE when cursor goto can be optimized */
static int attributes = 0; /* current attributes for screen character*/
static int highlight_attr = 0; /* attributes when highlighting on */
#ifdef RIGHTLEFT
static int rightleft = 0; /* set to 1 for right to left in screen_fill */
#endif
static int win_line __ARGS((WIN *, linenr_t, int, int));
static void comp_Botline_sub __ARGS((WIN *wp, linenr_t lnum, int done));
static void screen_char __ARGS((char_u *, int, int));
static void screenclear2 __ARGS((void));
static void lineclear __ARGS((char_u *p));
static int screen_ins_lines __ARGS((int, int, int, int));
/*
* updateline() - like updateScreen() but only for cursor line
*
* Check if the size of the cursor line has changed. If it did change, lines
* below the cursor will move up or down and we need to call the routine
* updateScreen() to examine the entire screen.
*/
void
updateline()
{
int row;
int n;
if (!screen_valid(TRUE))
return;
if (must_redraw) /* must redraw whole screen */
{
updateScreen(must_redraw);
return;
}
if (RedrawingDisabled)
{
must_redraw = NOT_VALID; /* remember to update later */
return;
}
cursor_off();
(void)set_highlight('v');
row = win_line(curwin, curwin->w_cursor.lnum,
curwin->w_cline_row, curwin->w_height);
if (row == curwin->w_height + 1) /* line too long for window */
{
/* window needs to be scrolled up to show the cursor line */
if (curwin->w_topline < curwin->w_cursor.lnum)
++curwin->w_topline;
updateScreen(VALID_TO_CURSCHAR);
cursupdate();
}
else if (!dollar_vcol)
{
n = row - curwin->w_cline_row;
if (n != curwin->w_cline_height) /* line changed size */
{
if (n < curwin->w_cline_height) /* got smaller: delete lines */
win_del_lines(curwin, row,
curwin->w_cline_height - n, FALSE, TRUE);
else /* got bigger: insert lines */
win_ins_lines(curwin,
curwin->w_cline_row + curwin->w_cline_height,
n - curwin->w_cline_height, FALSE, TRUE);
updateScreen(VALID_TO_CURSCHAR);
}
else if (clear_cmdline || redraw_cmdline)
showmode(); /* clear cmdline, show mode and ruler */
}
}
/*
* update all windows that are editing the current buffer
*/
void
update_curbuf(type)
int type;
{
WIN *wp;
for (wp = firstwin; wp; wp = wp->w_next)
if (wp->w_buffer == curbuf && wp->w_redr_type < type)
wp->w_redr_type = type;
updateScreen(type);
}
/*
* 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;
if (!screen_valid(TRUE))
return;
dollar_vcol = 0;
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 < NOT_VALID)
type = NOT_VALID;
if (RedrawingDisabled)
{
must_redraw = type; /* remember type for next time */
curwin->w_redr_type = type;
curwin->w_lsize_valid = 0; /* don't use w_lsize[] now */
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;
need_wait_return = FALSE;
}
/*
* 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) /* first clear cmdline */
{
if (emsg_on_display)
{
mch_delay(1000L, TRUE);
emsg_on_display = FALSE;
}
msg_row = cmdline_row;
msg_col = 0;
msg_clr_eos(); /* will reset clear_cmdline */
}
/* return if there is nothing to do */
if (!((type == VALID && curwin->w_topline == curwin->w_lsize_lnum[0]) ||
(type == INVERTED &&
curwin->w_old_cursor_lnum == curwin->w_cursor.lnum &&
curwin->w_old_cursor_vcol == curwin->w_virtcol &&
curwin->w_old_curswant == curwin->w_curswant)))
{
/*
* go from top to bottom through the windows, redrawing the ones that
* need it
*/
curwin->w_redr_type = type;
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();
}
#ifdef USE_GUI
/*
* Update a single window, its status line and maybe the command line msg.
* Used for the GUI scrollbar.
*/
void
updateWindow(wp)
WIN *wp;
{
win_update(wp);
if (wp->w_redr_status)
win_redr_status(wp);
if (redraw_cmdline)
showmode();
}
#endif
/*
* 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)
screenclear(); /* far off: clearing the screen is faster */
}
else if (lastwin == firstwin)
screenclear(); /* far off: clearing the screen is faster */
}
else /* may scroll up */
{
j = -1;
/* try to find wp->w_topline in wp->w_lsize_lnum[] */
for (i = 0; i < wp->w_lsize_valid; i++)
{
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;
if (VIsual_active) /* check if we are updating the inverted part */
{
linenr_t from, to;
/* find the line numbers that need to be updated */
if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum)
{
from = curwin->w_cursor.lnum;
to = wp->w_old_cursor_lnum;
}
else
{
from = wp->w_old_cursor_lnum;
to = curwin->w_cursor.lnum;
}
/* if VIsual changed, update the maximal area */
if (VIsual.lnum != wp->w_old_visual_lnum)
{
if (wp->w_old_visual_lnum < from)
from = wp->w_old_visual_lnum;
if (wp->w_old_visual_lnum > to)
to = wp->w_old_visual_lnum;
if (VIsual.lnum < from)
from = VIsual.lnum;
if (VIsual.lnum > to)
to = VIsual.lnum;
}
/* if in block mode and changed column or wp->w_curswant: update all
* lines */
if (VIsual_mode == Ctrl('V') &&
(curwin->w_virtcol != wp->w_old_cursor_vcol ||
wp->w_curswant != wp->w_old_curswant))
{
if (from > VIsual.lnum)
from = VIsual.lnum;
if (to < VIsual.lnum)
to = VIsual.lnum;
}
if (from < wp->w_topline)
from = wp->w_topline;
if (from >= wp->w_botline)
from = wp->w_botline - 1;
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];
}
}
/* if we update the lines between from and to set old_cursor */
if (type == INVERTED || (lnum <= from &&
(endrow == wp->w_height || lastline >= to)))
{
wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
wp->w_old_cursor_vcol = curwin->w_virtcol;
wp->w_old_visual_lnum = VIsual.lnum;
wp->w_old_curswant = wp->w_curswant;
}
}
else
{
wp->w_old_cursor_lnum = 0;
wp->w_old_visual_lnum = 0;
}
(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)
{
done = TRUE; /* hit the end of the file */
break;
}
srow = row;
row = win_line(wp, lnum, srow, endrow);
if (row > endrow) /* past end of screen */
{ /* we may need the size of that */
wp->w_lsize[idx] = plines_win(wp, lnum);
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.
*/
#ifdef RIGHTLEFT
if (wp->w_p_rl)
rightleft = 1;
#endif
screen_fill(wp->w_winpos + srow,
wp->w_winpos + wp->w_height, 0, (int)Columns, '@', ' ');
#ifdef RIGHTLEFT
rightleft = 0;
#endif
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. */
#ifdef RIGHTLEFT
if (wp->w_p_rl)
rightleft = 1;
#endif
screen_fill(wp->w_winpos + row,
wp->w_winpos + wp->w_height, 0, (int)Columns, '~', ' ');
#ifdef RIGHTLEFT
rightleft = 0;
#endif
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;
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 = '=';
p = wp->w_buffer->b_xfilename;
if (p == NULL)
STRCPY(NameBuff, "[No File]");
else
{
home_replace(wp->w_buffer, p, NameBuff, MAXPATHL);
trans_characters(NameBuff, MAXPATHL);
}
p = NameBuff;
len = STRLEN(p);
if (wp->w_buffer->b_help || wp->w_buffer->b_changed ||
wp->w_buffer->b_p_ro)
*(p + len++) = ' ';
if (wp->w_buffer->b_help)
{
STRCPY(p + len, "[help]");
len += 6;
}
if (wp->w_buffer->b_changed)
{
STRCPY(p + len, "[+]");
len += 3;
}
if (wp->w_buffer->b_p_ro)
{
STRCPY(p + len, "[RO]");
len += 4;
}
if (len > ru_col - 1)
{
p += len - (ru_col - 1);
*p = '<';
len = ru_col - 1;
}
row = wp->w_winpos + wp->w_height;
screen_msg(p, row, 0);
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, excl w_winpos */
int screen_row; /* row on the screen, incl w_winpos */
char_u *ptr;
char_u extra[16]; /* "%ld" must fit in here */
char_u *p_extra;
char_u *showbreak = NULL;
int n_extra;
int n_spaces = 0;
int fromcol, tocol; /* start/end of inverting */
int noinvcur = FALSE; /* don't invert the cursor */
FPOS *top, *bot;
if (startrow > endrow) /* past the end already! */
return startrow;
row = startrow;
screen_row = row + wp->w_winpos;
col = 0;
vcol = 0;
fromcol = -10;
tocol = MAXCOL;
canopt = TRUE;
/*
* handle visual active in this window
*/
if (VIsual_active && wp->w_buffer == curwin->w_buffer)
{
/* Visual is after curwin->w_cursor */
if (ltoreq(curwin->w_cursor, VIsual))
{
top = &curwin->w_cursor;
bot = &VIsual;
}
else /* Visual is before curwin->w_cursor */
{
top = &VIsual;
bot = &curwin->w_cursor;
}
if (VIsual_mode == Ctrl('V')) /* block mode */
{
if (lnum >= top->lnum && lnum <= bot->lnum)
{
colnr_t from, to;
getvcol(wp, top, (colnr_t *)&fromcol, NULL, (colnr_t *)&tocol);
getvcol(wp, bot, &from, NULL, &to);
if ((int)from < fromcol)
fromcol = from;
if ((int)to > tocol)
tocol = to;
++tocol;
if (wp->w_curswant == MAXCOL)
tocol = MAXCOL;
}
}
else /* non-block mode */
{
if (lnum > top->lnum && lnum <= bot->lnum)
fromcol = 0;
else if (lnum == top->lnum)
getvcol(wp, top, (colnr_t *)&fromcol, NULL, NULL);
if (lnum == bot->lnum)
{
getvcol(wp, bot, NULL, NULL, (colnr_t *)&tocol);
++tocol;
}
if (VIsual_mode == 'V') /* linewise */
{
if (fromcol > 0)
fromcol = 0;
tocol = MAXCOL;
}
}
/* if the cursor can't be switched off, don't invert the
* character where the cursor is */
#ifndef MSDOS
if (!highlight_match && *T_VI == NUL &&
lnum == curwin->w_cursor.lnum && wp == curwin)
noinvcur = TRUE;
#endif
if (tocol <= (int)wp->w_leftcol) /* inverting is left of screen */
fromcol = 0;
/* start of invert is left of screen */
else if (fromcol >= 0 && fromcol < (int)wp->w_leftcol)
fromcol = wp->w_leftcol;
/* if inverting in this line, can't optimize cursor positioning */
if (fromcol >= 0)
canopt = FALSE;
}
/*
* handle incremental search position highlighting
*/
else if (highlight_match && wp == curwin && search_match_len)
{
if (lnum == curwin->w_cursor.lnum)
{
getvcol(curwin, &(curwin->w_cursor),
(colnr_t *)&fromcol, NULL, NULL);
curwin->w_cursor.col += search_match_len;
getvcol(curwin, &(curwin->w_cursor),
(colnr_t *)&tocol, NULL, NULL);
curwin->w_cursor.col -= search_match_len;
canopt = FALSE;
if (fromcol == tocol) /* do at least one character */
tocol = fromcol + 1; /* happens when past end of line */
}
}
ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
if (!wp->w_p_wrap) /* advance to first character to be displayed */
{
while ((colnr_t)vcol < wp->w_leftcol && *ptr)
vcol += win_chartabsize(wp, *ptr++, (colnr_t)vcol);
if ((colnr_t)vcol > wp->w_leftcol)
{
n_spaces = vcol - wp->w_leftcol; /* begin with some spaces */
vcol = wp->w_leftcol;
}
}
screenp = LinePointers[screen_row];
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
col = Columns - 1; /* col follows screenp here */
screenp += Columns - 1;
}
#endif
if (wp->w_p_nu)
{
#ifdef RIGHTLEFT
if (wp->w_p_rl) /* reverse line numbers */
{
char_u *c1, *c2, t;
sprintf((char *)extra, " %-7ld", (long)lnum);
for (c1 = extra, c2 = extra + STRLEN(extra) - 1; c1 < c2;
c1++, c2--)
{
t = *c1;
*c1 = *c2;
*c2 = t;
}
}
else
#endif
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 or match highlighting in this line */
{
if (((vcol == fromcol && !(noinvcur &&
(colnr_t)vcol == wp->w_virtcol)) ||
(noinvcur && (colnr_t)vcol == wp->w_virtcol + 1 &&
vcol >= fromcol)) && vcol < tocol)
start_highlight(); /* start highlighting */
else if (attributes && (vcol == tocol ||
(noinvcur && (colnr_t)vcol == wp->w_virtcol)))
stop_highlight(); /* stop highlighting */
}
/* Get the next character to put on the screen. */
/*
* if 'showbreak' is set it contains the characters to put at the
* start of each broken line
*/
if (
#ifdef RIGHTLEFT
(wp->w_p_rl ? col == -1 : col == Columns)
#else
col == Columns
#endif
&& (*ptr != NUL || (wp->w_p_list && n_extra == 0) ||
(n_extra && *p_extra) || n_spaces) &&
vcol != 0 && STRLEN(p_sbr) != 0)
showbreak = p_sbr;
if (showbreak != NULL)
{
c = *showbreak++;
if (*showbreak == NUL)
showbreak = NULL;
}
/*
* The 'extra' array contains the extra stuff that is inserted to
* represent special characters (non-printable stuff).
*/
else if (n_extra)
{
c = *p_extra++;
n_extra--;
}
else if (n_spaces)
{
c = ' ';
n_spaces--;
}
else
{
c = *ptr++;
/*
* Found last space before word: check for line break
*/
if (wp->w_p_lbr && isbreak(c) && !isbreak(*ptr) && !wp->w_p_list)
{
n_spaces = win_lbr_chartabsize(wp, ptr - 1,
(colnr_t)vcol, NULL) - 1;
if (vim_iswhite(c))
c = ' ';
}
else if (!isprintchar(c))
{
/*
* 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 = '$';
--ptr; /* put it back at the NUL */
}
else if (c != NUL)
{
p_extra = transchar(c);
n_extra = charsize(c) - 1;
c = *p_extra++;
}
}
}
if (c == NUL)
{
if (attributes)
{
/* invert at least one char, used for Visual and empty line or
* highlight match at end of line. If it's beyond the last
* char on the screen, just overwrite that one (tricky!) */
if (vcol == fromcol)
{
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
if (col < 0)
{
++screenp;
++col;
}
}
else
#endif
{
if (col >= Columns)
{
--screenp;
--col;
}
}
if (*screenp != ' ' || *(screenp + Columns) != attributes)
{
*screenp = ' ';
*(screenp + Columns) = attributes;
screen_char(screenp, screen_row, col);
}
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
--screenp;
--col;
}
else
#endif
{
++screenp;
++col;
}
}
stop_highlight();
}
/*
* blank out the rest of this row, if necessary
*/
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
while (col >= 0 && *screenp == ' ' &&
*(screenp + Columns) == 0)
{
--screenp;
--col;
}
if (col >= 0)
screen_fill(screen_row, screen_row + 1,
0, col + 1, ' ', ' ');
}
else
#endif
{
while (col < Columns && *screenp == ' ' &&
*(screenp + Columns) == 0)
{
++screenp;
++col;
}
if (col < Columns)
screen_fill(screen_row, screen_row + 1,
col, (int)Columns, ' ', ' ');
}
row++;
break;
}
if (
#ifdef RIGHTLEFT
wp->w_p_rl ? (col < 0) :
#endif
(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];
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
col = Columns - 1; /* col is not used if breaking! */
screenp += Columns - 1;
}
#endif
}
/*
* Store the character in NextScreen.
*/
if (*screenp != c || *(screenp + Columns) != attributes)
{
/*
* Special trick to make copy/paste of wrapped lines work with
* xterm/screen:
* If the first column is to be written, write the preceding
* char twice. This will work with all terminal types
* (regardless of the xn,am settings).
* Only do this on a fast tty.
*/
if (p_tf && row > startrow && col == 0 &&
LinePointers[screen_row - 1][Columns - 1 + Columns] ==
attributes)
{
if (screen_cur_row != screen_row - 1 ||
screen_cur_col != Columns)
screen_char(LinePointers[screen_row - 1] + Columns - 1,
screen_row - 1, (int)(Columns - 1));
screen_char(LinePointers[screen_row - 1] + Columns - 1,
screen_row - 1, (int)Columns);
screen_start();
}
*screenp = c;
*(screenp + Columns) = attributes;
screen_char(screenp, screen_row, col);
}
#ifdef RIGHTLEFT
if (wp->w_p_rl)
{
--screenp;
--col;
}
else
#endif
{
++screenp;
++col;
}
++vcol;
/* stop before '$' of change command */
if (wp == curwin && dollar_vcol && vcol >= (long)wp->w_virtcol)
break;
}
stop_highlight();
return (row);
}
/*
* Called when p_dollar is set: display a '$' at the end of the changed text
* Only works when cursor is in the line that changes.
*/
void
display_dollar(col)
colnr_t col;
{
colnr_t save_col;
if (RedrawingDisabled)
return;
cursor_off();
save_col = curwin->w_cursor.col;
curwin->w_cursor.col = col;
curs_columns(FALSE);
if (!curwin->w_p_wrap)
curwin->w_col -= curwin->w_leftcol;
if (curwin->w_col < Columns)
{
screen_msg((char_u *)"$", curwin->w_winpos + curwin->w_row,
#ifdef RIGHTLEFT
curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
#endif
curwin->w_col);
dollar_vcol = curwin->w_virtcol;
}
curwin->w_cursor.col = save_col;
}
/*
* Call this function before moving the cursor from the normal insert position
* in insert mode.
*/
void
undisplay_dollar()
{
if (dollar_vcol)
{
dollar_vcol = 0;
updateline();
}
}
/*
* output a single character directly to the screen
* update NextScreen
*/
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 '*text' on the screen at position 'row' and 'col'
* update NextScreen
* Note: only outputs within one row, message is truncated at screen boundary!
* Note: if NextScreen, row and/or col is invalid, nothing is done.
*/
void
screen_msg(text, row, col)
char_u *text;
int row;
int col;
{
char_u *screenp;
if (NextScreen != NULL && row < Rows) /* safety check */
{
screenp = LinePointers[row] + col;
while (*text && col < Columns)
{
if (*screenp != *text || *(screenp + Columns) != attributes)
{
*screenp = *text;
*(screenp + Columns) = attributes;
screen_char(screenp, row, col);
}
++screenp;
++col;
++text;
}
}
}
/*
* Reset cursor position. Use whenever cursor was moved because of outputting
* something directly to the screen (shell commands) or a terminal control
* code.
*/
void
screen_start()
{
screen_cur_row = screen_cur_col = 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 i;
int mode;
char_u *p;
/*
* Try to find the mode in the 'highlight' option.
* If not found, try the default for the 'highlight' option.
* If still not found, use 'r' (should not happen).
*/
mode = 'r';
for (i = 0; i < 2; ++i)
{
if (i)
p = get_highlight_default();
else
p = p_hl;
if (p == NULL)
continue;
while (*p)
{
if (*p == context) /* found what we are looking for */
break;
while (*p && *p != ',') /* skip to comma */
++p;
p = skip_to_option_part(p); /* skip comma and spaces */
}
if (p[0] && p[1])
{
mode = p[1];
break;
}
}
switch (mode)
{
case 'b': highlight = T_MD; /* bold */
unhighlight = T_ME;
highlight_attr = CHAR_BOLD;
break;
case 's': highlight = T_SO; /* standout */
unhighlight = T_SE;
highlight_attr = CHAR_STDOUT;
break;
case 'n': highlight = NULL; /* no highlighting */
unhighlight = NULL;
highlight_attr = 0;
break;
case 'u': highlight = T_US; /* underline */
unhighlight = T_UE;
highlight_attr = CHAR_UNDERL;
break;
case 'i': highlight = T_CZH; /* italic */
unhighlight = T_CZR;
highlight_attr = CHAR_ITALIC;
break;
default: highlight = T_MR; /* reverse (invert) */
unhighlight = T_ME;
highlight_attr = CHAR_INVERT;
break;
}
if (highlight == NULL || *highlight == NUL ||
unhighlight == NULL || *unhighlight == NUL)
{
highlight = NULL;
return FAIL;
}
return OK;
}
void
start_highlight()
{
if (full_screen &&
#ifdef WIN32
termcap_active &&
#endif
highlight != NULL)
{
outstr(highlight);
attributes = highlight_attr;
}
}
void
stop_highlight()
{
if (attributes)
{
outstr(unhighlight);
attributes = 0;
}
}
/*
* variables used for one level depth of highlighting
* Used for "-- More --" message.
*/
static char_u *old_highlight = NULL;
static char_u *old_unhighlight = NULL;
static int old_highlight_attr = 0;
void
remember_highlight()
{
old_highlight = highlight;
old_unhighlight = unhighlight;
old_highlight_attr = highlight_attr;
}
void
recover_old_highlight()
{
highlight = old_highlight;
unhighlight = old_unhighlight;
highlight_attr = old_highlight_attr;
}
/*
* 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 (col == Columns - 1 && row == Rows - 1)
return;
if (screen_cur_col != col || screen_cur_row != row)
{
/* check if no cursor movement is allowed in standout mode */
if (attributes && !p_wiv && *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 switching between inverting and not
* inverting.
*/
if (screen_cur_row == row && screen_cur_col < col)
{
register int i;
i = col - screen_cur_col;
if (i <= 4 + noinvcurs)
{
/* stop at the first character that has different attributes
* from the ones that are active */
while (i && *(p - i + Columns) == attributes)
{
c = *(p - i--);
outchar(c);
}
}
if (i)
{
if (noinvcurs)
stop_highlight();
if (*T_CRI != NUL) /* use tgoto interface! jw */
OUTSTR(tgoto((char *)T_CRI, 0, i));
else
windgoto(row, col);
if (noinvcurs)
start_highlight();
}
}
/*
* If the cursor is at the line above where we want to be, use CR LF,
* this is quicker than windgoto().
* Don't do this if the cursor went beyond the last column, the cursor
* position is unknown then (some terminals wrap, some don't )
*/
else if (screen_cur_row + 1 == row && col == 0 &&
screen_cur_col < Columns)
{
if (noinvcurs)
stop_highlight();
outchar('\n');
if (noinvcurs)
start_highlight();
}
else
{
if (noinvcurs)
stop_highlight();
windgoto(row, col);
if (noinvcurs)
start_highlight();
}
screen_cur_row = row;
screen_cur_col = col;
}
/*
* For weird invert mechanism: output (un)highlight before every char
* Lots of extra output, but works.
*/
if (p_wiv)
{
if (attributes)
outstr(highlight);
else if (full_screen)
outstr(unhighlight);
}
outchar(*p);
screen_cur_col++;
}
/*
* 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;
char_u *attrp;
int did_delete;
int c;
if (end_row > Rows) /* safety check */
end_row = Rows;
if (end_col > Columns) /* safety check */
end_col = Columns;
if (NextScreen == NULL ||
start_row >= end_row || start_col >= end_col) /* nothing to do */
return;
for (row = start_row; row < end_row; ++row)
{
/* try to use delete-line termcap code */
did_delete = FALSE;
if (attributes == 0 && c2 == ' ' && end_col == Columns && *T_CE != NUL
#ifdef RIGHTLEFT
&& !rightleft
#endif
)
{
/*
* 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;
}
/* skip blanks (used often, keep it fast!) */
attrp = screenp + Columns;
while (col < end_col && *screenp == ' ' && *attrp == 0)
{
++col;
++screenp;
++attrp;
}
if (col < end_col) /* something to be cleared */
{
windgoto(row, col); /* clear rest of this screen line */
outstr(T_CE);
screen_start(); /* don't know where cursor is now */
col = end_col - col;
while (col--) /* clear chars in NextScreen */
{
*attrp++ = 0;
*screenp++ = ' ';
}
}
did_delete = TRUE; /* the chars are cleared now */
}
screenp = LinePointers[row] +
#ifdef RIGHTLEFT
(rightleft ? (int)Columns - 1 - start_col : start_col);
#else
start_col;
#endif
c = c1;
for (col = start_col; col < end_col; ++col)
{
if (*screenp != c || *(screenp + Columns) != attributes)
{
*screenp = c;
*(screenp + Columns) = attributes;
if (!did_delete || c != ' ')
screen_char(screenp, row,
#ifdef RIGHTLEFT
rightleft ? Columns - 1 - col :
#endif
col);
}
#ifdef RIGHTLEFT
if (rightleft)
--screenp;
else
#endif
++screenp;
if (col == start_col)
{
if (did_delete)
break;
c = c2;
}
}
if (row == Rows - 1) /* overwritten the command line */
{
redraw_cmdline = TRUE;
if (c1 == ' ' && c2 == ' ')
clear_cmdline = FALSE; /* command line has been cleared */
}
}
}
/*
* 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;
{
comp_Botline_sub(wp, wp->w_topline, 0);
}
/*
* Compute wp->w_botline, may have a start at the cursor position.
* Code shared between comp_Botline() and cursupdate().
*/
static void
comp_Botline_sub(wp, lnum, done)
WIN *wp;
linenr_t lnum;
int done;
{
int n;
for ( ; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
{
n = plines_win(wp, lnum);
if (done + n > wp->w_height)
break;
done += n;
}
/* wp->w_botline is the line that is just below the window */
wp->w_botline = lnum;
/* Also set wp->w_empty_rows, otherwise scroll_cursor_bot() won't work */
if (done == 0)
wp->w_empty_rows = 0; /* single line that doesn't fit */
else
wp->w_empty_rows = wp->w_height - done;
}
void
screenalloc(clear)
int clear;
{
register int new_row, old_row;
WIN *wp;
int outofmem = FALSE;
int len;
char_u *new_NextScreen;
char_u **new_LinePointers;
/*
* Allocation of the screen buffers is done only when the size changes
* and when Rows and Columns have been set and we are doing full screen
* stuff.
*/
if ((NextScreen != NULL && Rows == screen_Rows && Columns == screen_Columns)
|| Rows == 0 || Columns == 0 || !full_screen)
return;
comp_col(); /* recompute columns for shown command and ruler */
/*
* We're changing the size of the screen.
* - Allocate new arrays for NextScreen.
* - Move lines from the old arrays into the new arrays, clear extra
* lines (unless the screen is going to be cleared).
* - Free the old arrays.
*/
for (wp = firstwin; wp; wp = wp->w_next)
win_free_lsize(wp);
new_NextScreen = (char_u *)malloc((size_t) (Rows * Columns * 2));
new_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 (new_NextScreen == NULL || new_LinePointers == NULL || outofmem)
{
do_outofmem_msg();
vim_free(new_NextScreen);
new_NextScreen = NULL;
}
else
{
for (new_row = 0; new_row < Rows; ++new_row)
{
new_LinePointers[new_row] = new_NextScreen + new_row * Columns * 2;
/*
* If the screen is not going to be cleared, copy as much as
* possible from the old screen to the new one and clear the rest
* (used when resizing the window at the "--more--" prompt or when
* executing an external command, for the GUI).
*/
if (!clear)
{
lineclear(new_LinePointers[new_row]);
old_row = new_row + (screen_Rows - Rows);
if (old_row >= 0)
{
if (screen_Columns < Columns)
len = screen_Columns;
else
len = Columns;
vim_memmove(new_LinePointers[new_row],
LinePointers[old_row], (size_t)len);
vim_memmove(new_LinePointers[new_row] + Columns,
LinePointers[old_row] + screen_Columns, (size_t)len);
}
}
}
}
vim_free(NextScreen);
vim_free(LinePointers);
NextScreen = new_NextScreen;
LinePointers = new_LinePointers;
must_redraw = CLEAR; /* need to clear the screen later */
if (clear)
screenclear2();
#ifdef USE_GUI
else if (gui.in_use && NextScreen != NULL && Rows != screen_Rows)
{
gui_redraw_block(0, 0, Rows - 1, Columns - 1);
/*
* Adjust the position of the cursor, for when executing an external
* command.
*/
if (msg_row >= Rows) /* Rows got smaller */
msg_row = Rows - 1; /* put cursor at last row */
else if (Rows > screen_Rows) /* Rows got bigger */
msg_row += Rows - screen_Rows; /* put cursor in same place */
if (msg_col >= Columns) /* Columns got smaller */
msg_col = Columns - 1; /* put cursor at last column */
}
#endif
screen_Rows = Rows;
screen_Columns = Columns;
}
void
screenclear()
{
if (emsg_on_display)
{
mch_delay(1000L, TRUE);
emsg_on_display = FALSE;
}
screenalloc(FALSE); /* allocate screen buffers if size changed */
screenclear2(); /* clear the screen */
}
static void
screenclear2()
{
int i;
if (starting || NextScreen == NULL)
return;
outstr(T_CL); /* clear the display */
/* blank out NextScreen */
for (i = 0; i < Rows; ++i)
lineclear(LinePointers[i]);
screen_cleared = TRUE; /* can use contents of NextScreen now */
win_rest_invalid(firstwin);
clear_cmdline = FALSE;
redraw_cmdline = TRUE;
if (must_redraw == CLEAR) /* no need to clear again */
must_redraw = NOT_VALID;
compute_cmdrow();
msg_pos((int)Rows - 1, 0); /* put cursor on last line for messages */
screen_start(); /* don't know where cursor is now */
msg_scrolled = 0; /* can't scroll back */
msg_didany = FALSE;
msg_didout = FALSE;
}
/*
* Clear one line in NextScreen.
*/
static void
lineclear(p)
char_u *p;
{
(void)vim_memset(p, ' ', (size_t)Columns);
(void)vim_memset(p + Columns, 0, (size_t)Columns);
}
/*
* 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 lnum;
long line_count;
int sline;
int done;
int temp;
if (!screen_valid(TRUE))
return;
/*
* Make sure the cursor is on a valid line number
*/
check_cursor();
/*
* If the buffer is empty, always set topline to 1.
*/
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;
}
/*
* If the cursor is above the top of the window, scroll the window to put
* it at the top of the window.
* If we weren't very close to begin with, we scroll to put the cursor in
* the middle of the window.
*/
else if (curwin->w_cursor.lnum < curwin->w_topline + p_so &&
curwin->w_topline > 1)
{
temp = curwin->w_height / 2 - 1;
if (temp < 2)
temp = 2;
/* not very close, put cursor halfway screen */
if (curwin->w_topline + p_so - curwin->w_cursor.lnum >= temp)
scroll_cursor_halfway(FALSE);
else
scroll_cursor_top((int)p_sj, FALSE);
updateScreen(VALID);
}
/*
* If the cursor is below the bottom of the window, scroll the window
* to put the cursor on the window. If the cursor is less than a
* windowheight 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.
* Note: After curwin->w_botline was computed lines may have been
* added or deleted, it may be greater than ml_line_count.
*/
else if ((long)curwin->w_cursor.lnum >= (long)curwin->w_botline - p_so &&
curwin->w_botline <= curbuf->b_ml.ml_line_count)
{
line_count = curwin->w_cursor.lnum - curwin->w_botline + 1 + p_so;
if (line_count <= curwin->w_height + 1)
scroll_cursor_bot((int)p_sj, FALSE);
else
scroll_cursor_halfway(FALSE);
updateScreen(VALID);
}
/*
* If the window contents is unknown, need to update the screen.
*/
else if (curwin->w_lsize_valid == 0)
updateScreen(NOT_VALID);
/*
* Figure out the row number of the cursor line.
* This is used often, keep it fast!
*/
curwin->w_row = sline = 0;
/* curwin->w_lsize[] invalid */
if (RedrawingDisabled || curwin->w_lsize_valid == 0)
{
done = 0;
for (lnum = curwin->w_topline; lnum != curwin->w_cursor.lnum; ++lnum)
done += plines(lnum);
curwin->w_row = done;
/*
* Also need to compute w_botline and w_empty_rows, because
* updateScreen() will not have done that.
*/
comp_Botline_sub(curwin, lnum, done);
}
else
{
for (done = curwin->w_cursor.lnum - curwin->w_topline; done > 0; --done)
curwin->w_row += curwin->w_lsize[sline++];
}
curwin->w_cline_row = curwin->w_row;
curwin->w_col = curwin->w_virtcol = 0;
if (!RedrawingDisabled && sline > curwin->w_lsize_valid)
/* Should only happen with a line that is too */
/* long to fit on the last screen line. */
curwin->w_cline_height = 0;
else
{
/* curwin->w_lsize[] invalid */
if (RedrawingDisabled || curwin->w_lsize_valid == 0)
curwin->w_cline_height = plines(curwin->w_cursor.lnum);
else
curwin->w_cline_height = curwin->w_lsize[sline];
/* compute curwin->w_virtcol and curwin->w_col */
curs_columns(!RedrawingDisabled);
if (must_redraw)
updateScreen(must_redraw);
}
if (curwin->w_set_curswant)
{
curwin->w_curswant = curwin->w_virtcol;
curwin->w_set_curswant = FALSE;
}
}
/*
* Recompute topline to put the cursor at the top of the window.
* Scroll at least "min_scroll" lines.
* If "always" is TRUE, always set topline (for "zt").
*/
void
scroll_cursor_top(min_scroll, always)
int min_scroll;
int always;
{
int scrolled = 0;
int extra = 0;
int used;
int i;
int sline; /* screen line for cursor */
/*
* Decrease topline until:
* - it has become 1
* - (part of) the cursor line is moved off the screen or
* - moved at least 'scrolljump' lines and
* - at least 'scrolloff' lines above and below the cursor
*/
used = plines(curwin->w_cursor.lnum);
for (sline = 1; sline < curwin->w_cursor.lnum; ++sline)
{
i = plines(curwin->w_cursor.lnum - sline);
used += i;
extra += i;
if (extra <= p_so &&
curwin->w_cursor.lnum + sline < curbuf->b_ml.ml_line_count)
used += plines(curwin->w_cursor.lnum + sline);
if (used > curwin->w_height)
break;
if (curwin->w_cursor.lnum - sline < curwin->w_topline)
scrolled += i;
/*
* If scrolling is needed, scroll at least 'sj' lines.
*/
if ((curwin->w_cursor.lnum - (sline - 1) >= curwin->w_topline ||
scrolled >= min_scroll) && extra > p_so)
break;
}
/*
* If we don't have enough space, put cursor in the middle.
* This makes sure we get the same position when using "k" and "j"
* in a small window.
*/
if (used > curwin->w_height)
scroll_cursor_halfway(FALSE);
else
{
/*
* If "always" is FALSE, only adjust topline to a lower value, higher
* value may happen with wrapping lines
*/
if (curwin->w_cursor.lnum - (sline - 1) < curwin->w_topline || always)
curwin->w_topline = curwin->w_cursor.lnum - (sline - 1);
if (curwin->w_topline > curwin->w_cursor.lnum)
curwin->w_topline = curwin->w_cursor.lnum;
}
}
/*
* Recompute topline to put the cursor at the bottom of the window.
* Scroll at least "min_scroll" lines.
* If "set_topline" is TRUE, set topline and botline first (for "zb").
* This is messy stuff!!!
*/
void
scroll_cursor_bot(min_scroll, set_topline)
int min_scroll;
int set_topline;
{
int used;
int scrolled = 0;
int extra = 0;
int sline; /* screen line for cursor from bottom */
int i;
linenr_t lnum;
linenr_t line_count;
linenr_t old_topline = curwin->w_topline;
linenr_t old_botline = curwin->w_botline;
int old_empty_rows = curwin->w_empty_rows;
linenr_t cln; /* Cursor Line Number */
cln = curwin->w_cursor.lnum;
if (set_topline)
{
used = 0;
curwin->w_botline = cln + 1;
for (curwin->w_topline = curwin->w_botline;
curwin->w_topline != 1;
--curwin->w_topline)
{
i = plines(curwin->w_topline - 1);
if (used + i > curwin->w_height)
break;
used += i;
}
curwin->w_empty_rows = curwin->w_height - used;
}
used = plines(cln);
if (cln >= curwin->w_botline)
{
scrolled = used;
if (cln == curwin->w_botline)
scrolled -= curwin->w_empty_rows;
}
/*
* Stop counting lines to scroll when
* - hitting start of the file
* - scrolled nothing or at least 'sj' lines
* - at least 'so' lines below the cursor
* - lines between botline and cursor have been counted
*/
for (sline = 1; sline < cln; ++sline)
{
if ((((scrolled <= 0 || scrolled >= min_scroll) && extra >= p_so) ||
cln + sline > curbuf->b_ml.ml_line_count) &&
cln - sline < curwin->w_botline)
break;
i = plines(cln - sline);
used += i;
if (used > curwin->w_height)
break;
if (cln - sline >= curwin->w_botline)
{
scrolled += i;
if (cln - sline == curwin->w_botline)
scrolled -= curwin->w_empty_rows;
}
if (cln + sline <= curbuf->b_ml.ml_line_count)
{
i = plines(cln + sline);
used += i;
if (used > curwin->w_height)
break;
if (extra < p_so || scrolled < min_scroll)
{
extra += i;
if (cln + sline >= curwin->w_botline)
{
scrolled += i;
if (cln + sline == curwin->w_botline)
scrolled -= curwin->w_empty_rows;
}
}
}
}
/* curwin->w_empty_rows is larger, no need to scroll */
if (scrolled <= 0)
line_count = 0;
/* more than a screenfull, don't scroll but redraw */
else if (used > curwin->w_height)
line_count = used;
/* scroll minimal number of lines */
else
{
for (i = 0, lnum = curwin->w_topline;
i < scrolled && lnum < curwin->w_botline; ++lnum)
i += plines(lnum);
if (i >= scrolled) /* it's possible to scroll */
line_count = lnum - curwin->w_topline;
else /* below curwin->w_botline, don't scroll */
line_count = 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 (line_count >= curwin->w_height && line_count > min_scroll)
scroll_cursor_halfway(FALSE);
else
scrollup(line_count);
/*
* If topline didn't change we need to restore w_botline and w_empty_rows
* (we changed them).
* If topline did change, updateScreen() will set botline.
*/
if (curwin->w_topline == old_topline && set_topline)
{
curwin->w_botline = old_botline;
curwin->w_empty_rows = old_empty_rows;
}
}
/*
* Recompute topline to put the cursor halfway the window
* If "atend" is TRUE, also put it halfway at the end of the file.
*/
void
scroll_cursor_halfway(atend)
int atend;
{
int above = 0;
linenr_t topline;
int below = 0;
linenr_t botline;
int used;
int i;
linenr_t cln; /* Cursor Line Number */
topline = botline = cln = curwin->w_cursor.lnum;
used = plines(cln);
while (topline > 1)
{
if (below <= above) /* add a line below the cursor */
{
if (botline + 1 <= curbuf->b_ml.ml_line_count)
{
i = plines(botline + 1);
used += i;
if (used > curwin->w_height)
break;
below += i;
++botline;
}
else
{
++below; /* count a "~" line */
if (atend)
++used;
}
}
if (below > above) /* add a line above the cursor */
{
i = plines(topline - 1);
used += i;
if (used > curwin->w_height)
break;
above += i;
--topline;
}
}
curwin->w_topline = topline;
}
/*
* Correct the cursor position so that it is in a part of the screen at least
* 'so' lines from the top and bottom, if possible.
* If not possible, put it at the same position as scroll_cursor_halfway().
* When called topline and botline must be valid!
*/
void
cursor_correct()
{
int above = 0; /* screen lines above topline */
linenr_t topline;
int below = 0; /* screen lines below botline */
linenr_t botline;
int above_wanted, below_wanted;
linenr_t cln; /* Cursor Line Number */
int max_off;
/*
* How many lines we would like to have above/below the cursor depends on
* whether the first/last line of the file is on screen.
*/
above_wanted = p_so;
below_wanted = p_so;
if (curwin->w_topline == 1)
{
above_wanted = 0;
max_off = curwin->w_height / 2;
if (below_wanted > max_off)
below_wanted = max_off;
}
if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1)
{
below_wanted = 0;
max_off = (curwin->w_height - 1) / 2;
if (above_wanted > max_off)
above_wanted = max_off;
}
/*
* If there are sufficient file-lines above and below the cursor, we can
* return now.
*/
cln = curwin->w_cursor.lnum;
if (cln >= curwin->w_topline + above_wanted &&
cln < curwin->w_botline - below_wanted)
return;
/*
* Narrow down the area where the cursor can be put by taking lines from
* the top and the bottom until:
* - the desired context lines are found
* - the lines from the top is past the lines from the bottom
*/
topline = curwin->w_topline;
botline = curwin->w_botline - 1;
while ((above < above_wanted || below < below_wanted) && topline < botline)
{
if (below < below_wanted && (below <= above || above >= above_wanted))
{
below += plines(botline);
--botline;
}
if (above < above_wanted && (above < below || below >= below_wanted))
{
above += plines(topline);
++topline;
}
}
if (topline == botline || botline == 0)
curwin->w_cursor.lnum = topline;
else if (topline > botline)
curwin->w_cursor.lnum = botline;
else
{
if (cln < topline && curwin->w_topline > 1)
curwin->w_cursor.lnum = topline;
if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = botline;
}
}
/*
* Compute curwin->w_row.
* Can be called when topline and botline have not been updated.
* return OK when cursor is in the window, FAIL when it isn't.
*/
int
curs_rows()
{
linenr_t lnum;
int i;
if (curwin->w_cursor.lnum < curwin->w_topline ||
curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count ||
curwin->w_cursor.lnum >= curwin->w_botline)
return FAIL;
curwin->w_row = i = 0;
for (lnum = curwin->w_topline; lnum != curwin->w_cursor.lnum; ++lnum)
if (RedrawingDisabled) /* curwin->w_lsize[] invalid */
curwin->w_row += plines(lnum);
else
curwin->w_row += curwin->w_lsize[i++];
return OK;
}
/*
* compute curwin->w_col and curwin->w_virtcol
*/
void
curs_columns(scroll)
int scroll; /* when TRUE, may scroll horizontally */
{
int diff;
int extra;
int new_leftcol;
colnr_t startcol;
colnr_t endcol;
getvcol(curwin, &curwin->w_cursor,
&startcol, &(curwin->w_virtcol), &endcol);
/* remove '$' from change command when cursor moves onto it */
if (startcol > dollar_vcol)
dollar_vcol = 0;
curwin->w_col = curwin->w_virtcol;
if (curwin->w_p_nu)
{
curwin->w_col += 8;
endcol += 8;
}
curwin->w_row = curwin->w_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 left of the screen, scroll rightwards */
/* If Cursor is right of the screen, scroll leftwards */
if ((extra = (int)startcol - (int)curwin->w_leftcol) < 0 ||
(extra = (int)endcol - (int)(curwin->w_leftcol + Columns) + 1) > 0)
{
if (extra < 0)
diff = -extra;
else
diff = extra;
/* far off, put cursor in middle of window */
if (p_ss == 0 || diff >= Columns / 2)
new_leftcol = curwin->w_col - Columns / 2;
else
{
if (diff < p_ss)
diff = p_ss;
if (extra < 0)
new_leftcol = curwin->w_leftcol - diff;
else
new_leftcol = curwin->w_leftcol + diff;
}
if (new_leftcol < 0)
curwin->w_leftcol = 0;
else
curwin->w_leftcol = new_leftcol;
/* screen has to be redrawn with new curwin->w_leftcol */
redraw_later(NOT_VALID);
}
curwin->w_col -= curwin->w_leftcol;
}
/* Cursor past end of screen */
/* happens with line that does not fit on screen */
if (curwin->w_row > curwin->w_height - 1)
curwin->w_row = curwin->w_height - 1;
}
void
scrolldown(line_count)
long line_count;
{
register long done = 0; /* total # of physical lines done */
/* Scroll up 'line_count' lines. */
while (line_count--)
{
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--);
comp_Botline(curwin);
}
void
scrollup(line_count)
long line_count;
{
curwin->w_topline += line_count;
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;
comp_Botline(curwin);
}
/*
* Scroll the screen one line down, but don't do it if it would move the
* cursor off the screen.
*/
void
scrolldown_clamp()
{
int end_row;
if (curwin->w_topline == 1)
return;
/*
* Compute the row number of the last row of the cursor line
* and make sure it doesn't go off the screen. Make sure the cursor
* doesn't go past 'scrolloff' lines from the screen end.
*/
end_row = curwin->w_row + plines(curwin->w_topline - 1);
if (curwin->w_p_wrap)
end_row += plines(curwin->w_cursor.lnum) - 1 -
curwin->w_virtcol / Columns;
if (end_row < curwin->w_height - p_so)
--curwin->w_topline;
}
/*
* Scroll the screen one line up, but don't do it if it would move the cursor
* off the screen.
*/
void
scrollup_clamp()
{
int start_row;
if (curwin->w_topline == curbuf->b_ml.ml_line_count)
return;
/*
* Compute the row number of the first row of the cursor line
* and make sure it doesn't go off the screen. Make sure the cursor
* doesn't go before 'scrolloff' lines from the screen start.
*/
start_row = curwin->w_row - plines(curwin->w_topline);
if (curwin->w_p_wrap)
start_row -= curwin->w_virtcol / Columns;
if (start_row >= p_so)
++curwin->w_topline;
}
/*
* insert 'line_count' 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, line_count, invalid, mayclear)
WIN *wp;
int row;
int line_count;
int invalid;
int mayclear;
{
int did_delete;
int nextrow;
int lastrow;
int retval;
if (invalid)
wp->w_lsize_valid = 0;
if (RedrawingDisabled || line_count <= 0 || wp->w_height < 5)
return FAIL;
if (line_count > wp->w_height - row)
line_count = wp->w_height - row;
if (mayclear && Rows - line_count < 5) /* only a few lines left: redraw is faster */
{
screenclear(); /* will set wp->w_lsize_valid to 0 */
return FAIL;
}
/*
* Delete all remaining lines
*/
if (row + line_count >= wp->w_height)
{
screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
0, (int)Columns, ' ', ' ');
return OK;
}
/*
* 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, row);
retval = screen_ins_lines(wp->w_winpos + row, 0, line_count,
wp->w_height - row);
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 - line_count,
line_count, (int)Rows, FALSE) == 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 + line_count;
if (lastrow > Rows)
lastrow = Rows;
screen_fill(nextrow - line_count, lastrow - line_count,
0, (int)Columns, ' ', ' ');
}
if (screen_ins_lines(0, wp->w_winpos + row, line_count, (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 'line_count' 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, line_count, invalid, mayclear)
WIN *wp;
int row;
int line_count;
int invalid;
int mayclear;
{
int retval;
if (invalid)
wp->w_lsize_valid = 0;
if (RedrawingDisabled || line_count <= 0)
return FAIL;
if (line_count > wp->w_height - row)
line_count = wp->w_height - row;
/* only a few lines left: redraw is faster */
if (mayclear && Rows - line_count < 5)
{
screenclear(); /* will set wp->w_lsize_valid to 0 */
return FAIL;
}
/*
* Delete all remaining lines
*/
if (row + line_count >= wp->w_height)
{
screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
0, (int)Columns, ' ', ' ');
return OK;
}
/*
* 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, row);
retval = screen_del_lines(wp->w_winpos + row, 0, line_count,
wp->w_height - row, FALSE);
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, line_count,
(int)Rows, FALSE) == 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 - line_count,
line_count, (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.
*/
/*
* types for inserting or deleting lines
*/
#define USE_T_CAL 1
#define USE_T_CDL 2
#define USE_T_AL 3
#define USE_T_CE 4
#define USE_T_DL 5
#define USE_T_SR 6
#define USE_NL 7
#define USE_T_CD 8
/*
* 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, line_count, end)
int off;
int row;
int line_count;
int end;
{
int i;
int j;
char_u *temp;
int cursor_row;
int type;
int result_empty;
/*
* FAIL if
* - there is no valid screen
* - the screen has to be redrawn completely
* - the line count is less than one
* - the line count is more than 'ttyscroll'
*/
if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll)
return FAIL;
/*
* There are seven ways to insert lines:
* 1. Use T_CD (clear to end of display) if it exists and the result of
* the insert is just empty lines
* 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not
* present or line_count > 1. It looks better if we do all the inserts
* at once.
* 3. Use T_CDL (delete multiple lines) if it exists and the result of the
* insert is just empty lines and T_CE is not present or line_count >
* 1.
* 4. Use T_AL (insert line) if it exists.
* 5. Use T_CE (erase line) if it exists and the result of the insert is
* just empty lines.
* 6. Use T_DL (delete line) if it exists and the result of the insert is
* just empty lines.
* 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and
* the 'da' flag is not set or we have clear line capability.
*
* Careful: In a hpterm scroll reverse doesn't work as expected, it moves
* the scrollbar for the window. It does have insert line, use that if it
* exists.
*/
result_empty = (row + line_count >= end);
if (*T_CD != NUL && result_empty)
type = USE_T_CD;
else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
type = USE_T_CAL;
else if (*T_CDL != NUL && result_empty && (line_count > 1 || *T_CE == NUL))
type = USE_T_CDL;
else if (*T_AL != NUL)
type = USE_T_AL;
else if (*T_CE != NUL && result_empty)
type = USE_T_CE;
else if (*T_DL != NUL && result_empty)
type = USE_T_DL;
else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || *T_CE))
type = USE_T_SR;
else
return FAIL;
/*
* For clearing the lines screen_del_lines is used. This will also take
* care of t_db if necessary.
*/
if (type == USE_T_CD || type == USE_T_CDL ||
type == USE_T_CE || type == USE_T_DL)
return screen_del_lines(off, row, line_count, end, FALSE);
/*
* If text is retained below the screen, first clear or delete as many
* lines at the bottom of the window as are about to be inserted so that
* the deleted lines won't later surface during a screen_del_lines.
*/
if (*T_DB)
screen_del_lines(off, end - line_count, line_count, end, FALSE);
if (*T_CSC != NUL) /* cursor relative to region */
cursor_row = row;
else
cursor_row = row + off;
/*
* Shift LinePointers line_count down to reflect the inserted lines.
* Clear the inserted lines in NextScreen.
*/
row += off;
end += off;
for (i = 0; i < line_count; ++i)
{
j = end - 1 - i;
temp = LinePointers[j];
while ((j -= line_count) >= row)
LinePointers[j + line_count] = LinePointers[j];
LinePointers[j + line_count] = temp;
lineclear(temp);
}
windgoto(cursor_row, 0);
if (type == USE_T_CAL)
{
OUTSTR(tgoto((char *)T_CAL, 0, line_count));
screen_start(); /* don't know where cursor is now */
}
else
{
for (i = 0; i < line_count; i++)
{
if (type == USE_T_AL)
{
if (i && cursor_row != 0)
windgoto(cursor_row, 0);
outstr(T_AL);
}
else /* type == USE_T_SR */
outstr(T_SR);
screen_start(); /* don't know where cursor is now */
}
}
/*
* With scroll-reverse and 'da' flag set we need to clear the lines that
* have been scrolled down into the region.
*/
if (type == USE_T_SR && *T_DA)
{
for (i = 0; i < line_count; ++i)
{
windgoto(off + i, 0);
outstr(T_CE);
screen_start(); /* don't know where cursor is now */
}
}
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, line_count, end, force)
int off;
int row;
int line_count;
int end;
int force; /* even when line_count > p_ttyscroll */
{
int j;
int i;
char_u *temp;
int cursor_row;
int cursor_end;
int result_empty; /* result is empty until end of region */
int can_delete; /* deleting line codes can be used */
int type;
/*
* FAIL if
* - there is no valid screen
* - the screen has to be redrawn completely
* - the line count is less than one
* - the line count is more than 'ttyscroll'
*/
if (!screen_valid(TRUE) || line_count <= 0 ||
(!force && line_count > p_ttyscroll))
return FAIL;
/*
* Check if the rest of the current region will become empty.
*/
result_empty = row + line_count >= end;
/*
* We can delete lines only when 'db' flag not set or when 'ce' option
* available.
*/
can_delete = (*T_DB == NUL || *T_CE);
/*
* There are four ways to delete lines:
* 1. Use T_CD if it exists and the result is empty.
* 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist.
* 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or
* none of the other ways work.
* 4. Use T_CE (erase line) if the result is empty.
* 5. Use T_DL (delete line) if it exists.
*/
if (*T_CD != NUL && result_empty)
type = USE_T_CD;
else if (row == 0 && (line_count == 1 || *T_CDL == NUL))
type = USE_NL;
else if (*T_CDL != NUL && line_count > 1 && can_delete)
type = USE_T_CDL;
else if (*T_CE != NUL && result_empty)
type = USE_T_CE;
else if (*T_DL != NUL && can_delete)
type = USE_T_DL;
else if (*T_CDL != NUL && can_delete)
type = USE_T_CDL;
else
return FAIL;
if (*T_CSC != NUL) /* cursor relative to region */
{
cursor_row = row;
cursor_end = end;
}
else
{
cursor_row = row + off;
cursor_end = end + off;
}
/*
* Now shift LinePointers line_count up to reflect the deleted lines.
* Clear the inserted lines in NextScreen.
*/
row += off;
end += off;
for (i = 0; i < line_count; ++i)
{
j = row + i;
temp = LinePointers[j];
while ((j += line_count) <= end - 1)
LinePointers[j - line_count] = LinePointers[j];
LinePointers[j - line_count] = temp;
lineclear(temp);
}
/* delete the lines */
if (type == USE_T_CD)
{
windgoto(cursor_row, 0);
outstr(T_CD);
screen_start(); /* don't know where cursor is now */
}
else if (type == USE_T_CDL)
{
windgoto(cursor_row, 0);
OUTSTR(tgoto((char *)T_CDL, 0, line_count));
screen_start(); /* don't know where cursor is now */
}
/*
* Deleting lines at top of the screen or scroll region: Just scroll
* the whole screen (scroll region) up by outputting newlines on the
* last line.
*/
else if (type == USE_NL)
{
windgoto(cursor_end - 1, 0);
for (i = line_count; --i >= 0; )
outchar('\n'); /* cursor will remain on same line */
}
else
{
for (i = line_count; --i >= 0; )
{
if (type == USE_T_DL)
{
windgoto(cursor_row, 0);
outstr(T_DL); /* delete a line */
}
else /* type == USE_T_CE */
{
windgoto(cursor_row + i, 0);
outstr(T_CE); /* erase a line */
}
screen_start(); /* don't know where cursor is now */
}
}
/*
* If the 'db' flag is set, we need to clear the lines that have been
* scrolled up at the bottom of the region.
*/
if (*T_DB && (type == USE_T_DL || type == USE_T_CDL))
{
for (i = line_count; i > 0; --i)
{
windgoto(cursor_end - i, 0);
outstr(T_CE); /* erase a line */
screen_start(); /* don't know where cursor is now */
}
}
return OK;
}
/*
* show the current mode and ruler
*
* If clear_cmdline is TRUE, clear the rest of the cmdline.
* 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 need_clear = FALSE;
int do_mode = (p_smd &&
((State & INSERT) || restart_edit || VIsual_active));
if (do_mode || Recording)
{
if (emsg_on_display)
{
mch_delay(1000L, TRUE);
emsg_on_display = FALSE;
}
msg_didout = FALSE; /* never scroll up */
msg_col = 0;
gotocmdline(FALSE);
set_highlight('M'); /* Highlight mode */
if (do_mode)
{
start_highlight();
MSG_OUTSTR("--");
#ifdef INSERT_EXPAND
if (edit_submode != NULL) /* CTRL-X in Insert mode */
{
msg_outstr(edit_submode);
if (edit_submode_extra != NULL)
{
msg_outchar(' '); /* add a space in between */
if (edit_submode_highl)
{
stop_highlight();
set_highlight('r'); /* Highlight mode */
start_highlight();
}
msg_outstr(edit_submode_extra);
if (edit_submode_highl)
{
stop_highlight();
set_highlight('M'); /* Highlight mode */
start_highlight();
}
}
}
else
#endif
{
if (State == INSERT)
{
#ifdef RIGHTLEFT
if (p_ri)
MSG_OUTSTR(" REVERSE");
#endif
MSG_OUTSTR(" INSERT");
}
else if (State == REPLACE)
MSG_OUTSTR(" REPLACE");
else if (restart_edit == 'I')
MSG_OUTSTR(" (insert)");
else if (restart_edit == 'R')
MSG_OUTSTR(" (replace)");
#ifdef RIGHTLEFT
if (p_hkmap)
MSG_OUTSTR(" Hebrew");
#endif
if ((State & INSERT) && p_paste)
MSG_OUTSTR(" (paste)");
if (VIsual_active)
{
MSG_OUTSTR(" VISUAL");
if (VIsual_mode == Ctrl('V'))
MSG_OUTSTR(" BLOCK");
else if (VIsual_mode == 'V')
MSG_OUTSTR(" LINE");
}
}
MSG_OUTSTR(" --");
need_clear = TRUE;
}
if (Recording)
{
if (!need_clear)
start_highlight();
MSG_OUTSTR("recording");
need_clear = TRUE;
}
if (need_clear)
stop_highlight();
if (need_clear || clear_cmdline)
msg_clr_eos();
msg_didout = FALSE; /* overwrite this message */
msg_col = 0;
}
else if (clear_cmdline) /* just clear anything */
{
msg_row = cmdline_row;
msg_col = 0;
msg_clr_eos(); /* will reset clear_cmdline */
}
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 old_lnum = 0;
static colnr_t old_col = 0;
char_u buffer[30];
int row;
int fillchar;
if (p_ru && (redraw_cmdline || always ||
wp->w_cursor.lnum != old_lnum || wp->w_virtcol != old_col))
{
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 length, some return a pointer.
* To avoid portability problems we use strlen() here.
*/
sprintf((char *)buffer, "%ld,",
(wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ?
0L :
(long)(wp->w_cursor.lnum));
/*
* Check if cursor.lnum is valid, since win_redr_ruler() may be called
* after deleting lines, before cursor.lnum is corrected.
*/
if (wp->w_cursor.lnum <= wp->w_buffer->b_ml.ml_line_count)
col_print(buffer + STRLEN(buffer),
!(State & INSERT) &&
*ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL ?
0 :
(int)wp->w_cursor.col + 1,
(int)wp->w_virtcol + 1);
screen_msg(buffer, row, ru_col);
screen_fill(row, row + 1, ru_col + (int)STRLEN(buffer),
(int)Columns, fillchar, fillchar);
old_lnum = wp->w_cursor.lnum;
old_col = wp->w_virtcol;
stop_highlight();
}
}
/*
* screen_valid - allocate screen buffers if size changed
* If "clear" is TRUE: clear screen if it has been resized.
* Returns TRUE if there is a valid screen to write to.
* Returns FALSE when starting up and screen not initialized yet.
*/
int
screen_valid(clear)
int clear;
{
screenalloc(clear); /* allocate screen buffers if size changed */
return (NextScreen != NULL);
}
#ifdef USE_MOUSE
/*
* Move the cursor to the specified row and column on the screen.
* Change current window if neccesary. Returns an integer with the
* CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
*
* If flags has MOUSE_FOCUS, then the current window will not be changed, and
* if the mouse is outside the window then the text will scroll, or if the
* mouse was previously on a status line, then the status line may be dragged.
*
* If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
* cursor is moved unless the cursor was on a status line. Ignoring the
* CURSOR_MOVED bit, this function returns one of IN_UNKNOWN, IN_BUFFER, or
* IN_STATUS_LINE depending on where the cursor was clicked.
*
* If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
* the last call.
*
* If flags has MOUSE_SETPOS, nothing is done, only the current position is
* remembered.
*/
int
jump_to_mouse(flags)
int flags;
{
static int on_status_line = 0; /* #lines below bottom of window */
static int prev_row = -1;
static int prev_col = -1;
WIN *wp, *old_curwin;
FPOS old_cursor;
int count;
int first;
int row = mouse_row;
int col = mouse_col;
mouse_past_bottom = FALSE;
mouse_past_eol = FALSE;
if ((flags & MOUSE_DID_MOVE) && prev_row == mouse_row &&
prev_col == mouse_col)
return IN_BUFFER; /* mouse pointer didn't move */
prev_row = mouse_row;
prev_col = mouse_col;
if ((flags & MOUSE_SETPOS))
return IN_BUFFER; /* mouse pointer didn't move */
old_curwin = curwin;
old_cursor = curwin->w_cursor;
if (!(flags & MOUSE_FOCUS))
{
if (row < 0 || col < 0) /* check if it makes sense */
return IN_UNKNOWN;
/* find the window where the row is in */
for (wp = firstwin; wp->w_next; wp = wp->w_next)
if (row < wp->w_next->w_winpos)
break;
/*
* winpos and height may change in win_enter()!
*/
row -= wp->w_winpos;
if (row >= wp->w_height) /* In (or below) status line */
on_status_line = row - wp->w_height + 1;
else
on_status_line = 0;
win_enter(wp, TRUE);
if (on_status_line) /* In (or below) status line */
{
/* Don't use start_arrow() if we're in the same window */
if (curwin == old_curwin)
return IN_STATUS_LINE;
else
return IN_STATUS_LINE | CURSOR_MOVED;
}
curwin->w_cursor.lnum = curwin->w_topline;
}
else if (on_status_line)
{
/* Drag the status line */
count = row - curwin->w_winpos - curwin->w_height + 1 - on_status_line;
win_drag_status_line(count);
return IN_STATUS_LINE; /* Cursor didn't move */
}
else /* keep_window_focus must be TRUE */
{
row -= curwin->w_winpos;
/*
* When clicking beyond the end of the window, scroll the screen.
* Scroll by however many rows outside the window we are.
*/
if (row < 0)
{
count = 0;
for (first = TRUE; curwin->w_topline > 1; --curwin->w_topline)
{
count += plines(curwin->w_topline - 1);
if (!first && count > -row)
break;
first = FALSE;
}
redraw_later(VALID);
row = 0;
}
else if (row >= curwin->w_height)
{
count = 0;
for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count;
++curwin->w_topline)
{
count += plines(curwin->w_topline);
if (!first && count > row - curwin->w_height + 1)
break;
first = FALSE;
}
redraw_later(VALID);
row = curwin->w_height - 1;
}
curwin->w_cursor.lnum = curwin->w_topline;
}
#ifdef RIGHTLEFT
if (curwin->w_p_rl)
col = Columns - 1 - col;
#endif
if (curwin->w_p_nu) /* skip number in front of the line */
if ((col -= 8) < 0)
col = 0;
if (curwin->w_p_wrap) /* lines wrap */
{
while (row)
{
count = plines(curwin->w_cursor.lnum);
if (count > row)
{
col += row * Columns;
break;
}
if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
{
mouse_past_bottom = TRUE;
break;
}
row -= count;
++curwin->w_cursor.lnum;
}
}
else /* lines don't wrap */
{
curwin->w_cursor.lnum += row;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
{
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
mouse_past_bottom = TRUE;
}
col += curwin->w_leftcol;
}
curwin->w_curswant = col;
curwin->w_set_curswant = FALSE; /* May still have been TRUE */
if (coladvance(col) == FAIL)
{
/* Mouse click beyond end of line */
op_inclusive = TRUE;
mouse_past_eol = TRUE;
}
else
op_inclusive = FALSE;
if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
{
start_visual_highlight();
VIsual = old_cursor;
VIsual_active = TRUE;
#ifdef USE_MOUSE
setmouse();
#endif
if (p_smd)
redraw_cmdline = TRUE; /* show visual mode later */
}
if (curwin == old_curwin && curwin->w_cursor.lnum == old_cursor.lnum &&
curwin->w_cursor.col == old_cursor.col)
return IN_BUFFER; /* Cursor has not moved */
return IN_BUFFER | CURSOR_MOVED; /* Cursor has moved */
}
#endif /* USE_MOUSE */
/*
* Redraw the screen later, with UpdateScreen(type).
* Set must_redraw only of not already set to a higher value.
* e.g. if must_redraw is CLEAR, type == NOT_VALID will do nothing.
*/
void
redraw_later(type)
int type;
{
if (must_redraw < type)
must_redraw = type;
}