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-2019
   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"     /* menubar_visible */
  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 
 205     mcview_clear_mode_flags (&view->mode_flags);
 206     view->hexedit_mode = FALSE;
 207     view->hexview_in_text = FALSE;
 208     view->locked = FALSE;
 209 
 210     view->dpy_frame_size = is_panel ? 1 : 0;
 211     view->converter = str_cnv_from_term;
 212 
 213     mcview_init (view);
 214 
 215     if (mcview_global_flags.hex)
 216         mcview_toggle_hex_mode (view);
 217     if (mcview_global_flags.nroff)
 218         mcview_toggle_nroff_mode (view);
 219     if (mcview_global_flags.wrap)
 220         mcview_toggle_wrap_mode (view);
 221     if (mcview_global_flags.magic)
 222         mcview_toggle_magic_mode (view);
 223 
 224     return view;
 225 }
 226 
 227 /* --------------------------------------------------------------------------------------------- */
 228 /** Real view only */
 229 
 230 gboolean
 231 mcview_viewer (const char *command, const vfs_path_t * file_vpath, int start_line,
     /* [previous][next][first][last][top][bottom][index][help]  */
 232                off_t search_start, off_t search_end)
 233 {
 234     gboolean succeeded;
 235     WView *lc_mcview;
 236     WDialog *view_dlg;
 237     Widget *vw, *b;
 238 
 239     /* Create dialog and widgets, put them on the dialog */
 240     view_dlg = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, mcview_dialog_callback,
 241                            NULL, "[Internal File Viewer]", NULL);
 242     vw = WIDGET (view_dlg);
 243     widget_want_tab (vw, TRUE);
 244 
 245     lc_mcview = mcview_new (vw->y, vw->x, vw->lines - 1, vw->cols, FALSE);
 246     add_widget_autopos (view_dlg, lc_mcview, WPOS_KEEP_ALL, NULL);
 247 
 248     b = WIDGET (buttonbar_new (TRUE));
 249     add_widget_autopos (view_dlg, b, b->pos_flags, NULL);
 250 
 251     view_dlg->get_title = mcview_get_title;
 252 
 253     succeeded =
 254         mcview_load (lc_mcview, command, vfs_path_as_str (file_vpath), start_line, search_start,
 255                      search_end);
 256 
 257     if (succeeded)
 258         dlg_run (view_dlg);
 259     else
 260         dlg_stop (view_dlg);
 261 
 262     if (widget_get_state (vw, WST_CLOSED))
 263         dlg_destroy (view_dlg);
 264 
 265     return succeeded;
 266 }
 267 
 268 /* {{{ Miscellaneous functions }}} */
 269 
 270 /* --------------------------------------------------------------------------------------------- */
 271 
 272 gboolean
 273 mcview_load (WView * view, const char *command, const char *file, int start_line,
     /* [previous][next][first][last][top][bottom][index][help]  */
 274              off_t search_start, off_t search_end)
 275 {
 276     gboolean retval = FALSE;
 277     vfs_path_t *vpath = NULL;
 278 
 279     g_assert (view->bytes_per_line != 0);
 280 
 281     view->filename_vpath = vfs_path_from_str (file);
 282 
 283     /* get working dir */
 284     if (file != NULL && file[0] != '\0')
 285     {
 286         vfs_path_free (view->workdir_vpath);
 287 
 288         if (!g_path_is_absolute (file))
 289         {
 290             vfs_path_t *p;
 291 
 292             p = vfs_path_clone (vfs_get_raw_current_dir ());
 293             view->workdir_vpath = vfs_path_append_new (p, file, (char *) NULL);
 294             vfs_path_free (p);
 295         }
 296         else
 297         {
 298             /* try extract path from filename */
 299             const char *fname;
 300             char *dir;
 301 
 302             fname = x_basename (file);
 303             dir = g_strndup (file, (size_t) (fname - file));
 304             view->workdir_vpath = vfs_path_from_str (dir);
 305             g_free (dir);
 306         }
 307     }
 308 
 309     if (!mcview_is_in_panel (view))
 310         view->dpy_text_column = 0;
 311 
 312     mcview_set_codeset (view);
 313 
 314     if (command != NULL && (view->mode_flags.magic || file == NULL || file[0] == '\0'))
 315         retval = mcview_load_command_output (view, command);
 316     else if (file != NULL && file[0] != '\0')
 317     {
 318         int fd;
 319         char tmp[BUF_MEDIUM];
 320         struct stat st;
 321 
 322         /* Open the file */
 323         vpath = vfs_path_from_str (file);
 324         fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
 325         if (fd == -1)
 326         {
 327             g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"),
 328                         file, unix_error_string (errno));
 329             mcview_close_datasource (view);
 330             mcview_show_error (view, tmp);
 331             vfs_path_free (view->filename_vpath);
 332             view->filename_vpath = NULL;
 333             vfs_path_free (view->workdir_vpath);
 334             view->workdir_vpath = NULL;
 335             goto finish;
 336         }
 337 
 338         /* Make sure we are working with a regular file */
 339         if (mc_fstat (fd, &st) == -1)
 340         {
 341             mc_close (fd);
 342             g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"),
 343                         file, unix_error_string (errno));
 344             mcview_close_datasource (view);
 345             mcview_show_error (view, tmp);
 346             vfs_path_free (view->filename_vpath);
 347             view->filename_vpath = NULL;
 348             vfs_path_free (view->workdir_vpath);
 349             view->workdir_vpath = NULL;
 350             goto finish;
 351         }
 352 
 353         if (!S_ISREG (st.st_mode))
 354         {
 355             mc_close (fd);
 356             mcview_close_datasource (view);
 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;
 363         }
 364 
 365         if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
 366         {
 367             /* Must be one of those nice files that grow (/proc) */
 368             mcview_set_datasource_vfs_pipe (view, fd);
 369         }
 370         else
 371         {
 372             if (view->mode_flags.magic)
 373             {
 374                 int type;
 375 
 376                 type = get_compression_type (fd, file);
 377 
 378                 if (type != COMPRESSION_NONE)
 379                 {
 380                     char *tmp_filename;
 381                     vfs_path_t *vpath1;
 382                     int fd1;
 383 
 384                     tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
 385                     vpath1 = vfs_path_from_str (tmp_filename);
 386                     g_free (tmp_filename);
 387                     fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
 388                     vfs_path_free (vpath1);
 389 
 390                     if (fd1 == -1)
 391                     {
 392                         g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"),
 393                                     file, unix_error_string (errno));
 394                         mcview_close_datasource (view);
 395                         mcview_show_error (view, tmp);
 396                     }
 397                     else
 398                     {
 399                         mc_close (fd);
 400                         fd = fd1;
 401                         mc_fstat (fd, &st);
 402                     }
 403                 }
 404             }
 405 
 406             mcview_set_datasource_file (view, fd, &st);
 407         }
 408         retval = TRUE;
 409     }
 410 
 411   finish:
 412     view->command = g_strdup (command);
 413     view->dpy_start = 0;
 414     view->dpy_paragraph_skip_lines = 0;
 415     mcview_state_machine_init (&view->dpy_state_top, 0);
 416     view->dpy_wrap_dirty = FALSE;
 417     view->force_max = -1;
 418     view->dpy_text_column = 0;
 419 
 420     mcview_compute_areas (view);
 421     mcview_update_bytes_per_line (view);
 422 
 423     if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
 424     {
 425         long line, col;
 426         off_t new_offset, max_offset;
 427 
 428         load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
 429         max_offset = mcview_get_filesize (view) - 1;
 430         if (max_offset < 0)
 431             new_offset = 0;
 432         else
 433             new_offset = MIN (new_offset, max_offset);
 434         if (!view->mode_flags.hex)
 435         {
 436             view->dpy_start = mcview_bol (view, new_offset, 0);
 437             view->dpy_wrap_dirty = TRUE;
 438         }
 439         else
 440         {
 441             view->dpy_start = new_offset - new_offset % view->bytes_per_line;
 442             view->hex_cursor = new_offset;
 443         }
 444     }
 445     else if (start_line > 0)
 446         mcview_moveto (view, start_line - 1, 0);
 447 
 448     view->search_start = search_start;
 449     view->search_end = search_end;
 450     view->hexedit_lownibble = FALSE;
 451     view->hexview_in_text = FALSE;
 452     view->change_list = NULL;
 453     vfs_path_free (vpath);
 454     return retval;
 455 }
 456 
 457 /* --------------------------------------------------------------------------------------------- */

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