Reimplemented mouse event handling in the file manager.
[midnight-commander.git] / src / viewer / mcviewer.c
blobb2de90937db5054541586462739a664f2bf0e72d
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;
96 Widget *w = (Widget *) view;
98 /* rest of the upper frame - call menu */
99 if (mcview_is_in_panel (view) && (event->type & GPM_DOWN) != 0 && event->y == w->owner->y + 1)
101 *result = MOU_UNHANDLED;
102 return FALSE; /* don't draw viewer over menu */
105 *result = MOU_NORMAL;
107 local = mouse_get_local (event, w);
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 return TRUE;
119 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
121 mcview_move_down (view, 2);
122 return TRUE;
125 x = local.x;
126 y = local.y;
128 /* Scrolling left and right */
129 if (!view->text_wrap_mode)
131 if (x < view->data_area.width * 1 / 4)
133 mcview_move_left (view, 1);
134 goto processed;
137 if (x < view->data_area.width * 3 / 4)
139 /* ignore the click */
141 else
143 mcview_move_right (view, 1);
144 goto processed;
148 /* Scrolling up and down */
149 if (y < view->data_area.top + view->data_area.height * 1 / 3)
151 if (mcview_mouse_move_pages)
152 mcview_move_up (view, view->data_area.height / 2);
153 else
154 mcview_move_up (view, 1);
155 goto processed;
157 else if (y < view->data_area.top + view->data_area.height * 2 / 3)
159 /* ignore the click */
161 else
163 if (mcview_mouse_move_pages)
164 mcview_move_down (view, view->data_area.height / 2);
165 else
166 mcview_move_down (view, 1);
167 goto processed;
170 return FALSE;
172 processed:
173 *result = MOU_REPEAT;
174 return TRUE;
177 /* --------------------------------------------------------------------------------------------- */
179 /** Real view only */
180 static int
181 mcview_event (Gpm_Event * event, void *data)
183 mcview_t *view = (mcview_t *) data;
184 int result;
186 if (!mouse_global_in_widget (event, (Widget *) data))
187 return MOU_UNHANDLED;
189 if (do_mcview_event (view, event, &result))
190 mcview_update (view);
191 return result;
194 /* --------------------------------------------------------------------------------------------- */
195 /*** public functions ****************************************************************************/
196 /* --------------------------------------------------------------------------------------------- */
198 mcview_t *
199 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
201 mcview_t *view = g_new0 (mcview_t, 1);
203 init_widget (&view->widget, y, x, lines, cols, mcview_callback, mcview_event);
205 view->hex_mode = FALSE;
206 view->hexedit_mode = FALSE;
207 view->locked = FALSE;
208 view->hexview_in_text = FALSE;
209 view->text_nroff_mode = FALSE;
210 view->text_wrap_mode = FALSE;
211 view->magic_mode = FALSE;
213 view->dpy_frame_size = is_panel ? 1 : 0;
214 view->converter = str_cnv_from_term;
216 mcview_init (view);
218 if (mcview_default_hex_mode)
219 mcview_toggle_hex_mode (view);
220 if (mcview_default_nroff_flag)
221 mcview_toggle_nroff_mode (view);
222 if (mcview_global_wrap_mode)
223 mcview_toggle_wrap_mode (view);
224 if (mcview_default_magic_flag)
225 mcview_toggle_magic_mode (view);
227 return view;
230 /* --------------------------------------------------------------------------------------------- */
231 /** Real view only */
233 mcview_ret_t
234 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line)
236 gboolean succeeded;
237 mcview_t *lc_mcview;
238 Dlg_head *view_dlg;
239 mcview_ret_t ret;
241 /* Create dialog and widgets, put them on the dialog */
242 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback, NULL,
243 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
245 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
246 add_widget (view_dlg, lc_mcview);
248 add_widget (view_dlg, buttonbar_new (TRUE));
250 view_dlg->get_title = mcview_get_title;
253 char *file;
255 file = vfs_path_to_str (file_vpath);
256 succeeded = mcview_load (lc_mcview, command, file, start_line);
257 g_free (file);
260 if (succeeded)
262 run_dlg (view_dlg);
264 ret = lc_mcview->move_dir == 0 ? MCVIEW_EXIT_OK :
265 lc_mcview->move_dir > 0 ? MCVIEW_WANT_NEXT : MCVIEW_WANT_PREV;
267 else
269 view_dlg->state = DLG_CLOSED;
270 ret = MCVIEW_EXIT_FAILURE;
273 if (view_dlg->state == DLG_CLOSED)
274 destroy_dlg (view_dlg);
276 return ret;
279 /* {{{ Miscellaneous functions }}} */
281 /* --------------------------------------------------------------------------------------------- */
283 gboolean
284 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
286 gboolean retval = FALSE;
287 vfs_path_t *vpath = NULL;
289 #ifdef HAVE_ASSERT_H
290 assert (view->bytes_per_line != 0);
291 #endif
293 view->filename_vpath = vfs_path_from_str (file);
295 if ((view->workdir_vpath == NULL) && (file != NULL))
297 if (!g_path_is_absolute (file))
298 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
299 else
301 /* try extract path form filename */
302 char *dirname;
304 dirname = g_path_get_dirname (file);
305 if (strcmp (dirname, ".") != 0)
306 view->workdir_vpath = vfs_path_from_str (dirname);
307 else
309 view->workdir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
311 g_free (dirname);
315 if (!mcview_is_in_panel (view))
316 view->dpy_text_column = 0;
318 mcview_set_codeset (view);
320 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
321 retval = mcview_load_command_output (view, command);
322 else if (file != NULL && file[0] != '\0')
324 int fd = -1;
325 char tmp[BUF_MEDIUM];
326 struct stat st;
328 /* Open the file */
329 vpath = vfs_path_from_str (file);
330 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
331 if (fd == -1)
333 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
334 file, unix_error_string (errno));
335 mcview_show_error (view, tmp);
336 vfs_path_free (view->filename_vpath);
337 view->filename_vpath = NULL;
338 vfs_path_free (view->workdir_vpath);
339 view->workdir_vpath = NULL;
340 goto finish;
343 /* Make sure we are working with a regular file */
344 if (mc_fstat (fd, &st) == -1)
346 mc_close (fd);
347 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
348 file, unix_error_string (errno));
349 mcview_show_error (view, tmp);
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 (!S_ISREG (st.st_mode))
359 mc_close (fd);
360 mcview_show_error (view, _("Cannot view: not a regular file"));
361 vfs_path_free (view->filename_vpath);
362 view->filename_vpath = NULL;
363 vfs_path_free (view->workdir_vpath);
364 view->workdir_vpath = NULL;
365 goto finish;
368 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
370 /* Must be one of those nice files that grow (/proc) */
371 mcview_set_datasource_vfs_pipe (view, fd);
373 else
375 int type;
377 type = get_compression_type (fd, file);
379 if (view->magic_mode && (type != COMPRESSION_NONE))
381 char *tmp_filename;
383 vfs_path_free (view->filename_vpath);
384 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
385 view->filename_vpath = vfs_path_from_str (tmp_filename);
386 g_free (tmp_filename);
388 mcview_set_datasource_file (view, fd, &st);
390 retval = TRUE;
393 finish:
394 view->command = g_strdup (command);
395 view->dpy_start = 0;
396 view->search_start = 0;
397 view->search_end = 0;
398 view->dpy_text_column = 0;
400 mcview_compute_areas (view);
401 mcview_update_bytes_per_line (view);
403 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
405 long line, col;
406 off_t new_offset, max_offset;
408 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
409 max_offset = mcview_get_filesize (view) - 1;
410 if (max_offset < 0)
411 new_offset = 0;
412 else
413 new_offset = min (new_offset, max_offset);
414 if (!view->hex_mode)
415 view->dpy_start = mcview_bol (view, new_offset, 0);
416 else
418 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
419 view->hex_cursor = new_offset;
422 else if (start_line > 0)
423 mcview_moveto (view, start_line - 1, 0);
425 view->hexedit_lownibble = FALSE;
426 view->hexview_in_text = FALSE;
427 view->change_list = NULL;
428 vfs_path_free (vpath);
429 return retval;
432 /* --------------------------------------------------------------------------------------------- */