Add edit_add_window() function.
[midnight-commander.git] / src / viewer / mcviewer.c
blob5db10368d92634bf3756b64596cdb57014ac5855
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 gboolean
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;
240 /* Create dialog and widgets, put them on the dialog */
241 view_dlg = create_dlg (FALSE, 0, 0, LINES, COLS, NULL, mcview_dialog_callback, NULL,
242 "[Internal File Viewer]", NULL, DLG_WANT_TAB);
244 lc_mcview = mcview_new (0, 0, LINES - 1, COLS, FALSE);
245 add_widget (view_dlg, lc_mcview);
247 add_widget (view_dlg, buttonbar_new (TRUE));
249 view_dlg->get_title = mcview_get_title;
252 char *file;
254 file = vfs_path_to_str (file_vpath);
255 succeeded = mcview_load (lc_mcview, command, file, start_line);
256 g_free (file);
259 if (succeeded)
260 run_dlg (view_dlg);
261 else
262 view_dlg->state = DLG_CLOSED;
264 if (view_dlg->state == DLG_CLOSED)
265 destroy_dlg (view_dlg);
267 return succeeded;
270 /* {{{ Miscellaneous functions }}} */
272 /* --------------------------------------------------------------------------------------------- */
274 gboolean
275 mcview_load (mcview_t * view, const char *command, const char *file, int start_line)
277 gboolean retval = FALSE;
278 vfs_path_t *vpath = NULL;
280 #ifdef HAVE_ASSERT_H
281 assert (view->bytes_per_line != 0);
282 #endif
284 view->filename_vpath = vfs_path_from_str (file);
286 /* get working dir */
287 if (file != NULL && file[0] != '\0')
289 vfs_path_free (view->workdir_vpath);
291 if (!g_path_is_absolute (file))
293 vfs_path_t *p;
295 p = vfs_path_clone (vfs_get_raw_current_dir ());
296 view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
297 vfs_path_free (p);
299 else
301 /* try extract path form filename */
302 const char *fname;
303 char *dir;
305 fname = x_basename (file);
306 dir = g_strndup (file, (size_t) (fname - file));
307 view->workdir_vpath = vfs_path_from_str (dir);
308 g_free (dir);
312 if (!mcview_is_in_panel (view))
313 view->dpy_text_column = 0;
315 mcview_set_codeset (view);
317 if (command != NULL && (view->magic_mode || file == NULL || file[0] == '\0'))
318 retval = mcview_load_command_output (view, command);
319 else if (file != NULL && file[0] != '\0')
321 int fd = -1;
322 char tmp[BUF_MEDIUM];
323 struct stat st;
325 /* Open the file */
326 vpath = vfs_path_from_str (file);
327 fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
328 if (fd == -1)
330 g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
331 file, unix_error_string (errno));
332 mcview_show_error (view, tmp);
333 vfs_path_free (view->filename_vpath);
334 view->filename_vpath = NULL;
335 vfs_path_free (view->workdir_vpath);
336 view->workdir_vpath = NULL;
337 goto finish;
340 /* Make sure we are working with a regular file */
341 if (mc_fstat (fd, &st) == -1)
343 mc_close (fd);
344 g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
345 file, unix_error_string (errno));
346 mcview_show_error (view, tmp);
347 vfs_path_free (view->filename_vpath);
348 view->filename_vpath = NULL;
349 vfs_path_free (view->workdir_vpath);
350 view->workdir_vpath = NULL;
351 goto finish;
354 if (!S_ISREG (st.st_mode))
356 mc_close (fd);
357 mcview_show_error (view, _("Cannot view: not a regular file"));
358 vfs_path_free (view->filename_vpath);
359 view->filename_vpath = NULL;
360 vfs_path_free (view->workdir_vpath);
361 view->workdir_vpath = NULL;
362 goto finish;
365 if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
367 /* Must be one of those nice files that grow (/proc) */
368 mcview_set_datasource_vfs_pipe (view, fd);
370 else
372 int type;
374 type = get_compression_type (fd, file);
376 if (view->magic_mode && (type != COMPRESSION_NONE))
378 char *tmp_filename;
380 vfs_path_free (view->filename_vpath);
381 tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
382 view->filename_vpath = vfs_path_from_str (tmp_filename);
383 g_free (tmp_filename);
385 mcview_set_datasource_file (view, fd, &st);
387 retval = TRUE;
390 finish:
391 view->command = g_strdup (command);
392 view->dpy_start = 0;
393 view->search_start = 0;
394 view->search_end = 0;
395 view->dpy_text_column = 0;
397 mcview_compute_areas (view);
398 mcview_update_bytes_per_line (view);
400 if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
402 long line, col;
403 off_t new_offset, max_offset;
405 load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
406 max_offset = mcview_get_filesize (view) - 1;
407 if (max_offset < 0)
408 new_offset = 0;
409 else
410 new_offset = min (new_offset, max_offset);
411 if (!view->hex_mode)
412 view->dpy_start = mcview_bol (view, new_offset, 0);
413 else
415 view->dpy_start = new_offset - new_offset % view->bytes_per_line;
416 view->hex_cursor = new_offset;
419 else if (start_line > 0)
420 mcview_moveto (view, start_line - 1, 0);
422 view->hexedit_lownibble = FALSE;
423 view->hexview_in_text = FALSE;
424 view->change_list = NULL;
425 vfs_path_free (vpath);
426 return retval;
429 /* --------------------------------------------------------------------------------------------- */