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

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