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

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