Ticket #2817: add mouse handler to the dialog.
[midnight-commander.git] / src / viewer / mcviewer.c
blob621fb5b598dfedfd378db06a8abeac7daaafac4f
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 */
91 static gboolean
92 do_mcview_event (mcview_t * view, Gpm_Event * event, int *result)
94 screen_dimen y, x;
95 Gpm_Event local;
97 *result = MOU_NORMAL;
99 local = mouse_get_local (event, (Widget *) view);
101 /* rest of the upper frame, the menu is invisible - call menu */
102 if (mcview_is_in_panel (view) && (local.type & GPM_DOWN) != 0 && local.y == 1
103 && !menubar_visible)
105 *result = the_menubar->widget.mouse (event, the_menubar);
106 return FALSE; /* don't draw viewer over menu */
109 /* We are not interested in the release events */
110 if ((local.type & (GPM_DOWN | GPM_DRAG)) == 0)
111 return FALSE;
113 /* Wheel events */
114 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
116 mcview_move_up (view, 2);
117 *result = MOU_NORMAL;
118 return TRUE;
120 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
122 mcview_move_down (view, 2);
123 *result = MOU_NORMAL;
124 return TRUE;
127 x = local.x;
128 y = local.y;
130 /* Scrolling left and right */
131 if (!view->text_wrap_mode)
133 if (x < view->data_area.width * 1 / 4)
135 mcview_move_left (view, 1);
136 goto processed;
138 else if (x < view->data_area.width * 3 / 4)
140 /* ignore the click */
142 else
144 mcview_move_right (view, 1);
145 goto processed;
149 /* Scrolling up and down */
150 if (y < view->data_area.top + view->data_area.height * 1 / 3)
152 if (mcview_mouse_move_pages)
153 mcview_move_up (view, view->data_area.height / 2);
154 else
155 mcview_move_up (view, 1);
156 goto processed;
158 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
160 /* ignore the click */
162 else
164 if (mcview_mouse_move_pages)
165 mcview_move_down (view, view->data_area.height / 2);
166 else
167 mcview_move_down (view, 1);
168 goto processed;
171 return FALSE;
173 processed:
174 *result = MOU_REPEAT;
175 return TRUE;
178 /* --------------------------------------------------------------------------------------------- */
180 /** Real view only */
181 static int
182 mcview_event (Gpm_Event * event, void *data)
184 mcview_t *view = (mcview_t *) data;
185 int result;
187 if (!mouse_global_in_widget (event, (Widget *) data))
188 return MOU_UNHANDLED;
190 if (do_mcview_event (view, event, &result))
191 mcview_update (view);
192 return result;
195 /* --------------------------------------------------------------------------------------------- */
196 /*** public functions ****************************************************************************/
197 /* --------------------------------------------------------------------------------------------- */
199 mcview_t *
200 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
202 mcview_t *view = g_new0 (mcview_t, 1);
204 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_event);
206 view->hex_mode = FALSE;
207 view->hexedit_mode = FALSE;
208 view->locked = FALSE;
209 view->hexview_in_text = FALSE;
210 view->text_nroff_mode = FALSE;
211 view->text_wrap_mode = FALSE;
212 view->magic_mode = FALSE;
214 view->dpy_frame_size = is_panel ? 1 : 0;
215 view->converter = str_cnv_from_term;
217 mcview_init (view);
219 if (mcview_default_hex_mode)
220 mcview_toggle_hex_mode (view);
221 if (mcview_default_nroff_flag)
222 mcview_toggle_nroff_mode (view);
223 if (mcview_global_wrap_mode)
224 mcview_toggle_wrap_mode (view);
225 if (mcview_default_magic_flag)
226 mcview_toggle_magic_mode (view);
228 return view;
231 /* --------------------------------------------------------------------------------------------- */
232 /** Real view only */
234 mcview_ret_t
235 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line)
237 gboolean succeeded;
238 mcview_t *lc_mcview;
239 Dlg_head *view_dlg;
240 mcview_ret_t ret;
242 /* Create dialog and widgets, put them on the dialog */
243 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback, NULL,
244 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
246 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
247 add_widget (view_dlg, lc_mcview);
249 add_widget (view_dlg, buttonbar_new (TRUE));
251 view_dlg->get_title = mcview_get_title;
254 char *file;
256 file = vfs_path_to_str (file_vpath);
257 succeeded = mcview_load (lc_mcview, command, file, start_line);
258 g_free (file);
261 if (succeeded)
263 run_dlg (view_dlg);
265 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
266 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
268 else
270 view_dlg->state = DLG_CLOSED;
271 ret = MCVIEW_EXIT_FAILURE;
274 if (view_dlg->state == DLG_CLOSED)
275 destroy_dlg (view_dlg);
277 return ret;
280 /* {{{ Miscellaneous functions }}} */
282 /* --------------------------------------------------------------------------------------------- */
284 gboolean
285 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
287 gboolean retval = FALSE;
288 vfs_path_t *vpath = NULL;
290 #ifdef HAVE_ASSERT_H
291 assert (view->bytes_per_line != 0);
292 #endif
294 view->filename_vpath = vfs_path_from_str (file);
296 if ((view->workdir_vpath == NULL) && (file != NULL))
298 if (!g_path_is_absolute (file))
299 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
300 else
302 /* try extract path form filename */
303 char *dirname;
305 dirname = g_path_get_dirname (file);
306 if (strcmp (dirname, ".") != 0)
307 view->workdir_vpath = vfs_path_from_str (dirname);
308 else
310 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
312 g_free (dirname);
316 if (!mcview_is_in_panel (view))
317 view->dpy_text_column = 0;
319 mcview_set_codeset (view);
321 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
322 retval = mcview_load_command_output (view, command);
323 else if (file != NULL && file[0] != '\0')
325 int fd = -1;
326 char tmp[BUF_MEDIUM];
327 struct stat st;
329 /* Open the file */
330 vpath = vfs_path_from_str (file);
331 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
332 if (fd == -1)
334 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
335 file, unix_error_string (errno));
336 mcview_show_error (view, tmp);
337 vfs_path_free (view->filename_vpath);
338 view->filename_vpath = NULL;
339 vfs_path_free (view->workdir_vpath);
340 view->workdir_vpath = NULL;
341 goto finish;
344 /* Make sure we are working with a regular file */
345 if (mc_fstat (fd, &st) == -1)
347 mc_close (fd);
348 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
349 file, unix_error_string (errno));
350 mcview_show_error (view, tmp);
351 vfs_path_free (view->filename_vpath);
352 view->filename_vpath = NULL;
353 vfs_path_free (view->workdir_vpath);
354 view->workdir_vpath = NULL;
355 goto finish;
358 if (!S_ISREG (st.st_mode))
360 mc_close (fd);
361 mcview_show_error (view, _("Cannot view: not a regular file"));
362 vfs_path_free (view->filename_vpath);
363 view->filename_vpath = NULL;
364 vfs_path_free (view->workdir_vpath);
365 view->workdir_vpath = NULL;
366 goto finish;
369 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
371 /* Must be one of those nice files that grow (/proc) */
372 mcview_set_datasource_vfs_pipe (view, fd);
374 else
376 int type;
378 type = get_compression_type (fd, file);
380 if (view->magic_mode && (type != COMPRESSION_NONE))
382 char *tmp_filename;
384 vfs_path_free (view->filename_vpath);
385 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
386 view->filename_vpath = vfs_path_from_str (tmp_filename);
387 g_free (tmp_filename);
389 mcview_set_datasource_file (view, fd, &st);
391 retval = TRUE;
394 finish:
395 view->command = g_strdup (command);
396 view->dpy_start = 0;
397 view->search_start = 0;
398 view->search_end = 0;
399 view->dpy_text_column = 0;
401 mcview_compute_areas (view);
402 mcview_update_bytes_per_line (view);
404 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
406 long line, col;
407 off_t new_offset, max_offset;
409 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
410 max_offset = mcview_get_filesize (view) - 1;
411 if (max_offset < 0)
412 new_offset = 0;
413 else
414 new_offset = min (new_offset, max_offset);
415 if (!view->hex_mode)
416 view->dpy_start = mcview_bol (view, new_offset, 0);
417 else
419 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
420 view->hex_cursor = new_offset;
423 else if (start_line > 0)
424 mcview_moveto (view, start_line - 1, 0);
426 view->hexedit_lownibble = FALSE;
427 view->hexview_in_text = FALSE;
428 view->change_list = NULL;
429 vfs_path_free (vpath);
430 return retval;
433 /* --------------------------------------------------------------------------------------------- */