root/src/viewer/mcviewer.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. mcview_mouse_callback
  2. mcview_new
  3. mcview_viewer
  4. mcview_load

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Interface functions
   4 
   5    Copyright (C) 1994-2020
   6    Free Software Foundation, Inc
   7 
   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
  19 
  20    This file is part of the Midnight Commander.
  21 
  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.
  26 
  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.
  31 
  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/>.
  34  */
  35 
  36 #include <config.h>
  37 #include <errno.h>
  38 
  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"
  45 
  46 #include "src/filemanager/layout.h"
  47 #include "src/filemanager/midnight.h"   /* the_menubar */
  48 
  49 #include "internal.h"
  50 
  51 /*** global variables ****************************************************************************/
  52 
  53 mcview_mode_flags_t mcview_global_flags = {
  54     .wrap = TRUE,
  55     .hex = FALSE,
  56     .magic = TRUE,
  57     .nroff = FALSE
  58 };
  59 
  60 mcview_mode_flags_t mcview_altered_flags = {
  61     .wrap = FALSE,
  62     .hex = FALSE,
  63     .magic = FALSE,
  64     .nroff = FALSE
  65 };
  66 
  67 gboolean mcview_remember_file_position = FALSE;
  68 
  69 /* Maxlimit for skipping updates */
  70 int mcview_max_dirt_limit = 10;
  71 
  72 /* Scrolling is done in pages or line increments */
  73 gboolean mcview_mouse_move_pages = TRUE;
  74 
  75 /* end of file will be showen from mcview_show_eof */
  76 char *mcview_show_eof = NULL;
  77 
  78 /*** file scope macro definitions ****************************************************************/
  79 
  80 /*** file scope type declarations ****************************************************************/
  81 
  82 /*** file scope variables ************************************************************************/
  83 
  84 /* --------------------------------------------------------------------------------------------- */
  85 /*** file scope functions ************************************************************************/
  86 /* --------------------------------------------------------------------------------------------- */
  87 
  88 static void
  89 mcview_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
  90 {
  91     WView *view = (WView *) w;
  92     gboolean ok = TRUE;
  93 
  94     switch (msg)
  95     {
  96     case MSG_MOUSE_DOWN:
  97         if (mcview_is_in_panel (view))
  98         {
  99             if (event->y == WIDGET (w->owner)->y)
 100             {
 101                 /* return MOU_UNHANDLED */
 102                 event->result.abort = TRUE;
 103                 /* don't draw viewer over menu */
 104                 ok = FALSE;
 105                 break;
 106             }
 107 
 108             if (!widget_get_state (w, WST_FOCUSED))
 109             {
 110                 /* Grab focus */
 111                 change_panel ();
 112             }
 113         }
 114         MC_FALLTHROUGH;
 115 
 116     case MSG_MOUSE_CLICK:
 117         if (!view->mode_flags.wrap)
 118         {
 119             /* Scrolling left and right */
 120             screen_dimen x;
 121 
 122             x = event->x + 1;   /* FIXME */
 123 
 124             if (x < view->data_area.width * 1 / 4)
 125             {
 126                 mcview_move_left (view, 1);
 127                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 128             }
 129             else if (x < view->data_area.width * 3 / 4)
 130             {
 131                 /* ignore the click */
 132                 ok = FALSE;
 133             }
 134             else
 135             {
 136                 mcview_move_right (view, 1);
 137                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 138             }
 139         }
 140         else
 141         {
 142             /* Scrolling up and down */
 143             screen_dimen y;
 144 
 145             y = event->y + 1;   /* FIXME */
 146 
 147             if (y < view->data_area.top + view->data_area.height * 1 / 3)
 148             {
 149                 if (mcview_mouse_move_pages)
 150                     mcview_move_up (view, view->data_area.height / 2);
 151                 else
 152                     mcview_move_up (view, 1);
 153 
 154                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 155             }
 156             else if (y < view->data_area.top + view->data_area.height * 2 / 3)
 157             {
 158                 /* ignore the click */
 159                 ok = FALSE;
 160             }
 161             else
 162             {
 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 
 168                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 169             }
 170         }
 171         break;
 172 
 173     case MSG_MOUSE_SCROLL_UP:
 174         mcview_move_up (view, 2);
 175         break;
 176 
 177     case MSG_MOUSE_SCROLL_DOWN:
 178         mcview_move_down (view, 2);
 179         break;
 180 
 181     default:
 182         ok = FALSE;
 183         break;
 184     }
 185 
 186     if (ok)
 187         mcview_update (view);
 188 }
 189 
 190 /* --------------------------------------------------------------------------------------------- */
 191 /*** public functions ****************************************************************************/
 192 /* --------------------------------------------------------------------------------------------- */
 193 
 194 WView *
 195 mcview_new (int y, int x, int lines, int cols, gboolean is_panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 196 {
 197     WView *view;
 198     Widget *w;
 199 
 200     view = g_new0 (WView, 1);
 201     w = WIDGET (view);
 202     widget_init (w, y, x, lines, cols, mcview_callback, mcview_mouse_callback);
 203     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
 204     w->keymap = viewer_map;
 205 
 206     mcview_clear_mode_flags (&view->mode_flags);
 207     view->hexedit_mode = FALSE;
 208     view->hex_keymap = viewer_hex_map;
 209     view->hexview_in_text = FALSE;
 210     view->locked = FALSE;
 211 
 212     view->dpy_frame_size = is_panel ? 1 : 0;
 213     view->converter = str_cnv_from_term;
 214 
 215     mcview_init (view);
 216 
 217     if (mcview_global_flags.hex)
 218         mcview_toggle_hex_mode (view);
 219     if (mcview_global_flags.nroff)
 220         mcview_toggle_nroff_mode (view);
 221     if (mcview_global_flags.wrap)
 222         mcview_toggle_wrap_mode (view);
 223     if (mcview_global_flags.magic)
 224         mcview_toggle_magic_mode (view);
 225 
 226     return view;
 227 }
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 /** Real view only */
 231 
 232 gboolean
 233 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line,
     /* [previous][next][first][last][top][bottom][index][help]  */
 234                off_t search_start, off_t search_end)
 235 {
 236     gboolean succeeded;
 237     WView *lc_mcview;
 238     WDialog *view_dlg;
 239     Widget *vw, *b;
 240     WGroup *g;
 241 
 242     /* Create dialog and widgets, put them on the dialog */
 243     view_dlg = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, mcview_dialog_callback,
 244                            NULL, "[Internal File Viewer]", NULL);
 245     vw = WIDGET (view_dlg);
 246     widget_want_tab (vw, TRUE);
 247 
 248     g = GROUP (view_dlg);
 249 
 250     lc_mcview = mcview_new (vw->y, vw->x, vw->lines - 1, vw->cols, FALSE);
 251     group_add_widget_autopos (g, lc_mcview, WPOS_KEEP_ALL, NULL);
 252 
 253     b = WIDGET (buttonbar_new (TRUE));
 254     group_add_widget_autopos (g, b, b->pos_flags, NULL);
 255 
 256     view_dlg->get_title = mcview_get_title;
 257 
 258     succeeded =
 259         mcview_load (lc_mcview, command, vfs_path_as_str (file_vpath), start_line, search_start,
 260                      search_end);
 261 
 262     if (succeeded)
 263         dlg_run (view_dlg);
 264     else
 265         dlg_stop (view_dlg);
 266 
 267     if (widget_get_state (vw, WST_CLOSED))
 268         dlg_destroy (view_dlg);
 269 
 270     return succeeded;
 271 }
 272 
 273 /* {{{ Miscellaneous functions }}} */
 274 
 275 /* --------------------------------------------------------------------------------------------- */
 276 
 277 gboolean
 278 mcview_load (WView * view, const char *command, const char *file, int start_line,
     /* [previous][next][first][last][top][bottom][index][help]  */
 279              off_t search_start, off_t search_end)
 280 {
 281     gboolean retval = FALSE;
 282     vfs_path_t *vpath = NULL;
 283 
 284     g_assert (view->bytes_per_line != 0);
 285 
 286     view->filename_vpath = vfs_path_from_str (file);
 287 
 288     /* get working dir */
 289     if (file != NULL && file[0] != '\0')
 290     {
 291         vfs_path_free (view->workdir_vpath);
 292 
 293         if (!g_path_is_absolute (file))
 294         {
 295             vfs_path_t *p;
 296 
 297             p = vfs_path_clone (vfs_get_raw_current_dir ());
 298             view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
 299             vfs_path_free (p);
 300         }
 301         else
 302         {
 303             /* try extract path from filename */
 304             const char *fname;
 305             char *dir;
 306 
 307             fname = x_basename (file);
 308             dir = g_strndup (file, (size_t) (fname - file));
 309             view->workdir_vpath = vfs_path_from_str (dir);
 310             g_free (dir);
 311         }
 312     }
 313 
 314     if (!mcview_is_in_panel (view))
 315         view->dpy_text_column = 0;
 316 
 317 #ifdef HAVE_CHARSET
 318     mcview_set_codeset (view);
 319 #endif
 320 
 321     if (command != NULL && (view->mode_flags.magic || file == NULL || file[0] == '\0'))
 322         retval = mcview_load_command_output (view, command);
 323     else if (file != NULL && file[0] != '\0')
 324     {
 325         int fd;
 326         char tmp[BUF_MEDIUM];
 327         struct stat st;
 328 
 329         /* Open the file */
 330         vpath = vfs_path_from_str (file);
 331         fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
 332         if (fd == -1)
 333         {
 334             g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
 335                         file, unix_error_string (errno));
 336             mcview_close_datasource (view);
 337             mcview_show_error (view, tmp);
 338             vfs_path_free (view->filename_vpath);
 339             view->filename_vpath = NULL;
 340             vfs_path_free (view->workdir_vpath);
 341             view->workdir_vpath = NULL;
 342             goto finish;
 343         }
 344 
 345         /* Make sure we are working with a regular file */
 346         if (mc_fstat (fd, &st) == -1)
 347         {
 348             mc_close (fd);
 349             g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
 350                         file, unix_error_string (errno));
 351             mcview_close_datasource (view);
 352             mcview_show_error (view, tmp);
 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;
 358         }
 359 
 360         if (!S_ISREG (st.st_mode))
 361         {
 362             mc_close (fd);
 363             mcview_close_datasource (view);
 364             mcview_show_error (view, _("Cannot view: not a regular file"));
 365             vfs_path_free (view->filename_vpath);
 366             view->filename_vpath = NULL;
 367             vfs_path_free (view->workdir_vpath);
 368             view->workdir_vpath = NULL;
 369             goto finish;
 370         }
 371 
 372         if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
 373         {
 374             /* Must be one of those nice files that grow (/proc) */
 375             mcview_set_datasource_vfs_pipe (view, fd);
 376         }
 377         else
 378         {
 379             if (view->mode_flags.magic)
 380             {
 381                 int type;
 382 
 383                 type = get_compression_type (fd, file);
 384 
 385                 if (type != COMPRESSION_NONE)
 386                 {
 387                     char *tmp_filename;
 388                     vfs_path_t *vpath1;
 389                     int fd1;
 390 
 391                     tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
 392                     vpath1 = vfs_path_from_str (tmp_filename);
 393                     g_free (tmp_filename);
 394                     fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
 395                     vfs_path_free (vpath1);
 396 
 397                     if (fd1 == -1)
 398                     {
 399                         g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"),
 400                                     file, unix_error_string (errno));
 401                         mcview_close_datasource (view);
 402                         mcview_show_error (view, tmp);
 403                     }
 404                     else
 405                     {
 406                         mc_close (fd);
 407                         fd = fd1;
 408                         mc_fstat (fd, &st);
 409                     }
 410                 }
 411             }
 412 
 413             mcview_set_datasource_file (view, fd, &st);
 414         }
 415         retval = TRUE;
 416     }
 417 
 418   finish:
 419     view->command = g_strdup (command);
 420     view->dpy_start = 0;
 421     view->dpy_paragraph_skip_lines = 0;
 422     mcview_state_machine_init (&view->dpy_state_top, 0);
 423     view->dpy_wrap_dirty = FALSE;
 424     view->force_max = -1;
 425     view->dpy_text_column = 0;
 426 
 427     mcview_compute_areas (view);
 428     mcview_update_bytes_per_line (view);
 429 
 430     if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
 431     {
 432         long line, col;
 433         off_t new_offset, max_offset;
 434 
 435         load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
 436         max_offset = mcview_get_filesize (view) - 1;
 437         if (max_offset < 0)
 438             new_offset = 0;
 439         else
 440             new_offset = MIN (new_offset, max_offset);
 441         if (!view->mode_flags.hex)
 442         {
 443             view->dpy_start = mcview_bol (view, new_offset, 0);
 444             view->dpy_wrap_dirty = TRUE;
 445         }
 446         else
 447         {
 448             view->dpy_start = new_offset - new_offset % view->bytes_per_line;
 449             view->hex_cursor = new_offset;
 450         }
 451     }
 452     else if (start_line > 0)
 453         mcview_moveto (view, start_line - 1, 0);
 454 
 455     view->search_start = search_start;
 456     view->search_end = search_end;
 457     view->hexedit_lownibble = FALSE;
 458     view->hexview_in_text = FALSE;
 459     view->change_list = NULL;
 460     vfs_path_free (vpath);
 461     return retval;
 462 }
 463 
 464 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */