Manual pages: mcmcdiffmceditmcview

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-2025
   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 <https://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, .hex = FALSE, .magic = TRUE, .nroff = FALSE
  55 };
  56 
  57 mcview_mode_flags_t mcview_altered_flags = {
  58     .wrap = FALSE, .hex = FALSE, .magic = FALSE, .nroff = FALSE
  59 };
  60 
  61 gboolean mcview_remember_file_position = FALSE;
  62 
  63 /* Maxlimit for skipping updates */
  64 int mcview_max_dirt_limit = 10;
  65 
  66 /* Scrolling is done in pages or line increments */
  67 gboolean mcview_mouse_move_pages = TRUE;
  68 
  69 /* end of file will be showen from mcview_show_eof */
  70 char *mcview_show_eof = NULL;
  71 
  72 /*** file scope macro definitions ****************************************************************/
  73 
  74 /*** file scope type declarations ****************************************************************/
  75 
  76 /*** forward declarations (file scope functions) *************************************************/
  77 
  78 /*** file scope variables ************************************************************************/
  79 
  80 /* --------------------------------------------------------------------------------------------- */
  81 /*** file scope functions ************************************************************************/
  82 /* --------------------------------------------------------------------------------------------- */
  83 
  84 static void
  85 mcview_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
  86 {
  87     WView *view = (WView *) w;
  88     const WRect *r = &view->data_area;
  89     gboolean ok = TRUE;
  90 
  91     switch (msg)
  92     {
  93     case MSG_MOUSE_DOWN:
  94         if (mcview_is_in_panel (view))
  95         {
  96             if (event->y == WIDGET (w->owner)->rect.y)
  97             {
  98                 // return MOU_UNHANDLED
  99                 event->result.abort = TRUE;
 100                 // don't draw viewer over menu
 101                 ok = FALSE;
 102                 break;
 103             }
 104 
 105             if (!widget_get_state (w, WST_FOCUSED))
 106             {
 107                 // Grab focus
 108                 (void) change_panel ();
 109             }
 110         }
 111         MC_FALLTHROUGH;
 112 
 113     case MSG_MOUSE_CLICK:
 114         if (!view->mode_flags.wrap)
 115         {
 116             // Scrolling left and right
 117             int x;
 118 
 119             x = event->x + 1;  // FIXME
 120 
 121             if (x < r->cols * 1 / 4)
 122             {
 123                 mcview_move_left (view, 1);
 124                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 125             }
 126             else if (x < r->cols * 3 / 4)
 127             {
 128                 // ignore the click
 129                 ok = FALSE;
 130             }
 131             else
 132             {
 133                 mcview_move_right (view, 1);
 134                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 135             }
 136         }
 137         else
 138         {
 139             // Scrolling up and down
 140             int y;
 141 
 142             y = event->y + 1;  // FIXME
 143 
 144             if (y < r->y + r->lines * 1 / 3)
 145             {
 146                 if (mcview_mouse_move_pages)
 147                     mcview_move_up (view, r->lines / 2);
 148                 else
 149                     mcview_move_up (view, 1);
 150 
 151                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 152             }
 153             else if (y < r->y + r->lines * 2 / 3)
 154             {
 155                 // ignore the click
 156                 ok = FALSE;
 157             }
 158             else
 159             {
 160                 if (mcview_mouse_move_pages)
 161                     mcview_move_down (view, r->lines / 2);
 162                 else
 163                     mcview_move_down (view, 1);
 164 
 165                 event->result.repeat = msg == MSG_MOUSE_DOWN;
 166             }
 167         }
 168         break;
 169 
 170     case MSG_MOUSE_SCROLL_UP:
 171         mcview_move_up (view, 2);
 172         break;
 173 
 174     case MSG_MOUSE_SCROLL_DOWN:
 175         mcview_move_down (view, 2);
 176         break;
 177 
 178     default:
 179         ok = FALSE;
 180         break;
 181     }
 182 
 183     if (ok)
 184         mcview_update (view);
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 /*** public functions ****************************************************************************/
 189 /* --------------------------------------------------------------------------------------------- */
 190 
 191 WView *
 192 mcview_new (const WRect *r, gboolean is_panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 193 {
 194     WView *view;
 195     Widget *w;
 196 
 197     view = g_new0 (WView, 1);
 198     w = WIDGET (view);
 199 
 200     widget_init (w, r, mcview_callback, mcview_mouse_callback);
 201     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
 202     w->keymap = viewer_map;
 203 
 204     mcview_clear_mode_flags (&view->mode_flags);
 205     view->hexedit_mode = FALSE;
 206     view->hex_keymap = viewer_hex_map;
 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     WGroup *g;
 239     WRect r;
 240 
 241     // Create dialog and widgets, put them on the dialog
 242     view_dlg = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, mcview_dialog_callback,
 243                            NULL, "[Internal File Viewer]", NULL);
 244     vw = WIDGET (view_dlg);
 245     widget_want_tab (vw, TRUE);
 246 
 247     g = GROUP (view_dlg);
 248 
 249     r = vw->rect;
 250     r.lines--;
 251     lc_mcview = mcview_new (&r, FALSE);
 252     group_add_widget_autopos (g, lc_mcview, WPOS_KEEP_ALL, NULL);
 253 
 254     b = WIDGET (buttonbar_new ());
 255     group_add_widget_autopos (g, b, b->pos_flags, NULL);
 256 
 257     view_dlg->get_title = mcview_get_title;
 258 
 259     succeeded = mcview_load (lc_mcview, command, vfs_path_as_str (file_vpath), start_line,
 260                              search_start, search_end);
 261 
 262     if (succeeded)
 263         dlg_run (view_dlg);
 264     else
 265         dlg_close (view_dlg);
 266 
 267     if (widget_get_state (vw, WST_CLOSED))
 268         widget_destroy (vw);
 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, off_t search_start,
     /* [previous][next][first][last][top][bottom][index][help]  */
 279              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, TRUE);
 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, TRUE);
 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     mcview_set_codeset (view);
 318 
 319     if (command != NULL && (view->mode_flags.magic || file == NULL || file[0] == '\0'))
 320         retval = mcview_load_command_output (view, command);
 321     else if (file != NULL && file[0] != '\0')
 322     {
 323         int fd;
 324         char tmp[BUF_MEDIUM];
 325         struct stat st;
 326 
 327         // Open the file
 328         vpath = vfs_path_from_str (file);
 329         fd = mc_open (vpath, O_RDONLY | O_NONBLOCK);
 330         if (fd == -1)
 331         {
 332             g_snprintf (tmp, sizeof (tmp), _ ("Cannot open \"%s\"\n%s"), file,
 333                         unix_error_string (errno));
 334             mcview_close_datasource (view);
 335             mcview_show_error (view, tmp);
 336             vfs_path_free (view->filename_vpath, TRUE);
 337             view->filename_vpath = NULL;
 338             vfs_path_free (view->workdir_vpath, TRUE);
 339             view->workdir_vpath = NULL;
 340             goto finish;
 341         }
 342 
 343         // Make sure we are working with a regular file
 344         if (mc_fstat (fd, &st) == -1)
 345         {
 346             mc_close (fd);
 347             g_snprintf (tmp, sizeof (tmp), _ ("Cannot stat \"%s\"\n%s"), file,
 348                         unix_error_string (errno));
 349             mcview_close_datasource (view);
 350             mcview_show_error (view, tmp);
 351             vfs_path_free (view->filename_vpath, TRUE);
 352             view->filename_vpath = NULL;
 353             vfs_path_free (view->workdir_vpath, TRUE);
 354             view->workdir_vpath = NULL;
 355             goto finish;
 356         }
 357 
 358         if (!S_ISREG (st.st_mode))
 359         {
 360             mc_close (fd);
 361             mcview_close_datasource (view);
 362             mcview_show_error (view, _ ("Cannot view: not a regular file"));
 363             vfs_path_free (view->filename_vpath, TRUE);
 364             view->filename_vpath = NULL;
 365             vfs_path_free (view->workdir_vpath, TRUE);
 366             view->workdir_vpath = NULL;
 367             goto finish;
 368         }
 369 
 370         if (st.st_size == 0 || mc_lseek (fd, 0, SEEK_SET) == -1)
 371         {
 372             // Must be one of those nice files that grow (/proc)
 373             mcview_set_datasource_vfs_pipe (view, fd);
 374         }
 375         else
 376         {
 377             if (view->mode_flags.magic)
 378             {
 379                 int type;
 380 
 381                 type = get_compression_type (fd, file);
 382 
 383                 if (type != COMPRESSION_NONE)
 384                 {
 385                     char *tmp_filename;
 386                     vfs_path_t *vpath1;
 387                     int fd1;
 388 
 389                     tmp_filename = g_strconcat (file, decompress_extension (type), (char *) NULL);
 390                     vpath1 = vfs_path_from_str (tmp_filename);
 391                     g_free (tmp_filename);
 392                     fd1 = mc_open (vpath1, O_RDONLY | O_NONBLOCK);
 393                     vfs_path_free (vpath1, TRUE);
 394 
 395                     if (fd1 == -1)
 396                     {
 397                         g_snprintf (tmp, sizeof (tmp), _ ("Cannot open \"%s\" in parse mode\n%s"),
 398                                     file, unix_error_string (errno));
 399                         mcview_close_datasource (view);
 400                         mcview_show_error (view, tmp);
 401                     }
 402                     else
 403                     {
 404                         mc_close (fd);
 405                         fd = fd1;
 406                         mc_fstat (fd, &st);
 407                     }
 408                 }
 409             }
 410 
 411             mcview_set_datasource_file (view, fd, &st);
 412         }
 413         retval = TRUE;
 414     }
 415 
 416 finish:
 417     view->command = g_strdup (command);
 418     view->dpy_start = 0;
 419     view->dpy_paragraph_skip_lines = 0;
 420     mcview_state_machine_init (&view->dpy_state_top, 0);
 421     view->dpy_wrap_dirty = FALSE;
 422     view->force_max = -1;
 423     view->dpy_text_column = 0;
 424 
 425     mcview_compute_areas (view);
 426     mcview_update_bytes_per_line (view);
 427 
 428     if (mcview_remember_file_position && view->filename_vpath != NULL && start_line == 0)
 429     {
 430         long line, col;
 431         off_t new_offset, max_offset;
 432 
 433         load_file_position (view->filename_vpath, &line, &col, &new_offset, &view->saved_bookmarks);
 434         max_offset = mcview_get_filesize (view) - 1;
 435         if (max_offset < 0)
 436             new_offset = 0;
 437         else
 438             new_offset = MIN (new_offset, max_offset);
 439         if (!view->mode_flags.hex)
 440         {
 441             view->dpy_start = mcview_bol (view, new_offset, 0);
 442             view->dpy_wrap_dirty = TRUE;
 443         }
 444         else
 445         {
 446             view->dpy_start = new_offset - new_offset % view->bytes_per_line;
 447             view->hex_cursor = new_offset;
 448         }
 449     }
 450     else if (start_line > 0)
 451         mcview_moveto (view, start_line - 1, 0);
 452 
 453     view->search_start = search_start;
 454     view->search_end = search_end;
 455     view->hexedit_lownibble = FALSE;
 456     view->hexview_in_text = FALSE;
 457     view->change_list = NULL;
 458     vfs_path_free (vpath, TRUE);
 459     return retval;
 460 }
 461 
 462 /* --------------------------------------------------------------------------------------------- */

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