Ticket #2097: clean up before 4.7.2 release.
[midnight-commander.git] / src / viewer / mcviewer.c
blob6f0990f0f9078dbdac7a1c21592994742d9fad72
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 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
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 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 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, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
35 MA 02110-1301, USA.
38 #include <config.h>
39 #include <errno.h>
40 #include <fcntl.h>
42 #include "lib/global.h"
43 #include "lib/tty/tty.h"
44 #include "lib/vfs/mc-vfs/vfs.h"
45 #include "lib/strutil.h"
47 #include "src/main.h"
48 #include "src/charsets.h"
49 #include "src/main-widgets.h" /* the_menubar */
50 #include "src/menu.h" /* menubar_visible */
51 #include "src/widget.h"
53 #include "internal.h"
54 #include "mcviewer.h"
56 /*** global variables ****************************************************************************/
58 int mcview_default_hex_mode = 0;
59 int mcview_default_nroff_flag = 0;
60 int mcview_global_wrap_mode = 1;
61 int mcview_default_magic_flag = 1;
63 int mcview_altered_hex_mode = 0;
64 int mcview_altered_magic_flag = 0;
65 int mcview_altered_nroff_flag = 0;
67 int mcview_remember_file_position = FALSE;
69 /* Maxlimit for skipping updates */
70 int mcview_max_dirt_limit = 10;
72 /* Scrolling is done in pages or line increments */
73 int mcview_mouse_move_pages = 1;
75 /* end of file will be showen from mcview_show_eof */
76 char *mcview_show_eof = NULL;
78 /*** file scope macro definitions ****************************************************************/
80 /*** file scope type declarations ****************************************************************/
82 /*** file scope variables ************************************************************************/
85 /*** file scope functions ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
89 /* Both views */
90 static int
91 mcview_event (mcview_t * view, Gpm_Event * event, int *result)
93 screen_dimen y, x;
95 *result = MOU_NORMAL;
97 /* rest of the upper frame, the menu is invisible - call menu */
98 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
100 event->x += view->widget.x;
101 *result = the_menubar->widget.mouse (event, the_menubar);
102 return 0; /* don't draw viewer over menu */
105 /* We are not interested in the release events */
106 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
107 return 0;
109 /* Wheel events */
110 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN))
112 mcview_move_up (view, 2);
113 return 1;
115 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN))
117 mcview_move_down (view, 2);
118 return 1;
121 x = event->x;
122 y = event->y;
124 /* Scrolling left and right */
125 if (!view->text_wrap_mode)
127 if (x < view->data_area.width * 1 / 4)
129 mcview_move_left (view, 1);
130 goto processed;
132 else if (x < view->data_area.width * 3 / 4)
134 /* ignore the click */
136 else
138 mcview_move_right (view, 1);
139 goto processed;
143 /* Scrolling up and down */
144 if (y < view->data_area.top + view->data_area.height * 1 / 3)
146 if (mcview_mouse_move_pages)
147 mcview_move_up (view, view->data_area.height / 2);
148 else
149 mcview_move_up (view, 1);
150 goto processed;
152 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
154 /* ignore the click */
156 else
158 if (mcview_mouse_move_pages)
159 mcview_move_down (view, view->data_area.height / 2);
160 else
161 mcview_move_down (view, 1);
162 goto processed;
165 return 0;
167 processed:
168 *result = MOU_REPEAT;
169 return 1;
172 /* --------------------------------------------------------------------------------------------- */
174 /* Real view only */
175 static int
176 mcview_real_event (Gpm_Event * event, void *x)
178 mcview_t *view = (mcview_t *) x;
179 int result;
181 if (mcview_event (view, event, &result))
182 mcview_update (view);
183 return result;
186 static void
187 mcview_set_keymap (mcview_t * view)
189 view->plain_map = default_viewer_keymap;
190 if (viewer_keymap && viewer_keymap->len > 0)
191 view->plain_map = (global_keymap_t *) viewer_keymap->data;
193 view->hex_map = default_viewer_hex_keymap;
194 if (viewer_hex_keymap && viewer_hex_keymap->len > 0)
195 view->hex_map = (global_keymap_t *) viewer_hex_keymap->data;
198 /* --------------------------------------------------------------------------------------------- */
200 /*** public functions ****************************************************************************/
202 /* --------------------------------------------------------------------------------------------- */
204 mcview_t *
205 mcview_new (int y, int x, int lines, int cols, int is_panel)
207 mcview_t *view = g_new0 (mcview_t, 1);
208 size_t i;
210 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_real_event);
212 view->filename = NULL;
213 view->command = NULL;
214 view->search_nroff_seq = NULL;
216 mcview_set_datasource_none (view);
218 view->growbuf_in_use = FALSE;
219 /* leave the other growbuf fields uninitialized */
221 view->hex_mode = FALSE;
222 view->hexedit_mode = FALSE;
223 view->hexview_in_text = FALSE;
224 view->text_nroff_mode = FALSE;
225 view->text_wrap_mode = FALSE;
226 view->magic_mode = FALSE;
227 view->utf8 = FALSE;
229 view->hexedit_lownibble = FALSE;
230 view->coord_cache = NULL;
232 view->dpy_frame_size = is_panel ? 1 : 0;
233 view->dpy_start = 0;
234 view->dpy_text_column = 0;
235 view->dpy_end = 0;
236 view->hex_cursor = 0;
237 view->cursor_col = 0;
238 view->cursor_row = 0;
239 view->change_list = NULL;
240 view->converter = str_cnv_from_term;
241 mcview_set_codeset (view);
242 /* {status,ruler,data}_area are left uninitialized */
244 view->dirty = 0;
245 view->dpy_bbar_dirty = TRUE;
246 view->bytes_per_line = 1;
248 view->search_start = 0;
249 view->search_end = 0;
251 view->want_to_quit = FALSE;
252 view->marker = 0;
253 for (i = 0; i < sizeof (view->marks) / sizeof (view->marks[0]); i++)
254 view->marks[i] = 0;
256 view->move_dir = 0;
257 view->update_steps = 0;
258 view->update_activate = 0;
260 if (mcview_default_hex_mode)
261 mcview_toggle_hex_mode (view);
262 if (mcview_default_nroff_flag)
263 mcview_toggle_nroff_mode (view);
264 if (mcview_global_wrap_mode)
265 mcview_toggle_wrap_mode (view);
266 if (mcview_default_magic_flag)
267 mcview_toggle_magic_mode (view);
269 mcview_set_keymap (view);
271 return view;
274 /* --------------------------------------------------------------------------------------------- */
276 /* Real view only */
278 mcview_viewer (const char *command, const char *file, int *move_dir_p, int start_line)
280 gboolean succeeded;
281 mcview_t *lc_mcview;
282 Dlg_head *view_dlg;
284 /* Create dialog and widgets, put them on the dialog */
285 view_dlg = create_dlg (0, 0, LINES, COLS, NULL, mcview_dialog_callback,
286 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
288 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, 0);
289 add_widget (view_dlg, lc_mcview);
291 add_widget (view_dlg, buttonbar_new (TRUE));
293 succeeded = mcview_load (lc_mcview, command, file, start_line);
294 if (succeeded)
296 run_dlg (view_dlg);
297 if (move_dir_p)
298 *move_dir_p = lc_mcview->move_dir;
300 else
302 if (move_dir_p)
303 *move_dir_p = 0;
305 destroy_dlg (view_dlg);
307 return succeeded;
310 /* {{{ Miscellaneous functions }}} */
312 /* --------------------------------------------------------------------------------------------- */
314 gboolean
315 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
317 int i, type;
318 int fd = -1;
319 char tmp[BUF_MEDIUM];
320 char *canon_fname;
321 struct stat st;
323 gboolean retval = FALSE;
325 assert (view->bytes_per_line != 0);
326 mcview_done (view);
328 /* Set up the state */
329 mcview_set_datasource_none (view);
330 view->filename = g_strdup (file);
331 view->command = 0;
333 /* Clear the markers */
334 view->marker = 0;
335 for (i = 0; i < 10; i++)
336 view->marks[i] = 0;
338 if (!mcview_is_in_panel (view))
340 view->dpy_text_column = 0;
343 if (command && (view->magic_mode || file == NULL || file[0] == '\0'))
345 retval = mcview_load_command_output (view, command);
347 else if (file != NULL && file[0] != '\0')
349 /* Open the file */
350 fd = mc_open (file, O_RDONLY | O_NONBLOCK);
351 if (fd == -1)
353 g_snprintf (tmp, sizeof (tmp), _(" Cannot open \"%s\"\n %s "),
354 file, unix_error_string (errno));
355 mcview_show_error (view, tmp);
356 g_free (view->filename);
357 view->filename = NULL;
358 goto finish;
361 /* Make sure we are working with a regular file */
362 if (mc_fstat (fd, &st) == -1)
364 mc_close (fd);
365 g_snprintf (tmp, sizeof (tmp), _(" Cannot stat \"%s\"\n %s "),
366 file, unix_error_string (errno));
367 mcview_show_error (view, tmp);
368 g_free (view->filename);
369 view->filename = NULL;
370 goto finish;
373 if (!S_ISREG (st.st_mode))
375 mc_close (fd);
376 mcview_show_error (view, _(" Cannot view: not a regular file "));
377 g_free (view->filename);
378 view->filename = NULL;
379 goto finish;
382 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
384 /* Must be one of those nice files that grow (/proc) */
385 mcview_set_datasource_vfs_pipe (view, fd);
387 else
389 type = get_compression_type (fd, file);
391 if (view->magic_mode && (type != COMPRESSION_NONE))
393 g_free (view->filename);
394 view->filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
396 mcview_set_datasource_file (view, fd, &st);
398 retval = TRUE;
401 finish:
402 view->command = g_strdup (command);
403 view->dpy_start = 0;
404 view->search_start = 0;
405 view->search_end = 0;
406 view->dpy_text_column = 0;
408 mcview_compute_areas (view);
409 mcview_update_bytes_per_line (view);
411 if (mcview_remember_file_position && view->filename != NULL && start_line == 0)
413 long line, col;
414 off_t new_offset;
415 canon_fname = vfs_canon (view->filename);
416 load_file_position (canon_fname, &line, &col, &new_offset);
417 new_offset = min (new_offset, mcview_get_filesize (view));
418 view->dpy_start = mcview_bol (view, new_offset);
419 g_free (canon_fname);
421 else if (start_line > 0)
423 mcview_moveto (view, start_line - 1, 0);
426 view->hexedit_lownibble = FALSE;
427 view->hexview_in_text = FALSE;
428 view->change_list = NULL;
429 return retval;
432 /* --------------------------------------------------------------------------------------------- */