Core, mceditor, mcviewer and mcdiffviewer code tweak and cleanup
[midnight-commander.git] / src / viewer / mcviewer.c
blob27137ccf2ec11b54cccf84697db66f231405db8c
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
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
18 Andrew Borodin <aborodin@vmail.ru>, 2009
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/main.h"
51 #include "src/filemanager/layout.h" /* menubar_visible */
52 #include "src/filemanager/midnight.h" /* the_menubar */
54 #include "internal.h"
55 #include "mcviewer.h"
57 /*** global variables ****************************************************************************/
59 int mcview_default_hex_mode = 0;
60 int mcview_default_nroff_flag = 0;
61 int mcview_global_wrap_mode = 1;
62 int mcview_default_magic_flag = 1;
64 int mcview_altered_hex_mode = 0;
65 int mcview_altered_magic_flag = 0;
66 int mcview_altered_nroff_flag = 0;
68 int mcview_remember_file_position = FALSE;
70 /* Maxlimit for skipping updates */
71 int mcview_max_dirt_limit = 10;
73 /* Scrolling is done in pages or line increments */
74 int mcview_mouse_move_pages = 1;
76 /* end of file will be showen from mcview_show_eof */
77 char *mcview_show_eof = NULL;
79 /*** file scope macro definitions ****************************************************************/
81 /*** file scope type declarations ****************************************************************/
83 /*** file scope variables ************************************************************************/
86 /*** file scope functions ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
89 /** Both views */
91 static int
92 mcview_event (mcview_t * view, Gpm_Event * event, int *result)
94 screen_dimen y, x;
96 *result = MOU_NORMAL;
98 /* rest of the upper frame, the menu is invisible - call menu */
99 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
101 event->x += view->widget.x;
102 *result = the_menubar->widget.mouse (event, the_menubar);
103 return 0; /* don't draw viewer over menu */
106 /* We are not interested in the release events */
107 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
108 return 0;
110 /* Wheel events */
111 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN))
113 mcview_move_up (view, 2);
114 return 1;
116 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN))
118 mcview_move_down (view, 2);
119 return 1;
122 x = event->x;
123 y = event->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;
133 else if (x < view->data_area.width * 3 / 4)
135 /* ignore the click */
137 else
139 mcview_move_right (view, 1);
140 goto processed;
144 /* Scrolling up and down */
145 if (y < view->data_area.top + view->data_area.height * 1 / 3)
147 if (mcview_mouse_move_pages)
148 mcview_move_up (view, view->data_area.height / 2);
149 else
150 mcview_move_up (view, 1);
151 goto processed;
153 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
155 /* ignore the click */
157 else
159 if (mcview_mouse_move_pages)
160 mcview_move_down (view, view->data_area.height / 2);
161 else
162 mcview_move_down (view, 1);
163 goto processed;
166 return 0;
168 processed:
169 *result = MOU_REPEAT;
170 return 1;
173 /* --------------------------------------------------------------------------------------------- */
174 /** Real view only */
176 static int
177 mcview_real_event (Gpm_Event * event, void *x)
179 mcview_t *view = (mcview_t *) x;
180 int result;
182 if (mcview_event (view, event, &result))
183 mcview_update (view);
184 return result;
188 /* --------------------------------------------------------------------------------------------- */
189 /*** public functions ****************************************************************************/
190 /* --------------------------------------------------------------------------------------------- */
192 mcview_t *
193 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
195 mcview_t *view = g_new0 (mcview_t, 1);
197 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_real_event);
199 view->hex_mode = FALSE;
200 view->hexedit_mode = FALSE;
201 view->locked = FALSE;
202 view->hexview_in_text = FALSE;
203 view->text_nroff_mode = FALSE;
204 view->text_wrap_mode = FALSE;
205 view->magic_mode = FALSE;
207 view->dpy_frame_size = is_panel ? 1 : 0;
208 view->converter = str_cnv_from_term;
210 mcview_init (view);
212 if (mcview_default_hex_mode)
213 mcview_toggle_hex_mode (view);
214 if (mcview_default_nroff_flag)
215 mcview_toggle_nroff_mode (view);
216 if (mcview_global_wrap_mode)
217 mcview_toggle_wrap_mode (view);
218 if (mcview_default_magic_flag)
219 mcview_toggle_magic_mode (view);
221 return view;
224 /* --------------------------------------------------------------------------------------------- */
225 /** Real view only */
227 mcview_ret_t
228 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line)
230 gboolean succeeded;
231 mcview_t *lc_mcview;
232 Dlg_head *view_dlg;
233 mcview_ret_t ret;
235 /* Create dialog and widgets, put them on the dialog */
236 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback,
237 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
239 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
240 add_widget (view_dlg, lc_mcview);
242 add_widget (view_dlg, buttonbar_new (TRUE));
244 view_dlg->get_title = mcview_get_title;
247 char *file;
249 file = vfs_path_to_str (file_vpath);
250 succeeded = mcview_load (lc_mcview, command, file, start_line);
251 g_free (file);
254 if (succeeded)
256 run_dlg (view_dlg);
258 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
259 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
261 else
263 view_dlg->state = DLG_CLOSED;
264 ret = MCVIEW_EXIT_FAILURE;
267 if (view_dlg->state == DLG_CLOSED)
268 destroy_dlg (view_dlg);
270 return ret;
273 /* {{{ Miscellaneous functions }}} */
275 /* --------------------------------------------------------------------------------------------- */
277 gboolean
278 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
280 gboolean retval = FALSE;
281 vfs_path_t *vpath = NULL;
283 #ifdef HAVE_ASSERT_H
284 assert (view->bytes_per_line != 0);
285 #endif
287 view->filename_vpath = vfs_path_from_str (file);
289 if ((view->workdir_vpath == NULL) && (file != NULL))
291 if (!g_path_is_absolute (file))
292 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
293 else
295 /* try extract path form filename */
296 char *dirname;
298 dirname = g_path_get_dirname (file);
299 if (strcmp (dirname, ".") != 0)
300 view->workdir_vpath = vfs_path_from_str (dirname);
301 else
303 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
305 g_free (dirname);
309 if (!mcview_is_in_panel (view))
310 view->dpy_text_column = 0;
312 mcview_set_codeset (view);
314 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
315 retval = mcview_load_command_output (view, command);
316 else if (file != NULL && file[0] != '\0')
318 int fd = -1;
319 char tmp[BUF_MEDIUM];
320 struct stat st;
322 /* Open the file */
323 vpath = vfs_path_from_str (file);
324 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
325 if (fd == -1)
327 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
328 file, unix_error_string (errno));
329 mcview_show_error (view, tmp);
330 vfs_path_free (view->filename_vpath);
331 view->filename_vpath = NULL;
332 vfs_path_free (view->workdir_vpath);
333 view->workdir_vpath = NULL;
334 goto finish;
337 /* Make sure we are working with a regular file */
338 if (mc_fstat (fd, &st) == -1)
340 mc_close (fd);
341 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
342 file, unix_error_string (errno));
343 mcview_show_error (view, tmp);
344 vfs_path_free (view->filename_vpath);
345 view->filename_vpath = NULL;
346 vfs_path_free (view->workdir_vpath);
347 view->workdir_vpath = NULL;
348 goto finish;
351 if (!S_ISREG (st.st_mode))
353 mc_close (fd);
354 mcview_show_error (view, _("Cannot view: not a regular file"));
355 vfs_path_free (view->filename_vpath);
356 view->filename_vpath = NULL;
357 vfs_path_free (view->workdir_vpath);
358 view->workdir_vpath = NULL;
359 goto finish;
362 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
364 /* Must be one of those nice files that grow (/proc) */
365 mcview_set_datasource_vfs_pipe (view, fd);
367 else
369 int type;
371 type = get_compression_type (fd, file);
373 if (view->magic_mode && (type != COMPRESSION_NONE))
375 char *tmp_filename;
377 vfs_path_free (view->filename_vpath);
378 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
379 view->filename_vpath = vfs_path_from_str (tmp_filename);
380 g_free (tmp_filename);
382 mcview_set_datasource_file (view, fd, &st);
384 retval = TRUE;
387 finish:
388 view->command = g_strdup (command);
389 view->dpy_start = 0;
390 view->search_start = 0;
391 view->search_end = 0;
392 view->dpy_text_column = 0;
394 mcview_compute_areas (view);
395 mcview_update_bytes_per_line (view);
397 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
399 long line, col;
400 off_t new_offset, max_offset;
402 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
403 max_offset = mcview_get_filesize (view) - 1;
404 if (max_offset < 0)
405 new_offset = 0;
406 else
407 new_offset = min (new_offset, max_offset);
408 if (!view->hex_mode)
409 view->dpy_start = mcview_bol (view, new_offset, 0);
410 else
412 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
413 view->hex_cursor = new_offset;
416 else if (start_line > 0)
417 mcview_moveto (view, start_line - 1, 0);
419 view->hexedit_lownibble = FALSE;
420 view->hexview_in_text = FALSE;
421 view->change_list = NULL;
422 vfs_path_free (vpath);
423 return retval;
426 /* --------------------------------------------------------------------------------------------- */