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