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, 2013
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, 2013
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 /* --------------------------------------------------------------------------------------------- */
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
72 mcview_scroll_to_cursor (mcview_t
* view
)
76 off_t bytes
= view
->bytes_per_line
;
77 off_t cursor
= view
->hex_cursor
;
78 off_t topleft
= view
->dpy_start
;
81 displaysize
= view
->data_area
.height
* bytes
;
82 if (topleft
+ displaysize
<= cursor
)
83 topleft
= mcview_offset_rounddown (cursor
, bytes
) - (displaysize
- bytes
);
85 topleft
= mcview_offset_rounddown (cursor
, bytes
);
86 view
->dpy_start
= topleft
;
90 /* --------------------------------------------------------------------------------------------- */
93 mcview_movement_fixups (mcview_t
* view
, gboolean reset_search
)
95 mcview_scroll_to_cursor (view
);
98 view
->search_start
= view
->dpy_start
;
99 view
->search_end
= view
->dpy_start
;
104 /* --------------------------------------------------------------------------------------------- */
105 /*** public functions ****************************************************************************/
106 /* --------------------------------------------------------------------------------------------- */
109 mcview_move_up (mcview_t
* view
, off_t lines
)
115 off_t bytes
= lines
* view
->bytes_per_line
;
116 if (view
->hex_cursor
>= bytes
)
118 view
->hex_cursor
-= bytes
;
119 if (view
->hex_cursor
< view
->dpy_start
)
120 view
->dpy_start
= mcview_offset_doz (view
->dpy_start
, bytes
);
124 view
->hex_cursor
%= view
->bytes_per_line
;
131 for (i
= 0; i
< lines
; i
++)
133 if (view
->dpy_start
== 0)
135 if (view
->text_wrap_mode
)
137 new_offset
= mcview_bol (view
, view
->dpy_start
, view
->dpy_start
- (off_t
) 1);
138 /* check if dpy_start == BOL or not (then new_offset = dpy_start - 1,
139 * no need to check more) */
140 if (new_offset
== view
->dpy_start
)
142 size_t last_row_length
;
144 new_offset
= mcview_bol (view
, new_offset
- 1, 0);
145 last_row_length
= (view
->dpy_start
- new_offset
) % view
->data_area
.width
;
146 if (last_row_length
!= 0)
148 /* if dpy_start == BOL in wrapped mode, find BOL of previous line
149 * and move down all but the last rows */
150 new_offset
= view
->dpy_start
- (off_t
) last_row_length
;
155 /* if dpy_start != BOL in wrapped mode, just move one row up;
156 * no need to check if > 0 as there is at least exactly one wrap
157 * between dpy_start and BOL */
158 new_offset
= view
->dpy_start
- (off_t
) view
->data_area
.width
;
160 view
->dpy_start
= new_offset
;
164 /* if unwrapped -> current BOL equals dpy_start, just find BOL of previous line */
165 new_offset
= view
->dpy_start
- 1;
166 view
->dpy_start
= mcview_bol (view
, new_offset
, 0);
170 mcview_movement_fixups (view
, TRUE
);
173 /* --------------------------------------------------------------------------------------------- */
176 mcview_move_down (mcview_t
* view
, off_t lines
)
179 last_byte
= mcview_get_filesize (view
);
184 if (last_byte
>= (off_t
) view
->bytes_per_line
)
185 limit
= last_byte
- view
->bytes_per_line
;
188 for (i
= 0; i
< lines
&& view
->hex_cursor
< limit
; i
++)
190 view
->hex_cursor
+= view
->bytes_per_line
;
192 view
->dpy_start
+= view
->bytes_per_line
;
197 off_t new_offset
= 0;
199 if (view
->dpy_end
- view
->dpy_start
> last_byte
- view
->dpy_end
)
203 if (view
->text_wrap_mode
)
205 mcview_eol (view
, view
->dpy_end
,
206 view
->dpy_end
+ (off_t
) view
->data_area
.width
);
208 view
->dpy_end
= mcview_eol (view
, view
->dpy_end
, last_byte
);
210 if (view
->text_wrap_mode
)
212 mcview_eol (view
, view
->dpy_start
,
213 view
->dpy_start
+ (off_t
) view
->data_area
.width
);
215 new_offset
= mcview_eol (view
, view
->dpy_start
, last_byte
);
216 if (new_offset
< last_byte
)
217 view
->dpy_start
= new_offset
;
218 if (view
->dpy_end
>= last_byte
)
225 for (i
= 0; i
< lines
&& new_offset
< last_byte
; i
++)
227 if (view
->text_wrap_mode
)
229 mcview_eol (view
, view
->dpy_start
,
230 view
->dpy_start
+ (off_t
) view
->data_area
.width
);
232 new_offset
= mcview_eol (view
, view
->dpy_start
, last_byte
);
233 if (new_offset
< last_byte
)
234 view
->dpy_start
= new_offset
;
238 mcview_movement_fixups (view
, TRUE
);
241 /* --------------------------------------------------------------------------------------------- */
244 mcview_move_left (mcview_t
* view
, off_t columns
)
248 off_t old_cursor
= view
->hex_cursor
;
250 assert (columns
== 1);
252 if (view
->hexview_in_text
|| !view
->hexedit_lownibble
)
254 if (view
->hex_cursor
> 0)
257 if (!view
->hexview_in_text
)
258 if (old_cursor
> 0 || view
->hexedit_lownibble
)
259 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
263 if (view
->dpy_text_column
>= columns
)
264 view
->dpy_text_column
-= columns
;
266 view
->dpy_text_column
= 0;
268 mcview_movement_fixups (view
, FALSE
);
271 /* --------------------------------------------------------------------------------------------- */
274 mcview_move_right (mcview_t
* view
, off_t columns
)
279 off_t old_cursor
= view
->hex_cursor
;
280 last_byte
= mcview_offset_doz (mcview_get_filesize (view
), 1);
282 assert (columns
== 1);
284 if (view
->hexview_in_text
|| view
->hexedit_lownibble
)
286 if (view
->hex_cursor
< last_byte
)
289 if (!view
->hexview_in_text
)
290 if (old_cursor
< last_byte
|| !view
->hexedit_lownibble
)
291 view
->hexedit_lownibble
= !view
->hexedit_lownibble
;
295 view
->dpy_text_column
+= columns
;
297 mcview_movement_fixups (view
, FALSE
);
300 /* --------------------------------------------------------------------------------------------- */
303 mcview_moveto_top (mcview_t
* view
)
306 view
->hex_cursor
= 0;
307 view
->dpy_text_column
= 0;
308 mcview_movement_fixups (view
, TRUE
);
311 /* --------------------------------------------------------------------------------------------- */
314 mcview_moveto_bottom (mcview_t
* view
)
318 mcview_update_filesize (view
);
320 if (view
->growbuf_in_use
)
321 mcview_growbuf_read_until (view
, OFFSETTYPE_MAX
);
323 filesize
= mcview_get_filesize (view
);
327 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
328 mcview_movement_fixups (view
, TRUE
);
332 const off_t datalines
= view
->data_area
.height
;
334 view
->dpy_start
= filesize
;
335 mcview_move_up (view
, datalines
);
339 /* --------------------------------------------------------------------------------------------- */
342 mcview_moveto_bol (mcview_t
* view
)
346 view
->hex_cursor
-= view
->hex_cursor
% view
->bytes_per_line
;
348 else if (!view
->text_wrap_mode
)
350 view
->dpy_start
= mcview_bol (view
, view
->dpy_start
, 0);
352 view
->dpy_text_column
= 0;
353 mcview_movement_fixups (view
, TRUE
);
356 /* --------------------------------------------------------------------------------------------- */
359 mcview_moveto_eol (mcview_t
* view
)
366 bol
= mcview_offset_rounddown (view
->hex_cursor
, view
->bytes_per_line
);
367 if (mcview_get_byte_indexed (view
, bol
, view
->bytes_per_line
- 1, NULL
) == TRUE
)
369 view
->hex_cursor
= bol
+ view
->bytes_per_line
- 1;
373 filesize
= mcview_get_filesize (view
);
374 view
->hex_cursor
= mcview_offset_doz (filesize
, 1);
380 bol
= mcview_bol (view
, view
->dpy_start
, 0);
381 eol
= mcview_eol (view
, view
->dpy_start
, mcview_get_filesize (view
));
385 view
->dpy_text_column
= eol
- bol
;
390 switch (view
->datasource
)
394 str
= mcview_get_ptr_growing_buffer (view
, bol
);
397 str
= mcview_get_ptr_file (view
, bol
);
400 str
= mcview_get_ptr_string (view
, bol
);
405 if (str
!= NULL
&& eol
> bol
)
406 view
->dpy_text_column
= g_utf8_strlen (str
, eol
- bol
);
408 view
->dpy_text_column
= eol
- bol
;
411 if (view
->dpy_text_column
< (off_t
) view
->data_area
.width
)
412 view
->dpy_text_column
= 0;
414 view
->dpy_text_column
= view
->dpy_text_column
- (off_t
) view
->data_area
.width
;
416 mcview_movement_fixups (view
, FALSE
);
419 /* --------------------------------------------------------------------------------------------- */
422 mcview_moveto_offset (mcview_t
* view
, off_t offset
)
426 view
->hex_cursor
= offset
;
427 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
431 view
->dpy_start
= offset
;
433 mcview_movement_fixups (view
, TRUE
);
436 /* --------------------------------------------------------------------------------------------- */
439 mcview_moveto (mcview_t
* view
, off_t line
, off_t col
)
443 mcview_coord_to_offset (view
, &offset
, line
, col
);
444 mcview_moveto_offset (view
, offset
);
447 /* --------------------------------------------------------------------------------------------- */
450 mcview_coord_to_offset (mcview_t
* view
, off_t
* ret_offset
, off_t line
, off_t column
)
452 coord_cache_entry_t coord
;
454 coord
.cc_line
= line
;
455 coord
.cc_column
= column
;
456 coord
.cc_nroff_column
= column
;
457 mcview_ccache_lookup (view
, &coord
, CCACHE_OFFSET
);
458 *ret_offset
= coord
.cc_offset
;
461 /* --------------------------------------------------------------------------------------------- */
464 mcview_offset_to_coord (mcview_t
* view
, off_t
* ret_line
, off_t
* ret_column
, off_t offset
)
466 coord_cache_entry_t coord
;
468 coord
.cc_offset
= offset
;
469 mcview_ccache_lookup (view
, &coord
, CCACHE_LINECOL
);
471 *ret_line
= coord
.cc_line
;
472 *ret_column
= (view
->text_nroff_mode
) ? coord
.cc_nroff_column
: coord
.cc_column
;
475 /* --------------------------------------------------------------------------------------------- */
478 mcview_place_cursor (mcview_t
* view
)
480 const screen_dimen top
= view
->data_area
.top
;
481 const screen_dimen left
= view
->data_area
.left
;
482 screen_dimen col
= view
->cursor_col
;
483 if (!view
->hexview_in_text
&& view
->hexedit_lownibble
)
485 widget_move (view
, top
+ view
->cursor_row
, left
+ col
);
488 /* --------------------------------------------------------------------------------------------- */
489 /** we have set view->search_start and view->search_end and must set
490 * view->dpy_text_column and view->dpy_start
491 * try to display maximum of match */
494 mcview_moveto_match (mcview_t
* view
)
498 offset
= view
->search_start
;
502 view
->hex_cursor
= offset
;
503 view
->dpy_start
= offset
- offset
% view
->bytes_per_line
;
507 view
->dpy_start
= mcview_bol (view
, offset
, 0);
510 mcview_scroll_to_cursor (view
);
514 /* --------------------------------------------------------------------------------------------- */