Ticket# 2990: prepare to 4.8.9 release.
[midnight-commander.git] / src / viewer / mcviewer.c
blobe028255d5f6071dc01c782545d39734c9b4fff8e
1 /*
2 Internal file viewer for the Midnight Commander
3 Interface functions
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009, 2011, 2013
7 The Free Software Foundation, Inc.
9 Written by:
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
12 Jakub Jelinek, 1995
13 Joseph M. Hinkle, 1996
14 Norbert Warmuth, 1997
15 Pavel Machek, 1998
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009, 2013
18 Andrew Borodin <aborodin@vmail.ru>, 2009, 2013
19 Ilia Maslakov <il.smind@gmail.com>, 2009
21 This file is part of the Midnight Commander.
23 The Midnight Commander is free software: you can redistribute it
24 and/or modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation, either version 3 of the License,
26 or (at your option) any later version.
28 The Midnight Commander is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program. If not, see <http://www.gnu.org/licenses/>.
37 #include <config.h>
38 #include <errno.h>
39 #include <fcntl.h>
41 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/mouse.h"
44 #include "lib/vfs/vfs.h"
45 #include "lib/strutil.h"
46 #include "lib/util.h" /* load_file_position() */
47 #include "lib/widget.h"
49 #include "src/filemanager/layout.h" /* menubar_visible */
50 #include "src/filemanager/midnight.h" /* the_menubar */
52 #include "internal.h"
54 /*** global variables ****************************************************************************/
56 int mcview_default_hex_mode = 0;
57 int mcview_default_nroff_flag = 0;
58 int mcview_global_wrap_mode = 1;
59 int mcview_default_magic_flag = 1;
61 int mcview_altered_hex_mode = 0;
62 int mcview_altered_magic_flag = 0;
63 int mcview_altered_nroff_flag = 0;
65 int mcview_remember_file_position = FALSE;
67 /* Maxlimit for skipping updates */
68 int mcview_max_dirt_limit = 10;
70 /* Scrolling is done in pages or line increments */
71 int mcview_mouse_move_pages = 1;
73 /* end of file will be showen from mcview_show_eof */
74 char *mcview_show_eof = NULL;
76 /*** file scope macro definitions ****************************************************************/
78 /*** file scope type declarations ****************************************************************/
80 /*** file scope variables ************************************************************************/
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
86 /** Both views */
87 static gboolean
88 do_mcview_event (mcview_t * view, Gpm_Event * event, int *result)
90 screen_dimen y, x;
91 Gpm_Event local;
92 Widget *w = WIDGET (view);
94 /* rest of the upper frame - call menu */
95 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) != 0 &&
96 event->y == WIDGET (w->owner)->y + 1)
98 *result = MOU_UNHANDLED;
99 return FALSE; /* don't draw viewer over menu */
102 *result = MOU_NORMAL;
104 local = mouse_get_local (event, w);
106 /* We are not interested in the release events */
107 if ((local.type & (GPM_DOWN | GPM_DRAG)) == 0)
108 return FALSE;
110 /* Wheel events */
111 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
113 mcview_move_up (view, 2);
114 return TRUE;
116 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
118 mcview_move_down (view, 2);
119 return TRUE;
122 x = local.x;
123 y = local.y;
125 /* Scrolling left and right */
126 if (!view->text_wrap_mode)
128 if (x < view->data_area.width * 1 / 4)
130 mcview_move_left (view, 1);
131 goto processed;
134 if (x < view->data_area.width * 3 / 4)
136 /* ignore the click */
138 else
140 mcview_move_right (view, 1);
141 goto processed;
145 /* Scrolling up and down */
146 if (y < view->data_area.top + view->data_area.height * 1 / 3)
148 if (mcview_mouse_move_pages)
149 mcview_move_up (view, view->data_area.height / 2);
150 else
151 mcview_move_up (view, 1);
152 goto processed;
154 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
156 /* ignore the click */
158 else
160 if (mcview_mouse_move_pages)
161 mcview_move_down (view, view->data_area.height / 2);
162 else
163 mcview_move_down (view, 1);
164 goto processed;
167 return FALSE;
169 processed:
170 *result = MOU_REPEAT;
171 return TRUE;
174 /* --------------------------------------------------------------------------------------------- */
176 /** Real view only */
177 static int
178 mcview_event (Gpm_Event * event, void *data)
180 mcview_t *view = (mcview_t *) data;
181 int result;
183 if (!mouse_global_in_widget (event, WIDGET (data)))
184 return MOU_UNHANDLED;
186 if (do_mcview_event (view, event, &result))
187 mcview_update (view);
188 return result;
191 /* --------------------------------------------------------------------------------------------- */
192 /*** public functions ****************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
195 mcview_t *
196 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
198 mcview_t *view;
200 view = g_new0 (mcview_t, 1);
201 init_widget (WIDGET (view), y, x, lines, cols, mcview_callback, mcview_event);
203 view->hex_mode = FALSE;
204 view->hexedit_mode = FALSE;
205 view->locked = FALSE;
206 view->hexview_in_text = FALSE;
207 view->text_nroff_mode = FALSE;
208 view->text_wrap_mode = FALSE;
209 view->magic_mode = FALSE;
211 view->dpy_frame_size = is_panel ? 1 : 0;
212 view->converter = str_cnv_from_term;
214 mcview_init (view);
216 if (mcview_default_hex_mode)
217 mcview_toggle_hex_mode (view);
218 if (mcview_default_nroff_flag)
219 mcview_toggle_nroff_mode (view);
220 if (mcview_global_wrap_mode)
221 mcview_toggle_wrap_mode (view);
222 if (mcview_default_magic_flag)
223 mcview_toggle_magic_mode (view);
225 return view;
228 /* --------------------------------------------------------------------------------------------- */
229 /** Real view only */
231 gboolean
232 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line)
234 gboolean succeeded;
235 mcview_t *lc_mcview;
236 WDialog *view_dlg;
238 /* Create dialog and widgets, put them on the dialog */
239 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback, NULL,
240 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
242 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
243 add_widget (view_dlg, lc_mcview);
245 add_widget (view_dlg, buttonbar_new (TRUE));
247 view_dlg->get_title = mcview_get_title;
249 succeeded = mcview_load (lc_mcview, command, vfs_path_as_str (file_vpath), start_line);
251 if (succeeded)
252 run_dlg (view_dlg);
253 else
254 view_dlg->state = DLG_CLOSED;
256 if (view_dlg->state == DLG_CLOSED)
257 destroy_dlg (view_dlg);
259 return succeeded;
262 /* {{{ Miscellaneous functions }}} */
264 /* --------------------------------------------------------------------------------------------- */
266 gboolean
267 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
269 gboolean retval = FALSE;
270 vfs_path_t *vpath = NULL;
272 #ifdef HAVE_ASSERT_H
273 assert (view->bytes_per_line != 0);
274 #endif
276 view->filename_vpath = vfs_path_from_str (file);
278 /* get working dir */
279 if (file != NULL && file[0] != '\0')
281 vfs_path_free (view->workdir_vpath);
283 if (!g_path_is_absolute (file))
285 vfs_path_t *p;
287 p = vfs_path_clone (vfs_get_raw_current_dir ());
288 view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
289 vfs_path_free (p);
291 else
293 /* try extract path form filename */
294 const char *fname;
295 char *dir;
297 fname = x_basename (file);
298 dir = g_strndup (file, (size_t) (fname - file));
299 view->workdir_vpath = vfs_path_from_str (dir);
300 g_free (dir);
304 if (!mcview_is_in_panel (view))
305 view->dpy_text_column = 0;
307 mcview_set_codeset (view);
309 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
310 retval = mcview_load_command_output (view, command);
311 else if (file != NULL && file[0] != '\0')
313 int fd = -1;
314 char tmp[BUF_MEDIUM];
315 struct stat st;
317 /* Open the file */
318 vpath = vfs_path_from_str (file);
319 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
320 if (fd == -1)
322 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
323 file, unix_error_string (errno));
324 mcview_show_error (view, tmp);
325 vfs_path_free (view->filename_vpath);
326 view->filename_vpath = NULL;
327 vfs_path_free (view->workdir_vpath);
328 view->workdir_vpath = NULL;
329 goto finish;
332 /* Make sure we are working with a regular file */
333 if (mc_fstat (fd, &st) == -1)
335 mc_close (fd);
336 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
337 file, unix_error_string (errno));
338 mcview_show_error (view, tmp);
339 vfs_path_free (view->filename_vpath);
340 view->filename_vpath = NULL;
341 vfs_path_free (view->workdir_vpath);
342 view->workdir_vpath = NULL;
343 goto finish;
346 if (!S_ISREG (st.st_mode))
348 mc_close (fd);
349 mcview_show_error (view, _("Cannot view: not a regular file"));
350 vfs_path_free (view->filename_vpath);
351 view->filename_vpath = NULL;
352 vfs_path_free (view->workdir_vpath);
353 view->workdir_vpath = NULL;
354 goto finish;
357 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
359 /* Must be one of those nice files that grow (/proc) */
360 mcview_set_datasource_vfs_pipe (view, fd);
362 else
364 int type;
366 type = get_compression_type (fd, file);
368 if (view->magic_mode && (type != COMPRESSION_NONE))
370 char *tmp_filename;
371 vfs_path_t *vpath1;
372 int fd1;
374 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
375 vpath1 = vfs_path_from_str (tmp_filename);
376 fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
377 if (fd1 == -1)
379 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"),
380 file, unix_error_string (errno));
381 mcview_show_error (view, tmp);
383 else
385 mc_close (fd);
386 fd = fd1;
387 mc_fstat (fd, &st);
389 vfs_path_free (vpath1);
391 g_free (tmp_filename);
393 mcview_set_datasource_file (view, fd, &st);
395 retval = TRUE;
398 finish:
399 view->command = g_strdup (command);
400 view->dpy_start = 0;
401 view->search_start = 0;
402 view->search_end = 0;
403 view->dpy_text_column = 0;
405 mcview_compute_areas (view);
406 mcview_update_bytes_per_line (view);
408 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
410 long line, col;
411 off_t new_offset, max_offset;
413 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
414 max_offset = mcview_get_filesize (view) - 1;
415 if (max_offset < 0)
416 new_offset = 0;
417 else
418 new_offset = min (new_offset, max_offset);
419 if (!view->hex_mode)
420 view->dpy_start = mcview_bol (view, new_offset, 0);
421 else
423 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
424 view->hex_cursor = new_offset;
427 else if (start_line > 0)
428 mcview_moveto (view, start_line - 1, 0);
430 view->hexedit_lownibble = FALSE;
431 view->hexview_in_text = FALSE;
432 view->change_list = NULL;
433 vfs_path_free (vpath);
434 return retval;
437 /* --------------------------------------------------------------------------------------------- */