WView: remove WView::active. Use WST_FOCUSED instead.
[midnight-commander.git] / src / viewer / mcviewer.c
blob49165b4b583f3fe211f81cee466b1436f0d8cb86
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 #ifdef HAVE_ASSERT_H
275 assert (view->bytes_per_line != 0);
276 #endif
278 view->filename_vpath = vfs_path_from_str (file);
280 /* get working dir */
281 if (file != NULL && file[0] != '\0')
283 vfs_path_free (view->workdir_vpath);
285 if (!g_path_is_absolute (file))
287 vfs_path_t *p;
289 p = vfs_path_clone (vfs_get_raw_current_dir ());
290 view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
291 vfs_path_free (p);
293 else
295 /* try extract path from filename */
296 const char *fname;
297 char *dir;
299 fname = x_basename (file);
300 dir = g_strndup (file, (size_t) (fname - file));
301 view->workdir_vpath = vfs_path_from_str (dir);
302 g_free (dir);
306 if (!mcview_is_in_panel (view))
307 view->dpy_text_column = 0;
309 mcview_set_codeset (view);
311 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
312 retval = mcview_load_command_output (view, command);
313 else if (file != NULL && file[0] != '\0')
315 int fd;
316 char tmp[BUF_MEDIUM];
317 struct stat st;
319 /* Open the file */
320 vpath = vfs_path_from_str (file);
321 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
322 if (fd == -1)
324 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
325 file, unix_error_string (errno));
326 mcview_close_datasource (view);
327 mcview_show_error (view, tmp);
328 vfs_path_free (view->filename_vpath);
329 view->filename_vpath = NULL;
330 vfs_path_free (view->workdir_vpath);
331 view->workdir_vpath = NULL;
332 goto finish;
335 /* Make sure we are working with a regular file */
336 if (mc_fstat (fd, &st) == -1)
338 mc_close (fd);
339 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
340 file, unix_error_string (errno));
341 mcview_close_datasource (view);
342 mcview_show_error (view, tmp);
343 vfs_path_free (view->filename_vpath);
344 view->filename_vpath = NULL;
345 vfs_path_free (view->workdir_vpath);
346 view->workdir_vpath = NULL;
347 goto finish;
350 if (!S_ISREG (st.st_mode))
352 mc_close (fd);
353 mcview_close_datasource (view);
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 if (view->magic_mode)
371 int type;
373 type = get_compression_type (fd, file);
375 if (type != COMPRESSION_NONE)
377 char *tmp_filename;
378 vfs_path_t *vpath1;
379 int fd1;
381 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
382 vpath1 = vfs_path_from_str (tmp_filename);
383 g_free (tmp_filename);
384 fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
385 vfs_path_free (vpath1);
387 if (fd1 == -1)
389 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"),
390 file, unix_error_string (errno));
391 mcview_close_datasource (view);
392 mcview_show_error (view, tmp);
394 else
396 mc_close (fd);
397 fd = fd1;
398 mc_fstat (fd, &st);
403 mcview_set_datasource_file (view, fd, &st);
405 retval = TRUE;
408 finish:
409 view->command = g_strdup (command);
410 view->dpy_start = 0;
411 view->dpy_paragraph_skip_lines = 0;
412 mcview_state_machine_init (&view->dpy_state_top, 0);
413 view->dpy_wrap_dirty = FALSE;
414 view->force_max = -1;
415 view->dpy_text_column = 0;
417 mcview_compute_areas (view);
418 mcview_update_bytes_per_line (view);
420 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
422 long line, col;
423 off_t new_offset, max_offset;
425 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
426 max_offset = mcview_get_filesize (view) - 1;
427 if (max_offset < 0)
428 new_offset = 0;
429 else
430 new_offset = MIN (new_offset, max_offset);
431 if (!view->hex_mode)
433 view->dpy_start = mcview_bol (view, new_offset, 0);
434 view->dpy_wrap_dirty = TRUE;
436 else
438 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
439 view->hex_cursor = new_offset;
442 else if (start_line > 0)
443 mcview_moveto (view, start_line - 1, 0);
445 view->search_start = search_start;
446 view->search_end = search_end;
447 view->hexedit_lownibble = FALSE;
448 view->hexview_in_text = FALSE;
449 view->change_list = NULL;
450 vfs_path_free (vpath);
451 return retval;
454 /* --------------------------------------------------------------------------------------------- */