93908f3ea795e993313f809e625f8a25c68c5fc0
[midnight-commander.git] / src / viewer / mcviewer.c
blob93908f3ea795e993313f809e625f8a25c68c5fc0
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/filemanager/layout.h" /* menubar_visible */
50 #include "src/filemanager/midnight.h" /* the_menubar */
52 #include "internal.h"
53 #include "mcviewer.h"
55 /*** global variables ****************************************************************************/
57 int mcview_default_hex_mode = 0;
58 int mcview_default_nroff_flag = 0;
59 int mcview_global_wrap_mode = 1;
60 int mcview_default_magic_flag = 1;
62 int mcview_altered_hex_mode = 0;
63 int mcview_altered_magic_flag = 0;
64 int mcview_altered_nroff_flag = 0;
66 int mcview_remember_file_position = FALSE;
68 /* Maxlimit for skipping updates */
69 int mcview_max_dirt_limit = 10;
71 /* Scrolling is done in pages or line increments */
72 int mcview_mouse_move_pages = 1;
74 /* end of file will be showen from mcview_show_eof */
75 char *mcview_show_eof = NULL;
77 /*** file scope macro definitions ****************************************************************/
79 /*** file scope type declarations ****************************************************************/
81 /*** file scope variables ************************************************************************/
84 /*** file scope functions ************************************************************************/
85 /* --------------------------------------------------------------------------------------------- */
87 /** Both views */
88 static gboolean
89 do_mcview_event (mcview_t * view, Gpm_Event * event, int *result)
91 screen_dimen y, x;
92 Gpm_Event local;
93 Widget *w = WIDGET (view);
95 /* rest of the upper frame - call menu */
96 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) != 0 &&
97 event->y == WIDGET (w->owner)->y + 1)
99 *result = MOU_UNHANDLED;
100 return FALSE; /* don't draw viewer over menu */
103 *result = MOU_NORMAL;
105 local = mouse_get_local (event, w);
107 /* We are not interested in the release events */
108 if ((local.type & (GPM_DOWN | GPM_DRAG)) == 0)
109 return FALSE;
111 /* Wheel events */
112 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
114 mcview_move_up (view, 2);
115 return TRUE;
117 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
119 mcview_move_down (view, 2);
120 return TRUE;
123 x = local.x;
124 y = local.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;
135 if (x < view->data_area.width * 3 / 4)
137 /* ignore the click */
139 else
141 mcview_move_right (view, 1);
142 goto processed;
146 /* Scrolling up and down */
147 if (y < view->data_area.top + view->data_area.height * 1 / 3)
149 if (mcview_mouse_move_pages)
150 mcview_move_up (view, view->data_area.height / 2);
151 else
152 mcview_move_up (view, 1);
153 goto processed;
155 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
157 /* ignore the click */
159 else
161 if (mcview_mouse_move_pages)
162 mcview_move_down (view, view->data_area.height / 2);
163 else
164 mcview_move_down (view, 1);
165 goto processed;
168 return FALSE;
170 processed:
171 *result = MOU_REPEAT;
172 return TRUE;
175 /* --------------------------------------------------------------------------------------------- */
177 /** Real view only */
178 static int
179 mcview_event (Gpm_Event * event, void *data)
181 mcview_t *view = (mcview_t *) data;
182 int result;
184 if (!mouse_global_in_widget (event, WIDGET (data)))
185 return MOU_UNHANDLED;
187 if (do_mcview_event (view, event, &result))
188 mcview_update (view);
189 return result;
192 /* --------------------------------------------------------------------------------------------- */
193 /*** public functions ****************************************************************************/
194 /* --------------------------------------------------------------------------------------------- */
196 mcview_t *
197 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
199 mcview_t *view;
201 view = g_new0 (mcview_t, 1);
202 init_widget (WIDGET (view), y, x, lines, cols, mcview_callback, mcview_event);
204 view->hex_mode = FALSE;
205 view->hexedit_mode = FALSE;
206 view->locked = FALSE;
207 view->hexview_in_text = FALSE;
208 view->text_nroff_mode = FALSE;
209 view->text_wrap_mode = FALSE;
210 view->magic_mode = FALSE;
212 view->dpy_frame_size = is_panel ? 1 : 0;
213 view->converter = str_cnv_from_term;
215 mcview_init (view);
217 if (mcview_default_hex_mode)
218 mcview_toggle_hex_mode (view);
219 if (mcview_default_nroff_flag)
220 mcview_toggle_nroff_mode (view);
221 if (mcview_global_wrap_mode)
222 mcview_toggle_wrap_mode (view);
223 if (mcview_default_magic_flag)
224 mcview_toggle_magic_mode (view);
226 return view;
229 /* --------------------------------------------------------------------------------------------- */
230 /** Real view only */
232 gboolean
233 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line)
235 gboolean succeeded;
236 mcview_t *lc_mcview;
237 WDialog *view_dlg;
239 /* Create dialog and widgets, put them on the dialog */
240 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback, NULL,
241 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
243 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
244 add_widget (view_dlg, lc_mcview);
246 add_widget (view_dlg, buttonbar_new (TRUE));
248 view_dlg->get_title = mcview_get_title;
251 char *file;
253 file = vfs_path_to_str (file_vpath);
254 succeeded = mcview_load (lc_mcview, command, file, start_line);
255 g_free (file);
258 if (succeeded)
259 run_dlg (view_dlg);
260 else
261 view_dlg->state = DLG_CLOSED;
263 if (view_dlg->state == DLG_CLOSED)
264 destroy_dlg (view_dlg);
266 return succeeded;
269 /* {{{ Miscellaneous functions }}} */
271 /* --------------------------------------------------------------------------------------------- */
273 gboolean
274 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
276 gboolean retval = FALSE;
277 vfs_path_t *vpath = NULL;
279 #ifdef HAVE_ASSERT_H
280 assert (view->bytes_per_line != 0);
281 #endif
283 view->filename_vpath = vfs_path_from_str (file);
285 /* get working dir */
286 if (file != NULL && file[0] != '\0')
288 vfs_path_free (view->workdir_vpath);
290 if (!g_path_is_absolute (file))
292 vfs_path_t *p;
294 p = vfs_path_clone (vfs_get_raw_current_dir ());
295 view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
296 vfs_path_free (p);
298 else
300 /* try extract path form filename */
301 const char *fname;
302 char *dir;
304 fname = x_basename (file);
305 dir = g_strndup (file, (size_t) (fname - file));
306 view->workdir_vpath = vfs_path_from_str (dir);
307 g_free (dir);
311 if (!mcview_is_in_panel (view))
312 view->dpy_text_column = 0;
314 mcview_set_codeset (view);
316 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
317 retval = mcview_load_command_output (view, command);
318 else if (file != NULL && file[0] != '\0')
320 int fd = -1;
321 char tmp[BUF_MEDIUM];
322 struct stat st;
324 /* Open the file */
325 vpath = vfs_path_from_str (file);
326 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
327 if (fd == -1)
329 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
330 file, unix_error_string (errno));
331 mcview_show_error (view, tmp);
332 vfs_path_free (view->filename_vpath);
333 view->filename_vpath = NULL;
334 vfs_path_free (view->workdir_vpath);
335 view->workdir_vpath = NULL;
336 goto finish;
339 /* Make sure we are working with a regular file */
340 if (mc_fstat (fd, &st) == -1)
342 mc_close (fd);
343 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
344 file, unix_error_string (errno));
345 mcview_show_error (view, tmp);
346 vfs_path_free (view->filename_vpath);
347 view->filename_vpath = NULL;
348 vfs_path_free (view->workdir_vpath);
349 view->workdir_vpath = NULL;
350 goto finish;
353 if (!S_ISREG (st.st_mode))
355 mc_close (fd);
356 mcview_show_error (view, _("Cannot view: not a regular file"));
357 vfs_path_free (view->filename_vpath);
358 view->filename_vpath = NULL;
359 vfs_path_free (view->workdir_vpath);
360 view->workdir_vpath = NULL;
361 goto finish;
364 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
366 /* Must be one of those nice files that grow (/proc) */
367 mcview_set_datasource_vfs_pipe (view, fd);
369 else
371 int type;
373 type = get_compression_type (fd, file);
375 if (view->magic_mode && (type != COMPRESSION_NONE))
377 char *tmp_filename;
379 vfs_path_free (view->filename_vpath);
380 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
381 view->filename_vpath = vfs_path_from_str (tmp_filename);
382 g_free (tmp_filename);
384 mcview_set_datasource_file (view, fd, &st);
386 retval = TRUE;
389 finish:
390 view->command = g_strdup (command);
391 view->dpy_start = 0;
392 view->search_start = 0;
393 view->search_end = 0;
394 view->dpy_text_column = 0;
396 mcview_compute_areas (view);
397 mcview_update_bytes_per_line (view);
399 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
401 long line, col;
402 off_t new_offset, max_offset;
404 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
405 max_offset = mcview_get_filesize (view) - 1;
406 if (max_offset < 0)
407 new_offset = 0;
408 else
409 new_offset = min (new_offset, max_offset);
410 if (!view->hex_mode)
411 view->dpy_start = mcview_bol (view, new_offset, 0);
412 else
414 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
415 view->hex_cursor = new_offset;
418 else if (start_line > 0)
419 mcview_moveto (view, start_line - 1, 0);
421 view->hexedit_lownibble = FALSE;
422 view->hexview_in_text = FALSE;
423 view->change_list = NULL;
424 vfs_path_free (vpath);
425 return retval;
428 /* --------------------------------------------------------------------------------------------- */