Ticket #2107: viewer: enable left/right scrolling in wrap mode.
[midnight-commander.git] / src / viewer / move.c
blob28a1478d8c42ed9e35987983e4a9f0125f2fb83c
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 ************************************************************************/
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 /* --------------------------------------------------------------------------------------------- */
84 /*** public functions ****************************************************************************/
86 /* --------------------------------------------------------------------------------------------- */
88 void
89 mcview_move_up (mcview_t * view, off_t lines)
91 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;
109 for (i = 0; i < lines; i++)
111 new_offset = mcview_bol (view, view->dpy_start);
112 if (new_offset > 0)
113 new_offset--;
114 new_offset = mcview_bol (view, new_offset);
115 if (new_offset < 0)
116 new_offset = 0;
117 view->dpy_start = new_offset;
120 mcview_movement_fixups (view, TRUE);
123 /* --------------------------------------------------------------------------------------------- */
125 void
126 mcview_move_down (mcview_t * view, off_t lines)
128 off_t last_byte;
129 last_byte = mcview_get_filesize (view);
130 if (view->hex_mode)
132 off_t i, limit;
134 if (last_byte >= (off_t) view->bytes_per_line)
135 limit = last_byte - view->bytes_per_line;
136 else
137 limit = 0;
138 for (i = 0; i < lines && view->hex_cursor < limit; i++)
140 view->hex_cursor += view->bytes_per_line;
141 if (lines != 1)
142 view->dpy_start += view->bytes_per_line;
145 else
147 off_t i;
148 for (i = 0; i < lines; i++)
150 off_t new_offset;
151 new_offset = mcview_eol (view, view->dpy_start);
152 view->dpy_start = new_offset;
155 mcview_movement_fixups (view, TRUE);
158 /* --------------------------------------------------------------------------------------------- */
160 void
161 mcview_move_left (mcview_t * view, off_t columns)
163 if (view->hex_mode)
165 off_t old_cursor = view->hex_cursor;
166 assert (columns == 1);
167 if (view->hexview_in_text || !view->hexedit_lownibble)
169 if (view->hex_cursor > 0)
170 view->hex_cursor--;
172 if (!view->hexview_in_text)
173 if (old_cursor > 0 || view->hexedit_lownibble)
174 view->hexedit_lownibble = !view->hexedit_lownibble;
176 else
178 if (view->dpy_text_column >= columns)
179 view->dpy_text_column -= columns;
180 else
181 view->dpy_text_column = 0;
183 mcview_movement_fixups (view, FALSE);
186 /* --------------------------------------------------------------------------------------------- */
188 void
189 mcview_move_right (mcview_t * view, off_t columns)
191 if (view->hex_mode)
193 off_t last_byte;
194 off_t old_cursor = view->hex_cursor;
195 last_byte = mcview_offset_doz (mcview_get_filesize (view), 1);
196 assert (columns == 1);
197 if (view->hexview_in_text || view->hexedit_lownibble)
199 if (view->hex_cursor < last_byte)
200 view->hex_cursor++;
202 if (!view->hexview_in_text)
203 if (old_cursor < last_byte || !view->hexedit_lownibble)
204 view->hexedit_lownibble = !view->hexedit_lownibble;
206 else
208 view->dpy_text_column += columns;
210 mcview_movement_fixups (view, FALSE);
213 /* --------------------------------------------------------------------------------------------- */
215 void
216 mcview_scroll_to_cursor (mcview_t * view)
218 if (view->hex_mode)
220 const off_t bytes = view->bytes_per_line;
221 const off_t displaysize = view->data_area.height * bytes;
222 const off_t cursor = view->hex_cursor;
223 off_t topleft = view->dpy_start;
225 if (topleft + displaysize <= cursor)
226 topleft = mcview_offset_rounddown (cursor, bytes) - (displaysize - bytes);
227 if (cursor < topleft)
228 topleft = mcview_offset_rounddown (cursor, bytes);
229 view->dpy_start = topleft;
233 /* --------------------------------------------------------------------------------------------- */
235 void
236 mcview_moveto_top (mcview_t * view)
238 view->dpy_start = 0;
239 view->hex_cursor = 0;
240 view->dpy_text_column = 0;
241 mcview_movement_fixups (view, TRUE);
244 /* --------------------------------------------------------------------------------------------- */
246 void
247 mcview_moveto_bottom (mcview_t * view)
249 off_t datalines, lines_up, filesize, last_offset;
251 mcview_update_filesize (view);
253 if (view->growbuf_in_use)
254 mcview_growbuf_read_until (view, OFFSETTYPE_MAX);
256 filesize = mcview_get_filesize (view);
257 datalines = view->data_area.height;
258 lines_up = mcview_offset_doz (datalines, 1);
260 if (view->hex_mode)
262 last_offset = mcview_offset_doz (filesize, 1);
263 view->hex_cursor = filesize;
264 mcview_move_up (view, lines_up);
265 view->hex_cursor = last_offset;
267 else
269 view->dpy_start = filesize;
270 mcview_move_up (view, 1);
274 /* --------------------------------------------------------------------------------------------- */
276 void
277 mcview_moveto_bol (mcview_t * view)
279 if (view->hex_mode)
281 view->hex_cursor -= view->hex_cursor % view->bytes_per_line;
283 else if (!view->text_wrap_mode)
285 view->dpy_start = mcview_bol (view, view->dpy_start);
287 mcview_movement_fixups (view, TRUE);
290 /* --------------------------------------------------------------------------------------------- */
292 void
293 mcview_moveto_eol (mcview_t * view)
295 if (view->hex_mode)
297 off_t filesize, bol;
299 bol = mcview_offset_rounddown (view->hex_cursor, view->bytes_per_line);
300 if (mcview_get_byte_indexed (view, bol, view->bytes_per_line - 1, NULL) == TRUE)
302 view->hex_cursor = bol + view->bytes_per_line - 1;
304 else
306 filesize = mcview_get_filesize (view);
307 view->hex_cursor = mcview_offset_doz (filesize, 1);
310 mcview_movement_fixups (view, FALSE);
313 /* --------------------------------------------------------------------------------------------- */
315 void
316 mcview_moveto_offset (mcview_t * view, off_t offset)
318 if (view->hex_mode)
320 view->hex_cursor = offset;
321 view->dpy_start = offset - offset % view->bytes_per_line;
323 else
325 view->dpy_start = offset;
327 mcview_movement_fixups (view, TRUE);
330 /* --------------------------------------------------------------------------------------------- */
332 void
333 mcview_moveto (mcview_t * view, off_t line, off_t col)
335 off_t offset;
337 mcview_coord_to_offset (view, &offset, line, col);
338 mcview_moveto_offset (view, offset);
341 /* --------------------------------------------------------------------------------------------- */
343 void
344 mcview_coord_to_offset (mcview_t * view, off_t * ret_offset, off_t line, off_t column)
346 coord_cache_entry_t coord;
348 coord.cc_line = line;
349 coord.cc_column = column;
350 coord.cc_nroff_column = column;
351 mcview_ccache_lookup (view, &coord, CCACHE_OFFSET);
352 *ret_offset = coord.cc_offset;
355 /* --------------------------------------------------------------------------------------------- */
357 void
358 mcview_offset_to_coord (mcview_t * view, off_t * ret_line, off_t * ret_column, off_t offset)
360 coord_cache_entry_t coord;
362 coord.cc_offset = offset;
363 mcview_ccache_lookup (view, &coord, CCACHE_LINECOL);
365 *ret_line = coord.cc_line;
366 *ret_column = (view->text_nroff_mode) ? coord.cc_nroff_column : coord.cc_column;
369 /* --------------------------------------------------------------------------------------------- */
371 void
372 mcview_place_cursor (mcview_t * view)
374 const screen_dimen top = view->data_area.top;
375 const screen_dimen left = view->data_area.left;
376 screen_dimen col = view->cursor_col;
377 if (!view->hexview_in_text && view->hexedit_lownibble)
378 col++;
379 widget_move (&view->widget, top + view->cursor_row, left + col);
382 /* --------------------------------------------------------------------------------------------- */
384 /* we have set view->search_start and view->search_end and must set
385 * view->dpy_text_column and view->dpy_start
386 * try to display maximum of match */
387 void
388 mcview_moveto_match (mcview_t * view)
390 off_t offset;
392 offset = view->search_start;
394 if (view->hex_mode)
396 view->hex_cursor = offset;
397 view->dpy_start = offset - offset % view->bytes_per_line;
399 else
401 view->dpy_start = mcview_bol (view, offset);
404 mcview_scroll_to_cursor (view);
405 view->dirty++;
408 /* --------------------------------------------------------------------------------------------- */