summaryrefslogtreecommitdiff
path: root/src/move.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/move.c')
-rw-r--r--src/move.c670
1 files changed, 670 insertions, 0 deletions
diff --git a/src/move.c b/src/move.c
new file mode 100644
index 0000000..468a02f
--- /dev/null
+++ b/src/move.c
@@ -0,0 +1,670 @@
+/* $Id: move.c 4486 2010-03-21 04:56:37Z astyanax $ */
+/**************************************************************************
+ * move.c *
+ * *
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
+ * 2008, 2009 Free Software Foundation, Inc. *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3, or (at your option) *
+ * any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, but *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
+ * 02110-1301, USA. *
+ * *
+ **************************************************************************/
+
+#include "proto.h"
+
+#include <string.h>
+#include <ctype.h>
+
+/* Move to the first line of the file. */
+void do_first_line(void)
+{
+ openfile->current = openfile->edittop = openfile->fileage;
+ openfile->current_x = 0;
+ openfile->placewewant = 0;
+
+ edit_refresh_needed = 1;
+}
+
+/* Move to the last line of the file. */
+void do_last_line(void)
+{
+ openfile->current = openfile->filebot;
+ openfile->current_x = strlen(openfile->filebot->data);
+ openfile->placewewant = xplustabs();
+ openfile->current_y = editwinrows - 1;
+
+ edit_refresh_needed = 1;
+}
+
+/* Move up one page. */
+void do_page_up(void)
+{
+ int i, skipped = 0;
+
+ /* If there's less than a page of text left on the screen, put the
+ * cursor at the beginning of the first line of the file, and then
+ * update the edit window. */
+ if (openfile->current->lineno == 1 || (!ISSET(SOFTWRAP) &&
+ openfile->current->lineno <= editwinrows - 2)) {
+ do_first_line();
+ return;
+ }
+
+ /* If we're not in smooth scrolling mode, put the cursor at the
+ * beginning of the top line of the edit window, as Pico does. */
+
+#ifndef NANO_TINY
+ if (!ISSET(SMOOTH_SCROLL)) {
+#endif
+ openfile->current = openfile->edittop;
+ openfile->placewewant = openfile->current_y = 0;
+#ifndef NANO_TINY
+ }
+#endif
+
+ for (i = editwinrows - 2; i - skipped > 0 && openfile->current !=
+ openfile->fileage; i--) {
+ openfile->current = openfile->current->prev;
+ if (ISSET(SOFTWRAP) && openfile->current) {
+ skipped += strlenpt(openfile->current->data) / COLS;
+#ifdef DEBUG
+ fprintf(stderr, "do_page_up: i = %d, skipped = %d based on line %ld len %d\n", i, (unsigned long) skipped,
+openfile->current->lineno, strlenpt(openfile->current->data));
+#endif
+ }
+ }
+
+ openfile->current_x = actual_x(openfile->current->data,
+ openfile->placewewant);
+
+#ifdef DEBUG
+ fprintf(stderr, "do_page_up: openfile->current->lineno = %lu, skipped = %d\n", (unsigned long) openfile->current->lineno, skipped);
+#endif
+
+ /* Scroll the edit window up a page. */
+ edit_update(NONE);
+}
+
+/* Move down one page. */
+void do_page_down(void)
+{
+ int i;
+
+ /* If there's less than a page of text left on the screen, put the
+ * cursor at the beginning of the last line of the file, and then
+ * update the edit window. */
+ if (openfile->current->lineno + maxrows - 2 >=
+ openfile->filebot->lineno) {
+ do_last_line();
+ return;
+ }
+
+ /* If we're not in smooth scrolling mode, put the cursor at the
+ * beginning of the top line of the edit window, as Pico does. */
+#ifndef NANO_TINY
+ if (!ISSET(SMOOTH_SCROLL)) {
+#endif
+ openfile->current = openfile->edittop;
+ openfile->placewewant = openfile->current_y = 0;
+#ifndef NANO_TINY
+ }
+#endif
+
+ for (i = maxrows - 2; i > 0 && openfile->current !=
+ openfile->filebot; i--) {
+ openfile->current = openfile->current->next;
+#ifdef DEBUG
+ fprintf(stderr, "do_page_down: moving to line %lu\n", (unsigned long) openfile->current->lineno);
+#endif
+
+ }
+
+ openfile->current_x = actual_x(openfile->current->data,
+ openfile->placewewant);
+
+ /* Scroll the edit window down a page. */
+ edit_update(NONE);
+}
+
+#ifndef DISABLE_JUSTIFY
+/* Move up to the beginning of the last beginning-of-paragraph line
+ * before the current line. If allow_update is TRUE, update the screen
+ * afterwards. */
+void do_para_begin(bool allow_update)
+{
+ filestruct *current_save = openfile->current;
+ const size_t pww_save = openfile->placewewant;
+
+ if (openfile->current != openfile->fileage) {
+ do {
+ openfile->current = openfile->current->prev;
+ openfile->current_y--;
+ } while (!begpar(openfile->current));
+ }
+
+ openfile->current_x = 0;
+ openfile->placewewant = 0;
+
+ if (allow_update)
+ edit_redraw(current_save, pww_save);
+}
+
+/* Move up to the beginning of the last beginning-of-paragraph line
+ * before the current line, and update the screen afterwards. */
+void do_para_begin_void(void)
+{
+ do_para_begin(TRUE);
+}
+
+/* Move down to the beginning of the last line of the current paragraph.
+ * Then move down one line farther if there is such a line, or to the
+ * end of the current line if not. If allow_update is TRUE, update the
+ * screen afterwards. A line is the last line of a paragraph if it is
+ * in a paragraph, and the next line either is the beginning line of a
+ * paragraph or isn't in a paragraph. */
+void do_para_end(bool allow_update)
+{
+ filestruct *const current_save = openfile->current;
+ const size_t pww_save = openfile->placewewant;
+
+ while (openfile->current != openfile->filebot &&
+ !inpar(openfile->current))
+ openfile->current = openfile->current->next;
+
+ while (openfile->current != openfile->filebot &&
+ inpar(openfile->current->next) &&
+ !begpar(openfile->current->next)) {
+ openfile->current = openfile->current->next;
+ openfile->current_y++;
+ }
+
+ if (openfile->current != openfile->filebot) {
+ openfile->current = openfile->current->next;
+ openfile->current_x = 0;
+ openfile->placewewant = 0;
+ } else {
+ openfile->current_x = strlen(openfile->current->data);
+ openfile->placewewant = xplustabs();
+ }
+
+ if (allow_update)
+ edit_redraw(current_save, pww_save);
+}
+
+/* Move down to the beginning of the last line of the current paragraph.
+ * Then move down one line farther if there is such a line, or to the
+ * end of the current line if not, and update the screen afterwards. */
+void do_para_end_void(void)
+{
+ do_para_end(TRUE);
+}
+#endif /* !DISABLE_JUSTIFY */
+
+#ifndef NANO_TINY
+/* Move to the next word in the file. If allow_punct is TRUE, treat
+ * punctuation as part of a word. If allow_update is TRUE, update the
+ * screen afterwards. Return TRUE if we started on a word, and FALSE
+ * otherwise. */
+bool do_next_word(bool allow_punct, bool allow_update)
+{
+ size_t pww_save = openfile->placewewant;
+ filestruct *current_save = openfile->current;
+ char *char_mb;
+ int char_mb_len;
+ bool end_line = FALSE, started_on_word = FALSE;
+
+ assert(openfile->current != NULL && openfile->current->data != NULL);
+
+ char_mb = charalloc(mb_cur_max());
+
+ /* Move forward until we find the character after the last letter of
+ * the current word. */
+ while (!end_line) {
+ char_mb_len = parse_mbchar(openfile->current->data +
+ openfile->current_x, char_mb, NULL);
+
+ /* If we've found it, stop moving forward through the current
+ * line. */
+ if (!is_word_mbchar(char_mb, allow_punct))
+ break;
+
+ /* If we haven't found it, then we've started on a word, so set
+ * started_on_word to TRUE. */
+ started_on_word = TRUE;
+
+ if (openfile->current->data[openfile->current_x] == '\0')
+ end_line = TRUE;
+ else
+ openfile->current_x += char_mb_len;
+ }
+
+ /* Move forward until we find the first letter of the next word. */
+ if (openfile->current->data[openfile->current_x] == '\0')
+ end_line = TRUE;
+ else
+ openfile->current_x += char_mb_len;
+
+ for (; openfile->current != NULL;
+ openfile->current = openfile->current->next) {
+ while (!end_line) {
+ char_mb_len = parse_mbchar(openfile->current->data +
+ openfile->current_x, char_mb, NULL);
+
+ /* If we've found it, stop moving forward through the
+ * current line. */
+ if (is_word_mbchar(char_mb, allow_punct))
+ break;
+
+ if (openfile->current->data[openfile->current_x] == '\0')
+ end_line = TRUE;
+ else
+ openfile->current_x += char_mb_len;
+ }
+
+ /* If we've found it, stop moving forward to the beginnings of
+ * subsequent lines. */
+ if (!end_line)
+ break;
+
+ if (openfile->current != openfile->filebot) {
+ end_line = FALSE;
+ openfile->current_x = 0;
+ }
+ }
+
+ free(char_mb);
+
+ /* If we haven't found it, move to the end of the file. */
+ if (openfile->current == NULL)
+ openfile->current = openfile->filebot;
+
+ openfile->placewewant = xplustabs();
+
+ /* If allow_update is TRUE, update the screen. */
+ if (allow_update)
+ edit_redraw(current_save, pww_save);
+
+ /* Return whether we started on a word. */
+ return started_on_word;
+}
+
+/* Move to the next word in the file, treating punctuation as part of a
+ * word if the WORD_BOUNDS flag is set, and update the screen
+ * afterwards. */
+void do_next_word_void(void)
+{
+ do_next_word(ISSET(WORD_BOUNDS), TRUE);
+}
+
+/* Move to the previous word in the file. If allow_punct is TRUE, treat
+ * punctuation as part of a word. If allow_update is TRUE, update the
+ * screen afterwards. Return TRUE if we started on a word, and FALSE
+ * otherwise. */
+bool do_prev_word(bool allow_punct, bool allow_update)
+{
+ size_t pww_save = openfile->placewewant;
+ filestruct *current_save = openfile->current;
+ char *char_mb;
+ int char_mb_len;
+ bool begin_line = FALSE, started_on_word = FALSE;
+
+ assert(openfile->current != NULL && openfile->current->data != NULL);
+
+ char_mb = charalloc(mb_cur_max());
+
+ /* Move backward until we find the character before the first letter
+ * of the current word. */
+ while (!begin_line) {
+ char_mb_len = parse_mbchar(openfile->current->data +
+ openfile->current_x, char_mb, NULL);
+
+ /* If we've found it, stop moving backward through the current
+ * line. */
+ if (!is_word_mbchar(char_mb, allow_punct))
+ break;
+
+ /* If we haven't found it, then we've started on a word, so set
+ * started_on_word to TRUE. */
+ started_on_word = TRUE;
+
+ if (openfile->current_x == 0)
+ begin_line = TRUE;
+ else
+ openfile->current_x = move_mbleft(openfile->current->data,
+ openfile->current_x);
+ }
+
+ /* Move backward until we find the last letter of the previous
+ * word. */
+ if (openfile->current_x == 0)
+ begin_line = TRUE;
+ else
+ openfile->current_x = move_mbleft(openfile->current->data,
+ openfile->current_x);
+
+ for (; openfile->current != NULL;
+ openfile->current = openfile->current->prev) {
+ while (!begin_line) {
+ char_mb_len = parse_mbchar(openfile->current->data +
+ openfile->current_x, char_mb, NULL);
+
+ /* If we've found it, stop moving backward through the
+ * current line. */
+ if (is_word_mbchar(char_mb, allow_punct))
+ break;
+
+ if (openfile->current_x == 0)
+ begin_line = TRUE;
+ else
+ openfile->current_x =
+ move_mbleft(openfile->current->data,
+ openfile->current_x);
+ }
+
+ /* If we've found it, stop moving backward to the ends of
+ * previous lines. */
+ if (!begin_line)
+ break;
+
+ if (openfile->current != openfile->fileage) {
+ begin_line = FALSE;
+ openfile->current_x = strlen(openfile->current->prev->data);
+ }
+ }
+
+ /* If we haven't found it, move to the beginning of the file. */
+ if (openfile->current == NULL)
+ openfile->current = openfile->fileage;
+ /* If we've found it, move backward until we find the character
+ * before the first letter of the previous word. */
+ else if (!begin_line) {
+ if (openfile->current_x == 0)
+ begin_line = TRUE;
+ else
+ openfile->current_x = move_mbleft(openfile->current->data,
+ openfile->current_x);
+
+ while (!begin_line) {
+ char_mb_len = parse_mbchar(openfile->current->data +
+ openfile->current_x, char_mb, NULL);
+
+ /* If we've found it, stop moving backward through the
+ * current line. */
+ if (!is_word_mbchar(char_mb, allow_punct))
+ break;
+
+ if (openfile->current_x == 0)
+ begin_line = TRUE;
+ else
+ openfile->current_x =
+ move_mbleft(openfile->current->data,
+ openfile->current_x);
+ }
+
+ /* If we've found it, move forward to the first letter of the
+ * previous word. */
+ if (!begin_line)
+ openfile->current_x += char_mb_len;
+ }
+
+ free(char_mb);
+
+ openfile->placewewant = xplustabs();
+
+ /* If allow_update is TRUE, update the screen. */
+ if (allow_update)
+ edit_redraw(current_save, pww_save);
+
+ /* Return whether we started on a word. */
+ return started_on_word;
+}
+
+/* Move to the previous word in the file, treating punctuation as part
+ * of a word if the WORD_BOUNDS flag is set, and update the screen
+ * afterwards. */
+void do_prev_word_void(void)
+{
+ do_prev_word(ISSET(WORD_BOUNDS), TRUE);
+}
+#endif /* !NANO_TINY */
+
+/* Move to the beginning of the current line. If the SMART_HOME flag is
+ * set, move to the first non-whitespace character of the current line
+ * if we aren't already there, or to the beginning of the current line
+ * if we are. */
+void do_home(void)
+{
+ size_t pww_save = openfile->placewewant;
+
+#ifndef NANO_TINY
+ if (ISSET(SMART_HOME)) {
+ size_t current_x_save = openfile->current_x;
+
+ openfile->current_x = indent_length(openfile->current->data);
+
+ if (openfile->current_x == current_x_save ||
+ openfile->current->data[openfile->current_x] == '\0')
+ openfile->current_x = 0;
+
+ openfile->placewewant = xplustabs();
+ } else {
+#endif
+ openfile->current_x = 0;
+ openfile->placewewant = 0;
+#ifndef NANO_TINY
+ }
+#endif
+
+ if (need_horizontal_update(pww_save))
+ update_line(openfile->current, openfile->current_x);
+}
+
+/* Move to the end of the current line. */
+void do_end(void)
+{
+ size_t pww_save = openfile->placewewant;
+
+ openfile->current_x = strlen(openfile->current->data);
+ openfile->placewewant = xplustabs();
+
+ if (need_horizontal_update(pww_save))
+ update_line(openfile->current, openfile->current_x);
+}
+
+/* If scroll_only is FALSE, move up one line. If scroll_only is TRUE,
+ * scroll up one line without scrolling the cursor. */
+void do_up(
+#ifndef NANO_TINY
+ bool scroll_only
+#else
+ void
+#endif
+ )
+{
+ /* If we're at the top of the file, or if scroll_only is TRUE and
+ * the top of the file is onscreen, get out. */
+ if (openfile->current == openfile->fileage
+#ifndef NANO_TINY
+ || (scroll_only && openfile->edittop == openfile->fileage)
+#endif
+ )
+ return;
+
+ assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);
+
+ /* Move the current line of the edit window up. */
+ openfile->current = openfile->current->prev;
+ openfile->current_x = actual_x(openfile->current->data,
+ openfile->placewewant);
+
+ /* If scroll_only is FALSE and if we're on the first line of the
+ * edit window, scroll the edit window up one line if we're in
+ * smooth scrolling mode, or up half a page if we're not. If
+ * scroll_only is TRUE, scroll the edit window up one line
+ * unconditionally. */
+ if (openfile->current_y == 0 || (ISSET(SOFTWRAP) && openfile->edittop->lineno == openfile->current->next->lineno)
+#ifndef NANO_TINY
+ || scroll_only
+#endif
+ )
+ edit_scroll(UP_DIR,
+#ifndef NANO_TINY
+ (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 :
+#endif
+ editwinrows / 2 + 1);
+
+ /* If we're below the first line of the edit window, update the
+ * line we were on before and the line we're on now. The former
+ * needs to be redrawn if we're not on the first page, and the
+ * latter needs to be drawn unconditionally. */
+ if (openfile->current_y > 0) {
+ if (need_vertical_update(0))
+ update_line(openfile->current->next, 0);
+ update_line(openfile->current, openfile->current_x);
+ }
+}
+
+/* Move up one line. */
+void do_up_void(void)
+{
+ do_up(
+#ifndef NANO_TINY
+ FALSE
+#endif
+ );
+}
+
+#ifndef NANO_TINY
+/* Scroll up one line without scrolling the cursor. */
+void do_scroll_up(void)
+{
+ do_up(TRUE);
+}
+#endif
+
+/* If scroll_only is FALSE, move down one line. If scroll_only is TRUE,
+ * scroll down one line without scrolling the cursor. */
+void do_down(
+#ifndef NANO_TINY
+ bool scroll_only
+#else
+ void
+#endif
+ )
+{
+ bool onlastline = FALSE;
+
+ /* If we're at the bottom of the file, get out. */
+ if (openfile->current == openfile->filebot)
+ return;
+
+
+ assert(ISSET(SOFTWRAP) || openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);
+
+ /* Move the current line of the edit window down. */
+ openfile->current = openfile->current->next;
+ openfile->current_x = actual_x(openfile->current->data,
+ openfile->placewewant);
+
+ if (ISSET(SOFTWRAP)) {
+ if (openfile->current->lineno - openfile->edittop->lineno >= maxrows)
+ onlastline = TRUE;
+ }
+
+ /* If scroll_only is FALSE and if we're on the first line of the
+ * edit window, scroll the edit window down one line if we're in
+ * smooth scrolling mode, or down half a page if we're not. If
+ * scroll_only is TRUE, scroll the edit window down one line
+ * unconditionally. */
+ if (onlastline || openfile->current_y == editwinrows - 1
+#ifndef NANO_TINY
+ || scroll_only
+#endif
+ ) {
+ edit_scroll(DOWN_DIR,
+#ifndef NANO_TINY
+ (ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 :
+#endif
+ editwinrows / 2 + 1);
+
+ edit_refresh_needed = TRUE;
+ }
+ /* If we're above the last line of the edit window, update the line
+ * we were on before and the line we're on now. The former needs to
+ * be redrawn if we're not on the first page, and the latter needs
+ * to be drawn unconditionally. */
+ if (ISSET(SOFTWRAP) || openfile->current_y < editwinrows - 1) {
+ if (need_vertical_update(0))
+ update_line(openfile->current->prev, 0);
+ update_line(openfile->current, openfile->current_x);
+ }
+}
+
+/* Move down one line. */
+void do_down_void(void)
+{
+ do_down(
+#ifndef NANO_TINY
+ FALSE
+#endif
+ );
+}
+
+#ifndef NANO_TINY
+/* Scroll down one line without scrolling the cursor. */
+void do_scroll_down(void)
+{
+ do_down(TRUE);
+}
+#endif
+
+/* Move left one character. */
+void do_left(void)
+{
+ size_t pww_save = openfile->placewewant;
+
+ if (openfile->current_x > 0)
+ openfile->current_x = move_mbleft(openfile->current->data,
+ openfile->current_x);
+ else if (openfile->current != openfile->fileage) {
+ do_up_void();
+ openfile->current_x = strlen(openfile->current->data);
+ }
+
+ openfile->placewewant = xplustabs();
+
+ if (need_horizontal_update(pww_save))
+ update_line(openfile->current, openfile->current_x);
+}
+
+/* Move right one character. */
+void do_right(void)
+{
+ size_t pww_save = openfile->placewewant;
+
+ assert(openfile->current_x <= strlen(openfile->current->data));
+
+ if (openfile->current->data[openfile->current_x] != '\0')
+ openfile->current_x = move_mbright(openfile->current->data,
+ openfile->current_x);
+ else if (openfile->current != openfile->filebot) {
+ do_down_void();
+ openfile->current_x = 0;
+ }
+
+ openfile->placewewant = xplustabs();
+
+ if (need_horizontal_update(pww_save))
+ update_line(openfile->current, openfile->current_x);
+}