(get_paragraph): fix of pointer difference.
[midnight-commander.git] / src / viewer / move.c
blob9699f17e14d24f570d2bb277a805296a67e757fa
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 Free Software Foundation, Inc.
8 Written by: 1994, 1995, 1998 Miguel de Icaza
9 1994, 1995 Janne Kukonlehto
10 1995 Jakub Jelinek
11 1996 Joseph M. Hinkle
12 1997 Norbert Warmuth
13 1998 Pavel Machek
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,
35 MA 02110-1301, USA.
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().
54 #include <config.h>
56 #include "lib/global.h"
57 #include "lib/tty/tty.h"
58 #include "internal.h"
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
64 /*** file scope type declarations ****************************************************************/
66 /*** file scope variables ************************************************************************/
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
71 static void
72 mcview_movement_fixups (mcview_t * view, gboolean reset_search)
74 mcview_scroll_to_cursor (view);
75 if (reset_search)
77 view->search_start = view->dpy_start;
78 view->search_end = view->dpy_start;
80 view->dirty++;
83 /* --------------------------------------------------------------------------------------------- */
84 /*** public functions ****************************************************************************/
85 /* --------------------------------------------------------------------------------------------- */
87 void
88 mcview_move_up (mcview_t * view, off_t lines)
90 off_t new_offset;
92 if (view->hex_mode)
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);
101 else
103 view->hex_cursor %= view->bytes_per_line;
106 else
108 off_t i;
110 for (i = 0; i < lines; i++)
112 if (view->dpy_start == 0)
113 break;
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;
132 else
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;
141 else
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 /* --------------------------------------------------------------------------------------------- */
154 void
155 mcview_move_down (mcview_t * view, off_t lines)
157 off_t last_byte;
158 last_byte = mcview_get_filesize (view);
159 if (view->hex_mode)
161 off_t i, limit;
163 if (last_byte >= (off_t) view->bytes_per_line)
164 limit = last_byte - view->bytes_per_line;
165 else
166 limit = 0;
167 for (i = 0; i < lines && view->hex_cursor < limit; i++)
169 view->hex_cursor += view->bytes_per_line;
170 if (lines != 1)
171 view->dpy_start += view->bytes_per_line;
174 else
176 off_t new_offset = 0;
178 if (view->dpy_end - view->dpy_start > last_byte - view->dpy_end)
180 while (lines-- > 0)
182 if (view->text_wrap_mode)
183 view->dpy_end =
184 mcview_eol (view, view->dpy_end,
185 view->dpy_end + (off_t) view->data_area.width);
186 else
187 view->dpy_end = mcview_eol (view, view->dpy_end, last_byte);
189 if (view->text_wrap_mode)
190 new_offset =
191 mcview_eol (view, view->dpy_start,
192 view->dpy_start + (off_t) view->data_area.width);
193 else
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)
198 break;
201 else
203 off_t i;
204 for (i = 0; i < lines && new_offset < last_byte; i++)
206 if (view->text_wrap_mode)
207 new_offset =
208 mcview_eol (view, view->dpy_start,
209 view->dpy_start + (off_t) view->data_area.width);
210 else
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 /* --------------------------------------------------------------------------------------------- */
222 void
223 mcview_move_left (mcview_t * view, off_t columns)
225 if (view->hex_mode)
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)
232 view->hex_cursor--;
234 if (!view->hexview_in_text)
235 if (old_cursor > 0 || view->hexedit_lownibble)
236 view->hexedit_lownibble = !view->hexedit_lownibble;
238 else
240 if (view->dpy_text_column >= columns)
241 view->dpy_text_column -= columns;
242 else
243 view->dpy_text_column = 0;
245 mcview_movement_fixups (view, FALSE);
248 /* --------------------------------------------------------------------------------------------- */
250 void
251 mcview_move_right (mcview_t * view, off_t columns)
253 if (view->hex_mode)
255 off_t last_byte;
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)
262 view->hex_cursor++;
264 if (!view->hexview_in_text)
265 if (old_cursor < last_byte || !view->hexedit_lownibble)
266 view->hexedit_lownibble = !view->hexedit_lownibble;
268 else
270 view->dpy_text_column += columns;
272 mcview_movement_fixups (view, FALSE);
275 /* --------------------------------------------------------------------------------------------- */
277 void
278 mcview_scroll_to_cursor (mcview_t * view)
280 if (view->hex_mode)
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 /* --------------------------------------------------------------------------------------------- */
297 void
298 mcview_moveto_top (mcview_t * view)
300 view->dpy_start = 0;
301 view->hex_cursor = 0;
302 view->dpy_text_column = 0;
303 mcview_movement_fixups (view, TRUE);
306 /* --------------------------------------------------------------------------------------------- */
308 void
309 mcview_moveto_bottom (mcview_t * view)
311 off_t filesize;
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);
320 if (view->hex_mode)
322 view->hex_cursor = mcview_offset_doz (filesize, 1);
323 mcview_movement_fixups (view, TRUE);
325 else
327 const off_t datalines = view->data_area.height;
329 view->dpy_start = filesize;
330 mcview_move_up (view, datalines);
334 /* --------------------------------------------------------------------------------------------- */
336 void
337 mcview_moveto_bol (mcview_t * view)
339 if (view->hex_mode)
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 /* --------------------------------------------------------------------------------------------- */
353 void
354 mcview_moveto_eol (mcview_t * view)
356 off_t bol;
357 if (view->hex_mode)
359 off_t filesize;
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;
366 else
368 filesize = mcview_get_filesize (view);
369 view->hex_cursor = mcview_offset_doz (filesize, 1);
372 else
374 off_t eol;
375 bol = mcview_bol (view, view->dpy_start, 0);
376 eol = mcview_eol (view, view->dpy_start, mcview_get_filesize (view));
377 if (!view->utf8)
379 if (eol > bol)
380 view->dpy_text_column = eol - bol;
382 else
384 char *str = NULL;
385 switch (view->datasource)
387 case DS_STDIO_PIPE:
388 case DS_VFS_PIPE:
389 str = mcview_get_ptr_growing_buffer (view, bol);
390 break;
391 case DS_FILE:
392 str = mcview_get_ptr_file (view, bol);
393 break;
394 case DS_STRING:
395 str = mcview_get_ptr_string (view, bol);
396 break;
397 case DS_NONE:
398 break;
400 if (str != NULL && eol > bol)
401 view->dpy_text_column = g_utf8_strlen (str, eol - bol);
402 else
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 /* --------------------------------------------------------------------------------------------- */
412 void
413 mcview_moveto_offset (mcview_t * view, off_t offset)
415 if (view->hex_mode)
417 view->hex_cursor = offset;
418 view->dpy_start = offset - offset % view->bytes_per_line;
420 else
422 view->dpy_start = offset;
424 mcview_movement_fixups (view, TRUE);
427 /* --------------------------------------------------------------------------------------------- */
429 void
430 mcview_moveto (mcview_t * view, off_t line, off_t col)
432 off_t offset;
434 mcview_coord_to_offset (view, &offset, line, col);
435 mcview_moveto_offset (view, offset);
438 /* --------------------------------------------------------------------------------------------- */
440 void
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 /* --------------------------------------------------------------------------------------------- */
454 void
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 /* --------------------------------------------------------------------------------------------- */
468 void
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)
475 col++;
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 */
484 void
485 mcview_moveto_match (mcview_t * view)
487 off_t offset;
489 offset = view->search_start;
491 if (view->hex_mode)
493 view->hex_cursor = offset;
494 view->dpy_start = offset - offset % view->bytes_per_line;
496 else
498 view->dpy_start = mcview_bol (view, offset, 0);
501 mcview_scroll_to_cursor (view);
502 view->dirty++;
505 /* --------------------------------------------------------------------------------------------- */