doc/FAQ: fix typo.
[midnight-commander.git] / src / viewer / display.c
blob324a59219f539212c547c81f994f81630b394678
1 /*
2 Internal file viewer for the Midnight Commander
3 Function for whow info on display
5 Copyright (C) 1994-2025
6 Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
11 Jakub Jelinek, 1995
12 Joseph M. Hinkle, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009
17 Andrew Borodin <aborodin@vmail.ru>, 2009-2022
18 Ilia Maslakov <il.smind@gmail.com>, 2009, 2010
20 This file is part of the Midnight Commander.
22 The Midnight Commander is free software: you can redistribute it
23 and/or modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation, either version 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
32 You should have received a copy of the GNU General Public License
33 along with this program. If not, see <http://www.gnu.org/licenses/>.
36 #include <config.h>
37 #include <inttypes.h> /* uintmax_t */
39 #include "lib/global.h"
40 #include "lib/skin.h"
41 #include "lib/tty/tty.h"
42 #include "lib/tty/key.h"
43 #include "lib/strutil.h"
44 #include "lib/util.h"
45 #include "lib/widget.h"
46 #ifdef HAVE_CHARSET
47 #include "lib/charsets.h"
48 #endif
50 #include "src/setup.h" /* panels_options */
51 #include "src/keymap.h"
53 #include "internal.h"
55 /*** global variables ****************************************************************************/
57 /*** file scope macro definitions ****************************************************************/
59 #define BUF_TRUNC_LEN 5 /* The length of the line displays the file size */
61 /*** file scope type declarations ****************************************************************/
63 /*** forward declarations (file scope functions) *************************************************/
65 /*** file scope variables ************************************************************************/
67 /* If set, show a ruler */
68 static enum ruler_type
70 RULER_NONE,
71 RULER_TOP,
72 RULER_BOTTOM
73 } ruler = RULER_NONE;
75 /* --------------------------------------------------------------------------------------------- */
76 /*** file scope functions ************************************************************************/
77 /* --------------------------------------------------------------------------------------------- */
79 /** Define labels and handlers for functional keys */
81 static void
82 mcview_set_buttonbar (WView *view)
84 Widget *w = WIDGET (view);
85 WDialog *h = DIALOG (w->owner);
86 WButtonBar *b;
87 const global_keymap_t *keymap = view->mode_flags.hex ? view->hex_keymap : w->keymap;
89 b = buttonbar_find (h);
90 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), keymap, w);
92 if (view->mode_flags.hex)
94 if (view->hexedit_mode)
95 buttonbar_set_label (b, 2, Q_ ("ButtonBar|View"), keymap, w);
96 else if (view->datasource == DS_FILE)
97 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Edit"), keymap, w);
98 else
99 buttonbar_set_label (b, 2, "", keymap, WIDGET (view));
101 buttonbar_set_label (b, 4, Q_ ("ButtonBar|Ascii"), keymap, w);
102 buttonbar_set_label (b, 6, Q_ ("ButtonBar|Save"), keymap, w);
103 buttonbar_set_label (b, 7, Q_ ("ButtonBar|HxSrch"), keymap, w);
106 else
108 buttonbar_set_label (b, 2, view->mode_flags.wrap ? Q_ ("ButtonBar|UnWrap")
109 : Q_ ("ButtonBar|Wrap"), keymap, w);
110 buttonbar_set_label (b, 4, Q_ ("ButtonBar|Hex"), keymap, w);
111 buttonbar_set_label (b, 6, "", keymap, WIDGET (view));
112 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Search"), keymap, w);
115 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Goto"), keymap, w);
116 buttonbar_set_label (b, 8, view->mode_flags.magic ? Q_ ("ButtonBar|Raw")
117 : Q_ ("ButtonBar|Parse"), keymap, w);
119 if (!mcview_is_in_panel (view)) /* don't override some panel buttonbar keys */
121 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Quit"), keymap, w);
122 buttonbar_set_label (b, 9, view->mode_flags.nroff ? Q_ ("ButtonBar|Unform")
123 : Q_ ("ButtonBar|Format"), keymap, w);
124 buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), keymap, w);
128 /* --------------------------------------------------------------------------------------------- */
130 static void
131 mcview_display_percent (WView *view, off_t p)
133 int percent;
135 percent = mcview_calc_percent (view, p);
136 if (percent >= 0)
138 int top = view->status_area.y;
139 int right;
141 right = view->status_area.x + view->status_area.cols;
142 widget_gotoyx (view, top, right - 4);
143 tty_printf ("%3d%%", percent);
144 /* avoid cursor wrapping in NCurses-base MC */
145 widget_gotoyx (view, top, right - 1);
149 /* --------------------------------------------------------------------------------------------- */
151 static void
152 mcview_display_status (WView *view)
154 const WRect *r = &view->status_area;
155 const char *file_label;
157 if (r->lines < 1)
158 return;
160 tty_setcolor (STATUSBAR_COLOR);
161 tty_draw_hline (WIDGET (view)->rect.y + r->y, WIDGET (view)->rect.x + r->x, ' ', r->cols);
163 file_label =
164 view->filename_vpath != NULL ?
165 vfs_path_get_last_path_str (view->filename_vpath) : view->command != NULL ?
166 view->command : "";
168 if (r->cols > 40)
170 widget_gotoyx (view, r->y, r->cols - 32);
171 if (view->mode_flags.hex)
172 tty_printf ("0x%08" PRIxMAX, (uintmax_t) view->hex_cursor);
173 else
175 char buffer[BUF_TRUNC_LEN + 1];
177 size_trunc_len (buffer, BUF_TRUNC_LEN, mcview_get_filesize (view), 0,
178 panels_options.kilobyte_si);
179 tty_printf ("%9" PRIuMAX "/%s%s %s", (uintmax_t) view->dpy_end,
180 buffer, mcview_may_still_grow (view) ? "+" : " ",
181 #ifdef HAVE_CHARSET
182 mc_global.source_codepage >= 0 ?
183 get_codepage_id (mc_global.source_codepage) :
184 #endif
185 "");
188 widget_gotoyx (view, r->y, r->x);
189 if (r->cols > 40)
190 tty_print_string (str_fit_to_term (file_label, r->cols - 34, J_LEFT_FIT));
191 else
192 tty_print_string (str_fit_to_term (file_label, r->cols - 5, J_LEFT_FIT));
193 if (r->cols > 26)
194 mcview_display_percent (view, view->mode_flags.hex ? view->hex_cursor : view->dpy_end);
197 /* --------------------------------------------------------------------------------------------- */
198 /*** public functions ****************************************************************************/
199 /* --------------------------------------------------------------------------------------------- */
201 void
202 mcview_update (WView *view)
204 static int dirt_limit = 1;
206 if (view->dpy_bbar_dirty)
208 view->dpy_bbar_dirty = FALSE;
209 mcview_set_buttonbar (view);
210 widget_draw (WIDGET (buttonbar_find (DIALOG (WIDGET (view)->owner))));
213 if (view->dirty > dirt_limit)
215 /* Too many updates skipped -> force a update */
216 mcview_display (view);
217 view->dirty = 0;
218 /* Raise the update skipping limit */
219 dirt_limit++;
220 if (dirt_limit > mcview_max_dirt_limit)
221 dirt_limit = mcview_max_dirt_limit;
223 else if (view->dirty > 0)
225 if (is_idle ())
227 /* We have time to update the screen properly */
228 mcview_display (view);
229 view->dirty = 0;
230 if (dirt_limit > 1)
231 dirt_limit--;
233 else
235 /* We are busy -> skipping full update,
236 only the status line is updated */
237 mcview_display_status (view);
239 /* Here we had a refresh, if fast scrolling does not work
240 restore the refresh, although this should not happen */
244 /* --------------------------------------------------------------------------------------------- */
245 /** Displays as much data from view->dpy_start as fits on the screen */
247 void
248 mcview_display (WView *view)
250 if (view->mode_flags.hex)
251 mcview_display_hex (view);
252 else
253 mcview_display_text (view);
254 mcview_display_status (view);
257 /* --------------------------------------------------------------------------------------------- */
259 void
260 mcview_compute_areas (WView *view)
262 WRect view_area;
263 int height, rest, y;
265 /* The viewer is surrounded by a frame of size view->dpy_frame_size.
266 * Inside that frame, there are: The status line (at the top),
267 * the data area and an optional ruler, which is shown above or
268 * below the data area. */
270 view_area.y = view->dpy_frame_size;
271 view_area.x = view->dpy_frame_size;
272 view_area.lines = DOZ (WIDGET (view)->rect.lines, 2 * view->dpy_frame_size);
273 view_area.cols = DOZ (WIDGET (view)->rect.cols, 2 * view->dpy_frame_size);
275 /* Most coordinates of the areas equal those of the whole viewer */
276 view->status_area = view_area;
277 view->ruler_area = view_area;
278 view->data_area = view_area;
280 /* Compute the heights of the areas */
281 rest = view_area.lines;
283 height = MIN (rest, 1);
284 view->status_area.lines = height;
285 rest -= height;
287 height = (ruler == RULER_NONE || view->mode_flags.hex) ? 0 : 2;
288 height = MIN (rest, height);
289 view->ruler_area.lines = height;
290 rest -= height;
292 view->data_area.lines = rest;
294 /* Compute the position of the areas */
295 y = view_area.y;
297 view->status_area.y = y;
298 y += view->status_area.lines;
300 if (ruler == RULER_TOP)
302 view->ruler_area.y = y;
303 y += view->ruler_area.lines;
306 view->data_area.y = y;
307 y += view->data_area.lines;
309 if (ruler == RULER_BOTTOM)
310 view->ruler_area.y = y;
313 /* --------------------------------------------------------------------------------------------- */
315 void
316 mcview_update_bytes_per_line (WView *view)
318 int cols = view->data_area.cols;
319 int bytes;
321 if (cols < 9 + 17)
322 bytes = 4;
323 else
324 bytes = 4 * ((cols - 9) / ((cols <= 80) ? 17 : 18));
326 g_assert (bytes != 0);
328 view->bytes_per_line = bytes;
329 view->dirty = mcview_max_dirt_limit + 1; /* To force refresh */
332 /* --------------------------------------------------------------------------------------------- */
334 void
335 mcview_display_toggle_ruler (WView *view)
337 static const enum ruler_type next[3] = {
338 RULER_TOP,
339 RULER_BOTTOM,
340 RULER_NONE
343 g_assert ((size_t) ruler < 3);
345 ruler = next[(size_t) ruler];
346 mcview_compute_areas (view);
347 view->dirty++;
350 /* --------------------------------------------------------------------------------------------- */
352 void
353 mcview_display_clean (WView *view)
355 Widget *w = WIDGET (view);
357 tty_setcolor (VIEW_NORMAL_COLOR);
358 widget_erase (w);
359 if (view->dpy_frame_size != 0)
360 tty_draw_box (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, FALSE);
363 /* --------------------------------------------------------------------------------------------- */
365 void
366 mcview_display_ruler (WView *view)
368 static const char ruler_chars[] = "|----*----";
369 const WRect *r = &view->ruler_area;
370 const int line_row = (ruler == RULER_TOP) ? 0 : 1;
371 const int nums_row = (ruler == RULER_TOP) ? 1 : 0;
373 char r_buff[10];
374 off_t cl;
375 int c;
377 if (ruler == RULER_NONE || r->lines < 1)
378 return;
380 tty_setcolor (VIEW_BOLD_COLOR);
381 for (c = 0; c < r->cols; c++)
383 cl = view->dpy_text_column + c;
384 if (line_row < r->lines)
386 widget_gotoyx (view, r->y + line_row, r->x + c);
387 tty_print_char (ruler_chars[cl % 10]);
390 if ((cl != 0) && (cl % 10) == 0)
392 g_snprintf (r_buff, sizeof (r_buff), "%" PRIuMAX, (uintmax_t) cl);
393 if (nums_row < r->lines)
395 widget_gotoyx (view, r->y + nums_row, r->x + c - 1);
396 tty_print_string (r_buff);
400 tty_setcolor (VIEW_NORMAL_COLOR);
403 /* --------------------------------------------------------------------------------------------- */