2 Internal file viewer for the Midnight Commander
3 Functions for handle cursor movement
5 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
6 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
14 2004 Roland Illig <roland.illig@gmx.de>
15 2005 Roland Illig <roland.illig@gmx.de>
16 2009 Slava Zanko <slavazanko@google.com>
17 2009 Andrew Borodin <aborodin@vmail.ru>
18 2009 Ilia Maslakov <il.smind@gmail.com>
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 2 of the
25 License, or (at your option) any later version.
27 The Midnight Commander is distributed in the hope that it will be
28 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
29 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 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, write to the Free Software
34 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
39 The following variables have to do with the current position and are
40 updated by the cursor movement functions.
42 In hex view and wrapped text view mode, dpy_start marks the offset of
43 the top-left corner on the screen, in non-wrapping text mode it is
44 the beginning of the current line. In hex mode, hex_cursor is the
45 offset of the cursor. In non-wrapping text mode, dpy_text_column is
46 the number of columns that are hidden on the left side on the screen.
48 In hex mode, dpy_start is updated by the view_fix_cursor_position()
49 function in order to keep the other functions simple. In
50 non-wrapping text mode dpy_start and dpy_text_column are normalized
51 such that dpy_text_column < view_get_datacolumns().
56 #include "../src/global.h"
57 #include "../src/tty/tty.h"
58 #include "../src/viewer/internal.h"
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
64 /*** file scope type declarations ****************************************************************/
66 /*** file scope variables ************************************************************************/
68 /*** file scope functions ************************************************************************/
71 mcview_movement_fixups (mcview_t
* view
, gboolean reset_search
)
73 mcview_scroll_to_cursor (view
);
75 view
->search_start
= view
->dpy_start
;
76 view
->search_end
= view
->dpy_start
;
81 /* --------------------------------------------------------------------------------------------- */
83 /*** public functions ****************************************************************************/
85 /* --------------------------------------------------------------------------------------------- */
88 mcview_move_up (mcview_t
* view
, off_t lines
)
91 off_t bytes
= lines
* view
->bytes_per_line
;
92 if (view
->hex_cursor
>= bytes
) {
93 view
->hex_cursor
-= bytes
;
94 if (view
->hex_cursor
< view
->dpy_start
)
95 view
->dpy_start
= mcview_offset_doz (view
->dpy_start
, bytes
);
97 view
->hex_cursor
%= view
->bytes_per_line
;
99 } else if (view
->text_wrap_mode
) {
100 const screen_dimen width
= view
->data_area
.width
;
101 off_t i
, col
, line
, linestart
;
103 for (i
= 0; i
< lines
; i
++) {
104 mcview_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
107 } else if (line
>= 1) {
108 mcview_coord_to_offset (view
, &linestart
, line
, 0);
109 mcview_offset_to_coord (view
, &line
, &col
, linestart
- 1);
111 /* if the only thing that would be displayed were a
112 * single newline character, advance to the previous
113 * part of the line. */
114 if (col
> 0 && col
% width
== 0)
121 mcview_coord_to_offset (view
, &(view
->dpy_start
), line
, col
);
126 mcview_offset_to_coord (view
, &line
, &column
, view
->dpy_start
);
127 line
= mcview_offset_doz (line
, lines
);
128 mcview_coord_to_offset (view
, &(view
->dpy_start
), line
, column
);
130 mcview_movement_fixups (view
, (lines
!= 1));
133 /* --------------------------------------------------------------------------------------------- */
136 mcview_move_down (mcview_t
* view
, off_t lines
)
138 if (view
->hex_mode
) {
139 off_t i
, limit
, last_byte
;
141 last_byte
= mcview_get_filesize (view
);
142 if (last_byte
>= (off_t
) view
->bytes_per_line
)
143 limit
= last_byte
- view
->bytes_per_line
;
146 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++) {
147 view
->hex_cursor
+= view
->bytes_per_line
;
149 view
->dpy_start
+= view
->bytes_per_line
;
152 } else if (view
->dpy_end
== mcview_get_filesize (view
)) {
153 /* don't move further down. There's nothing more to see. */
155 } else if (view
->text_wrap_mode
) {
158 for (i
= 0; i
< lines
; i
++) {
159 off_t new_offset
, chk_line
, chk_col
;
161 mcview_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
162 col
+= view
->data_area
.width
;
163 mcview_coord_to_offset (view
, &new_offset
, line
, col
);
165 /* skip to the next line if the only thing that would be
166 * displayed is the newline character. */
167 mcview_offset_to_coord (view
, &chk_line
, &chk_col
, new_offset
);
168 if (chk_line
== line
&& chk_col
== col
&& mcview_get_byte (view
, new_offset
) == '\n')
171 view
->dpy_start
= new_offset
;
177 mcview_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
179 mcview_coord_to_offset (view
, &(view
->dpy_start
), line
, col
);
181 mcview_movement_fixups (view
, (lines
!= 1));
184 /* --------------------------------------------------------------------------------------------- */
187 mcview_move_left (mcview_t
* view
, off_t columns
)
189 if (view
->hex_mode
) {
190 assert (columns
== 1);
191 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
) {
192 if (view
->hex_cursor
> 0)
195 if (!view
->hexview_in_text
)
196 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
197 } else if (view
->text_wrap_mode
) {
200 if (view
->dpy_text_column
>= columns
)
201 view
->dpy_text_column
-= columns
;
203 view
->dpy_text_column
= 0;
205 mcview_movement_fixups (view
, FALSE
);
208 /* --------------------------------------------------------------------------------------------- */
211 mcview_move_right (mcview_t
* view
, off_t columns
)
213 if (view
->hex_mode
) {
214 assert (columns
== 1);
215 if (view
->hexview_in_text
|| view
->hexedit_lownibble
) {
218 if (!view
->hexview_in_text
)
219 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
220 } else if (view
->text_wrap_mode
) {
223 view
->dpy_text_column
+= columns
;
225 mcview_movement_fixups (view
, FALSE
);
228 /* --------------------------------------------------------------------------------------------- */
231 mcview_scroll_to_cursor (mcview_t
* view
)
233 if (view
->hex_mode
) {
234 const off_t bytes
= view
->bytes_per_line
;
235 const off_t displaysize
= view
->data_area
.height
* bytes
;
236 const off_t cursor
= view
->hex_cursor
;
237 off_t topleft
= view
->dpy_start
;
239 if (topleft
+ displaysize
<= cursor
)
240 topleft
= mcview_offset_rounddown (cursor
, bytes
)
241 - (displaysize
- bytes
);
242 if (cursor
< topleft
)
243 topleft
= mcview_offset_rounddown (cursor
, bytes
);
244 view
->dpy_start
= topleft
;
245 } else if (view
->text_wrap_mode
) {
246 off_t line
, col
, columns
;
248 columns
= view
->data_area
.width
;
249 mcview_offset_to_coord (view
, &line
, &col
, view
->dpy_start
+ view
->dpy_text_column
);
251 col
= mcview_offset_rounddown (col
, columns
);
252 mcview_coord_to_offset (view
, &(view
->dpy_start
), line
, col
);
253 view
->dpy_text_column
= 0;
259 /* --------------------------------------------------------------------------------------------- */
262 mcview_moveto_top (mcview_t
* view
)
265 view
->hex_cursor
= 0;
266 view
->dpy_text_column
= 0;
267 mcview_movement_fixups (view
, TRUE
);
270 /* --------------------------------------------------------------------------------------------- */
273 mcview_moveto_bottom (mcview_t
* view
)
275 off_t datalines
, lines_up
, filesize
, last_offset
;
277 if (view
->growbuf_in_use
)
278 mcview_growbuf_read_until (view
, OFFSETTYPE_MAX
);
280 filesize
= mcview_get_filesize (view
);
281 last_offset
= mcview_offset_doz (filesize
, 1);
282 datalines
= view
->data_area
.height
;
283 lines_up
= mcview_offset_doz (datalines
, 1);
285 if (view
->hex_mode
) {
286 view
->hex_cursor
= filesize
;
287 mcview_move_up (view
, lines_up
);
288 view
->hex_cursor
= last_offset
;
290 view
->dpy_start
= last_offset
;
291 mcview_moveto_bol (view
);
292 mcview_move_up (view
, lines_up
);
294 mcview_movement_fixups (view
, TRUE
);
297 /* --------------------------------------------------------------------------------------------- */
300 mcview_moveto_bol (mcview_t
* view
)
302 if (view
->hex_mode
) {
303 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
304 } else if (view
->text_wrap_mode
) {
308 mcview_offset_to_coord (view
, &line
, &column
, view
->dpy_start
);
309 mcview_coord_to_offset (view
, &(view
->dpy_start
), line
, 0);
310 view
->dpy_text_column
= 0;
312 mcview_movement_fixups (view
, TRUE
);
315 /* --------------------------------------------------------------------------------------------- */
318 mcview_moveto_eol (mcview_t
* view
)
320 if (view
->hex_mode
) {
323 bol
= mcview_offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
324 if (mcview_get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1) != -1) {
325 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
327 filesize
= mcview_get_filesize (view
);
328 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
330 } else if (view
->text_wrap_mode
) {
335 mcview_offset_to_coord (view
, &line
, &col
, view
->dpy_start
);
336 mcview_coord_to_offset (view
, &(view
->dpy_start
), line
, OFFSETTYPE_MAX
);
338 mcview_movement_fixups (view
, FALSE
);
341 /* --------------------------------------------------------------------------------------------- */
344 mcview_moveto_offset (mcview_t
* view
, off_t offset
)
346 if (view
->hex_mode
) {
347 view
->hex_cursor
= offset
;
348 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
350 view
->dpy_start
= offset
;
352 mcview_movement_fixups (view
, TRUE
);
355 /* --------------------------------------------------------------------------------------------- */
358 mcview_moveto (mcview_t
* view
, off_t line
, off_t col
)
362 mcview_coord_to_offset (view
, &offset
, line
, col
);
363 mcview_moveto_offset (view
, offset
);
366 /* --------------------------------------------------------------------------------------------- */
369 mcview_coord_to_offset (mcview_t
* view
, off_t
* ret_offset
, off_t line
, off_t column
)
371 struct coord_cache_entry coord
;
373 coord
.cc_line
= line
;
374 coord
.cc_column
= column
;
375 coord
.cc_nroff_column
= column
;
376 mcview_ccache_lookup (view
, &coord
, CCACHE_OFFSET
);
377 *ret_offset
= coord
.cc_offset
;
380 /* --------------------------------------------------------------------------------------------- */
383 mcview_offset_to_coord (mcview_t
* view
, off_t
* ret_line
, off_t
* ret_column
, off_t offset
)
385 struct coord_cache_entry coord
;
387 coord
.cc_offset
= offset
;
388 mcview_ccache_lookup (view
, &coord
, CCACHE_LINECOL
);
391 *ret_line
= coord
.cc_line
;
394 *ret_column
= (view
->text_nroff_mode
)
395 ? coord
.cc_nroff_column
: coord
.cc_column
;
398 /* --------------------------------------------------------------------------------------------- */
401 mcview_place_cursor (mcview_t
* view
)
403 const screen_dimen top
= view
->data_area
.top
;
404 const screen_dimen left
= view
->data_area
.left
;
407 col
= view
->cursor_col
;
408 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
410 widget_move (&view
->widget
, top
+ view
->cursor_row
, left
+ col
);
413 /* --------------------------------------------------------------------------------------------- */
415 /* we have set view->search_start and view->search_end and must set
416 * view->dpy_text_column and view->dpy_start
417 * try to display maximum of match */
419 mcview_moveto_match (mcview_t
* view
)
421 off_t search_line
, offset
;
423 mcview_offset_to_coord (view
, &search_line
, NULL
, view
->search_start
);
424 mcview_coord_to_offset (view
, &offset
, search_line
, 0);
426 if (view
->hex_mode
) {
427 view
->hex_cursor
= offset
;
428 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
430 view
->dpy_start
= offset
;
433 mcview_scroll_to_cursor (view
);
437 /* --------------------------------------------------------------------------------------------- */