Search in plain text (not nroff'ed) now worked.
[midnight-commander.git] / src / viewer / move.c
blob9f52aad51782747981577c9cba9817bd9fcaad23
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 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 "../src/global.h"
57 #include "../src/tty/tty.h"
58 #include "../src/viewer/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) {
75 view->search_start = view->dpy_start;
76 view->search_end = view->dpy_start;
78 view->dirty++;
81 /* --------------------------------------------------------------------------------------------- */
83 /*** public functions ****************************************************************************/
85 /* --------------------------------------------------------------------------------------------- */
87 void
88 mcview_move_up (mcview_t * view, off_t lines)
90 if (view->hex_mode) {
91 off_t bytes = lines * view->bytes_per_line;
92 if (view->hex_cursor >= bytes) {
93 view->hex_cursor -= bytes;
94 if (view->hex_cursor < view->dpy_start)
95 view->dpy_start = mcview_offset_doz (view->dpy_start, bytes);
96 } else {
97 view->hex_cursor %= view->bytes_per_line;
99 } else if (view->text_wrap_mode) {
100 const screen_dimen width = view->data_area.width;
101 off_t i, col, line, linestart;
103 for (i = 0; i < lines; i++) {
104 mcview_offset_to_coord (view, &line, &col, view->dpy_start);
105 if (col >= width) {
106 col -= width;
107 } else if (line >= 1) {
108 mcview_coord_to_offset (view, &linestart, line, 0);
109 mcview_offset_to_coord (view, &line, &col, linestart - 1);
111 /* if the only thing that would be displayed were a
112 * single newline character, advance to the previous
113 * part of the line. */
114 if (col > 0 && col % width == 0)
115 col -= width;
116 else
117 col -= col % width;
118 } else {
119 /* nothing to do */
121 mcview_coord_to_offset (view, &(view->dpy_start), line, col);
123 } else {
124 off_t line, column;
126 mcview_offset_to_coord (view, &line, &column, view->dpy_start);
127 line = mcview_offset_doz (line, lines);
128 mcview_coord_to_offset (view, &(view->dpy_start), line, column);
130 mcview_movement_fixups (view, (lines != 1));
133 /* --------------------------------------------------------------------------------------------- */
135 void
136 mcview_move_down (mcview_t * view, off_t lines)
138 if (view->hex_mode) {
139 off_t i, limit, last_byte;
141 last_byte = mcview_get_filesize (view);
142 if (last_byte >= (off_t) view->bytes_per_line)
143 limit = last_byte - view->bytes_per_line;
144 else
145 limit = 0;
146 for (i = 0; i < lines && view->hex_cursor < limit; i++) {
147 view->hex_cursor += view->bytes_per_line;
148 if (lines != 1)
149 view->dpy_start += view->bytes_per_line;
152 } else if (view->dpy_end == mcview_get_filesize (view)) {
153 /* don't move further down. There's nothing more to see. */
155 } else if (view->text_wrap_mode) {
156 off_t line, col, i;
158 for (i = 0; i < lines; i++) {
159 off_t new_offset, chk_line, chk_col;
161 mcview_offset_to_coord (view, &line, &col, view->dpy_start);
162 col += view->data_area.width;
163 mcview_coord_to_offset (view, &new_offset, line, col);
165 /* skip to the next line if the only thing that would be
166 * displayed is the newline character. */
167 mcview_offset_to_coord (view, &chk_line, &chk_col, new_offset);
168 if (chk_line == line && chk_col == col && mcview_get_byte (view, new_offset) == '\n')
169 new_offset++;
171 view->dpy_start = new_offset;
174 } else {
175 off_t line, col;
177 mcview_offset_to_coord (view, &line, &col, view->dpy_start);
178 line += lines;
179 mcview_coord_to_offset (view, &(view->dpy_start), line, col);
181 mcview_movement_fixups (view, (lines != 1));
184 /* --------------------------------------------------------------------------------------------- */
186 void
187 mcview_move_left (mcview_t * view, off_t columns)
189 if (view->hex_mode) {
190 assert (columns == 1);
191 if (view->hexview_in_text || !view->hexedit_lownibble) {
192 if (view->hex_cursor > 0)
193 view->hex_cursor--;
195 if (!view->hexview_in_text)
196 view->hexedit_lownibble = !view->hexedit_lownibble;
197 } else if (view->text_wrap_mode) {
198 /* nothing to do */
199 } else {
200 if (view->dpy_text_column >= columns)
201 view->dpy_text_column -= columns;
202 else
203 view->dpy_text_column = 0;
205 mcview_movement_fixups (view, FALSE);
208 /* --------------------------------------------------------------------------------------------- */
210 void
211 mcview_move_right (mcview_t * view, off_t columns)
213 if (view->hex_mode) {
214 assert (columns == 1);
215 if (view->hexview_in_text || view->hexedit_lownibble) {
216 view->hex_cursor++;
218 if (!view->hexview_in_text)
219 view->hexedit_lownibble = !view->hexedit_lownibble;
220 } else if (view->text_wrap_mode) {
221 /* nothing to do */
222 } else {
223 view->dpy_text_column += columns;
225 mcview_movement_fixups (view, FALSE);
228 /* --------------------------------------------------------------------------------------------- */
230 void
231 mcview_scroll_to_cursor (mcview_t * view)
233 if (view->hex_mode) {
234 const off_t bytes = view->bytes_per_line;
235 const off_t displaysize = view->data_area.height * bytes;
236 const off_t cursor = view->hex_cursor;
237 off_t topleft = view->dpy_start;
239 if (topleft + displaysize <= cursor)
240 topleft = mcview_offset_rounddown (cursor, bytes)
241 - (displaysize - bytes);
242 if (cursor < topleft)
243 topleft = mcview_offset_rounddown (cursor, bytes);
244 view->dpy_start = topleft;
245 } else if (view->text_wrap_mode) {
246 off_t line, col, columns;
248 columns = view->data_area.width;
249 mcview_offset_to_coord (view, &line, &col, view->dpy_start + view->dpy_text_column);
250 if (columns != 0)
251 col = mcview_offset_rounddown (col, columns);
252 mcview_coord_to_offset (view, &(view->dpy_start), line, col);
253 view->dpy_text_column = 0;
254 } else {
255 /* nothing to do */
259 /* --------------------------------------------------------------------------------------------- */
261 void
262 mcview_moveto_top (mcview_t * view)
264 view->dpy_start = 0;
265 view->hex_cursor = 0;
266 view->dpy_text_column = 0;
267 mcview_movement_fixups (view, TRUE);
270 /* --------------------------------------------------------------------------------------------- */
272 void
273 mcview_moveto_bottom (mcview_t * view)
275 off_t datalines, lines_up, filesize, last_offset;
277 if (view->growbuf_in_use)
278 mcview_growbuf_read_until (view, OFFSETTYPE_MAX);
280 filesize = mcview_get_filesize (view);
281 last_offset = mcview_offset_doz (filesize, 1);
282 datalines = view->data_area.height;
283 lines_up = mcview_offset_doz (datalines, 1);
285 if (view->hex_mode) {
286 view->hex_cursor = filesize;
287 mcview_move_up (view, lines_up);
288 view->hex_cursor = last_offset;
289 } else {
290 view->dpy_start = last_offset;
291 mcview_moveto_bol (view);
292 mcview_move_up (view, lines_up);
294 mcview_movement_fixups (view, TRUE);
297 /* --------------------------------------------------------------------------------------------- */
299 void
300 mcview_moveto_bol (mcview_t * view)
302 if (view->hex_mode) {
303 view->hex_cursor -= view->hex_cursor % view->bytes_per_line;
304 } else if (view->text_wrap_mode) {
305 /* do nothing */
306 } else {
307 off_t line, column;
308 mcview_offset_to_coord (view, &line, &column, view->dpy_start);
309 mcview_coord_to_offset (view, &(view->dpy_start), line, 0);
310 view->dpy_text_column = 0;
312 mcview_movement_fixups (view, TRUE);
315 /* --------------------------------------------------------------------------------------------- */
317 void
318 mcview_moveto_eol (mcview_t * view)
320 if (view->hex_mode) {
321 off_t filesize, bol;
323 bol = mcview_offset_rounddown (view->hex_cursor, view->bytes_per_line);
324 if (mcview_get_byte_indexed (view, bol, view->bytes_per_line - 1) != -1) {
325 view->hex_cursor = bol + view->bytes_per_line - 1;
326 } else {
327 filesize = mcview_get_filesize (view);
328 view->hex_cursor = mcview_offset_doz (filesize, 1);
330 } else if (view->text_wrap_mode) {
331 /* nothing to do */
332 } else {
333 off_t line, col;
335 mcview_offset_to_coord (view, &line, &col, view->dpy_start);
336 mcview_coord_to_offset (view, &(view->dpy_start), line, OFFSETTYPE_MAX);
338 mcview_movement_fixups (view, FALSE);
341 /* --------------------------------------------------------------------------------------------- */
343 void
344 mcview_moveto_offset (mcview_t * view, off_t offset)
346 if (view->hex_mode) {
347 view->hex_cursor = offset;
348 view->dpy_start = offset - offset % view->bytes_per_line;
349 } else {
350 view->dpy_start = offset;
352 mcview_movement_fixups (view, TRUE);
355 /* --------------------------------------------------------------------------------------------- */
357 void
358 mcview_moveto (mcview_t * view, off_t line, off_t col)
360 off_t offset;
362 mcview_coord_to_offset (view, &offset, line, col);
363 mcview_moveto_offset (view, offset);
366 /* --------------------------------------------------------------------------------------------- */
368 void
369 mcview_coord_to_offset (mcview_t * view, off_t * ret_offset, off_t line, off_t column)
371 struct coord_cache_entry coord;
373 coord.cc_line = line;
374 coord.cc_column = column;
375 coord.cc_nroff_column = column;
376 mcview_ccache_lookup (view, &coord, CCACHE_OFFSET);
377 *ret_offset = coord.cc_offset;
380 /* --------------------------------------------------------------------------------------------- */
382 void
383 mcview_offset_to_coord (mcview_t * view, off_t * ret_line, off_t * ret_column, off_t offset)
385 struct coord_cache_entry coord;
387 coord.cc_offset = offset;
388 mcview_ccache_lookup (view, &coord, CCACHE_LINECOL);
390 if (ret_line)
391 *ret_line = coord.cc_line;
393 if (ret_column)
394 *ret_column = (view->text_nroff_mode)
395 ? coord.cc_nroff_column : coord.cc_column;
398 /* --------------------------------------------------------------------------------------------- */
400 void
401 mcview_place_cursor (mcview_t * view)
403 const screen_dimen top = view->data_area.top;
404 const screen_dimen left = view->data_area.left;
405 screen_dimen col;
407 col = view->cursor_col;
408 if (!view->hexview_in_text && view->hexedit_lownibble)
409 col++;
410 widget_move (&view->widget, top + view->cursor_row, left + col);
413 /* --------------------------------------------------------------------------------------------- */
415 /* we have set view->search_start and view->search_end and must set
416 * view->dpy_text_column and view->dpy_start
417 * try to display maximum of match */
418 void
419 mcview_moveto_match (mcview_t * view)
421 off_t search_line, offset;
423 mcview_offset_to_coord (view, &search_line, NULL, view->search_start);
424 mcview_coord_to_offset (view, &offset, search_line, 0);
426 if (view->hex_mode) {
427 view->hex_cursor = offset;
428 view->dpy_start = offset - offset % view->bytes_per_line;
429 } else {
430 view->dpy_start = offset;
433 mcview_scroll_to_cursor (view);
434 view->dirty++;
437 /* --------------------------------------------------------------------------------------------- */