Merge branch '2976_broken_magic_mode'
[midnight-commander.git] / src / viewer / move.c
blob3ea809db0a4e66edb6fd994e7db8e80b886d76f7
1 /*
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.
9 Written by:
10 Miguel de Icaza, 1994, 1995, 1998
11 Janne Kukonlehto, 1994, 1995
12 Jakub Jelinek, 1995
13 Joseph M. Hinkle, 1996
14 Norbert Warmuth, 1997
15 Pavel Machek, 1998
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().
53 #include <config.h>
55 #include "lib/global.h"
56 #include "lib/tty/tty.h"
57 #include "internal.h"
59 /*** global variables ****************************************************************************/
61 /*** file scope macro definitions ****************************************************************/
63 /*** file scope type declarations ****************************************************************/
65 /*** file scope variables ************************************************************************/
67 /* --------------------------------------------------------------------------------------------- */
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
71 static void
72 mcview_scroll_to_cursor (mcview_t * view)
74 if (view->hex_mode)
76 off_t bytes = view->bytes_per_line;
77 off_t cursor = view->hex_cursor;
78 off_t topleft = view->dpy_start;
79 off_t displaysize;
81 displaysize = view->data_area.height * bytes;
82 if (topleft + displaysize <= cursor)
83 topleft = mcview_offset_rounddown (cursor, bytes) - (displaysize - bytes);
84 if (cursor < topleft)
85 topleft = mcview_offset_rounddown (cursor, bytes);
86 view->dpy_start = topleft;
90 /* --------------------------------------------------------------------------------------------- */
92 static void
93 mcview_movement_fixups (mcview_t * view, gboolean reset_search)
95 mcview_scroll_to_cursor (view);
96 if (reset_search)
98 view->search_start = view->dpy_start;
99 view->search_end = view->dpy_start;
101 view->dirty++;
104 /* --------------------------------------------------------------------------------------------- */
105 /*** public functions ****************************************************************************/
106 /* --------------------------------------------------------------------------------------------- */
108 void
109 mcview_move_up (mcview_t * view, off_t lines)
111 off_t new_offset;
113 if (view->hex_mode)
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);
122 else
124 view->hex_cursor %= view->bytes_per_line;
127 else
129 off_t i;
131 for (i = 0; i < lines; i++)
133 if (view->dpy_start == 0)
134 break;
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;
153 else
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;
162 else
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 /* --------------------------------------------------------------------------------------------- */
175 void
176 mcview_move_down (mcview_t * view, off_t lines)
178 off_t last_byte;
179 last_byte = mcview_get_filesize (view);
180 if (view->hex_mode)
182 off_t i, limit;
184 if (last_byte >= (off_t) view->bytes_per_line)
185 limit = last_byte - view->bytes_per_line;
186 else
187 limit = 0;
188 for (i = 0; i < lines && view->hex_cursor < limit; i++)
190 view->hex_cursor += view->bytes_per_line;
191 if (lines != 1)
192 view->dpy_start += view->bytes_per_line;
195 else
197 off_t new_offset = 0;
199 if (view->dpy_end - view->dpy_start > last_byte - view->dpy_end)
201 while (lines-- > 0)
203 if (view->text_wrap_mode)
204 view->dpy_end =
205 mcview_eol (view, view->dpy_end,
206 view->dpy_end + (off_t) view->data_area.width);
207 else
208 view->dpy_end = mcview_eol (view, view->dpy_end, last_byte);
210 if (view->text_wrap_mode)
211 new_offset =
212 mcview_eol (view, view->dpy_start,
213 view->dpy_start + (off_t) view->data_area.width);
214 else
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)
219 break;
222 else
224 off_t i;
225 for (i = 0; i < lines && new_offset < last_byte; i++)
227 if (view->text_wrap_mode)
228 new_offset =
229 mcview_eol (view, view->dpy_start,
230 view->dpy_start + (off_t) view->data_area.width);
231 else
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 /* --------------------------------------------------------------------------------------------- */
243 void
244 mcview_move_left (mcview_t * view, off_t columns)
246 if (view->hex_mode)
248 off_t old_cursor = view->hex_cursor;
249 #ifdef HAVE_ASSERT_H
250 assert (columns == 1);
251 #endif
252 if (view->hexview_in_text || !view->hexedit_lownibble)
254 if (view->hex_cursor > 0)
255 view->hex_cursor--;
257 if (!view->hexview_in_text)
258 if (old_cursor > 0 || view->hexedit_lownibble)
259 view->hexedit_lownibble = !view->hexedit_lownibble;
261 else
263 if (view->dpy_text_column >= columns)
264 view->dpy_text_column -= columns;
265 else
266 view->dpy_text_column = 0;
268 mcview_movement_fixups (view, FALSE);
271 /* --------------------------------------------------------------------------------------------- */
273 void
274 mcview_move_right (mcview_t * view, off_t columns)
276 if (view->hex_mode)
278 off_t last_byte;
279 off_t old_cursor = view->hex_cursor;
280 last_byte = mcview_offset_doz (mcview_get_filesize (view), 1);
281 #ifdef HAVE_ASSERT_H
282 assert (columns == 1);
283 #endif
284 if (view->hexview_in_text || view->hexedit_lownibble)
286 if (view->hex_cursor < last_byte)
287 view->hex_cursor++;
289 if (!view->hexview_in_text)
290 if (old_cursor < last_byte || !view->hexedit_lownibble)
291 view->hexedit_lownibble = !view->hexedit_lownibble;
293 else
295 view->dpy_text_column += columns;
297 mcview_movement_fixups (view, FALSE);
300 /* --------------------------------------------------------------------------------------------- */
302 void
303 mcview_moveto_top (mcview_t * view)
305 view->dpy_start = 0;
306 view->hex_cursor = 0;
307 view->dpy_text_column = 0;
308 mcview_movement_fixups (view, TRUE);
311 /* --------------------------------------------------------------------------------------------- */
313 void
314 mcview_moveto_bottom (mcview_t * view)
316 off_t filesize;
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);
325 if (view->hex_mode)
327 view->hex_cursor = mcview_offset_doz (filesize, 1);
328 mcview_movement_fixups (view, TRUE);
330 else
332 const off_t datalines = view->data_area.height;
334 view->dpy_start = filesize;
335 mcview_move_up (view, datalines);
339 /* --------------------------------------------------------------------------------------------- */
341 void
342 mcview_moveto_bol (mcview_t * view)
344 if (view->hex_mode)
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 /* --------------------------------------------------------------------------------------------- */
358 void
359 mcview_moveto_eol (mcview_t * view)
361 off_t bol;
362 if (view->hex_mode)
364 off_t filesize;
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;
371 else
373 filesize = mcview_get_filesize (view);
374 view->hex_cursor = mcview_offset_doz (filesize, 1);
377 else
379 off_t eol;
380 bol = mcview_bol (view, view->dpy_start, 0);
381 eol = mcview_eol (view, view->dpy_start, mcview_get_filesize (view));
382 if (!view->utf8)
384 if (eol > bol)
385 view->dpy_text_column = eol - bol;
387 else
389 char *str = NULL;
390 switch (view->datasource)
392 case DS_STDIO_PIPE:
393 case DS_VFS_PIPE:
394 str = mcview_get_ptr_growing_buffer (view, bol);
395 break;
396 case DS_FILE:
397 str = mcview_get_ptr_file (view, bol);
398 break;
399 case DS_STRING:
400 str = mcview_get_ptr_string (view, bol);
401 break;
402 case DS_NONE:
403 break;
405 if (str != NULL && eol > bol)
406 view->dpy_text_column = g_utf8_strlen (str, eol - bol);
407 else
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;
413 else
414 view->dpy_text_column = view->dpy_text_column - (off_t) view->data_area.width;
416 mcview_movement_fixups (view, FALSE);
419 /* --------------------------------------------------------------------------------------------- */
421 void
422 mcview_moveto_offset (mcview_t * view, off_t offset)
424 if (view->hex_mode)
426 view->hex_cursor = offset;
427 view->dpy_start = offset - offset % view->bytes_per_line;
429 else
431 view->dpy_start = offset;
433 mcview_movement_fixups (view, TRUE);
436 /* --------------------------------------------------------------------------------------------- */
438 void
439 mcview_moveto (mcview_t * view, off_t line, off_t col)
441 off_t offset;
443 mcview_coord_to_offset (view, &offset, line, col);
444 mcview_moveto_offset (view, offset);
447 /* --------------------------------------------------------------------------------------------- */
449 void
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 /* --------------------------------------------------------------------------------------------- */
463 void
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 /* --------------------------------------------------------------------------------------------- */
477 void
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)
484 col++;
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 */
493 void
494 mcview_moveto_match (mcview_t * view)
496 off_t offset;
498 offset = view->search_start;
500 if (view->hex_mode)
502 view->hex_cursor = offset;
503 view->dpy_start = offset - offset % view->bytes_per_line;
505 else
507 view->dpy_start = mcview_bol (view, offset, 0);
510 mcview_scroll_to_cursor (view);
511 view->dirty++;
514 /* --------------------------------------------------------------------------------------------- */