2 Internal file viewer for the Midnight Commander
3 Functions for handle cursor movement
5 Copyright (C) 1994-2024
6 Free Software Foundation, Inc.
9 Miguel de Icaza, 1994, 1995, 1998
10 Janne Kukonlehto, 1994, 1995
12 Joseph M. Hinkle, 1996
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().
54 #include "lib/global.h"
55 #include "lib/tty/tty.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 /* --------------------------------------------------------------------------------------------- */
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
;
82 displaysize
= view
->data_area
.lines
* bytes
;
83 if (topleft
+ displaysize
<= cursor
)
84 topleft
= mcview_offset_rounddown (cursor
, bytes
) - (displaysize
- bytes
);
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 /* --------------------------------------------------------------------------------------------- */
96 mcview_movement_fixups (WView
*view
, gboolean reset_search
)
98 mcview_scroll_to_cursor (view
);
102 view
->search_start
= view
->mode_flags
.hex
? view
->hex_cursor
: view
->dpy_start
;
103 view
->search_end
= view
->search_start
;
109 /* --------------------------------------------------------------------------------------------- */
110 /*** public functions ****************************************************************************/
111 /* --------------------------------------------------------------------------------------------- */
114 mcview_move_up (WView
*view
, off_t lines
)
116 if (!view
->mode_flags
.hex
)
117 mcview_ascii_move_up (view
, lines
);
122 bytes
= lines
* view
->bytes_per_line
;
124 if (view
->hex_cursor
< bytes
)
125 view
->hex_cursor
%= view
->bytes_per_line
;
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 /* --------------------------------------------------------------------------------------------- */
144 mcview_move_down (WView
*view
, off_t lines
)
148 last_byte
= mcview_get_filesize (view
);
150 if (!view
->mode_flags
.hex
)
151 mcview_ascii_move_down (view
, lines
);
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
;
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 /* --------------------------------------------------------------------------------------------- */
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)
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 /* --------------------------------------------------------------------------------------------- */
202 mcview_move_right (WView
*view
, off_t columns
)
204 if (view
->mode_flags
.hex
)
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
)
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 /* --------------------------------------------------------------------------------------------- */
231 mcview_moveto_top (WView
*view
)
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 /* --------------------------------------------------------------------------------------------- */
244 mcview_moveto_bottom (WView
*view
)
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
);
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 /* --------------------------------------------------------------------------------------------- */
272 mcview_moveto_bol (WView
*view
)
274 if (!view
->mode_flags
.hex
)
275 mcview_ascii_moveto_bol (view
);
278 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
279 view
->dpy_text_column
= 0;
282 mcview_movement_fixups (view
, TRUE
);
285 /* --------------------------------------------------------------------------------------------- */
288 mcview_moveto_eol (WView
*view
)
292 if (!view
->mode_flags
.hex
)
293 mcview_ascii_moveto_eol (view
);
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;
304 filesize
= mcview_get_filesize (view
);
305 view
->hex_cursor
= DOZ (filesize
, 1);
309 mcview_movement_fixups (view
, FALSE
);
312 /* --------------------------------------------------------------------------------------------- */
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
;
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 /* --------------------------------------------------------------------------------------------- */
337 mcview_moveto (WView
*view
, off_t line
, off_t col
)
341 mcview_coord_to_offset (view
, &offset
, line
, col
);
342 mcview_moveto_offset (view
, offset
);
345 /* --------------------------------------------------------------------------------------------- */
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 /* --------------------------------------------------------------------------------------------- */
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 /* --------------------------------------------------------------------------------------------- */
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
)
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 */
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
;
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
);
415 /* --------------------------------------------------------------------------------------------- */