Merge branch '3735_mcedit_search_error_messages'
[midnight-commander.git] / src / viewer / mcviewer.c
blobcaf4d1148758647c97325d15c9f284c1acb39c38
1 /*
2 Internal file viewer for the Midnight Commander
3 Interface functions
5 Copyright (C) 1994-2016
6 Free Software Foundation, Inc
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
11 Jakub Jelinek, 1995
12 Joseph M. Hinkle, 1996
13 Norbert Warmuth, 1997
14 Pavel Machek, 1998
15 Roland Illig <roland.illig@gmx.de>, 2004, 2005
16 Slava Zanko <slavazanko@google.com>, 2009, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2009, 2013
18 Ilia Maslakov <il.smind@gmail.com>, 2009
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 3 of the License,
25 or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU 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, see <http://www.gnu.org/licenses/>.
36 #include <config.h>
37 #include <errno.h>
39 #include "lib/global.h"
40 #include "lib/tty/tty.h"
41 #include "lib/vfs/vfs.h"
42 #include "lib/strutil.h"
43 #include "lib/util.h" /* load_file_position() */
44 #include "lib/widget.h"
46 #include "src/filemanager/layout.h" /* menubar_visible */
47 #include "src/filemanager/midnight.h" /* the_menubar */
49 #include "internal.h"
51 /*** global variables ****************************************************************************/
53 int mcview_default_hex_mode = 0;
54 int mcview_default_nroff_flag = 0;
55 int mcview_global_wrap_mode = 1;
56 int mcview_default_magic_flag = 1;
58 int mcview_altered_hex_mode = 0;
59 int mcview_altered_magic_flag = 0;
60 int mcview_altered_nroff_flag = 0;
62 int mcview_remember_file_position = FALSE;
64 /* Maxlimit for skipping updates */
65 int mcview_max_dirt_limit = 10;
67 /* Scrolling is done in pages or line increments */
68 int mcview_mouse_move_pages = 1;
70 /* end of file will be showen from mcview_show_eof */
71 char *mcview_show_eof = NULL;
73 /*** file scope macro definitions ****************************************************************/
75 /*** file scope type declarations ****************************************************************/
77 /*** file scope variables ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
80 /*** file scope functions ************************************************************************/
81 /* --------------------------------------------------------------------------------------------- */
83 static void
84 mcview_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
86 WView *view = (WView *) w;
87 gboolean ok = TRUE;
89 switch (msg)
91 case MSG_MOUSE_DOWN:
92 if (mcview_is_in_panel (view))
94 if (event->y == WIDGET (w->owner)->y)
96 /* return MOU_UNHANDLED */
97 event->result.abort = TRUE;
98 /* don't draw viewer over menu */
99 ok = FALSE;
100 break;
103 if (!widget_get_state (w, WST_FOCUSED))
105 /* Grab focus */
106 change_panel ();
109 /* fall throught */
111 case MSG_MOUSE_CLICK:
112 if (!view->text_wrap_mode)
114 /* Scrolling left and right */
115 screen_dimen x;
117 x = event->x + 1; /* FIXME */
119 if (x < view->data_area.width * 1 / 4)
121 mcview_move_left (view, 1);
122 event->result.repeat = msg == MSG_MOUSE_DOWN;
124 else if (x < view->data_area.width * 3 / 4)
126 /* ignore the click */
127 ok = FALSE;
129 else
131 mcview_move_right (view, 1);
132 event->result.repeat = msg == MSG_MOUSE_DOWN;
135 else
137 /* Scrolling up and down */
138 screen_dimen y;
140 y = event->y + 1; /* FIXME */
142 if (y < view->data_area.top + view->data_area.height * 1 / 3)
144 if (mcview_mouse_move_pages)
145 mcview_move_up (view, view->data_area.height / 2);
146 else
147 mcview_move_up (view, 1);
149 event->result.repeat = msg == MSG_MOUSE_DOWN;
151 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
153 /* ignore the click */
154 ok = FALSE;
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);
163 event->result.repeat = msg == MSG_MOUSE_DOWN;
166 break;
168 case MSG_MOUSE_SCROLL_UP:
169 mcview_move_up (view, 2);
170 break;
172 case MSG_MOUSE_SCROLL_DOWN:
173 mcview_move_down (view, 2);
174 break;
176 default:
177 ok = FALSE;
178 break;
181 if (ok)
182 mcview_update (view);
185 /* --------------------------------------------------------------------------------------------- */
186 /*** public functions ****************************************************************************/
187 /* --------------------------------------------------------------------------------------------- */
189 WView *
190 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
192 WView *view;
193 Widget *w;
195 view = g_new0 (WView, 1);
196 w = WIDGET (view);
197 widget_init (w, y, x, lines, cols, mcview_callback, mcview_mouse_callback);
198 w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
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 gboolean
229 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line,
230 off_t search_start, off_t search_end)
232 gboolean succeeded;
233 WView *lc_mcview;
234 WDialog *view_dlg;
236 /* Create dialog and widgets, put them on the dialog */
237 view_dlg = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, mcview_dialog_callback,
238 NULL, "[Internal File Viewer]", NULL);
239 widget_want_tab (WIDGET (view_dlg), TRUE);
241 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
242 add_widget (view_dlg, lc_mcview);
244 add_widget (view_dlg, buttonbar_new (TRUE));
246 view_dlg->get_title = mcview_get_title;
248 succeeded =
249 mcview_load (lc_mcview, command, vfs_path_as_str (file_vpath), start_line, search_start,
250 search_end);
252 if (succeeded)
253 dlg_run (view_dlg);
254 else
255 dlg_stop (view_dlg);
257 if (widget_get_state (WIDGET (view_dlg), WST_CLOSED))
258 dlg_destroy (view_dlg);
260 return succeeded;
263 /* {{{ Miscellaneous functions }}} */
265 /* --------------------------------------------------------------------------------------------- */
267 gboolean
268 mcview_load (WView * view, const char *command, const char *file, int start_line,
269 off_t search_start, off_t search_end)
271 gboolean retval = FALSE;
272 vfs_path_t *vpath = NULL;
274 g_assert (view->bytes_per_line != 0);
276 view->filename_vpath = vfs_path_from_str (file);
278 /* get working dir */
279 if (file != NULL && file[0] != '\0')
281 vfs_path_free (view->workdir_vpath);
283 if (!g_path_is_absolute (file))
285 vfs_path_t *p;
287 p = vfs_path_clone (vfs_get_raw_current_dir ());
288 view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
289 vfs_path_free (p);
291 else
293 /* try extract path from filename */
294 const char *fname;
295 char *dir;
297 fname = x_basename (file);
298 dir = g_strndup (file, (size_t) (fname - file));
299 view->workdir_vpath = vfs_path_from_str (dir);
300 g_free (dir);
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;
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_close_datasource (view);
325 mcview_show_error (view, tmp);
326 vfs_path_free (view->filename_vpath);
327 view->filename_vpath = NULL;
328 vfs_path_free (view->workdir_vpath);
329 view->workdir_vpath = NULL;
330 goto finish;
333 /* Make sure we are working with a regular file */
334 if (mc_fstat (fd, &st) == -1)
336 mc_close (fd);
337 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
338 file, unix_error_string (errno));
339 mcview_close_datasource (view);
340 mcview_show_error (view, tmp);
341 vfs_path_free (view->filename_vpath);
342 view->filename_vpath = NULL;
343 vfs_path_free (view->workdir_vpath);
344 view->workdir_vpath = NULL;
345 goto finish;
348 if (!S_ISREG (st.st_mode))
350 mc_close (fd);
351 mcview_close_datasource (view);
352 mcview_show_error (view, _("Cannot view: not a regular file"));
353 vfs_path_free (view->filename_vpath);
354 view->filename_vpath = NULL;
355 vfs_path_free (view->workdir_vpath);
356 view->workdir_vpath = NULL;
357 goto finish;
360 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
362 /* Must be one of those nice files that grow (/proc) */
363 mcview_set_datasource_vfs_pipe (view, fd);
365 else
367 if (view->magic_mode)
369 int type;
371 type = get_compression_type (fd, file);
373 if (type != COMPRESSION_NONE)
375 char *tmp_filename;
376 vfs_path_t *vpath1;
377 int fd1;
379 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
380 vpath1 = vfs_path_from_str (tmp_filename);
381 g_free (tmp_filename);
382 fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
383 vfs_path_free (vpath1);
385 if (fd1 == -1)
387 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"),
388 file, unix_error_string (errno));
389 mcview_close_datasource (view);
390 mcview_show_error (view, tmp);
392 else
394 mc_close (fd);
395 fd = fd1;
396 mc_fstat (fd, &st);
401 mcview_set_datasource_file (view, fd, &st);
403 retval = TRUE;
406 finish:
407 view->command = g_strdup (command);
408 view->dpy_start = 0;
409 view->dpy_paragraph_skip_lines = 0;
410 mcview_state_machine_init (&view->dpy_state_top, 0);
411 view->dpy_wrap_dirty = FALSE;
412 view->force_max = -1;
413 view->dpy_text_column = 0;
415 mcview_compute_areas (view);
416 mcview_update_bytes_per_line (view);
418 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
420 long line, col;
421 off_t new_offset, max_offset;
423 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
424 max_offset = mcview_get_filesize (view) - 1;
425 if (max_offset < 0)
426 new_offset = 0;
427 else
428 new_offset = MIN (new_offset, max_offset);
429 if (!view->hex_mode)
431 view->dpy_start = mcview_bol (view, new_offset, 0);
432 view->dpy_wrap_dirty = TRUE;
434 else
436 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
437 view->hex_cursor = new_offset;
440 else if (start_line > 0)
441 mcview_moveto (view, start_line - 1, 0);
443 view->search_start = search_start;
444 view->search_end = search_end;
445 view->hexedit_lownibble = FALSE;
446 view->hexview_in_text = FALSE;
447 view->change_list = NULL;
448 vfs_path_free (vpath);
449 return retval;
452 /* --------------------------------------------------------------------------------------------- */