This is display.c in view mode; [Download] [Up]
/* Display handling functions with optional update delay. Copyright (C) 1993 Sebastiano Vigna This file is part of ne, the nice editor. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding! */ #include "ne.h" #include "termchar.h" /* The functions in this file act as an interface between the main code and the raw screen updating functions of term.c. The basic idea is that one has a series of functions which normally just call the basic functions; however, if b->turbo is nonzero and more than b->turbo lines have been updated, the update stops and is delayed to the next call to refresh_window(). This function should be called whenever the screen has to be sync'd with its contents (for instance, whenever the user gets back in control). The mechanism allows for fast, responsive screen updates for short operations, and one-in-all updates for long operations. */ /* If window_needs_refresh, the window has to be refreshed from scratch, starting from first_line and ending on last_line. Update calls keeps track of the number of lines updated. If this number becomes greater than b->turbo, and the turbo flag is set, we enter turbo mode. */ static window_needs_refresh, first_line, last_line, updated_lines; /* This function prevents any other update from being actually done by setting updated_lines to a value greater than b->turbo. It is most useful when the we know that a great deal of update is going to happen, most of which is useless (for instance, when cutting clips). */ void delay_update(buffer *b) { updated_lines = b->turbo+1; window_needs_refresh = TRUE; } /* This function prints a line descriptor at coordinates y,x starting from a given x position, and continuing for at most len characters. The TABs are expanded and considered in the computation of the position. A NULL string is a valid argument, and represents an empty string. start and len are not constrained by the length of the string (the string is really considered as terminating with an infinite sequence of spaces). cleared_at_end has the same meaning as in update_partial_line(). */ void mvaddstrn(int y, int x, line_desc *ld, int start, int len, int tab_size, int cleared_at_end) { int i,j,pos, cursor_moved = FALSE; char *s = ld->line; assert(ld != NULL); assert(y<lines && x<columns); for(i=pos=0; i<start+len && pos<ld->line_len; i++,pos++) { if (*s == '\t') { for(j=0; j < tab_size - i%tab_size; j++) if (i+j>=start && i+j<start+len) { if (!cursor_moved) { cursor_moved = TRUE; move_cursor(y,x); } output_chars(NULL, 1); } i += j-1; } else if (i>=start) { if (!cursor_moved) { cursor_moved = TRUE; move_cursor(y,x); } output_chars(s, 1); } s++; } if (i<start+len && !cleared_at_end) { if (!cursor_moved) { cursor_moved = TRUE; move_cursor(y,x); } clear_to_eol(); } } /* This function updates part of a line given its number and a starting x position. It can handle lines with no associated line descriptor (such as lines after the end of the buffer). It checks for updated_lines bypassing b->turbo. if cleared_at_end is TRUE, this function assumes that it doesn't have to clean up the rest of the line if the string is not long enough to fill the line. */ void update_partial_line(buffer *b, int n, int start_x, int cleared_at_end) { int i; line_desc *ld = b->top_line_desc; assert(n<lines-1); assert_line_desc(ld); if (b->turbo && ++updated_lines > b->turbo) window_needs_refresh = TRUE; if (window_needs_refresh) { if (n<first_line) first_line = n; if (n>last_line) last_line = n; return; } for(i=0; i<n && ld->ld_node.next; i++) ld = (line_desc *)ld->ld_node.next; if (!ld->ld_node.next) { move_cursor(n, start_x); clear_to_eol(); return; } mvaddstrn(n, start_x, ld, start_x+b->win_x, columns-start_x, b->tab_size, cleared_at_end); } /* Similar to the previous call, but updates the whole line. */ void update_line(buffer *b, int n) { update_partial_line(b, n, 0, FALSE); } /* This function updates the text window between given lines. If doit is not true, b->turbo is nonzero, and the number of lines that have been updated bypasses b->turbo, the update is not done. Rather, first_line, last_line and window_needs_refresh record that some refresh is needed, and from where it should be done. Setting doit to TRUE forces a real update. Generally, doit should be FALSE. */ void update_window_lines(buffer *b, int start_line, int end_line, int doit) { int i; line_desc *ld = b->top_line_desc; assert_line_desc(ld); window_needs_refresh = TRUE; if (first_line > start_line) first_line = start_line; if (last_line < end_line) last_line = end_line; if (b->turbo && (updated_lines += (end_line-start_line+1)) > b->turbo && !doit) return; for(i=0; i<=last_line && i+b->win_y<b->line_num; i++) { assert(ld->ld_node.next != NULL); if (i >= first_line) mvaddstrn(i, 0, ld, b->win_x, columns, b->tab_size, FALSE); ld = (line_desc *)ld->ld_node.next; } for( ; i<=last_line; i++) { move_cursor(i, 0); clear_to_eol(); } window_needs_refresh = FALSE; first_line = lines; last_line = -1; } /* This function is like the previous one, but it updates the whole window, and never forces an update. */ void update_window(buffer *b) { update_window_lines(b, 0, lines-2, FALSE); } /* The following functions update a character on the screen. Three operations are supported---insertion, deletion, overwriting. The semantics is a bit involved. Essentially, they should be called *immediately* after the modification has been done. They assume that the video is perfectly up to date, and that only the given modification has been performed. Thus, for instance, update_inserted_char() assumes that ld->line[pos] contains the inserted character. The tough part is expanding/contracting the tabs following the modified position in such a way to maintain them consistent. Moreover, a lot of special cases are considered and optimized (such as writing a space at the end of a line). b->turbo is considered. */ void update_deleted_char(buffer *b, char c, line_desc *ld, int pos, int line, int x) { int i, j, width, old_width; if (b->turbo && ++updated_lines > b->turbo) window_needs_refresh = TRUE; if (window_needs_refresh) { if (line<first_line) first_line = line; if (line>last_line) last_line = line; return; } if (pos > ld->line_len || (pos == ld->line_len && (c == '\t' || c == ' '))) return; move_cursor(line, x); if (c == '\t') width = b->tab_size - x % b->tab_size; else width = 1; if (c != '\t' && (pos == ld->line_len || (ld->line[pos] == '\t' && (x+1) % b->tab_size))) { /* Just overwrite the deleted char if it is followed by an expandable tab. */ output_chars(NULL, 1); return; } if (!char_ins_del_ok) { /* Argh! We can't insert or delete! Just update the rest of the line. */ update_partial_line(b, line, x, FALSE); return; } /* Now we search for a visible tab. If none is found, we just delete width characters and update the end of the line. */ for(i=x, j=pos; i<columns && j<ld->line_len; i++,j++) { if (ld->line[j] == '\t') { old_width = b->tab_size - (i+width) % b->tab_size; if (width+old_width > b->tab_size) { delete_chars(width); if (width != b->tab_size) { move_cursor(line, i); delete_chars(b->tab_size-width); } update_partial_line(b, line, columns-b->tab_size, TRUE); } else { /* Note that this is slower than inserting and deleting, but MUCH nicer to see. */ output_chars(&ld->line[pos], j-pos); output_chars(NULL, width); } return; } } delete_chars(width); update_partial_line(b, line, columns-width, TRUE); } /* See comments for update_deleted_char(). */ void update_inserted_char(buffer *b, line_desc *ld, int pos, int line, int x) { int i, j, width, old_width; char new_char = ld->line[pos]; assert(pos < ld->line_len); if (b->turbo && ++updated_lines > b->turbo) window_needs_refresh = TRUE; if (window_needs_refresh) { if (line<first_line) first_line = line; if (line>last_line) last_line = line; return; } if (pos == ld->line_len && (new_char == '\t' || new_char == ' ')) return; move_cursor(line, x); if (new_char == '\t') width = b->tab_size - x % b->tab_size; else width = 1; if (pos == ld->line_len-1 || (new_char != '\t' && ld->line[pos+1] == '\t' && (x+1) % b->tab_size)) { if (new_char != '\t' && new_char != ' ') output_chars(&new_char, 1); return; } if (!char_ins_del_ok) { update_partial_line(b, line, x, FALSE); return; } for(i=x+width, j=pos+1; i<columns && j<ld->line_len; i++,j++) { if (ld->line[j] == '\t') { old_width = b->tab_size - (i-width) % b->tab_size; if (old_width > width) { if (new_char == '\t') output_chars(NULL, width); else output_chars(&new_char, 1); output_chars(&ld->line[pos+1], j-(pos+1)); } else { insert_chars(new_char == '\t' ? NULL : &new_char, width); move_cursor(line, i); insert_chars(NULL, b->tab_size-width); } return; } } insert_chars(new_char == '\t' ? NULL : &new_char, width); } /* See comments for update_deleted_char(). */ void update_overwritten_char(buffer *b, char old_char, line_desc *ld, int pos, int line, int x) { int i, j, width, new_width, old_width; char new_char = ld->line[pos]; assert(ld != NULL); assert(pos < ld->line_len); if (b->turbo && ++updated_lines > b->turbo) window_needs_refresh = TRUE; if (window_needs_refresh) { if (line<first_line) first_line = line; if (line>last_line) last_line = line; return; } if (old_char == '\t') old_width = b->tab_size - x % b->tab_size; else old_width = 1; if (new_char == '\t') new_width = b->tab_size - x % b->tab_size; else new_width = 1; move_cursor(line, x); if (old_width == new_width) { if (old_char != new_char) { if (new_char == '\t') output_chars(NULL, old_width); else output_chars(&new_char, 1); } return; } if (!char_ins_del_ok) { update_partial_line(b, line, x, FALSE); return; } output_chars(new_char == '\t' ? NULL : &new_char, 1); if (old_width > new_width) { width = old_width-new_width; for(i=x, j=pos; i<columns && j<ld->line_len; i++,j++) { if (ld->line[j] == '\t') { old_width = b->tab_size - (i+width) % b->tab_size; if (width+old_width > b->tab_size) { delete_chars(width); if (width != b->tab_size) { move_cursor(line, i); delete_chars(b->tab_size-width); } update_partial_line(b, line, columns-b->tab_size, TRUE); } else { output_chars(&ld->line[pos], j-pos); output_chars(NULL, width); } return; } } delete_chars(width); update_partial_line(b, line, columns-width, TRUE); } else { width = new_width-old_width; for(i=x+width, j=pos+1; i<columns && j<ld->line_len; i++,j++) { if (ld->line[j] == '\t') { old_width = b->tab_size - (i-width) % b->tab_size; if (old_width > width) { if (new_char == '\t') output_chars(NULL, width); else output_chars(&new_char, 1); output_chars(&ld->line[pos+1], j-(pos+1)); } else { insert_chars(new_char == '\t' ? NULL : &new_char, width); move_cursor(line, i); insert_chars(NULL, b->tab_size-width); } return; } } insert_chars(new_char == '\t' ? NULL : &new_char, width); } } /* This function completely resets the terminal status, updating the whole window and resetting the status bar. It *never* does any real update; it is just used to mark that the window and the status bar have to be completely rebuilt. */ void reset_window(void) { window_needs_refresh = TRUE; first_line = 0; last_line = lines-2; reset_status_bar(); } /* This function forces the screen update. It should be called whenever the user has to interact, so that he is presented with a correctly updated display. */ void refresh_window(buffer *b) { if (window_needs_refresh) update_window_lines(b, first_line, last_line, TRUE); updated_lines = 0; } /* This function scrolls a region starting at a given line upward (n == -1) or downward (n == -1). b->turbo is checked. */ void scroll_window(buffer *b, int line, int n) { assert(n == -1 || n == 1); assert(line >= 0); assert(line < lines); if (line_ins_del_ok) { if (b->turbo && updated_lines++ > b->turbo || window_needs_refresh) { window_needs_refresh = TRUE; if (first_line > line) first_line = line; last_line = lines-2; return; } } else { /* Argh! We can't insert or delete lines. The only chance is rewriting the last lines of the screen. */ update_window_lines(b, line, lines-2, FALSE); return; } if (n>0) { ins_del_lines(line, 1); update_line(b, line); } else { ins_del_lines(line, -1); update_line(b, lines-2); } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.