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

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