void mcview_moveto_eol (mcview_t * view) { off_t bol; if (view->hex_mode) { off_t filesize; bol = mcview_offset_rounddown (view->hex_cursor, view->bytes_per_line); if (mcview_get_byte_indexed (view, bol, view->bytes_per_line - 1, NULL) == TRUE) { view->hex_cursor = bol + view->bytes_per_line - 1; } else { filesize = mcview_get_filesize (view); view->hex_cursor = mcview_offset_doz (filesize, 1); } } else { off_t eol; bol = mcview_bol (view, view->dpy_start, 0); eol = mcview_eol (view, view->dpy_start, mcview_get_filesize (view)); if (!view->utf8) { if (eol > bol) view->dpy_text_column = eol - bol; } else { char *str = NULL; switch (view->datasource) { case DS_STDIO_PIPE: case DS_VFS_PIPE: str = mcview_get_ptr_growing_buffer (view, bol); break; case DS_FILE: str = mcview_get_ptr_file (view, bol); break; case DS_STRING: str = mcview_get_ptr_string (view, bol); break; case DS_NONE: break; } if (str != NULL && eol > bol) view->dpy_text_column = g_utf8_strlen (str, eol - bol); else view->dpy_text_column = eol - bol; } view->dpy_text_column = max (0, view->dpy_text_column - view->data_area.width); } mcview_movement_fixups (view, FALSE); }
/** * Parse, format and possibly display one visual line of text. * * Formatting starts at the given "state" (which encodes the file offset and parser and formatter's * internal state). In unwrap mode, this should point to the beginning of the paragraph with the * default state, the additional horizontal scrolling is added here. In wrap mode, this should * point to the beginning of the line, with the proper state at that point. * * In wrap mode, if a line ends in a newline, it is consumed, even if it's exactly at the right * edge. In unwrap mode, the whole remaining line, including the newline is consumed. Displaying * the next line should start at "state"'s new value, or if we displayed the bottom line then * state->offset tells the file offset to be shown in the top bar. * * If "row" is offscreen, don't actually display the line but still update "state" and return the * proper value. This is used by mcview_wrap_move_down to advance in the file. * * @param view ... * @param state the parser-formatter state machine's state, updated * @param row print to this row * @param paragraph_ended store TRUE if paragraph ended by newline or EOF, FALSE if wraps to next * line * @param linewidth store the width of the line here * @return the number of rows, that is, 0 if we were already at EOF, otherwise 1 */ static int mcview_display_line (WView * view, mcview_state_machine_t * state, int row, gboolean * paragraph_ended, off_t * linewidth) { const screen_dimen left = view->data_area.left; const screen_dimen top = view->data_area.top; const screen_dimen width = view->data_area.width; const screen_dimen height = view->data_area.height; off_t dpy_text_column = view->text_wrap_mode ? 0 : view->dpy_text_column; screen_dimen col = 0; int cs[1 + MAX_COMBINING_CHARS]; char str[(1 + MAX_COMBINING_CHARS) * UTF8_CHAR_LEN + 1]; int i, j; if (paragraph_ended != NULL) *paragraph_ended = TRUE; if (!view->text_wrap_mode && (row < 0 || row >= (int) height) && linewidth == NULL) { /* Optimization: Fast forward to the end of the line, rather than carefully * parsing and then not actually displaying it. */ off_t eol; int retval; eol = mcview_eol (view, state->offset, mcview_get_filesize (view)); retval = (eol > state->offset) ? 1 : 0; mcview_state_machine_init (state, eol); return retval; } while (TRUE) { int charwidth = 0; mcview_state_machine_t state_saved; int n; int color; state_saved = *state; n = mcview_next_combining_char_sequence (view, state, cs, 1 + MAX_COMBINING_CHARS, &color); if (n == 0) { if (linewidth != NULL) *linewidth = col; return (col > 0) ? 1 : 0; } if (view->search_start <= state->offset && state->offset < view->search_end) color = VIEW_SELECTED_COLOR; if (cs[0] == '\n') { /* New line: reset all formatting state for the next paragraph. */ mcview_state_machine_init (state, state->offset); if (linewidth != NULL) *linewidth = col; return 1; } if (mcview_is_non_spacing_mark (view, cs[0])) { /* Lonely combining character. Probably leftover after too many combining chars. Just ignore. */ continue; } /* Nonprintable, or lonely spacing mark */ if ((!mcview_isprint (view, cs[0]) || mcview_ismark (view, cs[0])) && cs[0] != '\t') cs[0] = '.'; for (i = 0; i < n; i++) charwidth += mcview_wcwidth (view, cs[i]); /* Adjust the width for TAB. It's handled below along with the normal characters, * so that it's wrapped consistently with them, and is painted with the proper * attributes (although currently it can't have a special color). */ if (cs[0] == '\t') { charwidth = option_tab_spacing - state->unwrapped_column % option_tab_spacing; state->print_lonely_combining = TRUE; } else state->print_lonely_combining = FALSE; /* In wrap mode only: We're done with this row if the character sequence wouldn't fit. * Except if at the first column, because then it wouldn't fit in the next row either. * In this extreme case let the unwrapped code below do its best to display it. */ if (view->text_wrap_mode && (off_t) col + charwidth > dpy_text_column + (off_t) width && col > 0) { *state = state_saved; if (paragraph_ended != NULL) *paragraph_ended = FALSE; if (linewidth != NULL) *linewidth = col; return 1; } /* Display, unless outside of the viewport. */ if (row >= 0 && row < (int) height) { if ((off_t) col >= dpy_text_column && (off_t) col + charwidth <= dpy_text_column + (off_t) width) { /* The combining character sequence fits entirely in the viewport. Print it. */ tty_setcolor (color); widget_move (view, top + row, left + ((off_t) col - dpy_text_column)); if (cs[0] == '\t') { for (i = 0; i < charwidth; i++) tty_print_char (' '); } else { j = 0; for (i = 0; i < n; i++) j += mcview_char_display (view, cs[i], str + j); str[j] = '\0'; /* This is probably a bug in our tty layer, but tty_print_string * normalizes the string, whereas tty_printf doesn't. Don't normalize, * since we handle combining characters ourselves correctly, it's * better if they are copy-pasted correctly. Ticket 3255. */ tty_printf ("%s", str); } } else if ((off_t) col < dpy_text_column && (off_t) col + charwidth > dpy_text_column) { /* The combining character sequence would cross the left edge of the viewport. * This cannot happen with wrap mode. Print replacement character(s), * or spaces with the correct attributes for partial Tabs. */ tty_setcolor (color); for (i = dpy_text_column; i < (off_t) col + charwidth && i < dpy_text_column + (off_t) width; i++) { widget_move (view, top + row, left + (i - dpy_text_column)); tty_print_anychar ((cs[0] == '\t') ? ' ' : PARTIAL_CJK_AT_LEFT_MARGIN); } } else if ((off_t) col < dpy_text_column + (off_t) width && (off_t) col + charwidth > dpy_text_column + (off_t) width) { /* The combining character sequence would cross the right edge of the viewport * and we're not wrapping. Print replacement character(s), * or spaces with the correct attributes for partial Tabs. */ tty_setcolor (color); for (i = col; i < dpy_text_column + (off_t) width; i++) { widget_move (view, top + row, left + (i - dpy_text_column)); tty_print_anychar ((cs[0] == '\t') ? ' ' : PARTIAL_CJK_AT_RIGHT_MARGIN); } } } col += charwidth; state->unwrapped_column += charwidth; if (!view->text_wrap_mode && (off_t) col >= dpy_text_column + (off_t) width && linewidth == NULL) { /* Optimization: Fast forward to the end of the line, rather than carefully * parsing and then not actually displaying it. */ off_t eol; eol = mcview_eol (view, state->offset, mcview_get_filesize (view)); mcview_state_machine_init (state, eol); return 1; } } }
void mcview_move_down (mcview_t * view, off_t lines) { off_t last_byte; last_byte = mcview_get_filesize (view); if (view->hex_mode) { off_t i, limit; if (last_byte >= (off_t) view->bytes_per_line) limit = last_byte - view->bytes_per_line; else limit = 0; for (i = 0; i < lines && view->hex_cursor < limit; i++) { view->hex_cursor += view->bytes_per_line; if (lines != 1) view->dpy_start += view->bytes_per_line; } } else { off_t new_offset = 0; if (view->dpy_end - view->dpy_start > last_byte - view->dpy_end) { while (lines-- > 0) { if (view->text_wrap_mode) view->dpy_end = mcview_eol (view, view->dpy_end, view->dpy_end + (off_t) view->data_area.width); else view->dpy_end = mcview_eol (view, view->dpy_end, last_byte); if (view->text_wrap_mode) new_offset = mcview_eol (view, view->dpy_start, view->dpy_start + (off_t) view->data_area.width); else new_offset = mcview_eol (view, view->dpy_start, last_byte); if (new_offset < last_byte) view->dpy_start = new_offset; if (view->dpy_end >= last_byte) break; } } else { off_t i; for (i = 0; i < lines && new_offset < last_byte; i++) { if (view->text_wrap_mode) new_offset = mcview_eol (view, view->dpy_start, view->dpy_start + (off_t) view->data_area.width); else new_offset = mcview_eol (view, view->dpy_start, last_byte); if (new_offset < last_byte) view->dpy_start = new_offset; } } } mcview_movement_fixups (view, TRUE); }