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, 2010 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 "lib/global.h"
57 #include "lib/tty/tty.h"
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
64 /*** file scope type declarations ****************************************************************/
66 /*** file scope variables ************************************************************************/
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
72 mcview_movement_fixups (mcview_t
* view
, gboolean reset_search
)
74 mcview_scroll_to_cursor (view
);
77 view
->search_start
= view
->dpy_start
;
78 view
->search_end
= view
->dpy_start
;
83 /* --------------------------------------------------------------------------------------------- */
84 /*** public functions ****************************************************************************/
85 /* --------------------------------------------------------------------------------------------- */
88 mcview_move_up (mcview_t
* view
, off_t lines
)
94 off_t bytes
= lines
* view
->bytes_per_line
;
95 if (view
->hex_cursor
>= bytes
)
97 view
->hex_cursor
-= bytes
;
98 if (view
->hex_cursor
< view
->dpy_start
)
99 view
->dpy_start
= mcview_offset_doz (view
->dpy_start
, bytes
);
103 view
->hex_cursor
%= view
->bytes_per_line
;
110 for (i
= 0; i
< lines
; i
++)
112 if (view
->dpy_start
== 0)
114 if (view
->text_wrap_mode
)
116 new_offset
= mcview_bol (view
, view
->dpy_start
, view
->dpy_start
- (off_t
) 1);
117 /* check if dpy_start == BOL or not (then new_offset = dpy_start - 1,
118 * no need to check more) */
119 if (new_offset
== view
->dpy_start
)
121 size_t last_row_length
;
123 new_offset
= mcview_bol (view
, new_offset
- 1, 0);
124 last_row_length
= (view
->dpy_start
- new_offset
) % view
->data_area
.width
;
125 if (last_row_length
!= 0)
127 /* if dpy_start == BOL in wrapped mode, find BOL of previous line
128 * and move down all but the last rows */
129 new_offset
= view
->dpy_start
- (off_t
) last_row_length
;
134 /* if dpy_start != BOL in wrapped mode, just move one row up;
135 * no need to check if > 0 as there is at least exactly one wrap
136 * between dpy_start and BOL */
137 new_offset
= view
->dpy_start
- (off_t
) view
->data_area
.width
;
139 view
->dpy_start
= new_offset
;
143 /* if unwrapped -> current BOL equals dpy_start, just find BOL of previous line */
144 new_offset
= view
->dpy_start
- 1;
145 view
->dpy_start
= mcview_bol (view
, new_offset
, 0);
149 mcview_movement_fixups (view
, TRUE
);
152 /* --------------------------------------------------------------------------------------------- */
155 mcview_move_down (mcview_t
* view
, off_t lines
)
158 last_byte
= mcview_get_filesize (view
);
163 if (last_byte
>= (off_t
) view
->bytes_per_line
)
164 limit
= last_byte
- view
->bytes_per_line
;
167 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++)
169 view
->hex_cursor
+= view
->bytes_per_line
;
171 view
->dpy_start
+= view
->bytes_per_line
;
176 off_t new_offset
= 0;
178 if (view
->dpy_end
- view
->dpy_start
> last_byte
- view
->dpy_end
)
182 if (view
->text_wrap_mode
)
184 mcview_eol (view
, view
->dpy_end
,
185 view
->dpy_end
+ (off_t
) view
->data_area
.width
);
187 view
->dpy_end
= mcview_eol (view
, view
->dpy_end
, last_byte
);
189 if (view
->text_wrap_mode
)
191 mcview_eol (view
, view
->dpy_start
,
192 view
->dpy_start
+ (off_t
) view
->data_area
.width
);
194 new_offset
= mcview_eol (view
, view
->dpy_start
, last_byte
);
195 if (new_offset
< last_byte
)
196 view
->dpy_start
= new_offset
;
197 if (view
->dpy_end
>= last_byte
)
204 for (i
= 0; i
< lines
&& new_offset
< last_byte
; i
++)
206 if (view
->text_wrap_mode
)
208 mcview_eol (view
, view
->dpy_start
,
209 view
->dpy_start
+ (off_t
) view
->data_area
.width
);
211 new_offset
= mcview_eol (view
, view
->dpy_start
, last_byte
);
212 if (new_offset
< last_byte
)
213 view
->dpy_start
= new_offset
;
217 mcview_movement_fixups (view
, TRUE
);
220 /* --------------------------------------------------------------------------------------------- */
223 mcview_move_left (mcview_t
* view
, off_t columns
)
227 off_t old_cursor
= view
->hex_cursor
;
228 assert (columns
== 1);
229 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
)
231 if (view
->hex_cursor
> 0)
234 if (!view
->hexview_in_text
)
235 if (old_cursor
> 0 || view
->hexedit_lownibble
)
236 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
240 if (view
->dpy_text_column
>= columns
)
241 view
->dpy_text_column
-= columns
;
243 view
->dpy_text_column
= 0;
245 mcview_movement_fixups (view
, FALSE
);
248 /* --------------------------------------------------------------------------------------------- */
251 mcview_move_right (mcview_t
* view
, off_t columns
)
256 off_t old_cursor
= view
->hex_cursor
;
257 last_byte
= mcview_offset_doz (mcview_get_filesize (view
), 1);
258 assert (columns
== 1);
259 if (view
->hexview_in_text
|| view
->hexedit_lownibble
)
261 if (view
->hex_cursor
< last_byte
)
264 if (!view
->hexview_in_text
)
265 if (old_cursor
< last_byte
|| !view
->hexedit_lownibble
)
266 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
270 view
->dpy_text_column
+= columns
;
272 mcview_movement_fixups (view
, FALSE
);
275 /* --------------------------------------------------------------------------------------------- */
278 mcview_scroll_to_cursor (mcview_t
* view
)
282 const off_t bytes
= view
->bytes_per_line
;
283 const off_t displaysize
= view
->data_area
.height
* bytes
;
284 const off_t cursor
= view
->hex_cursor
;
285 off_t topleft
= view
->dpy_start
;
287 if (topleft
+ displaysize
<= cursor
)
288 topleft
= mcview_offset_rounddown (cursor
, bytes
) - (displaysize
- bytes
);
289 if (cursor
< topleft
)
290 topleft
= mcview_offset_rounddown (cursor
, bytes
);
291 view
->dpy_start
= topleft
;
295 /* --------------------------------------------------------------------------------------------- */
298 mcview_moveto_top (mcview_t
* view
)
301 view
->hex_cursor
= 0;
302 view
->dpy_text_column
= 0;
303 mcview_movement_fixups (view
, TRUE
);
306 /* --------------------------------------------------------------------------------------------- */
309 mcview_moveto_bottom (mcview_t
* view
)
313 mcview_update_filesize (view
);
315 if (view
->growbuf_in_use
)
316 mcview_growbuf_read_until (view
, OFFSETTYPE_MAX
);
318 filesize
= mcview_get_filesize (view
);
322 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
323 mcview_movement_fixups (view
, TRUE
);
327 const off_t datalines
= view
->data_area
.height
;
329 view
->dpy_start
= filesize
;
330 mcview_move_up (view
, datalines
);
334 /* --------------------------------------------------------------------------------------------- */
337 mcview_moveto_bol (mcview_t
* view
)
341 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
343 else if (!view
->text_wrap_mode
)
345 view
->dpy_start
= mcview_bol (view
, view
->dpy_start
, 0);
347 view
->dpy_text_column
= 0;
348 mcview_movement_fixups (view
, TRUE
);
351 /* --------------------------------------------------------------------------------------------- */
354 mcview_moveto_eol (mcview_t
* view
)
361 bol
= mcview_offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
362 if (mcview_get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1, NULL
) == TRUE
)
364 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
368 filesize
= mcview_get_filesize (view
);
369 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
375 bol
= mcview_bol (view
, view
->dpy_start
, 0);
376 eol
= mcview_eol (view
, view
->dpy_start
, mcview_get_filesize (view
));
380 view
->dpy_text_column
= eol
- bol
;
385 switch (view
->datasource
)
389 str
= mcview_get_ptr_growing_buffer (view
, bol
);
392 str
= mcview_get_ptr_file (view
, bol
);
395 str
= mcview_get_ptr_string (view
, bol
);
400 if (str
!= NULL
&& eol
> bol
)
401 view
->dpy_text_column
= g_utf8_strlen (str
, eol
- bol
);
403 view
->dpy_text_column
= eol
- bol
;
405 view
->dpy_text_column
= max (0, view
->dpy_text_column
- view
->data_area
.width
);
407 mcview_movement_fixups (view
, FALSE
);
410 /* --------------------------------------------------------------------------------------------- */
413 mcview_moveto_offset (mcview_t
* view
, off_t offset
)
417 view
->hex_cursor
= offset
;
418 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
422 view
->dpy_start
= offset
;
424 mcview_movement_fixups (view
, TRUE
);
427 /* --------------------------------------------------------------------------------------------- */
430 mcview_moveto (mcview_t
* view
, off_t line
, off_t col
)
434 mcview_coord_to_offset (view
, &offset
, line
, col
);
435 mcview_moveto_offset (view
, offset
);
438 /* --------------------------------------------------------------------------------------------- */
441 mcview_coord_to_offset (mcview_t
* view
, off_t
* ret_offset
, off_t line
, off_t column
)
443 coord_cache_entry_t coord
;
445 coord
.cc_line
= line
;
446 coord
.cc_column
= column
;
447 coord
.cc_nroff_column
= column
;
448 mcview_ccache_lookup (view
, &coord
, CCACHE_OFFSET
);
449 *ret_offset
= coord
.cc_offset
;
452 /* --------------------------------------------------------------------------------------------- */
455 mcview_offset_to_coord (mcview_t
* view
, off_t
* ret_line
, off_t
* ret_column
, off_t offset
)
457 coord_cache_entry_t coord
;
459 coord
.cc_offset
= offset
;
460 mcview_ccache_lookup (view
, &coord
, CCACHE_LINECOL
);
462 *ret_line
= coord
.cc_line
;
463 *ret_column
= (view
->text_nroff_mode
) ? coord
.cc_nroff_column
: coord
.cc_column
;
466 /* --------------------------------------------------------------------------------------------- */
469 mcview_place_cursor (mcview_t
* view
)
471 const screen_dimen top
= view
->data_area
.top
;
472 const screen_dimen left
= view
->data_area
.left
;
473 screen_dimen col
= view
->cursor_col
;
474 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
476 widget_move (&view
->widget
, top
+ view
->cursor_row
, left
+ col
);
479 /* --------------------------------------------------------------------------------------------- */
480 /** we have set view->search_start and view->search_end and must set
481 * view->dpy_text_column and view->dpy_start
482 * try to display maximum of match */
485 mcview_moveto_match (mcview_t
* view
)
489 offset
= view
->search_start
;
493 view
->hex_cursor
= offset
;
494 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
498 view
->dpy_start
= mcview_bol (view
, offset
, 0);
501 mcview_scroll_to_cursor (view
);
505 /* --------------------------------------------------------------------------------------------- */