Update po/mc.pot and po/*.po files.
[midnight-commander.git] / src / viewer / move.c
blob23ab0226580bde865564545cfeba961e0fe2bf08
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
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
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 /*** file scope functions ************************************************************************/
68 /* --------------------------------------------------------------------------------------------- */
70 static void
71 mcview_movement_fixups (mcview_t * view, gboolean reset_search)
73 mcview_scroll_to_cursor (view);
74 if (reset_search)
76 view->search_start = view->dpy_start;
77 view->search_end = view->dpy_start;
79 view->dirty++;
82 /* --------------------------------------------------------------------------------------------- */
83 /*** public functions ****************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
86 void
87 mcview_move_up (mcview_t * view, off_t lines)
89 off_t new_offset;
91 if (view->hex_mode)
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);
100 else
102 view->hex_cursor %= view->bytes_per_line;
105 else
107 off_t i;
109 for (i = 0; i < lines; i++)
111 if (view->dpy_start == 0)
112 break;
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;
131 else
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;
140 else
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 /* --------------------------------------------------------------------------------------------- */
153 void
154 mcview_move_down (mcview_t * view, off_t lines)
156 off_t last_byte;
157 last_byte = mcview_get_filesize (view);
158 if (view->hex_mode)
160 off_t i, limit;
162 if (last_byte >= (off_t) view->bytes_per_line)
163 limit = last_byte - view->bytes_per_line;
164 else
165 limit = 0;
166 for (i = 0; i < lines && view->hex_cursor < limit; i++)
168 view->hex_cursor += view->bytes_per_line;
169 if (lines != 1)
170 view->dpy_start += view->bytes_per_line;
173 else
175 off_t new_offset = 0;
177 if (view->dpy_end - view->dpy_start > last_byte - view->dpy_end)
179 while (lines-- > 0)
181 if (view->text_wrap_mode)
182 view->dpy_end =
183 mcview_eol (view, view->dpy_end,
184 view->dpy_end + (off_t) view->data_area.width);
185 else
186 view->dpy_end = mcview_eol (view, view->dpy_end, last_byte);
188 if (view->text_wrap_mode)
189 new_offset =
190 mcview_eol (view, view->dpy_start,
191 view->dpy_start + (off_t) view->data_area.width);
192 else
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)
197 break;
200 else
202 off_t i;
203 for (i = 0; i < lines && new_offset < last_byte; i++)
205 if (view->text_wrap_mode)
206 new_offset =
207 mcview_eol (view, view->dpy_start,
208 view->dpy_start + (off_t) view->data_area.width);
209 else
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 /* --------------------------------------------------------------------------------------------- */
221 void
222 mcview_move_left (mcview_t * view, off_t columns)
224 if (view->hex_mode)
226 off_t old_cursor = view->hex_cursor;
227 #ifdef HAVE_ASSERT_H
228 assert (columns == 1);
229 #endif
230 if (view->hexview_in_text || !view->hexedit_lownibble)
232 if (view->hex_cursor > 0)
233 view->hex_cursor--;
235 if (!view->hexview_in_text)
236 if (old_cursor > 0 || view->hexedit_lownibble)
237 view->hexedit_lownibble = !view->hexedit_lownibble;
239 else
241 if (view->dpy_text_column >= columns)
242 view->dpy_text_column -= columns;
243 else
244 view->dpy_text_column = 0;
246 mcview_movement_fixups (view, FALSE);
249 /* --------------------------------------------------------------------------------------------- */
251 void
252 mcview_move_right (mcview_t * view, off_t columns)
254 if (view->hex_mode)
256 off_t last_byte;
257 off_t old_cursor = view->hex_cursor;
258 last_byte = mcview_offset_doz (mcview_get_filesize (view), 1);
259 #ifdef HAVE_ASSERT_H
260 assert (columns == 1);
261 #endif
262 if (view->hexview_in_text || view->hexedit_lownibble)
264 if (view->hex_cursor < last_byte)
265 view->hex_cursor++;
267 if (!view->hexview_in_text)
268 if (old_cursor < last_byte || !view->hexedit_lownibble)
269 view->hexedit_lownibble = !view->hexedit_lownibble;
271 else
273 view->dpy_text_column += columns;
275 mcview_movement_fixups (view, FALSE);
278 /* --------------------------------------------------------------------------------------------- */
280 void
281 mcview_scroll_to_cursor (mcview_t * view)
283 if (view->hex_mode)
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 /* --------------------------------------------------------------------------------------------- */
300 void
301 mcview_moveto_top (mcview_t * view)
303 view->dpy_start = 0;
304 view->hex_cursor = 0;
305 view->dpy_text_column = 0;
306 mcview_movement_fixups (view, TRUE);
309 /* --------------------------------------------------------------------------------------------- */
311 void
312 mcview_moveto_bottom (mcview_t * view)
314 off_t filesize;
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);
323 if (view->hex_mode)
325 view->hex_cursor = mcview_offset_doz (filesize, 1);
326 mcview_movement_fixups (view, TRUE);
328 else
330 const off_t datalines = view->data_area.height;
332 view->dpy_start = filesize;
333 mcview_move_up (view, datalines);
337 /* --------------------------------------------------------------------------------------------- */
339 void
340 mcview_moveto_bol (mcview_t * view)
342 if (view->hex_mode)
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 /* --------------------------------------------------------------------------------------------- */
356 void
357 mcview_moveto_eol (mcview_t * view)
359 off_t bol;
360 if (view->hex_mode)
362 off_t filesize;
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;
369 else
371 filesize = mcview_get_filesize (view);
372 view->hex_cursor = mcview_offset_doz (filesize, 1);
375 else
377 off_t eol;
378 bol = mcview_bol (view, view->dpy_start, 0);
379 eol = mcview_eol (view, view->dpy_start, mcview_get_filesize (view));
380 if (!view->utf8)
382 if (eol > bol)
383 view->dpy_text_column = eol - bol;
385 else
387 char *str = NULL;
388 switch (view->datasource)
390 case DS_STDIO_PIPE:
391 case DS_VFS_PIPE:
392 str = mcview_get_ptr_growing_buffer (view, bol);
393 break;
394 case DS_FILE:
395 str = mcview_get_ptr_file (view, bol);
396 break;
397 case DS_STRING:
398 str = mcview_get_ptr_string (view, bol);
399 break;
400 case DS_NONE:
401 break;
403 if (str != NULL && eol > bol)
404 view->dpy_text_column = g_utf8_strlen (str, eol - bol);
405 else
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;
411 else
412 view->dpy_text_column = view->dpy_text_column - (off_t) view->data_area.width;
414 mcview_movement_fixups (view, FALSE);
417 /* --------------------------------------------------------------------------------------------- */
419 void
420 mcview_moveto_offset (mcview_t * view, off_t offset)
422 if (view->hex_mode)
424 view->hex_cursor = offset;
425 view->dpy_start = offset - offset % view->bytes_per_line;
427 else
429 view->dpy_start = offset;
431 mcview_movement_fixups (view, TRUE);
434 /* --------------------------------------------------------------------------------------------- */
436 void
437 mcview_moveto (mcview_t * view, off_t line, off_t col)
439 off_t offset;
441 mcview_coord_to_offset (view, &offset, line, col);
442 mcview_moveto_offset (view, offset);
445 /* --------------------------------------------------------------------------------------------- */
447 void
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 /* --------------------------------------------------------------------------------------------- */
461 void
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 /* --------------------------------------------------------------------------------------------- */
475 void
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)
482 col++;
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 */
491 void
492 mcview_moveto_match (mcview_t * view)
494 off_t offset;
496 offset = view->search_start;
498 if (view->hex_mode)
500 view->hex_cursor = offset;
501 view->dpy_start = offset - offset % view->bytes_per_line;
503 else
505 view->dpy_start = mcview_bol (view, offset, 0);
508 mcview_scroll_to_cursor (view);
509 view->dirty++;
512 /* --------------------------------------------------------------------------------------------- */