/* vifm * Copyright (C) 2015 xaizek. * * 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 of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "more.h" #include <curses.h> #include <assert.h> /* assert() */ #include <limits.h> /* INT_MAX */ #include <stddef.h> /* NULL size_t */ #include <string.h> /* strdup() */ #include "../cfg/config.h" #include "../compat/curses.h" #include "../compat/reallocarray.h" #include "../engine/keys.h" #include "../engine/mode.h" #include "../menus/menus.h" #include "../ui/ui.h" #include "../utils/macros.h" #include "../utils/str.h" #include "../utils/utf8.h" #include "cmdline.h" #include "modes.h" #include "wk.h" /* Provide more readable definitions of key codes. */ static void calc_vlines_wrapped(void); static void leave_more_mode(void); static const char * get_text_beginning(void); static void draw_all(const char text[]); static void cmd_leave(key_info_t key_info, keys_info_t *keys_info); static void cmd_ctrl_l(key_info_t key_info, keys_info_t *keys_info); static void cmd_colon(key_info_t key_info, keys_info_t *keys_info); static void cmd_bottom(key_info_t key_info, keys_info_t *keys_info); static void cmd_top(key_info_t key_info, keys_info_t *keys_info); static void cmd_down_line(key_info_t key_info, keys_info_t *keys_info); static void cmd_up_line(key_info_t key_info, keys_info_t *keys_info); static void cmd_down_screen(key_info_t key_info, keys_info_t *keys_info); static void cmd_up_screen(key_info_t key_info, keys_info_t *keys_info); static void cmd_down_page(key_info_t key_info, keys_info_t *keys_info); static void cmd_up_page(key_info_t key_info, keys_info_t *keys_info); static void goto_vline(int line); static void goto_vline_below(int by); static void goto_vline_above(int by); /* Text displayed by the mode. */ static char *text; /* (first virtual line, screen width, offset in text) triples per real line. */ static int (*data)[3]; /* Number of virtual lines. */ static int nvlines; /* Current real line number. */ static int curr_line; /* Current virtual line number. */ static int curr_vline; /* Width of the area text is printed on. */ static int viewport_width; /* Height of the area text is printed on. */ static int viewport_height; /* List of builtin keys. */ static keys_add_info_t builtin_keys[] = { {WK_C_c, {{&cmd_leave}, .descr = "leave more mode"}}, {WK_C_j, {{&cmd_down_line}, .descr = "scroll one line down"}}, {WK_C_l, {{&cmd_ctrl_l}, .descr = "redraw"}}, {WK_COLON, {{&cmd_colon}, .descr = "switch to cmdline mode"}}, {WK_ESC, {{&cmd_leave}, .descr = "leave more mode"}}, {WK_CR, {{&cmd_leave}, .descr = "leave more mode"}}, {WK_SPACE, {{&cmd_down_screen}, .descr = "scroll one screen down"}}, {WK_G, {{&cmd_bottom}, .descr = "scroll to the end"}}, {WK_b, {{&cmd_up_screen}, .descr = "scroll one screen up"}}, {WK_d, {{&cmd_down_page}, .descr = "scroll page down"}}, {WK_f, {{&cmd_down_screen}, .descr = "scroll one screen down"}}, {WK_g, {{&cmd_top}, .descr = "scroll to the beginning"}}, {WK_j, {{&cmd_down_line}, .descr = "scroll one line down"}}, {WK_k, {{&cmd_up_line}, .descr = "scroll one line up"}}, {WK_q, {{&cmd_leave}, .descr = "leave more mode"}}, {WK_u, {{&cmd_up_page}, .descr = "scroll page up"}}, #ifdef ENABLE_EXTENDED_KEYS {{K(KEY_BACKSPACE)}, {{&cmd_up_line}, .descr = "scroll one line up"}}, {{K(KEY_DOWN)}, {{&cmd_down_line}, .descr = "scroll one line down"}}, {{K(KEY_UP)}, {{&cmd_up_line}, .descr = "scroll one line up"}}, {{K(KEY_HOME)}, {{&cmd_top}, .descr = "scroll to the beginning"}}, {{K(KEY_END)}, {{&cmd_bottom}, .descr = "scroll to the end"}}, {{K(KEY_NPAGE)}, {{&cmd_down_screen}, .descr = "scroll one screen down"}}, {{K(KEY_PPAGE)}, {{&cmd_up_screen}, .descr = "scroll one screen up"}}, #endif /* ENABLE_EXTENDED_KEYS */ }; void modmore_init(void) { int ret_code; ret_code = vle_keys_add(builtin_keys, ARRAY_LEN(builtin_keys), MORE_MODE); assert(ret_code == 0 && "Failed to initialize more mode keys."); (void)ret_code; } void modmore_enter(const char txt[]) { text = strdup(txt); curr_line = 0; curr_vline = 0; ui_hide_graphics(); vle_mode_set(MORE_MODE, VMT_PRIMARY); modmore_redraw(); } void modmore_abort(void) { leave_more_mode(); } /* Recalculates virtual lines of a view with line wrapping. */ static void calc_vlines_wrapped(void) { const char *p; char *q; int i; const int nlines = count_lines(text, INT_MAX); data = reallocarray(NULL, nlines, sizeof(*data)); nvlines = 0; p = text; q = text - 1; for(i = 0; i < nlines; ++i) { char saved_char; q = until_first(q + 1, '\n'); saved_char = *q; *q = '\0'; data[i][0] = nvlines++; data[i][1] = utf8_strsw_with_tabs(p, cfg.tab_stop); data[i][2] = p - text; nvlines += data[i][1]/viewport_width; *q = saved_char; p = q + 1; } } /* Quits the mode restoring previously active one. */ static void leave_more_mode(void) { update_string(&text, NULL); free(data); data = NULL; vle_mode_set(NORMAL_MODE, VMT_PRIMARY); stats_redraw_later(); } void modmore_redraw(void) { if(resize_for_menu_like() != 0) { return; } wresize(status_bar, 1, getmaxx(stdscr)); viewport_width = getmaxx(menu_win); viewport_height = getmaxy(menu_win); calc_vlines_wrapped(); goto_vline(curr_vline); draw_all(get_text_beginning()); checked_wmove(menu_win, 0, 0); } /* Retrieves beginning of the text that should be displayed. Returns the * beginning. */ static const char * get_text_beginning(void) { int skipped = 0; const char *text_piece = text + data[curr_line][2]; /* Skip invisible virtual lines (those that are above current one). */ while(skipped < curr_vline - data[curr_line][0]) { text_piece += utf8_strsnlen(text_piece, viewport_width); ++skipped; } return text_piece; } /* Draws all components of the mode onto the screen. */ static void draw_all(const char text[]) { /* Setup correct attributes for the windows. */ ui_set_attr(menu_win, &cfg.cs.color[WIN_COLOR], cfg.cs.pair[WIN_COLOR]); ui_set_attr(status_bar, &cfg.cs.color[CMD_LINE_COLOR], cfg.cs.pair[CMD_LINE_COLOR]); /* Clean up everything. */ werase(menu_win); werase(status_bar); /* Draw the text. */ checked_wmove(menu_win, 0, 0); wprint(menu_win, text); /* Draw status line. */ checked_wmove(status_bar, 0, 0); mvwprintw(status_bar, 0, 0, "-- More -- %d-%d/%d", curr_vline + 1, MIN(nvlines, curr_vline + viewport_height), nvlines); /* Inform curses of the changes. */ wnoutrefresh(menu_win); wnoutrefresh(status_bar); /* Apply all changes. */ doupdate(); } /* Leaves the mode. */ static void cmd_leave(key_info_t key_info, keys_info_t *keys_info) { leave_more_mode(); } /* Redraws the mode. */ static void cmd_ctrl_l(key_info_t key_info, keys_info_t *keys_info) { modmore_redraw(); } /* Switches to command-line mode. */ static void cmd_colon(key_info_t key_info, keys_info_t *keys_info) { leave_more_mode(); modcline_enter(CLS_COMMAND, ""); } /* Navigate to the bottom. */ static void cmd_bottom(key_info_t key_info, keys_info_t *keys_info) { goto_vline(INT_MAX); } /* Navigate to the top. */ static void cmd_top(key_info_t key_info, keys_info_t *keys_info) { goto_vline(0); } /* Go one line below. */ static void cmd_down_line(key_info_t key_info, keys_info_t *keys_info) { goto_vline(curr_vline + 1); } /* Go one line above. */ static void cmd_up_line(key_info_t key_info, keys_info_t *keys_info) { goto_vline(curr_vline - 1); } /* Go one screen below. */ static void cmd_down_screen(key_info_t key_info, keys_info_t *keys_info) { goto_vline(curr_vline + viewport_height); } /* Go one screen above. */ static void cmd_up_screen(key_info_t key_info, keys_info_t *keys_info) { goto_vline(curr_vline - viewport_height); } /* Go one page (half of the screen) below. */ static void cmd_down_page(key_info_t key_info, keys_info_t *keys_info) { goto_vline(curr_vline + viewport_height/2); } /* Go one page (half of the screen) above. */ static void cmd_up_page(key_info_t key_info, keys_info_t *keys_info) { goto_vline(curr_vline - viewport_height/2); } /* Navigates to the specified virtual line taking care of values that are out of * range. */ static void goto_vline(int line) { const int max_vline = nvlines - viewport_height; if(line > max_vline) { line = max_vline; } if(line < 0) { line = 0; } if(curr_vline == line) { return; } if(line > curr_vline) { goto_vline_below(line - curr_vline); } else { goto_vline_above(curr_vline - line); } modmore_redraw(); } /* Navigates by virtual lines below. */ static void goto_vline_below(int by) { while(by-- > 0) { const int height = MAX(DIV_ROUND_UP(data[curr_line][1], viewport_width), 1); if(curr_vline + 1 >= data[curr_line][0] + height) { ++curr_line; } ++curr_vline; } } /* Navigates by virtual lines above. */ static void goto_vline_above(int by) { while(by-- > 0) { if(curr_vline - 1 < data[curr_line][0]) { --curr_line; } --curr_vline; } } /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set cinoptions+=t0 filetype=c : */