/* Internal file viewer for the Midnight Commander Function for whow info on display Copyright (C) 1994-2024 Free Software Foundation, Inc. Written by: Miguel de Icaza, 1994, 1995, 1998 Janne Kukonlehto, 1994, 1995 Jakub Jelinek, 1995 Joseph M. Hinkle, 1996 Norbert Warmuth, 1997 Pavel Machek, 1998 Roland Illig , 2004, 2005 Slava Zanko , 2009 Andrew Borodin , 2009-2022 Ilia Maslakov , 2009, 2010 This file is part of the Midnight Commander. The Midnight Commander 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 3 of the License, or (at your option) any later version. The Midnight Commander 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, see . */ #include #include /* uintmax_t */ #include "lib/global.h" #include "lib/skin.h" #include "lib/tty/tty.h" #include "lib/tty/key.h" #include "lib/strutil.h" #include "lib/util.h" #include "lib/widget.h" #ifdef HAVE_CHARSET #include "lib/charsets.h" #endif #include "src/setup.h" /* panels_options */ #include "src/keymap.h" #include "internal.h" /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ #define BUF_TRUNC_LEN 5 /* The length of the line displays the file size */ /*** file scope type declarations ****************************************************************/ /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ /* If set, show a ruler */ static enum ruler_type { RULER_NONE, RULER_TOP, RULER_BOTTOM } ruler = RULER_NONE; /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /** Define labels and handlers for functional keys */ static void mcview_set_buttonbar (WView * view) { Widget *w = WIDGET (view); WDialog *h = DIALOG (w->owner); WButtonBar *b; const global_keymap_t *keymap = view->mode_flags.hex ? view->hex_keymap : w->keymap; b = buttonbar_find (h); buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), keymap, w); if (view->mode_flags.hex) { if (view->hexedit_mode) buttonbar_set_label (b, 2, Q_ ("ButtonBar|View"), keymap, w); else if (view->datasource == DS_FILE) buttonbar_set_label (b, 2, Q_ ("ButtonBar|Edit"), keymap, w); else buttonbar_set_label (b, 2, "", keymap, WIDGET (view)); buttonbar_set_label (b, 4, Q_ ("ButtonBar|Ascii"), keymap, w); buttonbar_set_label (b, 6, Q_ ("ButtonBar|Save"), keymap, w); buttonbar_set_label (b, 7, Q_ ("ButtonBar|HxSrch"), keymap, w); } else { buttonbar_set_label (b, 2, view->mode_flags.wrap ? Q_ ("ButtonBar|UnWrap") : Q_ ("ButtonBar|Wrap"), keymap, w); buttonbar_set_label (b, 4, Q_ ("ButtonBar|Hex"), keymap, w); buttonbar_set_label (b, 6, "", keymap, WIDGET (view)); buttonbar_set_label (b, 7, Q_ ("ButtonBar|Search"), keymap, w); } buttonbar_set_label (b, 5, Q_ ("ButtonBar|Goto"), keymap, w); buttonbar_set_label (b, 8, view->mode_flags.magic ? Q_ ("ButtonBar|Raw") : Q_ ("ButtonBar|Parse"), keymap, w); if (!mcview_is_in_panel (view)) /* don't override some panel buttonbar keys */ { buttonbar_set_label (b, 3, Q_ ("ButtonBar|Quit"), keymap, w); buttonbar_set_label (b, 9, view->mode_flags.nroff ? Q_ ("ButtonBar|Unform") : Q_ ("ButtonBar|Format"), keymap, w); buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), keymap, w); } } /* --------------------------------------------------------------------------------------------- */ static void mcview_display_percent (WView * view, off_t p) { int percent; percent = mcview_calc_percent (view, p); if (percent >= 0) { int top = view->status_area.y; int right; right = view->status_area.x + view->status_area.cols; widget_gotoyx (view, top, right - 4); tty_printf ("%3d%%", percent); /* avoid cursor wrapping in NCurses-base MC */ widget_gotoyx (view, top, right - 1); } } /* --------------------------------------------------------------------------------------------- */ static void mcview_display_status (WView * view) { const WRect *r = &view->status_area; const char *file_label; if (r->lines < 1) return; tty_setcolor (STATUSBAR_COLOR); tty_draw_hline (WIDGET (view)->rect.y + r->y, WIDGET (view)->rect.x + r->x, ' ', r->cols); file_label = view->filename_vpath != NULL ? vfs_path_get_last_path_str (view->filename_vpath) : view->command != NULL ? view->command : ""; if (r->cols > 40) { widget_gotoyx (view, r->y, r->cols - 32); if (view->mode_flags.hex) tty_printf ("0x%08" PRIxMAX, (uintmax_t) view->hex_cursor); else { char buffer[BUF_TRUNC_LEN + 1]; size_trunc_len (buffer, BUF_TRUNC_LEN, mcview_get_filesize (view), 0, panels_options.kilobyte_si); tty_printf ("%9" PRIuMAX "/%s%s %s", (uintmax_t) view->dpy_end, buffer, mcview_may_still_grow (view) ? "+" : " ", #ifdef HAVE_CHARSET mc_global.source_codepage >= 0 ? get_codepage_id (mc_global.source_codepage) : #endif ""); } } widget_gotoyx (view, r->y, r->x); if (r->cols > 40) tty_print_string (str_fit_to_term (file_label, r->cols - 34, J_LEFT_FIT)); else tty_print_string (str_fit_to_term (file_label, r->cols - 5, J_LEFT_FIT)); if (r->cols > 26) mcview_display_percent (view, view->mode_flags.hex ? view->hex_cursor : view->dpy_end); } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ void mcview_update (WView * view) { static int dirt_limit = 1; if (view->dpy_bbar_dirty) { view->dpy_bbar_dirty = FALSE; mcview_set_buttonbar (view); widget_draw (WIDGET (buttonbar_find (DIALOG (WIDGET (view)->owner)))); } if (view->dirty > dirt_limit) { /* Too many updates skipped -> force a update */ mcview_display (view); view->dirty = 0; /* Raise the update skipping limit */ dirt_limit++; if (dirt_limit > mcview_max_dirt_limit) dirt_limit = mcview_max_dirt_limit; } else if (view->dirty > 0) { if (is_idle ()) { /* We have time to update the screen properly */ mcview_display (view); view->dirty = 0; if (dirt_limit > 1) dirt_limit--; } else { /* We are busy -> skipping full update, only the status line is updated */ mcview_display_status (view); } /* Here we had a refresh, if fast scrolling does not work restore the refresh, although this should not happen */ } } /* --------------------------------------------------------------------------------------------- */ /** Displays as much data from view->dpy_start as fits on the screen */ void mcview_display (WView * view) { if (view->mode_flags.hex) mcview_display_hex (view); else mcview_display_text (view); mcview_display_status (view); } /* --------------------------------------------------------------------------------------------- */ void mcview_compute_areas (WView * view) { WRect view_area; int height, rest, y; /* The viewer is surrounded by a frame of size view->dpy_frame_size. * Inside that frame, there are: The status line (at the top), * the data area and an optional ruler, which is shown above or * below the data area. */ view_area.y = view->dpy_frame_size; view_area.x = view->dpy_frame_size; view_area.lines = DOZ (WIDGET (view)->rect.lines, 2 * view->dpy_frame_size); view_area.cols = DOZ (WIDGET (view)->rect.cols, 2 * view->dpy_frame_size); /* Most coordinates of the areas equal those of the whole viewer */ view->status_area = view_area; view->ruler_area = view_area; view->data_area = view_area; /* Compute the heights of the areas */ rest = view_area.lines; height = MIN (rest, 1); view->status_area.lines = height; rest -= height; height = (ruler == RULER_NONE || view->mode_flags.hex) ? 0 : 2; height = MIN (rest, height); view->ruler_area.lines = height; rest -= height; view->data_area.lines = rest; /* Compute the position of the areas */ y = view_area.y; view->status_area.y = y; y += view->status_area.lines; if (ruler == RULER_TOP) { view->ruler_area.y = y; y += view->ruler_area.lines; } view->data_area.y = y; y += view->data_area.lines; if (ruler == RULER_BOTTOM) view->ruler_area.y = y; } /* --------------------------------------------------------------------------------------------- */ void mcview_update_bytes_per_line (WView * view) { int cols = view->data_area.cols; int bytes; if (cols < 9 + 17) bytes = 4; else bytes = 4 * ((cols - 9) / ((cols <= 80) ? 17 : 18)); g_assert (bytes != 0); view->bytes_per_line = bytes; view->dirty = mcview_max_dirt_limit + 1; /* To force refresh */ } /* --------------------------------------------------------------------------------------------- */ void mcview_display_toggle_ruler (WView * view) { static const enum ruler_type next[3] = { RULER_TOP, RULER_BOTTOM, RULER_NONE }; g_assert ((size_t) ruler < 3); ruler = next[(size_t) ruler]; mcview_compute_areas (view); view->dirty++; } /* --------------------------------------------------------------------------------------------- */ void mcview_display_clean (WView * view) { Widget *w = WIDGET (view); tty_setcolor (VIEW_NORMAL_COLOR); widget_erase (w); if (view->dpy_frame_size != 0) tty_draw_box (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, FALSE); } /* --------------------------------------------------------------------------------------------- */ void mcview_display_ruler (WView * view) { static const char ruler_chars[] = "|----*----"; const WRect *r = &view->ruler_area; const int line_row = (ruler == RULER_TOP) ? 0 : 1; const int nums_row = (ruler == RULER_TOP) ? 1 : 0; char r_buff[10]; off_t cl; int c; if (ruler == RULER_NONE || r->lines < 1) return; tty_setcolor (VIEW_BOLD_COLOR); for (c = 0; c < r->cols; c++) { cl = view->dpy_text_column + c; if (line_row < r->lines) { widget_gotoyx (view, r->y + line_row, r->x + c); tty_print_char (ruler_chars[cl % 10]); } if ((cl != 0) && (cl % 10) == 0) { g_snprintf (r_buff, sizeof (r_buff), "%" PRIuMAX, (uintmax_t) cl); if (nums_row < r->lines) { widget_gotoyx (view, r->y + nums_row, r->x + c - 1); tty_print_string (r_buff); } } } tty_setcolor (VIEW_NORMAL_COLOR); } /* --------------------------------------------------------------------------------------------- */