/*======================================================================*\ |* Editor mined *| |* text buffer functions *| \*======================================================================*/ #include "mined.h" /*======================================================================*\ |* Data section *| \*======================================================================*/ FLAG tab_left = True; /* Set if moving up/down on TAB should go left */ /*======================================================================*\ |* Text buffer routines *| \*======================================================================*/ #define dont_debug_move int old_x = 0; /* previous x position */ static int iscombined_display (unichar, charpos, linebegin) unsigned long unichar; char * charpos; char * linebegin; { return iscombined (unichar, charpos, linebegin) && ! (separate_isolated_combinings && (charpos == linebegin || * (charpos - 1) == '\t')); } /* * Find_x () returns the x coordinate belonging to address. * (Tabs are expanded). */ int find_x (line, address) LINE * line; char * address; { char * textp = line->text; register int x_left = line->shift_count * - SHIFT_SIZE; int x_in_line = 0; /* must start from 0 to calculate correct tab positions (since SHIFT_SIZE is not guaranteed to be a multiple of tabsize (usually 8)) */ /* Alright, SHIFT_SIZE is now guaranteed to be a multiple of 8 due to lots of display problems related to that matter. Leave this code anyway. */ unsigned long unichar; int x_before = 0; int prevx; while (textp < address && * textp != '\0') { /* use "<" rather than "!=" because address may point amidst a UTF-8 character sequence after mode switching */ prevx = x_in_line; advance_char_scr (& textp, & x_in_line, line->text); /* determine screen pos. to stay on if moving on a combining char. */ if (x_in_line > prevx) { x_before = prevx; } } /* if on combining character, skip to base character */ if (combining_mode && encoding_has_combining ()) { unichar = unicodevalue (textp); if (iscombined_display (unichar, textp, line->text)) { x_in_line = x_before; } } #ifdef debug_move printf ("find_x %08X -> %d\n", address, x_in_line + x_left); #endif return x_in_line + x_left; } /* * Find_address () returns the pointer in the line with given offset. * (Tabs are expanded). * find_address is only called by move_it () tab (cnt) is (((cnt) + tabsize) & ~07) is_tab (c) is ((c) == '\t') */ static char * find_address (line, new_x, cur_x) LINE * line; int new_x; int * cur_x; { char * textp = line->text; int tx = line->shift_count * - SHIFT_SIZE; char * prev_poi; int prev_x; unsigned long unichar; while (tx < new_x && * textp != '\n') { if (is_tab (* textp)) { if (new_x == old_x /* (* cur_x) */ - 1 && tab (tx) > new_x) { /* Moving left over tab */ break; } else if (tab_left && new_x == old_x && tab (tx) > new_x) { /* Moving up/down on tab */ break; } else { tx = tab (tx); } textp ++; } else { prev_poi = textp; prev_x = tx; advance_char_scr (& textp, & tx, line->text); /* skip over combining characters */ if (combining_mode && encoding_has_combining ()) { unichar = unicodevalue (textp); while (iscombined_display (unichar, textp, line->text)) { advance_char_scr (& textp, & tx, line->text); unichar = unicodevalue (textp); } } if (tx > new_x && new_x < * cur_x) { /* moving left into multi-width character, stay before it */ textp = prev_poi; tx = prev_x; new_x = tx; } } } #ifdef debug_move printf ("find_address %d (%d) -> %08X\n", new_x, * cur_x, textp); #endif * cur_x = tx; return textp; } /* * move_to: move to given coordinates on screen. * move_y: move to given line on screen, staying in last explicit column. * move_address: move to given line at given text position. * The caller must check that scrolling is not needed. * If new x-position is < 0 or > XBREAK, move_it () will check if * the line can be shifted. If it can it sets (or resets) the shift_count * field of the current line accordingly. By this mechanism, the * pseudo-x-positions LINE_START / LINE_END (a very small / big value) * perform the appropriate positioning actions. * Move also sets cur_text to the right char. * "If we're moving to the same x coordinate, try to move to the x-coordinate * used on the other previous call." -- This worked erroneously and was * replaced by an explicit old_x variable and move_y call. * move_address is directly called by move_next/previous_word(), * re_search(), RDwin(), load_file(), justi(), JUSandreturn() */ static void move_it (new_x, new_address, new_y) register int new_x; int new_y; char * new_address; { register LINE * line = cur_line; /* For building new cur_line */ register int lineno = line_number; int shift = 0; /* How many shifts to make */ char * utf_search; char * utf_prev; char * char_begin; /* static int rel_x = 0; */ /* Remember relative x position */ /* This was used as a trick to stay virtually in the previous column even when moving across shorter lines; but it had >= 2 errors. Renamed to old_x, made globally accessible and explicitly used by appropriate calls to avoid these problems. TW */ int tx = x; /* Check for illegal values */ if (new_y < 0 || new_y > last_y) { return; } /* Adjust y-coordinate and cur_line */ if (new_y < y) { while (y != new_y) { y --; line = line->prev; lineno --; } } else { while (y != new_y) { y ++; line = line->next; lineno ++; } } /* Set or unset relative x-coordinate */ if (new_address == NIL_PTR) { new_address = find_address (line, new_x, & tx); new_x = tx; } else { /* rel_x = */ new_x = find_x (line, new_address); } /* Adjust on character boundary */ if (cjk_text) { char_begin = charbegin (line->text, new_address); if (char_begin != new_address) { /* adjust position which is currently within a CJK double-width, multi-byte character */ if (new_x >= x) { if (* new_address != '\n') { new_x ++; new_address = char_begin; advance_char (& new_address); } else { /* incomplete CJK half character */ } } else { new_x --; new_address = char_begin; } } } else if (utf8_text) { if ((* new_address & 0xC0) == 0x80) { /* UTF-8 sequence byte? */ /* adjust position which is currently within a UTF-8 character */ /* adjust new_x for multi-width characters ? */ utf_search = line->text; utf_prev = utf_search; while (utf_search < new_address) { utf_prev = utf_search; advance_utf8 (& utf_search); } if (utf_search != new_address) { if (new_x >= x) { new_address = utf_search; } else { new_address = utf_prev; } } } } /* Adjust shift_count if new_x lower than 0 or higher than XBREAK */ /* Allow adjustment also if new_x == 0 to enable left shift mark */ if (new_x <= 0 || new_x >= XBREAK || (new_x == XBREAK - 1 && iswide (unicodevalue (new_address))) ) { if (new_x > XBREAK || (new_x == XBREAK && * new_address != '\n') || new_x == XBREAK - 1 ) { shift = (new_x - XBREAK) / SHIFT_SIZE + 1; } else { shift = new_x / SHIFT_SIZE; if (new_x % SHIFT_SIZE) { shift --; } if (new_x == 0 && line->shift_count != 0 && marker_defined (SHIFT_BEG_marker, UTF_SHIFT_BEG_marker)) { /* do not stay on left shift marker; adjust line */ shift --; } } if (shift != 0) { line->shift_count += shift; new_x = find_x (line, new_address); if (new_x == 0 && line->shift_count != 0 && marker_defined (SHIFT_BEG_marker, UTF_SHIFT_BEG_marker)) { /* do not stay on left shift marker; adjust line */ line->shift_count --; new_x = find_x (line, new_address); } set_cursor (0, y); line_print (y, line); /* rel_x = new_x; */ } } /* Assign and position cursor */ x = new_x; cur_text = new_address; cur_line = line; calc_line_no (); line_number = lineno; set_cursor_xy (); } void move_y (ny) register int ny; { move_it (old_x, NIL_PTR, ny); } void move_to (nx, ny) register int nx; register int ny; { old_x = x; move_it (nx, NIL_PTR, ny); old_x = x; } void move_address (nadd, ny) register char * nadd; register int ny; { old_x = x; move_it (0, nadd, ny); old_x = x; } void move_address_w_o_RD (nadd, ny) register char * nadd; register int ny; { old_x = x; move_it (0, nadd, ny); old_x = x; } /* * Just count to determine the current line number. */ void calc_line_no () { register LINE * line; register int line_num = 0; for (line = header->next; line != cur_line; line = line->next) { line_num ++; } line_number = line_num + 1; } /* * Proceed returns the count'th line after 'line'. When count is negative * it returns the count'th line before 'line'. When the next (previous) * line is the tail (header) indicating EOF (TOF) it stops. */ LINE * proceed (line, count) register LINE * line; register int count; { if (count < 0) { while (count ++ < 0 && line != header) { line = line->prev; } } else { while (count -- > 0 && line != tail) { line = line->next; } } return line; } /* * Reset assigns bot_line, top_line and cur_line according to 'head_line' * which must be the first line of the screen, and a y-coordinate, * which will be the current y-coordinate (if it isn't larger than last_y) */ void reset (head_line, screen_y) LINE * head_line; int screen_y; { register LINE * line; top_line = line = head_line; /* Search for bot_line (might be last line in file) */ for (last_y = 0; last_y < total_lines - 1 && last_y < SCREENMAX && line->next != tail; last_y ++ ) { line = line->next; } bot_line = line; y = (screen_y > last_y) ? last_y : screen_y; /* Set cur_line according to the new y value */ cur_line = proceed (top_line, y); calc_line_no (); } /* Determine current character position within line (# characters). */ int get_cur_pos () { int cur_pos = 0; char * cpoi = cur_line->text; while (* cpoi != '\0' && cpoi < cur_text) { advance_char (& cpoi); cur_pos ++; } return cur_pos; } #ifdef get_cur_col /* Return current visual position within line (# screen columns + line shift). */ int get_cur_col () { return cur_line->shift_count * SHIFT_SIZE + x; /* int cur_column = 0; char * cpoi = cur_line->text; while (* cpoi != '\0' && cpoi != cur_text) { advance_char_scr (& cpoi, & cur_column, cur_line->text); } return cur_column; */ } #endif /*======================================================================*\ |* Text/file information *| \*======================================================================*/ /* * Display a line telling how many chars and lines the file contains. Also tell * whether the file is readonly and/or modified. called via fstatus macro: fstatus (mess, bytes, chars) is file_status ((mess), (bytes), (chars), file_name, \ total_lines, True, writable, modified, viewonly) also called directly from WB */ void file_status (message, bytecount, charcount, filename, lines, textstat, writefl, changed, viewing) char * message; register long bytecount; /* Contains number of bytes in file */ register long charcount; /* Contains number of characters in file */ char * filename; int lines; FLAG textstat; FLAG writefl; FLAG changed; FLAG viewing; { register LINE * line; register int line_num = 0; static char msg [maxLINE_LEN + 40]; /* Buffer to hold line */ #ifdef buffer_info char yank_msg [maxLINE_LEN]; /* Buffer for msg of yank_file */ #endif /* determine information */ if (bytecount < 0) { /* # chars not given; count them */ bytecount = 0L; charcount = 0L; for (line = header->next; line != tail; line = line->next) { bytecount += length_of (line->text); charcount += char_count (line->text); if (line->return_type == lineend_NONE) { charcount --; bytecount --; } else if (line->return_type == lineend_CRLF) { bytecount ++; } else if (line->return_type == lineend_LS || line->return_type == lineend_PS) { bytecount += 2; } line_num ++; if (line == cur_line) { line_number = line_num; } } } else { /* check for current line # only */ for (line = header->next; line != cur_line->next; line = line->next) { line_num ++; if (line == cur_line) { line_number = line_num; } } } #ifdef buffer_info if (yank_status == VALID && textstat) { /* Append buffer info */ /* build_string (yank_msg, " Buffer: %ld char%s.", chars_saved, (chars_saved == 1L) ? "" : "s"); */ yank_msg [0] = '\0'; /* Empty paste buffer is only an initial condition and thus this would be no significant information; since buffer contents may be appended, the exact count is not available anyway. */ } else { yank_msg [0] = '\0'; } #endif if (textstat) { build_string (msg, (lines_per_page != 0) ? "%s %s%s%s%s, line %d of %d (chars/bytes: %ld/%ld), page %d" : "%s %s%s%s%s, line %d of %d (chars/bytes: %ld/%ld)", message, (reading_pipe && * message != '[') ? "standard input" : filename, viewing ? " (View only)" : "", changed ? " (modified)" : "", writefl ? "" : file_is_dir ? " (Directory)" : " (Readonly)", line_number, lines, charcount, bytecount, (lines_per_page != 0) ? (line_number - 1) / lines_per_page + 1 : 1 #ifdef buffer_info , yank_msg #endif ); } else { build_string (msg, "%s %s%s%s%s, lines: %d (chars/bytes: %ld/%ld)", message, (reading_pipe && * message != '[') ? "standard input" : filename, viewing ? " (View only)" : "", changed ? " (modified)" : "", writefl ? "" : file_is_dir ? " (Directory)" : " (Readonly)", lines, charcount, bytecount ); } /* don't use status_msg here, msg contains filename characters */ status_line (msg, NIL_PTR); /* display the information */ } /*======================================================================*\ |* End *| \*======================================================================*/