1 /*
2 Internal file viewer for the Midnight Commander
3 Function for whow info on display
4
5 Copyright (C) 1994-2021
6 Free Software Foundation, Inc.
7
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, 2013
18 Ilia Maslakov <il.smind@gmail.com>, 2009, 2010
19
20 This file is part of the Midnight Commander.
21
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.
26
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.
31
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/>.
34 */
35
36 #include <config.h>
37 #include <inttypes.h> /* uintmax_t */
38
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
49
50 #include "src/setup.h" /* panels_options */
51 #include "src/keymap.h"
52
53 #include "internal.h"
54
55 /*** global variables ****************************************************************************/
56
57 /*** file scope macro definitions ****************************************************************/
58
59 #define BUF_TRUNC_LEN 5 /* The length of the line displays the file size */
60
61 /*** file scope type declarations ****************************************************************/
62
63 /*** file scope variables ************************************************************************/
64
65 /* If set, show a ruler */
66 static enum ruler_type
67 {
68 RULER_NONE,
69 RULER_TOP,
70 RULER_BOTTOM
71 } ruler = RULER_NONE;
72
73 /*** file scope functions ************************************************************************/
74 /* --------------------------------------------------------------------------------------------- */
75
76 /** Define labels and handlers for functional keys */
77
78 static void
mcview_set_buttonbar(WView * view)79 mcview_set_buttonbar (WView * view)
80 {
81 Widget *w = WIDGET (view);
82 WDialog *h = DIALOG (w->owner);
83 WButtonBar *b;
84 const global_keymap_t *keymap = view->mode_flags.hex ? view->hex_keymap : w->keymap;
85
86 b = find_buttonbar (h);
87 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), keymap, w);
88
89 if (view->mode_flags.hex)
90 {
91 if (view->hexedit_mode)
92 buttonbar_set_label (b, 2, Q_ ("ButtonBar|View"), keymap, w);
93 else if (view->datasource == DS_FILE)
94 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Edit"), keymap, w);
95 else
96 buttonbar_set_label (b, 2, "", keymap, WIDGET (view));
97
98 buttonbar_set_label (b, 4, Q_ ("ButtonBar|Ascii"), keymap, w);
99 buttonbar_set_label (b, 6, Q_ ("ButtonBar|Save"), keymap, w);
100 buttonbar_set_label (b, 7, Q_ ("ButtonBar|HxSrch"), keymap, w);
101
102 }
103 else
104 {
105 buttonbar_set_label (b, 2, view->mode_flags.wrap ? Q_ ("ButtonBar|UnWrap")
106 : Q_ ("ButtonBar|Wrap"), keymap, w);
107 buttonbar_set_label (b, 4, Q_ ("ButtonBar|Hex"), keymap, w);
108 buttonbar_set_label (b, 6, "", keymap, WIDGET (view));
109 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Search"), keymap, w);
110 }
111
112 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Goto"), keymap, w);
113 buttonbar_set_label (b, 8, view->mode_flags.magic ? Q_ ("ButtonBar|Raw")
114 : Q_ ("ButtonBar|Parse"), keymap, w);
115
116 if (!mcview_is_in_panel (view)) /* don't override some panel buttonbar keys */
117 {
118 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Quit"), keymap, w);
119 buttonbar_set_label (b, 9, view->mode_flags.nroff ? Q_ ("ButtonBar|Unform")
120 : Q_ ("ButtonBar|Format"), keymap, w);
121 buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), keymap, w);
122 }
123 }
124
125 /* --------------------------------------------------------------------------------------------- */
126
127 static void
mcview_display_percent(WView * view,off_t p)128 mcview_display_percent (WView * view, off_t p)
129 {
130 int percent;
131
132 percent = mcview_calc_percent (view, p);
133 if (percent >= 0)
134 {
135 const screen_dimen top = view->status_area.top;
136 const screen_dimen right = view->status_area.left + view->status_area.width;
137
138 widget_gotoyx (view, top, right - 4);
139 tty_printf ("%3d%%", percent);
140 /* avoid cursor wrapping in NCurses-base MC */
141 widget_gotoyx (view, top, right - 1);
142 }
143 }
144
145 /* --------------------------------------------------------------------------------------------- */
146
147 static void
mcview_display_status(WView * view)148 mcview_display_status (WView * view)
149 {
150 const screen_dimen top = view->status_area.top;
151 const screen_dimen left = view->status_area.left;
152 const screen_dimen width = view->status_area.width;
153 const screen_dimen height = view->status_area.height;
154 const char *file_label;
155
156 if (height < 1)
157 return;
158
159 tty_setcolor (STATUSBAR_COLOR);
160 tty_draw_hline (WIDGET (view)->y + top, WIDGET (view)->x + left, ' ', width);
161
162 file_label =
163 view->filename_vpath != NULL ?
164 vfs_path_get_last_path_str (view->filename_vpath) : view->command != NULL ?
165 view->command : "";
166
167 if (width > 40)
168 {
169 widget_gotoyx (view, top, width - 32);
170 if (view->mode_flags.hex)
171 tty_printf ("0x%08" PRIxMAX, (uintmax_t) view->hex_cursor);
172 else
173 {
174 char buffer[BUF_TRUNC_LEN + 1];
175
176 size_trunc_len (buffer, BUF_TRUNC_LEN, mcview_get_filesize (view), 0,
177 panels_options.kilobyte_si);
178 tty_printf ("%9" PRIuMAX "/%s%s %s", (uintmax_t) view->dpy_end,
179 buffer, mcview_may_still_grow (view) ? "+" : " ",
180 #ifdef HAVE_CHARSET
181 mc_global.source_codepage >= 0 ?
182 get_codepage_id (mc_global.source_codepage) :
183 #endif
184 "");
185 }
186 }
187 widget_gotoyx (view, top, left);
188 if (width > 40)
189 tty_print_string (str_fit_to_term (file_label, width - 34, J_LEFT_FIT));
190 else
191 tty_print_string (str_fit_to_term (file_label, width - 5, J_LEFT_FIT));
192 if (width > 26)
193 mcview_display_percent (view, view->mode_flags.hex ? view->hex_cursor : view->dpy_end);
194 }
195
196 /* --------------------------------------------------------------------------------------------- */
197 /*** public functions ****************************************************************************/
198 /* --------------------------------------------------------------------------------------------- */
199
200 void
mcview_update(WView * view)201 mcview_update (WView * view)
202 {
203 static int dirt_limit = 1;
204
205 if (view->dpy_bbar_dirty)
206 {
207 view->dpy_bbar_dirty = FALSE;
208 mcview_set_buttonbar (view);
209 widget_draw (WIDGET (find_buttonbar (DIALOG (WIDGET (view)->owner))));
210 }
211
212 if (view->dirty > dirt_limit)
213 {
214 /* Too many updates skipped -> force a update */
215 mcview_display (view);
216 view->dirty = 0;
217 /* Raise the update skipping limit */
218 dirt_limit++;
219 if (dirt_limit > mcview_max_dirt_limit)
220 dirt_limit = mcview_max_dirt_limit;
221 }
222 else if (view->dirty > 0)
223 {
224 if (is_idle ())
225 {
226 /* We have time to update the screen properly */
227 mcview_display (view);
228 view->dirty = 0;
229 if (dirt_limit > 1)
230 dirt_limit--;
231 }
232 else
233 {
234 /* We are busy -> skipping full update,
235 only the status line is updated */
236 mcview_display_status (view);
237 }
238 /* Here we had a refresh, if fast scrolling does not work
239 restore the refresh, although this should not happen */
240 }
241 }
242
243 /* --------------------------------------------------------------------------------------------- */
244 /** Displays as much data from view->dpy_start as fits on the screen */
245
246 void
mcview_display(WView * view)247 mcview_display (WView * view)
248 {
249 if (view->mode_flags.hex)
250 mcview_display_hex (view);
251 else
252 mcview_display_text (view);
253 mcview_display_status (view);
254 }
255
256 /* --------------------------------------------------------------------------------------------- */
257
258 void
mcview_compute_areas(WView * view)259 mcview_compute_areas (WView * view)
260 {
261 struct area view_area;
262 screen_dimen height, rest, y;
263
264 /* The viewer is surrounded by a frame of size view->dpy_frame_size.
265 * Inside that frame, there are: The status line (at the top),
266 * the data area and an optional ruler, which is shown above or
267 * below the data area. */
268
269 view_area.top = view->dpy_frame_size;
270 view_area.left = view->dpy_frame_size;
271 view_area.height = DOZ ((screen_dimen) WIDGET (view)->lines, 2 * view->dpy_frame_size);
272 view_area.width = DOZ ((screen_dimen) WIDGET (view)->cols, 2 * view->dpy_frame_size);
273
274 /* Most coordinates of the areas equal those of the whole viewer */
275 view->status_area = view_area;
276 view->ruler_area = view_area;
277 view->data_area = view_area;
278
279 /* Compute the heights of the areas */
280 rest = view_area.height;
281
282 height = MIN (rest, 1);
283 view->status_area.height = height;
284 rest -= height;
285
286 height = (ruler == RULER_NONE || view->mode_flags.hex) ? 0 : 2;
287 height = MIN (rest, height);
288 view->ruler_area.height = height;
289 rest -= height;
290
291 view->data_area.height = rest;
292
293 /* Compute the position of the areas */
294 y = view_area.top;
295
296 view->status_area.top = y;
297 y += view->status_area.height;
298
299 if (ruler == RULER_TOP)
300 {
301 view->ruler_area.top = y;
302 y += view->ruler_area.height;
303 }
304
305 view->data_area.top = y;
306 y += view->data_area.height;
307
308 if (ruler == RULER_BOTTOM)
309 view->ruler_area.top = y;
310 }
311
312 /* --------------------------------------------------------------------------------------------- */
313
314 void
mcview_update_bytes_per_line(WView * view)315 mcview_update_bytes_per_line (WView * view)
316 {
317 const screen_dimen cols = view->data_area.width;
318 int bytes;
319
320 if (cols < 9 + 17)
321 bytes = 4;
322 else
323 bytes = 4 * ((cols - 9) / ((cols <= 80) ? 17 : 18));
324
325 g_assert (bytes != 0);
326
327 view->bytes_per_line = bytes;
328 view->dirty = mcview_max_dirt_limit + 1; /* To force refresh */
329 }
330
331 /* --------------------------------------------------------------------------------------------- */
332
333 void
mcview_display_toggle_ruler(WView * view)334 mcview_display_toggle_ruler (WView * view)
335 {
336 static const enum ruler_type next[3] =
337 {
338 RULER_TOP,
339 RULER_BOTTOM,
340 RULER_NONE
341 };
342
343 g_assert ((size_t) ruler < 3);
344
345 ruler = next[(size_t) ruler];
346 mcview_compute_areas (view);
347 view->dirty++;
348 }
349
350 /* --------------------------------------------------------------------------------------------- */
351
352 void
mcview_display_clean(WView * view)353 mcview_display_clean (WView * view)
354 {
355 Widget *w = WIDGET (view);
356
357 tty_setcolor (VIEW_NORMAL_COLOR);
358 widget_erase (w);
359 if (view->dpy_frame_size != 0)
360 tty_draw_box (w->y, w->x, w->lines, w->cols, FALSE);
361 }
362
363 /* --------------------------------------------------------------------------------------------- */
364
365 void
mcview_display_ruler(WView * view)366 mcview_display_ruler (WView * view)
367 {
368 static const char ruler_chars[] = "|----*----";
369 const screen_dimen top = view->ruler_area.top;
370 const screen_dimen left = view->ruler_area.left;
371 const screen_dimen width = view->ruler_area.width;
372 const screen_dimen height = view->ruler_area.height;
373 const screen_dimen line_row = (ruler == RULER_TOP) ? 0 : 1;
374 const screen_dimen nums_row = (ruler == RULER_TOP) ? 1 : 0;
375
376 char r_buff[10];
377 off_t cl;
378 screen_dimen c;
379
380 if (ruler == RULER_NONE || height < 1)
381 return;
382
383 tty_setcolor (VIEW_BOLD_COLOR);
384 for (c = 0; c < width; c++)
385 {
386 cl = view->dpy_text_column + c;
387 if (line_row < height)
388 {
389 widget_gotoyx (view, top + line_row, left + c);
390 tty_print_char (ruler_chars[cl % 10]);
391 }
392
393 if ((cl != 0) && (cl % 10) == 0)
394 {
395 g_snprintf (r_buff, sizeof (r_buff), "%" PRIuMAX, (uintmax_t) cl);
396 if (nums_row < height)
397 {
398 widget_gotoyx (view, top + nums_row, left + c - 1);
399 tty_print_string (r_buff);
400 }
401 }
402 }
403 tty_setcolor (VIEW_NORMAL_COLOR);
404 }
405
406 /* --------------------------------------------------------------------------------------------- */
407