1 /* $Id: move.c 4486 2010-03-21 04:56:37Z astyanax $ */
2 /**************************************************************************
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. *
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. *
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 *
22 **************************************************************************/
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. */
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)) {
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. */
68 if (!ISSET(SMOOTH_SCROLL
)) {
70 openfile
->current
= openfile
->edittop
;
71 openfile
->placewewant
= openfile
->current_y
= 0;
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
;
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
));
88 openfile
->current_x
= actual_x(openfile
->current
->data
,
89 openfile
->placewewant
);
92 fprintf(stderr
, "do_page_up: openfile->current->lineno = %lu, skipped = %d\n", (unsigned long) openfile
->current
->lineno
, skipped
);
95 /* Scroll the edit window up a page. */
99 /* Move down one page. */
100 void do_page_down(void)
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
) {
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. */
116 if (!ISSET(SMOOTH_SCROLL
)) {
118 openfile
->current
= openfile
->edittop
;
119 openfile
->placewewant
= openfile
->current_y
= 0;
124 for (i
= maxrows
- 2; i
> 0 && openfile
->current
!=
125 openfile
->filebot
; i
--) {
126 openfile
->current
= openfile
->current
->next
;
128 fprintf(stderr
, "do_page_down: moving to line %lu\n", (unsigned long) openfile
->current
->lineno
);
133 openfile
->current_x
= actual_x(openfile
->current
->data
,
134 openfile
->placewewant
);
136 /* Scroll the edit window down a page. */
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
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
) {
151 openfile
->current
= openfile
->current
->prev
;
152 openfile
->current_y
--;
153 } while (!begpar(openfile
->current
));
156 openfile
->current_x
= 0;
157 openfile
->placewewant
= 0;
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)
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;
197 openfile
->current_x
= strlen(openfile
->current
->data
);
198 openfile
->placewewant
= xplustabs();
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)
212 #endif /* !DISABLE_JUSTIFY */
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
219 bool do_next_word(bool allow_punct
, bool allow_update
)
221 size_t pww_save
= openfile
->placewewant
;
222 filestruct
*current_save
= openfile
->current
;
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. */
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
239 if (!is_word_mbchar(char_mb
, allow_punct
))
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')
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')
256 openfile
->current_x
+= char_mb_len
;
258 for (; openfile
->current
!= NULL
;
259 openfile
->current
= openfile
->current
->next
) {
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
266 if (is_word_mbchar(char_mb
, allow_punct
))
269 if (openfile
->current
->data
[openfile
->current_x
] == '\0')
272 openfile
->current_x
+= char_mb_len
;
275 /* If we've found it, stop moving forward to the beginnings of
276 * subsequent lines. */
280 if (openfile
->current
!= openfile
->filebot
) {
282 openfile
->current_x
= 0;
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. */
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
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
314 bool do_prev_word(bool allow_punct
, bool allow_update
)
316 size_t pww_save
= openfile
->placewewant
;
317 filestruct
*current_save
= openfile
->current
;
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
334 if (!is_word_mbchar(char_mb
, allow_punct
))
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)
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
350 if (openfile
->current_x
== 0)
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
364 if (is_word_mbchar(char_mb
, allow_punct
))
367 if (openfile
->current_x
== 0)
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
380 if (openfile
->current
!= openfile
->fileage
) {
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)
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
404 if (!is_word_mbchar(char_mb
, allow_punct
))
407 if (openfile
->current_x
== 0)
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
418 openfile
->current_x
+= char_mb_len
;
423 openfile
->placewewant
= xplustabs();
425 /* If allow_update is TRUE, update the screen. */
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
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
448 size_t pww_save
= openfile
->placewewant
;
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();
463 openfile
->current_x
= 0;
464 openfile
->placewewant
= 0;
469 if (need_horizontal_update(pww_save
))
470 update_line(openfile
->current
, openfile
->current_x
);
473 /* Move to the end of the current line. */
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. */
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
499 || (scroll_only
&& openfile
->edittop
== openfile
->fileage
)
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
)
523 (ISSET(SMOOTH_SCROLL
) || scroll_only
) ? 1 :
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)
549 /* Scroll up one line without scrolling the cursor. */
550 void do_scroll_up(void)
556 /* If scroll_only is FALSE, move down one line. If scroll_only is TRUE,
557 * scroll down one line without scrolling the cursor. */
566 bool onlastline
= FALSE
;
568 /* If we're at the bottom of the file, get out. */
569 if (openfile
->current
== openfile
->filebot
)
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
)
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
595 edit_scroll(DOWN_DIR
,
597 (ISSET(SMOOTH_SCROLL
) || scroll_only
) ? 1 :
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)
625 /* Scroll down one line without scrolling the cursor. */
626 void do_scroll_down(void)
632 /* Move left one character. */
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
) {
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. */
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
) {
663 openfile
->current_x
= 0;
666 openfile
->placewewant
= xplustabs();
668 if (need_horizontal_update(pww_save
))
669 update_line(openfile
->current
, openfile
->current_x
);