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, 2011
7 The Free Software Foundation, Inc.
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
13 Joseph M. Hinkle, 1996
16 Roland Illig <roland.illig@gmx.de>, 2004, 2005
17 Slava Zanko <slavazanko@google.com>, 2009
18 Andrew Borodin <aborodin@vmail.ru>, 2009
19 Ilia Maslakov <il.smind@gmail.com>, 2009, 2010
21 This file is part of the Midnight Commander.
23 The Midnight Commander is free software: you can redistribute it
24 and/or modify it under the terms of the GNU General Public License as
25 published by the Free Software Foundation, either version 3 of the License,
26 or (at your option) any later version.
28 The Midnight Commander is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
33 You should have received a copy of the GNU General Public License
34 along with this program. If not, see <http://www.gnu.org/licenses/>.
38 The following variables have to do with the current position and are
39 updated by the cursor movement functions.
41 In hex view and wrapped text view mode, dpy_start marks the offset of
42 the top-left corner on the screen, in non-wrapping text mode it is
43 the beginning of the current line. In hex mode, hex_cursor is the
44 offset of the cursor. In non-wrapping text mode, dpy_text_column is
45 the number of columns that are hidden on the left side on the screen.
47 In hex mode, dpy_start is updated by the view_fix_cursor_position()
48 function in order to keep the other functions simple. In
49 non-wrapping text mode dpy_start and dpy_text_column are normalized
50 such that dpy_text_column < view_get_datacolumns().
55 #include "lib/global.h"
56 #include "lib/tty/tty.h"
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 /*** file scope type declarations ****************************************************************/
65 /*** file scope variables ************************************************************************/
67 /*** file scope functions ************************************************************************/
68 /* --------------------------------------------------------------------------------------------- */
71 mcview_movement_fixups (mcview_t
* view
, gboolean reset_search
)
73 mcview_scroll_to_cursor (view
);
76 view
->search_start
= view
->dpy_start
;
77 view
->search_end
= view
->dpy_start
;
82 /* --------------------------------------------------------------------------------------------- */
83 /*** public functions ****************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
87 mcview_move_up (mcview_t
* view
, off_t lines
)
93 off_t bytes
= lines
* view
->bytes_per_line
;
94 if (view
->hex_cursor
>= bytes
)
96 view
->hex_cursor
-= bytes
;
97 if (view
->hex_cursor
< view
->dpy_start
)
98 view
->dpy_start
= mcview_offset_doz (view
->dpy_start
, bytes
);
102 view
->hex_cursor
%= view
->bytes_per_line
;
109 for (i
= 0; i
< lines
; i
++)
111 if (view
->dpy_start
== 0)
113 if (view
->text_wrap_mode
)
115 new_offset
= mcview_bol (view
, view
->dpy_start
, view
->dpy_start
- (off_t
) 1);
116 /* check if dpy_start == BOL or not (then new_offset = dpy_start - 1,
117 * no need to check more) */
118 if (new_offset
== view
->dpy_start
)
120 size_t last_row_length
;
122 new_offset
= mcview_bol (view
, new_offset
- 1, 0);
123 last_row_length
= (view
->dpy_start
- new_offset
) % view
->data_area
.width
;
124 if (last_row_length
!= 0)
126 /* if dpy_start == BOL in wrapped mode, find BOL of previous line
127 * and move down all but the last rows */
128 new_offset
= view
->dpy_start
- (off_t
) last_row_length
;
133 /* if dpy_start != BOL in wrapped mode, just move one row up;
134 * no need to check if > 0 as there is at least exactly one wrap
135 * between dpy_start and BOL */
136 new_offset
= view
->dpy_start
- (off_t
) view
->data_area
.width
;
138 view
->dpy_start
= new_offset
;
142 /* if unwrapped -> current BOL equals dpy_start, just find BOL of previous line */
143 new_offset
= view
->dpy_start
- 1;
144 view
->dpy_start
= mcview_bol (view
, new_offset
, 0);
148 mcview_movement_fixups (view
, TRUE
);
151 /* --------------------------------------------------------------------------------------------- */
154 mcview_move_down (mcview_t
* view
, off_t lines
)
157 last_byte
= mcview_get_filesize (view
);
162 if (last_byte
>= (off_t
) view
->bytes_per_line
)
163 limit
= last_byte
- view
->bytes_per_line
;
166 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++)
168 view
->hex_cursor
+= view
->bytes_per_line
;
170 view
->dpy_start
+= view
->bytes_per_line
;
175 off_t new_offset
= 0;
177 if (view
->dpy_end
- view
->dpy_start
> last_byte
- view
->dpy_end
)
181 if (view
->text_wrap_mode
)
183 mcview_eol (view
, view
->dpy_end
,
184 view
->dpy_end
+ (off_t
) view
->data_area
.width
);
186 view
->dpy_end
= mcview_eol (view
, view
->dpy_end
, last_byte
);
188 if (view
->text_wrap_mode
)
190 mcview_eol (view
, view
->dpy_start
,
191 view
->dpy_start
+ (off_t
) view
->data_area
.width
);
193 new_offset
= mcview_eol (view
, view
->dpy_start
, last_byte
);
194 if (new_offset
< last_byte
)
195 view
->dpy_start
= new_offset
;
196 if (view
->dpy_end
>= last_byte
)
203 for (i
= 0; i
< lines
&& new_offset
< last_byte
; i
++)
205 if (view
->text_wrap_mode
)
207 mcview_eol (view
, view
->dpy_start
,
208 view
->dpy_start
+ (off_t
) view
->data_area
.width
);
210 new_offset
= mcview_eol (view
, view
->dpy_start
, last_byte
);
211 if (new_offset
< last_byte
)
212 view
->dpy_start
= new_offset
;
216 mcview_movement_fixups (view
, TRUE
);
219 /* --------------------------------------------------------------------------------------------- */
222 mcview_move_left (mcview_t
* view
, off_t columns
)
226 off_t old_cursor
= view
->hex_cursor
;
228 assert (columns
== 1);
230 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
)
232 if (view
->hex_cursor
> 0)
235 if (!view
->hexview_in_text
)
236 if (old_cursor
> 0 || view
->hexedit_lownibble
)
237 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
241 if (view
->dpy_text_column
>= columns
)
242 view
->dpy_text_column
-= columns
;
244 view
->dpy_text_column
= 0;
246 mcview_movement_fixups (view
, FALSE
);
249 /* --------------------------------------------------------------------------------------------- */
252 mcview_move_right (mcview_t
* view
, off_t columns
)
257 off_t old_cursor
= view
->hex_cursor
;
258 last_byte
= mcview_offset_doz (mcview_get_filesize (view
), 1);
260 assert (columns
== 1);
262 if (view
->hexview_in_text
|| view
->hexedit_lownibble
)
264 if (view
->hex_cursor
< last_byte
)
267 if (!view
->hexview_in_text
)
268 if (old_cursor
< last_byte
|| !view
->hexedit_lownibble
)
269 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
273 view
->dpy_text_column
+= columns
;
275 mcview_movement_fixups (view
, FALSE
);
278 /* --------------------------------------------------------------------------------------------- */
281 mcview_scroll_to_cursor (mcview_t
* view
)
285 const off_t bytes
= view
->bytes_per_line
;
286 const off_t displaysize
= view
->data_area
.height
* bytes
;
287 const off_t cursor
= view
->hex_cursor
;
288 off_t topleft
= view
->dpy_start
;
290 if (topleft
+ displaysize
<= cursor
)
291 topleft
= mcview_offset_rounddown (cursor
, bytes
) - (displaysize
- bytes
);
292 if (cursor
< topleft
)
293 topleft
= mcview_offset_rounddown (cursor
, bytes
);
294 view
->dpy_start
= topleft
;
298 /* --------------------------------------------------------------------------------------------- */
301 mcview_moveto_top (mcview_t
* view
)
304 view
->hex_cursor
= 0;
305 view
->dpy_text_column
= 0;
306 mcview_movement_fixups (view
, TRUE
);
309 /* --------------------------------------------------------------------------------------------- */
312 mcview_moveto_bottom (mcview_t
* view
)
316 mcview_update_filesize (view
);
318 if (view
->growbuf_in_use
)
319 mcview_growbuf_read_until (view
, OFFSETTYPE_MAX
);
321 filesize
= mcview_get_filesize (view
);
325 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
326 mcview_movement_fixups (view
, TRUE
);
330 const off_t datalines
= view
->data_area
.height
;
332 view
->dpy_start
= filesize
;
333 mcview_move_up (view
, datalines
);
337 /* --------------------------------------------------------------------------------------------- */
340 mcview_moveto_bol (mcview_t
* view
)
344 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
346 else if (!view
->text_wrap_mode
)
348 view
->dpy_start
= mcview_bol (view
, view
->dpy_start
, 0);
350 view
->dpy_text_column
= 0;
351 mcview_movement_fixups (view
, TRUE
);
354 /* --------------------------------------------------------------------------------------------- */
357 mcview_moveto_eol (mcview_t
* view
)
364 bol
= mcview_offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
365 if (mcview_get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1, NULL
) == TRUE
)
367 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
371 filesize
= mcview_get_filesize (view
);
372 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
378 bol
= mcview_bol (view
, view
->dpy_start
, 0);
379 eol
= mcview_eol (view
, view
->dpy_start
, mcview_get_filesize (view
));
383 view
->dpy_text_column
= eol
- bol
;
388 switch (view
->datasource
)
392 str
= mcview_get_ptr_growing_buffer (view
, bol
);
395 str
= mcview_get_ptr_file (view
, bol
);
398 str
= mcview_get_ptr_string (view
, bol
);
403 if (str
!= NULL
&& eol
> bol
)
404 view
->dpy_text_column
= g_utf8_strlen (str
, eol
- bol
);
406 view
->dpy_text_column
= eol
- bol
;
409 if (view
->dpy_text_column
< (off_t
) view
->data_area
.width
)
410 view
->dpy_text_column
= 0;
412 view
->dpy_text_column
= view
->dpy_text_column
- (off_t
) view
->data_area
.width
;
414 mcview_movement_fixups (view
, FALSE
);
417 /* --------------------------------------------------------------------------------------------- */
420 mcview_moveto_offset (mcview_t
* view
, off_t offset
)
424 view
->hex_cursor
= offset
;
425 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
429 view
->dpy_start
= offset
;
431 mcview_movement_fixups (view
, TRUE
);
434 /* --------------------------------------------------------------------------------------------- */
437 mcview_moveto (mcview_t
* view
, off_t line
, off_t col
)
441 mcview_coord_to_offset (view
, &offset
, line
, col
);
442 mcview_moveto_offset (view
, offset
);
445 /* --------------------------------------------------------------------------------------------- */
448 mcview_coord_to_offset (mcview_t
* view
, off_t
* ret_offset
, off_t line
, off_t column
)
450 coord_cache_entry_t coord
;
452 coord
.cc_line
= line
;
453 coord
.cc_column
= column
;
454 coord
.cc_nroff_column
= column
;
455 mcview_ccache_lookup (view
, &coord
, CCACHE_OFFSET
);
456 *ret_offset
= coord
.cc_offset
;
459 /* --------------------------------------------------------------------------------------------- */
462 mcview_offset_to_coord (mcview_t
* view
, off_t
* ret_line
, off_t
* ret_column
, off_t offset
)
464 coord_cache_entry_t coord
;
466 coord
.cc_offset
= offset
;
467 mcview_ccache_lookup (view
, &coord
, CCACHE_LINECOL
);
469 *ret_line
= coord
.cc_line
;
470 *ret_column
= (view
->text_nroff_mode
) ? coord
.cc_nroff_column
: coord
.cc_column
;
473 /* --------------------------------------------------------------------------------------------- */
476 mcview_place_cursor (mcview_t
* view
)
478 const screen_dimen top
= view
->data_area
.top
;
479 const screen_dimen left
= view
->data_area
.left
;
480 screen_dimen col
= view
->cursor_col
;
481 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
483 widget_move (view
, top
+ view
->cursor_row
, left
+ col
);
486 /* --------------------------------------------------------------------------------------------- */
487 /** we have set view->search_start and view->search_end and must set
488 * view->dpy_text_column and view->dpy_start
489 * try to display maximum of match */
492 mcview_moveto_match (mcview_t
* view
)
496 offset
= view
->search_start
;
500 view
->hex_cursor
= offset
;
501 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
505 view
->dpy_start
= mcview_bol (view
, offset
, 0);
508 mcview_scroll_to_cursor (view
);
512 /* --------------------------------------------------------------------------------------------- */