root/src/viewer/move.c

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

DEFINITIONS

This source file includes following definitions.
  1. mcview_scroll_to_cursor
  2. mcview_movement_fixups
  3. mcview_move_up
  4. mcview_move_down
  5. mcview_move_left
  6. mcview_move_right
  7. mcview_moveto_top
  8. mcview_moveto_bottom
  9. mcview_moveto_bol
  10. mcview_moveto_eol
  11. mcview_moveto_offset
  12. mcview_moveto
  13. mcview_coord_to_offset
  14. mcview_offset_to_coord
  15. mcview_place_cursor
  16. mcview_moveto_match

   1 /*
   2    Internal file viewer for the Midnight Commander
   3    Functions for handle cursor movement
   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
  17    Andrew Borodin <aborodin@vmail.ru>, 2009-2022
  18    Ilia Maslakov <il.smind@gmail.com>, 2009, 2010
  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 /*
  37    The following variables have to do with the current position and are
  38    updated by the cursor movement functions.
  39 
  40    In hex view and wrapped text view mode, dpy_start marks the offset of
  41    the top-left corner on the screen, in non-wrapping text mode it is
  42    the beginning of the current line.  In hex mode, hex_cursor is the
  43    offset of the cursor.  In non-wrapping text mode, dpy_text_column is
  44    the number of columns that are hidden on the left side on the screen.
  45 
  46    In hex mode, dpy_start is updated by the view_fix_cursor_position()
  47    function in order to keep the other functions simple.  In
  48    non-wrapping text mode dpy_start and dpy_text_column are normalized
  49    such that dpy_text_column < view_get_datacolumns().
  50  */
  51 
  52 #include <config.h>
  53 
  54 #include "lib/global.h"
  55 #include "lib/tty/tty.h"
  56 #include "internal.h"
  57 
  58 /*** global variables ****************************************************************************/
  59 
  60 /*** file scope macro definitions ****************************************************************/
  61 
  62 /*** file scope type declarations ****************************************************************/
  63 
  64 /*** forward declarations (file scope functions) *************************************************/
  65 
  66 /*** file scope variables ************************************************************************/
  67 
  68 /* --------------------------------------------------------------------------------------------- */
  69 /*** file scope functions ************************************************************************/
  70 /* --------------------------------------------------------------------------------------------- */
  71 
  72 static void
  73 mcview_scroll_to_cursor (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
  74 {
  75     if (view->mode_flags.hex)
  76     {
  77         off_t bytes = view->bytes_per_line;
  78         off_t cursor = view->hex_cursor;
  79         off_t topleft = view->dpy_start;
  80         off_t displaysize;
  81 
  82         displaysize = view->data_area.lines * bytes;
  83         if (topleft + displaysize <= cursor)
  84             topleft = mcview_offset_rounddown (cursor, bytes) - (displaysize - bytes);
  85         if (cursor < topleft)
  86             topleft = mcview_offset_rounddown (cursor, bytes);
  87         view->dpy_start = topleft;
  88         view->dpy_paragraph_skip_lines = 0;
  89         view->dpy_wrap_dirty = TRUE;
  90     }
  91 }
  92 
  93 /* --------------------------------------------------------------------------------------------- */
  94 
  95 static void
  96 mcview_movement_fixups (WView *view, gboolean reset_search)
     /* [previous][next][first][last][top][bottom][index][help]  */
  97 {
  98     mcview_scroll_to_cursor (view);
  99 
 100     if (reset_search)
 101     {
 102         view->search_start = view->mode_flags.hex ? view->hex_cursor : view->dpy_start;
 103         view->search_end = view->search_start;
 104     }
 105 
 106     view->dirty++;
 107 }
 108 
 109 /* --------------------------------------------------------------------------------------------- */
 110 /*** public functions ****************************************************************************/
 111 /* --------------------------------------------------------------------------------------------- */
 112 
 113 void
 114 mcview_move_up (WView *view, off_t lines)
     /* [previous][next][first][last][top][bottom][index][help]  */
 115 {
 116     if (!view->mode_flags.hex)
 117         mcview_ascii_move_up (view, lines);
 118     else
 119     {
 120         off_t bytes;
 121 
 122         bytes = lines * view->bytes_per_line;
 123 
 124         if (view->hex_cursor < bytes)
 125             view->hex_cursor %= view->bytes_per_line;
 126         else
 127         {
 128             view->hex_cursor -= bytes;
 129             if (view->hex_cursor < view->dpy_start)
 130             {
 131                 view->dpy_start = DOZ (view->dpy_start, bytes);
 132                 view->dpy_paragraph_skip_lines = 0;
 133                 view->dpy_wrap_dirty = TRUE;
 134             }
 135         }
 136     }
 137 
 138     mcview_movement_fixups (view, TRUE);
 139 }
 140 
 141 /* --------------------------------------------------------------------------------------------- */
 142 
 143 void
 144 mcview_move_down (WView *view, off_t lines)
     /* [previous][next][first][last][top][bottom][index][help]  */
 145 {
 146     off_t last_byte;
 147 
 148     last_byte = mcview_get_filesize (view);
 149 
 150     if (!view->mode_flags.hex)
 151         mcview_ascii_move_down (view, lines);
 152     else
 153     {
 154         off_t i, limit;
 155 
 156         limit = DOZ (last_byte, (off_t) view->bytes_per_line);
 157 
 158         for (i = 0; i < lines && view->hex_cursor < limit; i++)
 159         {
 160             view->hex_cursor += view->bytes_per_line;
 161 
 162             if (lines != 1)
 163             {
 164                 view->dpy_start += view->bytes_per_line;
 165                 view->dpy_paragraph_skip_lines = 0;
 166                 view->dpy_wrap_dirty = TRUE;
 167             }
 168         }
 169     }
 170 
 171     mcview_movement_fixups (view, TRUE);
 172 }
 173 
 174 /* --------------------------------------------------------------------------------------------- */
 175 
 176 void
 177 mcview_move_left (WView *view, off_t columns)
     /* [previous][next][first][last][top][bottom][index][help]  */
 178 {
 179     if (view->mode_flags.hex)
 180     {
 181         off_t old_cursor = view->hex_cursor;
 182 
 183         g_assert (columns == 1);
 184 
 185         if (view->hexview_in_text || !view->hexedit_lownibble)
 186             if (view->hex_cursor > 0)
 187                 view->hex_cursor--;
 188 
 189         if (!view->hexview_in_text)
 190             if (old_cursor > 0 || view->hexedit_lownibble)
 191                 view->hexedit_lownibble = !view->hexedit_lownibble;
 192     }
 193     else if (!view->mode_flags.wrap)
 194         view->dpy_text_column = DOZ (view->dpy_text_column, columns);
 195 
 196     mcview_movement_fixups (view, FALSE);
 197 }
 198 
 199 /* --------------------------------------------------------------------------------------------- */
 200 
 201 void
 202 mcview_move_right (WView *view, off_t columns)
     /* [previous][next][first][last][top][bottom][index][help]  */
 203 {
 204     if (view->mode_flags.hex)
 205     {
 206         off_t last_byte;
 207         off_t old_cursor = view->hex_cursor;
 208 
 209         last_byte = mcview_get_filesize (view);
 210         last_byte = DOZ (last_byte, 1);
 211 
 212         g_assert (columns == 1);
 213 
 214         if (view->hexview_in_text || view->hexedit_lownibble)
 215             if (view->hex_cursor < last_byte)
 216                 view->hex_cursor++;
 217 
 218         if (!view->hexview_in_text)
 219             if (old_cursor < last_byte || !view->hexedit_lownibble)
 220                 view->hexedit_lownibble = !view->hexedit_lownibble;
 221     }
 222     else if (!view->mode_flags.wrap)
 223         view->dpy_text_column += columns;
 224 
 225     mcview_movement_fixups (view, FALSE);
 226 }
 227 
 228 /* --------------------------------------------------------------------------------------------- */
 229 
 230 void
 231 mcview_moveto_top (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 232 {
 233     view->dpy_start = 0;
 234     view->dpy_paragraph_skip_lines = 0;
 235     mcview_state_machine_init (&view->dpy_state_top, 0);
 236     view->hex_cursor = 0;
 237     view->dpy_text_column = 0;
 238     mcview_movement_fixups (view, TRUE);
 239 }
 240 
 241 /* --------------------------------------------------------------------------------------------- */
 242 
 243 void
 244 mcview_moveto_bottom (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 245 {
 246     off_t filesize;
 247 
 248     mcview_update_filesize (view);
 249 
 250     if (view->growbuf_in_use)
 251         mcview_growbuf_read_all_data (view);
 252 
 253     filesize = mcview_get_filesize (view);
 254 
 255     if (view->mode_flags.hex)
 256     {
 257         view->hex_cursor = DOZ (filesize, 1);
 258         mcview_movement_fixups (view, TRUE);
 259     }
 260     else
 261     {
 262         view->dpy_start = filesize;
 263         view->dpy_paragraph_skip_lines = 0;
 264         view->dpy_wrap_dirty = TRUE;
 265         mcview_move_up (view, view->data_area.lines);
 266         /* start backward search from EOF */
 267         view->search_start = filesize;
 268         view->search_end = view->search_start;
 269     }
 270 }
 271 
 272 /* --------------------------------------------------------------------------------------------- */
 273 
 274 void
 275 mcview_moveto_bol (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 276 {
 277     if (!view->mode_flags.hex)
 278         mcview_ascii_moveto_bol (view);
 279     else
 280     {
 281         view->hex_cursor -= view->hex_cursor % view->bytes_per_line;
 282         view->dpy_text_column = 0;
 283     }
 284 
 285     mcview_movement_fixups (view, TRUE);
 286 }
 287 
 288 /* --------------------------------------------------------------------------------------------- */
 289 
 290 void
 291 mcview_moveto_eol (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 292 {
 293     off_t bol;
 294 
 295     if (!view->mode_flags.hex)
 296         mcview_ascii_moveto_eol (view);
 297     else
 298     {
 299         off_t filesize;
 300 
 301         bol = mcview_offset_rounddown (view->hex_cursor, view->bytes_per_line);
 302 
 303         if (mcview_get_byte_indexed (view, bol, view->bytes_per_line - 1, NULL))
 304             view->hex_cursor = bol + view->bytes_per_line - 1;
 305         else
 306         {
 307             filesize = mcview_get_filesize (view);
 308             view->hex_cursor = DOZ (filesize, 1);
 309         }
 310     }
 311 
 312     mcview_movement_fixups (view, FALSE);
 313 }
 314 
 315 /* --------------------------------------------------------------------------------------------- */
 316 
 317 void
 318 mcview_moveto_offset (WView *view, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 319 {
 320     if (view->mode_flags.hex)
 321     {
 322         view->hex_cursor = offset;
 323         view->dpy_start = offset - offset % view->bytes_per_line;
 324         view->dpy_paragraph_skip_lines = 0;
 325         view->dpy_wrap_dirty = TRUE;
 326     }
 327     else
 328     {
 329         view->dpy_start = offset;
 330         view->dpy_paragraph_skip_lines = 0;
 331         view->dpy_wrap_dirty = TRUE;
 332     }
 333 
 334     mcview_movement_fixups (view, TRUE);
 335 }
 336 
 337 /* --------------------------------------------------------------------------------------------- */
 338 
 339 void
 340 mcview_moveto (WView *view, off_t line, off_t col)
     /* [previous][next][first][last][top][bottom][index][help]  */
 341 {
 342     off_t offset;
 343 
 344     mcview_coord_to_offset (view, &offset, line, col);
 345     mcview_moveto_offset (view, offset);
 346 }
 347 
 348 /* --------------------------------------------------------------------------------------------- */
 349 
 350 void
 351 mcview_coord_to_offset (WView *view, off_t *ret_offset, off_t line, off_t column)
     /* [previous][next][first][last][top][bottom][index][help]  */
 352 {
 353     coord_cache_entry_t coord;
 354 
 355     coord.cc_line = line;
 356     coord.cc_column = column;
 357     coord.cc_nroff_column = column;
 358     mcview_ccache_lookup (view, &coord, CCACHE_OFFSET);
 359     *ret_offset = coord.cc_offset;
 360 }
 361 
 362 /* --------------------------------------------------------------------------------------------- */
 363 
 364 void
 365 mcview_offset_to_coord (WView *view, off_t *ret_line, off_t *ret_column, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
 366 {
 367     coord_cache_entry_t coord;
 368 
 369     coord.cc_offset = offset;
 370     mcview_ccache_lookup (view, &coord, CCACHE_LINECOL);
 371 
 372     *ret_line = coord.cc_line;
 373     *ret_column = view->mode_flags.nroff ? coord.cc_nroff_column : coord.cc_column;
 374 }
 375 
 376 /* --------------------------------------------------------------------------------------------- */
 377 
 378 void
 379 mcview_place_cursor (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 380 {
 381     const WRect *r = &view->data_area;
 382     int col = view->cursor_col;
 383 
 384     if (!view->hexview_in_text && view->hexedit_lownibble)
 385         col++;
 386 
 387     widget_gotoyx (view, r->y + view->cursor_row, r->x + col);
 388 }
 389 
 390 /* --------------------------------------------------------------------------------------------- */
 391 /** we have set view->search_start and view->search_end and must set
 392  * view->dpy_text_column and view->dpy_start
 393  * try to display maximum of match */
 394 
 395 void
 396 mcview_moveto_match (WView *view)
     /* [previous][next][first][last][top][bottom][index][help]  */
 397 {
 398     if (view->mode_flags.hex)
 399     {
 400         view->hex_cursor = view->search_start;
 401         view->hexedit_lownibble = FALSE;
 402         view->dpy_start = view->search_start - view->search_start % view->bytes_per_line;
 403         view->dpy_end = view->search_end - view->search_end % view->bytes_per_line;
 404         view->dpy_paragraph_skip_lines = 0;
 405         view->dpy_wrap_dirty = TRUE;
 406     }
 407     else
 408     {
 409         view->dpy_start = mcview_bol (view, view->search_start, 0);
 410         view->dpy_paragraph_skip_lines = 0;
 411         view->dpy_wrap_dirty = TRUE;
 412     }
 413 
 414     mcview_scroll_to_cursor (view);
 415     view->dirty++;
 416 }
 417 
 418 /* --------------------------------------------------------------------------------------------- */

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