Add Nano tool - user-friendly text editor
[tomato.git] / release / src / router / nano / src / move.c
blob468a02fd6ddd077ca26da20a7b6fcb9c0b487ff7
1 /* $Id: move.c 4486 2010-03-21 04:56:37Z astyanax $ */
2 /**************************************************************************
3 * move.c *
4 * *
5 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
6 * 2008, 2009 Free Software Foundation, Inc. *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 3, or (at your option) *
10 * any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
20 * 02110-1301, USA. *
21 * *
22 **************************************************************************/
24 #include "proto.h"
26 #include <string.h>
27 #include <ctype.h>
29 /* Move to the first line of the file. */
30 void do_first_line(void)
32 openfile->current = openfile->edittop = openfile->fileage;
33 openfile->current_x = 0;
34 openfile->placewewant = 0;
36 edit_refresh_needed = 1;
39 /* Move to the last line of the file. */
40 void do_last_line(void)
42 openfile->current = openfile->filebot;
43 openfile->current_x = strlen(openfile->filebot->data);
44 openfile->placewewant = xplustabs();
45 openfile->current_y = editwinrows - 1;
47 edit_refresh_needed = 1;
50 /* Move up one page. */
51 void do_page_up(void)
53 int i, skipped = 0;
55 /* If there's less than a page of text left on the screen, put the
56 * cursor at the beginning of the first line of the file, and then
57 * update the edit window. */
58 if (openfile->current->lineno == 1 || (!ISSET(SOFTWRAP) &&
59 openfile->current->lineno <= editwinrows - 2)) {
60 do_first_line();
61 return;
64 /* If we're not in smooth scrolling mode, put the cursor at the
65 * beginning of the top line of the edit window, as Pico does. */
67 #ifndef NANO_TINY
68 if (!ISSET(SMOOTH_SCROLL)) {
69 #endif
70 openfile->current = openfile->edittop;
71 openfile->placewewant = openfile->current_y = 0;
72 #ifndef NANO_TINY
74 #endif
76 for (i = editwinrows - 2; i - skipped > 0 && openfile->current !=
77 openfile->fileage; i--) {
78 openfile->current = openfile->current->prev;
79 if (ISSET(SOFTWRAP) && openfile->current) {
80 skipped += strlenpt(openfile->current->data) / COLS;
81 #ifdef DEBUG
82 fprintf(stderr, "do_page_up: i = %d, skipped = %d based on line %ld len %d\n", i, (unsigned long) skipped,
83 openfile->current->lineno, strlenpt(openfile->current->data));
84 #endif
88 openfile->current_x = actual_x(openfile->current->data,
89 openfile->placewewant);
91 #ifdef DEBUG
92 fprintf(stderr, "do_page_up: openfile->current->lineno = %lu, skipped = %d\n", (unsigned long) openfile->current->lineno, skipped);
93 #endif
95 /* Scroll the edit window up a page. */
96 edit_update(NONE);
99 /* Move down one page. */
100 void do_page_down(void)
102 int i;
104 /* If there's less than a page of text left on the screen, put the
105 * cursor at the beginning of the last line of the file, and then
106 * update the edit window. */
107 if (openfile->current->lineno + maxrows - 2 >=
108 openfile->filebot->lineno) {
109 do_last_line();
110 return;
113 /* If we're not in smooth scrolling mode, put the cursor at the
114 * beginning of the top line of the edit window, as Pico does. */
115 #ifndef NANO_TINY
116 if (!ISSET(SMOOTH_SCROLL)) {
117 #endif
118 openfile->current = openfile->edittop;
119 openfile->placewewant = openfile->current_y = 0;
120 #ifndef NANO_TINY
122 #endif
124 for (i = maxrows - 2; i > 0 && openfile->current !=
125 openfile->filebot; i--) {
126 openfile->current = openfile->current->next;
127 #ifdef DEBUG
128 fprintf(stderr, "do_page_down: moving to line %lu\n", (unsigned long) openfile->current->lineno);
129 #endif
133 openfile->current_x = actual_x(openfile->current->data,
134 openfile->placewewant);
136 /* Scroll the edit window down a page. */
137 edit_update(NONE);
140 #ifndef DISABLE_JUSTIFY
141 /* Move up to the beginning of the last beginning-of-paragraph line
142 * before the current line. If allow_update is TRUE, update the screen
143 * afterwards. */
144 void do_para_begin(bool allow_update)
146 filestruct *current_save = openfile->current;
147 const size_t pww_save = openfile->placewewant;
149 if (openfile->current != openfile->fileage) {
150 do {
151 openfile->current = openfile->current->prev;
152 openfile->current_y--;
153 } while (!begpar(openfile->current));
156 openfile->current_x = 0;
157 openfile->placewewant = 0;
159 if (allow_update)
160 edit_redraw(current_save, pww_save);
163 /* Move up to the beginning of the last beginning-of-paragraph line
164 * before the current line, and update the screen afterwards. */
165 void do_para_begin_void(void)
167 do_para_begin(TRUE);
170 /* Move down to the beginning of the last line of the current paragraph.
171 * Then move down one line farther if there is such a line, or to the
172 * end of the current line if not. If allow_update is TRUE, update the
173 * screen afterwards. A line is the last line of a paragraph if it is
174 * in a paragraph, and the next line either is the beginning line of a
175 * paragraph or isn't in a paragraph. */
176 void do_para_end(bool allow_update)
178 filestruct *const current_save = openfile->current;
179 const size_t pww_save = openfile->placewewant;
181 while (openfile->current != openfile->filebot &&
182 !inpar(openfile->current))
183 openfile->current = openfile->current->next;
185 while (openfile->current != openfile->filebot &&
186 inpar(openfile->current->next) &&
187 !begpar(openfile->current->next)) {
188 openfile->current = openfile->current->next;
189 openfile->current_y++;
192 if (openfile->current != openfile->filebot) {
193 openfile->current = openfile->current->next;
194 openfile->current_x = 0;
195 openfile->placewewant = 0;
196 } else {
197 openfile->current_x = strlen(openfile->current->data);
198 openfile->placewewant = xplustabs();
201 if (allow_update)
202 edit_redraw(current_save, pww_save);
205 /* Move down to the beginning of the last line of the current paragraph.
206 * Then move down one line farther if there is such a line, or to the
207 * end of the current line if not, and update the screen afterwards. */
208 void do_para_end_void(void)
210 do_para_end(TRUE);
212 #endif /* !DISABLE_JUSTIFY */
214 #ifndef NANO_TINY
215 /* Move to the next word in the file. If allow_punct is TRUE, treat
216 * punctuation as part of a word. If allow_update is TRUE, update the
217 * screen afterwards. Return TRUE if we started on a word, and FALSE
218 * otherwise. */
219 bool do_next_word(bool allow_punct, bool allow_update)
221 size_t pww_save = openfile->placewewant;
222 filestruct *current_save = openfile->current;
223 char *char_mb;
224 int char_mb_len;
225 bool end_line = FALSE, started_on_word = FALSE;
227 assert(openfile->current != NULL && openfile->current->data != NULL);
229 char_mb = charalloc(mb_cur_max());
231 /* Move forward until we find the character after the last letter of
232 * the current word. */
233 while (!end_line) {
234 char_mb_len = parse_mbchar(openfile->current->data +
235 openfile->current_x, char_mb, NULL);
237 /* If we've found it, stop moving forward through the current
238 * line. */
239 if (!is_word_mbchar(char_mb, allow_punct))
240 break;
242 /* If we haven't found it, then we've started on a word, so set
243 * started_on_word to TRUE. */
244 started_on_word = TRUE;
246 if (openfile->current->data[openfile->current_x] == '\0')
247 end_line = TRUE;
248 else
249 openfile->current_x += char_mb_len;
252 /* Move forward until we find the first letter of the next word. */
253 if (openfile->current->data[openfile->current_x] == '\0')
254 end_line = TRUE;
255 else
256 openfile->current_x += char_mb_len;
258 for (; openfile->current != NULL;
259 openfile->current = openfile->current->next) {
260 while (!end_line) {
261 char_mb_len = parse_mbchar(openfile->current->data +
262 openfile->current_x, char_mb, NULL);
264 /* If we've found it, stop moving forward through the
265 * current line. */
266 if (is_word_mbchar(char_mb, allow_punct))
267 break;
269 if (openfile->current->data[openfile->current_x] == '\0')
270 end_line = TRUE;
271 else
272 openfile->current_x += char_mb_len;
275 /* If we've found it, stop moving forward to the beginnings of
276 * subsequent lines. */
277 if (!end_line)
278 break;
280 if (openfile->current != openfile->filebot) {
281 end_line = FALSE;
282 openfile->current_x = 0;
286 free(char_mb);
288 /* If we haven't found it, move to the end of the file. */
289 if (openfile->current == NULL)
290 openfile->current = openfile->filebot;
292 openfile->placewewant = xplustabs();
294 /* If allow_update is TRUE, update the screen. */
295 if (allow_update)
296 edit_redraw(current_save, pww_save);
298 /* Return whether we started on a word. */
299 return started_on_word;
302 /* Move to the next word in the file, treating punctuation as part of a
303 * word if the WORD_BOUNDS flag is set, and update the screen
304 * afterwards. */
305 void do_next_word_void(void)
307 do_next_word(ISSET(WORD_BOUNDS), TRUE);
310 /* Move to the previous word in the file. If allow_punct is TRUE, treat
311 * punctuation as part of a word. If allow_update is TRUE, update the
312 * screen afterwards. Return TRUE if we started on a word, and FALSE
313 * otherwise. */
314 bool do_prev_word(bool allow_punct, bool allow_update)
316 size_t pww_save = openfile->placewewant;
317 filestruct *current_save = openfile->current;
318 char *char_mb;
319 int char_mb_len;
320 bool begin_line = FALSE, started_on_word = FALSE;
322 assert(openfile->current != NULL && openfile->current->data != NULL);
324 char_mb = charalloc(mb_cur_max());
326 /* Move backward until we find the character before the first letter
327 * of the current word. */
328 while (!begin_line) {
329 char_mb_len = parse_mbchar(openfile->current->data +
330 openfile->current_x, char_mb, NULL);
332 /* If we've found it, stop moving backward through the current
333 * line. */
334 if (!is_word_mbchar(char_mb, allow_punct))
335 break;
337 /* If we haven't found it, then we've started on a word, so set
338 * started_on_word to TRUE. */
339 started_on_word = TRUE;
341 if (openfile->current_x == 0)
342 begin_line = TRUE;
343 else
344 openfile->current_x = move_mbleft(openfile->current->data,
345 openfile->current_x);
348 /* Move backward until we find the last letter of the previous
349 * word. */
350 if (openfile->current_x == 0)
351 begin_line = TRUE;
352 else
353 openfile->current_x = move_mbleft(openfile->current->data,
354 openfile->current_x);
356 for (; openfile->current != NULL;
357 openfile->current = openfile->current->prev) {
358 while (!begin_line) {
359 char_mb_len = parse_mbchar(openfile->current->data +
360 openfile->current_x, char_mb, NULL);
362 /* If we've found it, stop moving backward through the
363 * current line. */
364 if (is_word_mbchar(char_mb, allow_punct))
365 break;
367 if (openfile->current_x == 0)
368 begin_line = TRUE;
369 else
370 openfile->current_x =
371 move_mbleft(openfile->current->data,
372 openfile->current_x);
375 /* If we've found it, stop moving backward to the ends of
376 * previous lines. */
377 if (!begin_line)
378 break;
380 if (openfile->current != openfile->fileage) {
381 begin_line = FALSE;
382 openfile->current_x = strlen(openfile->current->prev->data);
386 /* If we haven't found it, move to the beginning of the file. */
387 if (openfile->current == NULL)
388 openfile->current = openfile->fileage;
389 /* If we've found it, move backward until we find the character
390 * before the first letter of the previous word. */
391 else if (!begin_line) {
392 if (openfile->current_x == 0)
393 begin_line = TRUE;
394 else
395 openfile->current_x = move_mbleft(openfile->current->data,
396 openfile->current_x);
398 while (!begin_line) {
399 char_mb_len = parse_mbchar(openfile->current->data +
400 openfile->current_x, char_mb, NULL);
402 /* If we've found it, stop moving backward through the
403 * current line. */
404 if (!is_word_mbchar(char_mb, allow_punct))
405 break;
407 if (openfile->current_x == 0)
408 begin_line = TRUE;
409 else
410 openfile->current_x =
411 move_mbleft(openfile->current->data,
412 openfile->current_x);
415 /* If we've found it, move forward to the first letter of the
416 * previous word. */
417 if (!begin_line)
418 openfile->current_x += char_mb_len;
421 free(char_mb);
423 openfile->placewewant = xplustabs();
425 /* If allow_update is TRUE, update the screen. */
426 if (allow_update)
427 edit_redraw(current_save, pww_save);
429 /* Return whether we started on a word. */
430 return started_on_word;
433 /* Move to the previous word in the file, treating punctuation as part
434 * of a word if the WORD_BOUNDS flag is set, and update the screen
435 * afterwards. */
436 void do_prev_word_void(void)
438 do_prev_word(ISSET(WORD_BOUNDS), TRUE);
440 #endif /* !NANO_TINY */
442 /* Move to the beginning of the current line. If the SMART_HOME flag is
443 * set, move to the first non-whitespace character of the current line
444 * if we aren't already there, or to the beginning of the current line
445 * if we are. */
446 void do_home(void)
448 size_t pww_save = openfile->placewewant;
450 #ifndef NANO_TINY
451 if (ISSET(SMART_HOME)) {
452 size_t current_x_save = openfile->current_x;
454 openfile->current_x = indent_length(openfile->current->data);
456 if (openfile->current_x == current_x_save ||
457 openfile->current->data[openfile->current_x] == '\0')
458 openfile->current_x = 0;
460 openfile->placewewant = xplustabs();
461 } else {
462 #endif
463 openfile->current_x = 0;
464 openfile->placewewant = 0;
465 #ifndef NANO_TINY
467 #endif
469 if (need_horizontal_update(pww_save))
470 update_line(openfile->current, openfile->current_x);
473 /* Move to the end of the current line. */
474 void do_end(void)
476 size_t pww_save = openfile->placewewant;
478 openfile->current_x = strlen(openfile->current->data);
479 openfile->placewewant = xplustabs();
481 if (need_horizontal_update(pww_save))
482 update_line(openfile->current, openfile->current_x);
485 /* If scroll_only is FALSE, move up one line. If scroll_only is TRUE,
486 * scroll up one line without scrolling the cursor. */
487 void do_up(
488 #ifndef NANO_TINY
489 bool scroll_only
490 #else
491 void
492 #endif
495 /* If we're at the top of the file, or if scroll_only is TRUE and
496 * the top of the file is onscreen, get out. */
497 if (openfile->current == openfile->fileage
498 #ifndef NANO_TINY
499 || (scroll_only && openfile->edittop == openfile->fileage)
500 #endif
502 return;
504 assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);
506 /* Move the current line of the edit window up. */
507 openfile->current = openfile->current->prev;
508 openfile->current_x = actual_x(openfile->current->data,
509 openfile->placewewant);
511 /* If scroll_only is FALSE and if we're on the first line of the
512 * edit window, scroll the edit window up one line if we're in
513 * smooth scrolling mode, or up half a page if we're not. If
514 * scroll_only is TRUE, scroll the edit window up one line
515 * unconditionally. */
516 if (openfile->current_y == 0 || (ISSET(SOFTWRAP) && openfile->edittop->lineno == openfile->current->next->lineno)
517 #ifndef NANO_TINY
518 || scroll_only
519 #endif
521 edit_scroll(UP_DIR,
522 #ifndef NANO_TINY
523 (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 :
524 #endif
525 editwinrows / 2 + 1);
527 /* If we're below the first line of the edit window, update the
528 * line we were on before and the line we're on now. The former
529 * needs to be redrawn if we're not on the first page, and the
530 * latter needs to be drawn unconditionally. */
531 if (openfile->current_y > 0) {
532 if (need_vertical_update(0))
533 update_line(openfile->current->next, 0);
534 update_line(openfile->current, openfile->current_x);
538 /* Move up one line. */
539 void do_up_void(void)
541 do_up(
542 #ifndef NANO_TINY
543 FALSE
544 #endif
548 #ifndef NANO_TINY
549 /* Scroll up one line without scrolling the cursor. */
550 void do_scroll_up(void)
552 do_up(TRUE);
554 #endif
556 /* If scroll_only is FALSE, move down one line. If scroll_only is TRUE,
557 * scroll down one line without scrolling the cursor. */
558 void do_down(
559 #ifndef NANO_TINY
560 bool scroll_only
561 #else
562 void
563 #endif
566 bool onlastline = FALSE;
568 /* If we're at the bottom of the file, get out. */
569 if (openfile->current == openfile->filebot)
570 return;
573 assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);
575 /* Move the current line of the edit window down. */
576 openfile->current = openfile->current->next;
577 openfile->current_x = actual_x(openfile->current->data,
578 openfile->placewewant);
580 if (ISSET(SOFTWRAP)) {
581 if (openfile->current->lineno - openfile->edittop->lineno >= maxrows)
582 onlastline = TRUE;
585 /* If scroll_only is FALSE and if we're on the first line of the
586 * edit window, scroll the edit window down one line if we're in
587 * smooth scrolling mode, or down half a page if we're not. If
588 * scroll_only is TRUE, scroll the edit window down one line
589 * unconditionally. */
590 if (onlastline || openfile->current_y == editwinrows - 1
591 #ifndef NANO_TINY
592 || scroll_only
593 #endif
595 edit_scroll(DOWN_DIR,
596 #ifndef NANO_TINY
597 (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 :
598 #endif
599 editwinrows / 2 + 1);
601 edit_refresh_needed = TRUE;
603 /* If we're above the last line of the edit window, update the line
604 * we were on before and the line we're on now. The former needs to
605 * be redrawn if we're not on the first page, and the latter needs
606 * to be drawn unconditionally. */
607 if (ISSET(SOFTWRAP) || openfile->current_y < editwinrows - 1) {
608 if (need_vertical_update(0))
609 update_line(openfile->current->prev, 0);
610 update_line(openfile->current, openfile->current_x);
614 /* Move down one line. */
615 void do_down_void(void)
617 do_down(
618 #ifndef NANO_TINY
619 FALSE
620 #endif
624 #ifndef NANO_TINY
625 /* Scroll down one line without scrolling the cursor. */
626 void do_scroll_down(void)
628 do_down(TRUE);
630 #endif
632 /* Move left one character. */
633 void do_left(void)
635 size_t pww_save = openfile->placewewant;
637 if (openfile->current_x > 0)
638 openfile->current_x = move_mbleft(openfile->current->data,
639 openfile->current_x);
640 else if (openfile->current != openfile->fileage) {
641 do_up_void();
642 openfile->current_x = strlen(openfile->current->data);
645 openfile->placewewant = xplustabs();
647 if (need_horizontal_update(pww_save))
648 update_line(openfile->current, openfile->current_x);
651 /* Move right one character. */
652 void do_right(void)
654 size_t pww_save = openfile->placewewant;
656 assert(openfile->current_x <= strlen(openfile->current->data));
658 if (openfile->current->data[openfile->current_x] != '\0')
659 openfile->current_x = move_mbright(openfile->current->data,
660 openfile->current_x);
661 else if (openfile->current != openfile->filebot) {
662 do_down_void();
663 openfile->current_x = 0;
666 openfile->placewewant = xplustabs();
668 if (need_horizontal_update(pww_save))
669 update_line(openfile->current, openfile->current_x);