Ticket #2287 (mcedit: persistent bookmarks)
[midnight-commander.git] / src / viewer / mcviewer.c
blob75fa8c21a312cf72faec108cb97dd4227a37b1c4
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/tty/mouse.h"
45 #include "lib/vfs/mc-vfs/vfs.h"
46 #include "lib/strutil.h"
48 #include "src/main.h"
49 #include "src/charsets.h"
50 #include "src/main-widgets.h" /* the_menubar */
51 #include "src/menu.h" /* menubar_visible */
52 #include "src/widget.h"
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 ************************************************************************/
88 /* --------------------------------------------------------------------------------------------- */
90 /* 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 /* --------------------------------------------------------------------------------------------- */
175 /* 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;
187 static void
188 mcview_set_keymap (mcview_t * view)
190 view->plain_map = default_viewer_keymap;
191 if (viewer_keymap && viewer_keymap->len > 0)
192 view->plain_map = (global_keymap_t *) viewer_keymap->data;
194 view->hex_map = default_viewer_hex_keymap;
195 if (viewer_hex_keymap && viewer_hex_keymap->len > 0)
196 view->hex_map = (global_keymap_t *) viewer_hex_keymap->data;
199 /* --------------------------------------------------------------------------------------------- */
201 /*** public functions ****************************************************************************/
203 /* --------------------------------------------------------------------------------------------- */
205 mcview_t *
206 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
208 mcview_t *view = g_new0 (mcview_t, 1);
210 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_real_event);
212 mcview_set_keymap (view);
214 view->hex_mode = FALSE;
215 view->hexedit_mode = FALSE;
216 view->locked = FALSE;
217 view->hexview_in_text = FALSE;
218 view->text_nroff_mode = FALSE;
219 view->text_wrap_mode = FALSE;
220 view->magic_mode = FALSE;
222 view->dpy_frame_size = is_panel ? 1 : 0;
223 view->converter = str_cnv_from_term;
225 mcview_init (view);
227 if (mcview_default_hex_mode)
228 mcview_toggle_hex_mode (view);
229 if (mcview_default_nroff_flag)
230 mcview_toggle_nroff_mode (view);
231 if (mcview_global_wrap_mode)
232 mcview_toggle_wrap_mode (view);
233 if (mcview_default_magic_flag)
234 mcview_toggle_magic_mode (view);
236 return view;
239 /* --------------------------------------------------------------------------------------------- */
241 /* Real view only */
242 mcview_ret_t
243 mcview_viewer (const char *command, const char *file, int start_line)
245 gboolean succeeded;
246 mcview_t *lc_mcview;
247 Dlg_head *view_dlg;
248 mcview_ret_t ret;
250 /* Create dialog and widgets, put them on the dialog */
251 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback,
252 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
254 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
255 add_widget (view_dlg, lc_mcview);
257 add_widget (view_dlg, buttonbar_new (TRUE));
259 view_dlg->get_title = mcview_get_title;
261 succeeded = mcview_load (lc_mcview, command, file, start_line);
263 if (succeeded)
265 run_dlg (view_dlg);
267 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
268 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
270 else
272 view_dlg->state = DLG_CLOSED;
273 ret = MCVIEW_EXIT_FAILURE;
276 if (view_dlg->state == DLG_CLOSED)
277 destroy_dlg (view_dlg);
279 return ret;
282 /* {{{ Miscellaneous functions }}} */
284 /* --------------------------------------------------------------------------------------------- */
286 gboolean
287 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
289 gboolean retval = FALSE;
291 assert (view->bytes_per_line != 0);
293 view->filename = g_strdup (file);
295 if ((view->workdir == NULL) && (file != NULL))
297 if (!g_path_is_absolute (file))
299 #ifdef ENABLE_VFS
300 view->workdir = g_strdup (vfs_get_current_dir ());
301 #else /* ENABLE_VFS */
302 view->workdir = g_get_current_dir ();
303 #endif /* ENABLE_VFS */
305 else
307 /* try extract path form filename */
308 char *dirname;
310 dirname = g_path_get_dirname (file);
311 if (strcmp (dirname, ".") != 0)
312 view->workdir = dirname;
313 else
315 g_free (dirname);
316 #ifdef ENABLE_VFS
317 view->workdir = g_strdup (vfs_get_current_dir ());
318 #else /* ENABLE_VFS */
319 view->workdir = g_get_current_dir ();
320 #endif /* ENABLE_VFS */
325 if (!mcview_is_in_panel (view))
326 view->dpy_text_column = 0;
328 mcview_set_codeset (view);
330 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
331 retval = mcview_load_command_output (view, command);
332 else if (file != NULL && file[0] != '\0')
334 int fd = -1;
335 char tmp[BUF_MEDIUM];
336 struct stat st;
338 /* Open the file */
339 fd = mc_open (file, O_RDONLY | O_NONBLOCK);
340 if (fd == -1)
342 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
343 file, unix_error_string (errno));
344 mcview_show_error (view, tmp);
345 g_free (view->filename);
346 view->filename = NULL;
347 g_free (view->workdir);
348 view->workdir = NULL;
349 goto finish;
352 /* Make sure we are working with a regular file */
353 if (mc_fstat (fd, &st) == -1)
355 mc_close (fd);
356 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
357 file, unix_error_string (errno));
358 mcview_show_error (view, tmp);
359 g_free (view->filename);
360 view->filename = NULL;
361 g_free (view->workdir);
362 view->workdir = NULL;
363 goto finish;
366 if (!S_ISREG (st.st_mode))
368 mc_close (fd);
369 mcview_show_error (view, _("Cannot view: not a regular file"));
370 g_free (view->filename);
371 view->filename = NULL;
372 g_free (view->workdir);
373 view->workdir = NULL;
374 goto finish;
377 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
379 /* Must be one of those nice files that grow (/proc) */
380 mcview_set_datasource_vfs_pipe (view, fd);
382 else
384 int type;
386 type = get_compression_type (fd, file);
388 if (view->magic_mode && (type != COMPRESSION_NONE))
390 g_free (view->filename);
391 view->filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
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 != NULL && start_line == 0)
410 char *canon_fname;
411 long line, col;
412 off_t new_offset;
414 canon_fname = vfs_canon (view->filename);
415 load_file_position (canon_fname, &line, &col, &new_offset, &view->saved_bookmarks);
416 new_offset = min (new_offset, mcview_get_filesize (view));
417 view->dpy_start = mcview_bol (view, new_offset);
418 g_free (canon_fname);
420 else if (start_line > 0)
421 mcview_moveto (view, start_line - 1, 0);
423 view->hexedit_lownibble = FALSE;
424 view->hexview_in_text = FALSE;
425 view->change_list = NULL;
426 return retval;
429 /* --------------------------------------------------------------------------------------------- */