Changed internal viewer to use vfs_path_t objects.
[midnight-commander.git] / src / viewer / mcviewer.c
blobcc90b5b413056c18054ffece54db8a8e3f82b4cd
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"
48 #include "lib/charsets.h"
50 #include "src/main.h"
52 #include "src/filemanager/layout.h" /* menubar_visible */
53 #include "src/filemanager/midnight.h" /* the_menubar */
55 #include "internal.h"
56 #include "mcviewer.h"
58 /*** global variables ****************************************************************************/
60 int mcview_default_hex_mode = 0;
61 int mcview_default_nroff_flag = 0;
62 int mcview_global_wrap_mode = 1;
63 int mcview_default_magic_flag = 1;
65 int mcview_altered_hex_mode = 0;
66 int mcview_altered_magic_flag = 0;
67 int mcview_altered_nroff_flag = 0;
69 int mcview_remember_file_position = FALSE;
71 /* Maxlimit for skipping updates */
72 int mcview_max_dirt_limit = 10;
74 /* Scrolling is done in pages or line increments */
75 int mcview_mouse_move_pages = 1;
77 /* end of file will be showen from mcview_show_eof */
78 char *mcview_show_eof = NULL;
80 /*** file scope macro definitions ****************************************************************/
82 /*** file scope type declarations ****************************************************************/
84 /*** file scope variables ************************************************************************/
87 /*** file scope functions ************************************************************************/
88 /* --------------------------------------------------------------------------------------------- */
90 /** Both views */
92 static int
93 mcview_event (mcview_t * view, Gpm_Event * event, int *result)
95 screen_dimen y, x;
97 *result = MOU_NORMAL;
99 /* rest of the upper frame, the menu is invisible - call menu */
100 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
102 event->x += view->widget.x;
103 *result = the_menubar->widget.mouse (event, the_menubar);
104 return 0; /* don't draw viewer over menu */
107 /* We are not interested in the release events */
108 if (!(event->type & (GPM_DOWN | GPM_DRAG)))
109 return 0;
111 /* Wheel events */
112 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN))
114 mcview_move_up (view, 2);
115 return 1;
117 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN))
119 mcview_move_down (view, 2);
120 return 1;
123 x = event->x;
124 y = event->y;
126 /* Scrolling left and right */
127 if (!view->text_wrap_mode)
129 if (x < view->data_area.width * 1 / 4)
131 mcview_move_left (view, 1);
132 goto processed;
134 else 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 0;
169 processed:
170 *result = MOU_REPEAT;
171 return 1;
174 /* --------------------------------------------------------------------------------------------- */
175 /** Real view only */
177 static int
178 mcview_real_event (Gpm_Event * event, void *x)
180 mcview_t *view = (mcview_t *) x;
181 int result;
183 if (mcview_event (view, event, &result))
184 mcview_update (view);
185 return result;
189 /* --------------------------------------------------------------------------------------------- */
190 /*** public functions ****************************************************************************/
191 /* --------------------------------------------------------------------------------------------- */
193 mcview_t *
194 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
196 mcview_t *view = g_new0 (mcview_t, 1);
198 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_real_event);
200 view->hex_mode = FALSE;
201 view->hexedit_mode = FALSE;
202 view->locked = FALSE;
203 view->hexview_in_text = FALSE;
204 view->text_nroff_mode = FALSE;
205 view->text_wrap_mode = FALSE;
206 view->magic_mode = FALSE;
208 view->dpy_frame_size = is_panel ? 1 : 0;
209 view->converter = str_cnv_from_term;
211 mcview_init (view);
213 if (mcview_default_hex_mode)
214 mcview_toggle_hex_mode (view);
215 if (mcview_default_nroff_flag)
216 mcview_toggle_nroff_mode (view);
217 if (mcview_global_wrap_mode)
218 mcview_toggle_wrap_mode (view);
219 if (mcview_default_magic_flag)
220 mcview_toggle_magic_mode (view);
222 return view;
225 /* --------------------------------------------------------------------------------------------- */
226 /** Real view only */
228 mcview_ret_t
229 mcview_viewer (const char *command, const char *file, int start_line)
231 gboolean succeeded;
232 mcview_t *lc_mcview;
233 Dlg_head *view_dlg;
234 mcview_ret_t ret;
236 /* Create dialog and widgets, put them on the dialog */
237 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback,
238 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
240 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
241 add_widget (view_dlg, lc_mcview);
243 add_widget (view_dlg, buttonbar_new (TRUE));
245 view_dlg->get_title = mcview_get_title;
247 succeeded = mcview_load (lc_mcview, command, file, start_line);
249 if (succeeded)
251 run_dlg (view_dlg);
253 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
254 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
256 else
258 view_dlg->state = DLG_CLOSED;
259 ret = MCVIEW_EXIT_FAILURE;
262 if (view_dlg->state == DLG_CLOSED)
263 destroy_dlg (view_dlg);
265 return ret;
268 /* {{{ Miscellaneous functions }}} */
270 /* --------------------------------------------------------------------------------------------- */
272 gboolean
273 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
275 gboolean retval = FALSE;
276 vfs_path_t *vpath = NULL;
278 #ifdef HAVE_ASSERT_H
279 assert (view->bytes_per_line != 0);
280 #endif
282 view->filename_vpath = vfs_path_from_str (file);
284 if ((view->workdir_vpath == NULL) && (file != NULL))
286 if (!g_path_is_absolute (file))
287 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
288 else
290 /* try extract path form filename */
291 char *dirname;
293 dirname = g_path_get_dirname (file);
294 if (strcmp (dirname, ".") != 0)
295 view->workdir_vpath = vfs_path_from_str (dirname);
296 else
298 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
300 g_free (dirname);
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;
372 vfs_path_free (view->filename_vpath);
373 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
374 view->filename_vpath = vfs_path_from_str (tmp_filename);
375 g_free (tmp_filename);
377 mcview_set_datasource_file (view, fd, &st);
379 retval = TRUE;
382 finish:
383 view->command = g_strdup (command);
384 view->dpy_start = 0;
385 view->search_start = 0;
386 view->search_end = 0;
387 view->dpy_text_column = 0;
389 mcview_compute_areas (view);
390 mcview_update_bytes_per_line (view);
392 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
394 long line, col;
395 off_t new_offset, max_offset;
397 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
398 max_offset = mcview_get_filesize (view) - 1;
399 if (max_offset < 0)
400 new_offset = 0;
401 else
402 new_offset = min (new_offset, max_offset);
403 if (!view->hex_mode)
404 view->dpy_start = mcview_bol (view, new_offset, 0);
405 else
407 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
408 view->hex_cursor = new_offset;
411 else if (start_line > 0)
412 mcview_moveto (view, start_line - 1, 0);
414 view->hexedit_lownibble = FALSE;
415 view->hexview_in_text = FALSE;
416 view->change_list = NULL;
417 vfs_path_free (vpath);
418 return retval;
421 /* --------------------------------------------------------------------------------------------- */