tar: avoid strtoul().
[midnight-commander.git] / src / viewer / move.c
blob6c77df3f299b4051c649baa1838506dda3e7521c
1 /*
2 Internal file viewer for the Midnight Commander
3 Functions for handle cursor movement
5 Copyright (C) 1994-2024
6 Free Software Foundation, Inc.
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
20 This file is part of the Midnight Commander.
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.
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.
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/>.
37 The following variables have to do with the current position and are
38 updated by the cursor movement functions.
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.
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().
52 #include <config.h>
54 #include "lib/global.h"
55 #include "lib/tty/tty.h"
56 #include "internal.h"
58 /*** global variables ****************************************************************************/
60 /*** file scope macro definitions ****************************************************************/
62 /*** file scope type declarations ****************************************************************/
64 /*** forward declarations (file scope functions) *************************************************/
66 /*** file scope variables ************************************************************************/
68 /* --------------------------------------------------------------------------------------------- */
69 /*** file scope functions ************************************************************************/
70 /* --------------------------------------------------------------------------------------------- */
72 static void
73 mcview_scroll_to_cursor (WView *view)
75 if (view->mode_flags.hex)
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;
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;
93 /* --------------------------------------------------------------------------------------------- */
95 static void
96 mcview_movement_fixups (WView *view, gboolean reset_search)
98 mcview_scroll_to_cursor (view);
100 if (reset_search)
102 view->search_start = view->mode_flags.hex ? view->hex_cursor : view->dpy_start;
103 view->search_end = view->search_start;
106 view->dirty++;
109 /* --------------------------------------------------------------------------------------------- */
110 /*** public functions ****************************************************************************/
111 /* --------------------------------------------------------------------------------------------- */
113 void
114 mcview_move_up (WView *view, off_t lines)
116 if (!view->mode_flags.hex)
117 mcview_ascii_move_up (view, lines);
118 else
120 off_t bytes;
122 bytes = lines * view->bytes_per_line;
124 if (view->hex_cursor < bytes)
125 view->hex_cursor %= view->bytes_per_line;
126 else
128 view->hex_cursor -= bytes;
129 if (view->hex_cursor < view->dpy_start)
131 view->dpy_start = DOZ (view->dpy_start, bytes);
132 view->dpy_paragraph_skip_lines = 0;
133 view->dpy_wrap_dirty = TRUE;
138 mcview_movement_fixups (view, TRUE);
141 /* --------------------------------------------------------------------------------------------- */
143 void
144 mcview_move_down (WView *view, off_t lines)
146 off_t last_byte;
148 last_byte = mcview_get_filesize (view);
150 if (!view->mode_flags.hex)
151 mcview_ascii_move_down (view, lines);
152 else
154 off_t i, limit;
156 limit = DOZ (last_byte, (off_t) view->bytes_per_line);
158 for (i = 0; i < lines && view->hex_cursor < limit; i++)
160 view->hex_cursor += view->bytes_per_line;
162 if (lines != 1)
164 view->dpy_start += view->bytes_per_line;
165 view->dpy_paragraph_skip_lines = 0;
166 view->dpy_wrap_dirty = TRUE;
171 mcview_movement_fixups (view, TRUE);
174 /* --------------------------------------------------------------------------------------------- */
176 void
177 mcview_move_left (WView *view, off_t columns)
179 if (view->mode_flags.hex)
181 off_t old_cursor = view->hex_cursor;
183 g_assert (columns == 1);
185 if (view->hexview_in_text || !view->hexedit_lownibble)
186 if (view->hex_cursor > 0)
187 view->hex_cursor--;
189 if (!view->hexview_in_text)
190 if (old_cursor > 0 || view->hexedit_lownibble)
191 view->hexedit_lownibble = !view->hexedit_lownibble;
193 else if (!view->mode_flags.wrap)
194 view->dpy_text_column = DOZ (view->dpy_text_column, columns);
196 mcview_movement_fixups (view, FALSE);
199 /* --------------------------------------------------------------------------------------------- */
201 void
202 mcview_move_right (WView *view, off_t columns)
204 if (view->mode_flags.hex)
206 off_t last_byte;
207 off_t old_cursor = view->hex_cursor;
209 last_byte = mcview_get_filesize (view);
210 last_byte = DOZ (last_byte, 1);
212 g_assert (columns == 1);
214 if (view->hexview_in_text || view->hexedit_lownibble)
215 if (view->hex_cursor < last_byte)
216 view->hex_cursor++;
218 if (!view->hexview_in_text)
219 if (old_cursor < last_byte || !view->hexedit_lownibble)
220 view->hexedit_lownibble = !view->hexedit_lownibble;
222 else if (!view->mode_flags.wrap)
223 view->dpy_text_column += columns;
225 mcview_movement_fixups (view, FALSE);
228 /* --------------------------------------------------------------------------------------------- */
230 void
231 mcview_moveto_top (WView *view)
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);
241 /* --------------------------------------------------------------------------------------------- */
243 void
244 mcview_moveto_bottom (WView *view)
246 off_t filesize;
248 mcview_update_filesize (view);
250 if (view->growbuf_in_use)
251 mcview_growbuf_read_all_data (view);
253 filesize = mcview_get_filesize (view);
255 if (view->mode_flags.hex)
257 view->hex_cursor = DOZ (filesize, 1);
258 mcview_movement_fixups (view, TRUE);
260 else
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);
269 /* --------------------------------------------------------------------------------------------- */
271 void
272 mcview_moveto_bol (WView *view)
274 if (!view->mode_flags.hex)
275 mcview_ascii_moveto_bol (view);
276 else
278 view->hex_cursor -= view->hex_cursor % view->bytes_per_line;
279 view->dpy_text_column = 0;
282 mcview_movement_fixups (view, TRUE);
285 /* --------------------------------------------------------------------------------------------- */
287 void
288 mcview_moveto_eol (WView *view)
290 off_t bol;
292 if (!view->mode_flags.hex)
293 mcview_ascii_moveto_eol (view);
294 else
296 off_t filesize;
298 bol = mcview_offset_rounddown (view->hex_cursor, view->bytes_per_line);
300 if (mcview_get_byte_indexed (view, bol, view->bytes_per_line - 1, NULL))
301 view->hex_cursor = bol + view->bytes_per_line - 1;
302 else
304 filesize = mcview_get_filesize (view);
305 view->hex_cursor = DOZ (filesize, 1);
309 mcview_movement_fixups (view, FALSE);
312 /* --------------------------------------------------------------------------------------------- */
314 void
315 mcview_moveto_offset (WView *view, off_t offset)
317 if (view->mode_flags.hex)
319 view->hex_cursor = offset;
320 view->dpy_start = offset - offset % view->bytes_per_line;
321 view->dpy_paragraph_skip_lines = 0;
322 view->dpy_wrap_dirty = TRUE;
324 else
326 view->dpy_start = offset;
327 view->dpy_paragraph_skip_lines = 0;
328 view->dpy_wrap_dirty = TRUE;
331 mcview_movement_fixups (view, TRUE);
334 /* --------------------------------------------------------------------------------------------- */
336 void
337 mcview_moveto (WView *view, off_t line, off_t col)
339 off_t offset;
341 mcview_coord_to_offset (view, &offset, line, col);
342 mcview_moveto_offset (view, offset);
345 /* --------------------------------------------------------------------------------------------- */
347 void
348 mcview_coord_to_offset (WView *view, off_t *ret_offset, off_t line, off_t column)
350 coord_cache_entry_t coord;
352 coord.cc_line = line;
353 coord.cc_column = column;
354 coord.cc_nroff_column = column;
355 mcview_ccache_lookup (view, &coord, CCACHE_OFFSET);
356 *ret_offset = coord.cc_offset;
359 /* --------------------------------------------------------------------------------------------- */
361 void
362 mcview_offset_to_coord (WView *view, off_t *ret_line, off_t *ret_column, off_t offset)
364 coord_cache_entry_t coord;
366 coord.cc_offset = offset;
367 mcview_ccache_lookup (view, &coord, CCACHE_LINECOL);
369 *ret_line = coord.cc_line;
370 *ret_column = view->mode_flags.nroff ? coord.cc_nroff_column : coord.cc_column;
373 /* --------------------------------------------------------------------------------------------- */
375 void
376 mcview_place_cursor (WView *view)
378 const WRect *r = &view->data_area;
379 int col = view->cursor_col;
381 if (!view->hexview_in_text && view->hexedit_lownibble)
382 col++;
384 widget_gotoyx (view, r->y + view->cursor_row, r->x + col);
387 /* --------------------------------------------------------------------------------------------- */
388 /** we have set view->search_start and view->search_end and must set
389 * view->dpy_text_column and view->dpy_start
390 * try to display maximum of match */
392 void
393 mcview_moveto_match (WView *view)
395 if (view->mode_flags.hex)
397 view->hex_cursor = view->search_start;
398 view->hexedit_lownibble = FALSE;
399 view->dpy_start = view->search_start - view->search_start % view->bytes_per_line;
400 view->dpy_end = view->search_end - view->search_end % view->bytes_per_line;
401 view->dpy_paragraph_skip_lines = 0;
402 view->dpy_wrap_dirty = TRUE;
404 else
406 view->dpy_start = mcview_bol (view, view->search_start, 0);
407 view->dpy_paragraph_skip_lines = 0;
408 view->dpy_wrap_dirty = TRUE;
411 mcview_scroll_to_cursor (view);
412 view->dirty++;
415 /* --------------------------------------------------------------------------------------------- */