/* Editor low level data handling and cursor fundamentals. Copyright (C) 1996-2024 Free Software Foundation, Inc. Written by: Paul Sheer 1996, 1997 Ilia Maslakov 2009, 2010, 2011 Andrew Borodin 2012-2022 This file is part of the Midnight Commander. The Midnight Commander 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 of the License, or (at your option) any later version. The Midnight Commander 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, see . */ /** \file * \brief Source: editor low level data handling and cursor fundamentals * \author Paul Sheer * \date 1996, 1997 */ #include #include #include #include #include #include #include #include #include /* UINTMAX_MAX */ #include #include "lib/global.h" #include "lib/tty/color.h" #include "lib/tty/tty.h" /* attrset() */ #include "lib/tty/key.h" /* is_idle() */ #include "lib/skin.h" /* EDITOR_NORMAL_COLOR */ #include "lib/fileloc.h" /* EDIT_HOME_BLOCK_FILE */ #include "lib/vfs/vfs.h" #include "lib/strutil.h" /* utf string functions */ #include "lib/util.h" /* load_file_position(), save_file_position() */ #include "lib/timefmt.h" /* time formatting */ #include "lib/lock.h" #include "lib/widget.h" #ifdef HAVE_CHARSET #include "lib/charsets.h" /* get_codepage_id */ #endif #include "src/usermenu.h" /* user_menu_cmd() */ #include "src/keymap.h" #include "edit-impl.h" #include "editwidget.h" #include "editsearch.h" #include "editcomplete.h" /* edit_complete_word_cmd() */ #include "editmacros.h" #include "etags.h" /* edit_get_match_keyword_cmd() */ #ifdef HAVE_ASPELL #include "spell.h" #endif /*** global variables ****************************************************************************/ edit_options_t edit_options = { .word_wrap_line_length = DEFAULT_WRAP_LINE_LENGTH, .typewriter_wrap = FALSE, .auto_para_formatting = FALSE, .fill_tabs_with_spaces = FALSE, .return_does_auto_indent = TRUE, .backspace_through_tabs = FALSE, .fake_half_tabs = TRUE, .persistent_selections = TRUE, .drop_selection_on_copy = TRUE, .cursor_beyond_eol = FALSE, .cursor_after_inserted_block = FALSE, .state_full_filename = FALSE, .line_state = FALSE, .line_state_width = 0, .save_mode = EDIT_QUICK_SAVE, .confirm_save = TRUE, .save_position = TRUE, .syntax_highlighting = TRUE, .group_undo = FALSE, .backup_ext = NULL, .filesize_threshold = NULL, .stop_format_chars = NULL, .visible_tabs = TRUE, .visible_tws = TRUE, .show_right_margin = FALSE, .simple_statusbar = FALSE, .check_nl_at_eof = FALSE }; int max_undo = 32768; gboolean enable_show_tabs_tws = TRUE; unsigned int edit_stack_iterator = 0; edit_stack_type edit_history_moveto[MAX_HISTORY_MOVETO]; /* magic sequence for say than block is vertical */ const char VERTICAL_MAGIC[] = { '\1', '\1', '\1', '\1', '\n' }; /*** file scope macro definitions ****************************************************************/ #define TEMP_BUF_LEN 1024 #define space_width 1 /*** file scope type declarations ****************************************************************/ /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ /* detecting an error on save is easy: just check if every byte has been written. */ /* detecting an error on read, is not so easy 'cos there is not way to tell whether you read everything or not. */ /* FIXME: add proper 'triple_pipe_open' to read, write and check errors. */ static const struct edit_filters { const char *read, *write, *extension; } all_filters[] = { /* *INDENT-OFF* */ { "xz -cd %s 2>&1", "xz > %s", ".xz"}, { "zstd -cd %s 2>&1", "zstd > %s", ".zst"}, { "lz4 -cd %s 2>&1", "lz4 > %s", ".lz4" }, { "lzip -cd %s 2>&1", "lzip > %s", ".lz"}, { "lzma -cd %s 2>&1", "lzma > %s", ".lzma" }, { "lzop -cd %s 2>&1", "lzop > %s", ".lzo"}, { "bzip2 -cd %s 2>&1", "bzip2 > %s", ".bz2" }, { "gzip -cd %s 2>&1", "gzip > %s", ".gz" }, { "gzip -cd %s 2>&1", "gzip > %s", ".Z" } /* *INDENT-ON* */ }; static const off_t filesize_default_threshold = 64 * 1024 * 1024; /* 64 MB */ /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ static int edit_load_status_update_cb (status_msg_t * sm) { simple_status_msg_t *ssm = SIMPLE_STATUS_MSG (sm); edit_buffer_read_file_status_msg_t *rsm = (edit_buffer_read_file_status_msg_t *) sm; Widget *wd = WIDGET (sm->dlg); if (verbose) label_set_textv (ssm->label, _("Loading: %3d%%"), edit_buffer_calc_percent (rsm->buf, rsm->loaded)); else label_set_text (ssm->label, _("Loading...")); if (rsm->first) { Widget *lw = WIDGET (ssm->label); WRect r; r = wd->rect; r.cols = MAX (r.cols, lw->rect.cols + 6); widget_set_size_rect (wd, &r); r = lw->rect; r.x = wd->rect.x + (wd->rect.cols - r.cols) / 2; widget_set_size_rect (lw, &r); rsm->first = FALSE; } return status_msg_common_update (sm); } /* --------------------------------------------------------------------------------------------- */ /** * Load file OR text into buffers. Set cursor to the beginning of file. * * @return FALSE on error. */ static gboolean edit_load_file_fast (edit_buffer_t * buf, const vfs_path_t * filename_vpath) { int file; gboolean ret; edit_buffer_read_file_status_msg_t rsm; gboolean aborted; file = mc_open (filename_vpath, O_RDONLY | O_BINARY); if (file < 0) { gchar *errmsg; errmsg = g_strdup_printf (_("Cannot open %s for reading"), vfs_path_as_str (filename_vpath)); edit_error_dialog (_("Error"), errmsg); g_free (errmsg); return FALSE; } rsm.first = TRUE; rsm.buf = buf; rsm.loaded = 0; status_msg_init (STATUS_MSG (&rsm), _("Load file"), 1.0, simple_status_msg_init_cb, edit_load_status_update_cb, NULL); ret = (edit_buffer_read_file (buf, file, buf->size, &rsm, &aborted) == buf->size); status_msg_deinit (STATUS_MSG (&rsm)); if (!ret && !aborted) { gchar *errmsg; errmsg = g_strdup_printf (_("Error reading %s"), vfs_path_as_str (filename_vpath)); edit_error_dialog (_("Error"), errmsg); g_free (errmsg); } mc_close (file); return ret; } /* --------------------------------------------------------------------------------------------- */ /** Return index of the filter or -1 is there is no appropriate filter */ static int edit_find_filter (const vfs_path_t * filename_vpath) { if (filename_vpath != NULL) { const char *s; size_t i; s = vfs_path_as_str (filename_vpath); for (i = 0; i < G_N_ELEMENTS (all_filters); i++) if (g_str_has_suffix (s, all_filters[i].extension)) return i; } return -1; } /* --------------------------------------------------------------------------------------------- */ static char * edit_get_filter (const vfs_path_t * filename_vpath) { int i; char *quoted_name; char *p = NULL; i = edit_find_filter (filename_vpath); if (i < 0) return NULL; quoted_name = name_quote (vfs_path_as_str (filename_vpath), FALSE); if (quoted_name != NULL) { p = g_strdup_printf (all_filters[i].read, quoted_name); g_free (quoted_name); } return p; } /* --------------------------------------------------------------------------------------------- */ static off_t edit_insert_stream (WEdit * edit, FILE * f) { int c; off_t i; for (i = 0; (c = fgetc (f)) >= 0; i++) edit_insert (edit, c); return i; } /* --------------------------------------------------------------------------------------------- */ /** * Open file and create it if necessary. * * @param edit editor object * @param filename_vpath file name * @param st buffer for store stat info * @return TRUE for success, FALSE for error. */ static gboolean check_file_access (WEdit * edit, const vfs_path_t * filename_vpath, struct stat *st) { static uintmax_t threshold = UINTMAX_MAX; int file; gchar *errmsg = NULL; gboolean ret = TRUE; /* Try opening an existing file */ file = mc_open (filename_vpath, O_NONBLOCK | O_RDONLY | O_BINARY, 0666); if (file < 0) { /* * Try creating the file. O_EXCL prevents following broken links * and opening existing files. */ file = mc_open (filename_vpath, O_NONBLOCK | O_RDONLY | O_BINARY | O_CREAT | O_EXCL, 0666); if (file < 0) { errmsg = g_strdup_printf (_("Cannot open %s for reading"), vfs_path_as_str (filename_vpath)); goto cleanup; } /* New file, delete it if it's not modified or saved */ edit->delete_file = 1; } /* Check what we have opened */ if (mc_fstat (file, st) < 0) { errmsg = g_strdup_printf (_("Cannot get size/permissions for %s"), vfs_path_as_str (filename_vpath)); goto cleanup; } /* We want to open regular files only */ if (!S_ISREG (st->st_mode)) { errmsg = g_strdup_printf (_("\"%s\" is not a regular file"), vfs_path_as_str (filename_vpath)); goto cleanup; } /* get file size threshold for alarm */ if (threshold == UINTMAX_MAX) { gboolean err = FALSE; threshold = parse_integer (edit_options.filesize_threshold, &err); if (err) threshold = filesize_default_threshold; } /* * Don't delete non-empty files. * O_EXCL should prevent it, but let's be on the safe side. */ if (st->st_size > 0) edit->delete_file = 0; if ((uintmax_t) st->st_size > threshold) { int act; errmsg = g_strdup_printf (_("File \"%s\" is too large.\nOpen it anyway?"), vfs_path_as_str (filename_vpath)); act = edit_query_dialog2 (_("Warning"), errmsg, _("&Yes"), _("&No")); MC_PTR_FREE (errmsg); if (act != 0) ret = FALSE; } cleanup: (void) mc_close (file); if (errmsg != NULL) { edit_error_dialog (_("Error"), errmsg); g_free (errmsg); ret = FALSE; } return ret; } /* --------------------------------------------------------------------------------------------- */ /** * Open the file and load it into the buffers, either directly or using * a filter. Return TRUE on success, FALSE on error. * * Fast loading (edit_load_file_fast) is used when the file size is * known. In this case the data is read into the buffers by blocks. * If the file size is not known, the data is loaded byte by byte in * edit_insert_file. * * @param edit editor object * @return TRUE if file was successfully opened and loaded to buffers, FALSE otherwise */ static gboolean edit_load_file (WEdit * edit) { gboolean fast_load = TRUE; /* Cannot do fast load if a filter is used */ if (edit_find_filter (edit->filename_vpath) >= 0) fast_load = FALSE; /* * FIXME: line end translation should disable fast loading as well * Consider doing fseek() to the end and ftell() for the real size. */ if (edit->filename_vpath != NULL) { /* * VFS may report file size incorrectly, and slow load is not a big * deal considering overhead in VFS. */ if (!vfs_file_is_local (edit->filename_vpath)) fast_load = FALSE; /* If we are dealing with a real file, check that it exists */ if (!check_file_access (edit, edit->filename_vpath, &edit->stat1)) { edit_clean (edit); return FALSE; } } else { /* nothing to load */ fast_load = FALSE; } if (fast_load) { edit_buffer_init (&edit->buffer, edit->stat1.st_size); if (!edit_load_file_fast (&edit->buffer, edit->filename_vpath)) { edit_clean (edit); return FALSE; } } else { edit_buffer_init (&edit->buffer, 0); if (edit->filename_vpath != NULL && *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) != '\0') { edit->undo_stack_disable = 1; if (edit_insert_file (edit, edit->filename_vpath) < 0) { edit_clean (edit); return FALSE; } edit->undo_stack_disable = 0; } } edit->lb = LB_ASIS; return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** * Restore saved cursor position and/or bookmarks in the file * * @param edit editor object * @param load_position If TRUE, load bookmarks and cursor position and apply them. * If FALSE, load bookmarks only. */ static void edit_load_position (WEdit * edit, gboolean load_position) { long line, column; off_t offset; off_t b; if (edit->filename_vpath == NULL || *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) == '\0') return; load_file_position (edit->filename_vpath, &line, &column, &offset, &edit->serialized_bookmarks); /* apply bookmarks in any case */ book_mark_restore (edit, BOOK_MARK_COLOR); if (!load_position) return; if (line > 0) { edit_move_to_line (edit, line - 1); edit->prev_col = column; } else if (offset > 0) { edit_cursor_move (edit, offset); line = edit->buffer.curs_line; edit->search_start = edit->buffer.curs1; } b = edit_buffer_get_current_bol (&edit->buffer); edit_move_to_prev_col (edit, b); edit_move_display (edit, line - (WIDGET (edit)->rect.lines / 2)); } /* --------------------------------------------------------------------------------------------- */ /** Save cursor position in the file */ static void edit_save_position (WEdit * edit) { if (edit->filename_vpath == NULL || *(vfs_path_get_by_index (edit->filename_vpath, 0)->path) == '\0') return; book_mark_serialize (edit, BOOK_MARK_COLOR); save_file_position (edit->filename_vpath, edit->buffer.curs_line + 1, edit->curs_col, edit->buffer.curs1, edit->serialized_bookmarks); edit->serialized_bookmarks = NULL; } /* --------------------------------------------------------------------------------------------- */ /** Clean the WEdit stricture except the widget part */ static void edit_purge_widget (WEdit * edit) { size_t len = sizeof (WEdit) - sizeof (Widget); char *start = (char *) edit + sizeof (Widget); memset (start, 0, len); } /* --------------------------------------------------------------------------------------------- */ /* TODO: if the user undos until the stack bottom, and the stack has not wrapped, then the file should be as it was when he loaded up. Then set edit->modified to 0. */ static long edit_pop_undo_action (WEdit * edit) { long c; unsigned long sp = edit->undo_stack_pointer; if (sp == edit->undo_stack_bottom) return STACK_BOTTOM; sp = (sp - 1) & edit->undo_stack_size_mask; c = edit->undo_stack[sp]; if (c >= 0) { /* edit->undo_stack[sp] = '@'; */ edit->undo_stack_pointer = (edit->undo_stack_pointer - 1) & edit->undo_stack_size_mask; return c; } if (sp == edit->undo_stack_bottom) return STACK_BOTTOM; c = edit->undo_stack[(sp - 1) & edit->undo_stack_size_mask]; if (edit->undo_stack[sp] == -2) { /* edit->undo_stack[sp] = '@'; */ edit->undo_stack_pointer = sp; } else edit->undo_stack[sp]++; return c; } /* --------------------------------------------------------------------------------------------- */ static long edit_pop_redo_action (WEdit * edit) { long c; unsigned long sp = edit->redo_stack_pointer; if (sp == edit->redo_stack_bottom) return STACK_BOTTOM; sp = (sp - 1) & edit->redo_stack_size_mask; c = edit->redo_stack[sp]; if (c >= 0) { edit->redo_stack_pointer = (edit->redo_stack_pointer - 1) & edit->redo_stack_size_mask; return c; } if (sp == edit->redo_stack_bottom) return STACK_BOTTOM; c = edit->redo_stack[(sp - 1) & edit->redo_stack_size_mask]; if (edit->redo_stack[sp] == -2) edit->redo_stack_pointer = sp; else edit->redo_stack[sp]++; return c; } /* --------------------------------------------------------------------------------------------- */ static long get_prev_undo_action (WEdit * edit) { long c; unsigned long sp = edit->undo_stack_pointer; if (sp == edit->undo_stack_bottom) return STACK_BOTTOM; sp = (sp - 1) & edit->undo_stack_size_mask; c = edit->undo_stack[sp]; if (c >= 0) return c; if (sp == edit->undo_stack_bottom) return STACK_BOTTOM; c = edit->undo_stack[(sp - 1) & edit->undo_stack_size_mask]; return c; } /* --------------------------------------------------------------------------------------------- */ /** is called whenever a modification is made by one of the four routines below */ static void edit_modification (WEdit * edit) { edit->caches_valid = FALSE; /* raise lock when file modified */ if (edit->modified == 0 && edit->delete_file == 0) edit->locked = lock_file (edit->filename_vpath); edit->modified = 1; } /* --------------------------------------------------------------------------------------------- */ /* high level cursor movement commands */ /* --------------------------------------------------------------------------------------------- */ /** check whether cursor is in indent part of line * * @param edit editor object * * @return TRUE if cursor is in indent, FALSE otherwise */ static gboolean is_in_indent (const edit_buffer_t * buf) { off_t p; for (p = edit_buffer_get_current_bol (buf); p < buf->curs1; p++) if (strchr (" \t", edit_buffer_get_byte (buf, p)) == NULL) return FALSE; return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** check whether line in editor is blank or not * * @param edit editor object * @param offset position in file * * @return TRUE if line in blank, FALSE otherwise */ static gboolean is_blank (const edit_buffer_t * buf, off_t offset) { off_t s, f; s = edit_buffer_get_bol (buf, offset); f = edit_buffer_get_eol (buf, offset); for (; s < f; s++) { int c; c = edit_buffer_get_byte (buf, s); if (!isspace (c)) return FALSE; } return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** returns the offset of line i */ static off_t edit_find_line (WEdit * edit, long line) { long i; long j = 0; long m = 2000000000; /* what is the magic number? */ if (!edit->caches_valid) { memset (edit->line_numbers, 0, sizeof (edit->line_numbers)); memset (edit->line_offsets, 0, sizeof (edit->line_offsets)); /* three offsets that we *know* are line 0 at 0 and these two: */ edit->line_numbers[1] = edit->buffer.curs_line; edit->line_offsets[1] = edit_buffer_get_current_bol (&edit->buffer); edit->line_numbers[2] = edit->buffer.lines; edit->line_offsets[2] = edit_buffer_get_bol (&edit->buffer, edit->buffer.size); edit->caches_valid = TRUE; } if (line >= edit->buffer.lines) return edit->line_offsets[2]; if (line <= 0) return 0; /* find the closest known point */ for (i = 0; i < N_LINE_CACHES; i++) { long n; n = labs (edit->line_numbers[i] - line); if (n < m) { m = n; j = i; } } if (m == 0) return edit->line_offsets[j]; /* know the offset exactly */ if (m == 1 && j >= 3) i = j; /* one line different - caller might be looping, so stay in this cache */ else i = 3 + (rand () % (N_LINE_CACHES - 3)); if (line > edit->line_numbers[j]) edit->line_offsets[i] = edit_buffer_get_forward_offset (&edit->buffer, edit->line_offsets[j], line - edit->line_numbers[j], 0); else edit->line_offsets[i] = edit_buffer_get_backward_offset (&edit->buffer, edit->line_offsets[j], edit->line_numbers[j] - line); edit->line_numbers[i] = line; return edit->line_offsets[i]; } /* --------------------------------------------------------------------------------------------- */ /** moves up until a blank line is reached, or until just before a non-blank line is reached */ static void edit_move_up_paragraph (WEdit * edit, gboolean do_scroll) { long i = 0; if (edit->buffer.curs_line > 1) { if (!edit_line_is_blank (edit, edit->buffer.curs_line)) { for (i = edit->buffer.curs_line - 1; i != 0; i--) if (edit_line_is_blank (edit, i)) break; } else if (edit_line_is_blank (edit, edit->buffer.curs_line - 1)) { for (i = edit->buffer.curs_line - 1; i != 0; i--) if (!edit_line_is_blank (edit, i)) { i++; break; } } else { for (i = edit->buffer.curs_line - 1; i != 0; i--) if (edit_line_is_blank (edit, i)) break; } } edit_move_up (edit, edit->buffer.curs_line - i, do_scroll); } /* --------------------------------------------------------------------------------------------- */ /** moves down until a blank line is reached, or until just before a non-blank line is reached */ static void edit_move_down_paragraph (WEdit * edit, gboolean do_scroll) { long i; if (edit->buffer.curs_line >= edit->buffer.lines - 1) i = edit->buffer.lines; else if (!edit_line_is_blank (edit, edit->buffer.curs_line)) { for (i = edit->buffer.curs_line + 1; i != 0; i++) if (edit_line_is_blank (edit, i) || i >= edit->buffer.lines) break; } else if (edit_line_is_blank (edit, edit->buffer.curs_line + 1)) { for (i = edit->buffer.curs_line + 1; i != 0; i++) if (!edit_line_is_blank (edit, i) || i > edit->buffer.lines) { i--; break; } } else { for (i = edit->buffer.curs_line + 1; i != 0; i++) if (edit_line_is_blank (edit, i) || i >= edit->buffer.lines) break; } edit_move_down (edit, i - edit->buffer.curs_line, do_scroll); } /* --------------------------------------------------------------------------------------------- */ static void edit_begin_page (WEdit * edit) { edit_update_curs_row (edit); edit_move_up (edit, edit->curs_row, FALSE); } /* --------------------------------------------------------------------------------------------- */ static void edit_end_page (WEdit * edit) { edit_update_curs_row (edit); edit_move_down (edit, WIDGET (edit)->rect.lines - edit->curs_row - 1, FALSE); } /* --------------------------------------------------------------------------------------------- */ /** goto beginning of text */ static void edit_move_to_top (WEdit * edit) { if (edit->buffer.curs_line != 0) { edit_cursor_move (edit, -edit->buffer.curs1); edit_move_to_prev_col (edit, 0); edit->force |= REDRAW_PAGE; edit->search_start = 0; edit_update_curs_row (edit); } } /* --------------------------------------------------------------------------------------------- */ /** goto end of text */ static void edit_move_to_bottom (WEdit * edit) { if (edit->buffer.curs_line < edit->buffer.lines) { edit_move_down (edit, edit->buffer.lines - edit->curs_row, FALSE); edit->start_display = edit->buffer.size; edit->start_line = edit->buffer.lines; edit_scroll_upward (edit, WIDGET (edit)->rect.lines - 1); edit->force |= REDRAW_PAGE; } } /* --------------------------------------------------------------------------------------------- */ /** goto beginning of line */ static void edit_cursor_to_bol (WEdit * edit) { off_t b; b = edit_buffer_get_current_bol (&edit->buffer); edit_cursor_move (edit, b - edit->buffer.curs1); edit->search_start = edit->buffer.curs1; edit->prev_col = edit_get_col (edit); edit->over_col = 0; } /* --------------------------------------------------------------------------------------------- */ /** goto end of line */ static void edit_cursor_to_eol (WEdit * edit) { off_t b; b = edit_buffer_get_current_eol (&edit->buffer); edit_cursor_move (edit, b - edit->buffer.curs1); edit->search_start = edit->buffer.curs1; edit->prev_col = edit_get_col (edit); edit->over_col = 0; } /* --------------------------------------------------------------------------------------------- */ static unsigned long my_type_of (int c) { unsigned long r = 0; const char *q; const char chars_move_whole_word[] = "!=&|<>^~ !:;, !'!`!.?!\"!( !) !{ !} !Aa0 !+-*/= |<> ![ !] !\\#! "; if (c == 0) return 0; if (c == '!') return 2; if (g_ascii_isupper ((gchar) c)) c = 'A'; else if (g_ascii_islower ((gchar) c)) c = 'a'; else if (g_ascii_isalpha (c)) c = 'a'; else if (isdigit (c)) c = '0'; else if (isspace (c)) c = ' '; q = strchr (chars_move_whole_word, c); if (q == NULL) return 0xFFFFFFFFUL; do { unsigned long x; const char *p; for (x = 1, p = chars_move_whole_word; p < q; p++) if (*p == '!') x <<= 1; r |= x; } while ((q = strchr (q + 1, c)) != NULL); return r; } /* --------------------------------------------------------------------------------------------- */ static void edit_left_word_move (WEdit * edit, int s) { while (TRUE) { int c1, c2; if (edit->column_highlight && edit->mark1 != edit->mark2 && edit->over_col == 0 && edit->buffer.curs1 == edit_buffer_get_current_bol (&edit->buffer)) break; edit_cursor_move (edit, -1); if (edit->buffer.curs1 == 0) break; c1 = edit_buffer_get_previous_byte (&edit->buffer); if (c1 == '\n') break; c2 = edit_buffer_get_current_byte (&edit->buffer); if (c2 == '\n') break; if ((my_type_of (c1) & my_type_of (c2)) == 0) break; if (isspace (c1) && !isspace (c2)) break; if (s != 0 && !isspace (c1) && isspace (c2)) break; } } /* --------------------------------------------------------------------------------------------- */ static void edit_left_word_move_cmd (WEdit * edit) { edit_left_word_move (edit, 0); edit->force |= REDRAW_PAGE; } /* --------------------------------------------------------------------------------------------- */ static void edit_right_word_move (WEdit * edit, int s) { while (TRUE) { int c1, c2; if (edit->column_highlight && edit->mark1 != edit->mark2 && edit->over_col == 0 && edit->buffer.curs1 == edit_buffer_get_current_eol (&edit->buffer)) break; edit_cursor_move (edit, 1); if (edit->buffer.curs1 >= edit->buffer.size) break; c1 = edit_buffer_get_previous_byte (&edit->buffer); if (c1 == '\n') break; c2 = edit_buffer_get_current_byte (&edit->buffer); if (c2 == '\n') break; if ((my_type_of (c1) & my_type_of (c2)) == 0) break; if (isspace (c1) && !isspace (c2)) break; if (s != 0 && !isspace (c1) && isspace (c2)) break; } } /* --------------------------------------------------------------------------------------------- */ static void edit_right_word_move_cmd (WEdit * edit) { edit_right_word_move (edit, 0); edit->force |= REDRAW_PAGE; } /* --------------------------------------------------------------------------------------------- */ static void edit_right_char_move_cmd (WEdit * edit) { int char_length = 1; int c; #ifdef HAVE_CHARSET if (edit->utf8) { c = edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length); if (char_length < 1) char_length = 1; } else #endif c = edit_buffer_get_current_byte (&edit->buffer); if (edit_options.cursor_beyond_eol && c == '\n') edit->over_col++; else edit_cursor_move (edit, char_length); } /* --------------------------------------------------------------------------------------------- */ static void edit_left_char_move_cmd (WEdit * edit) { int char_length = 1; if (edit->column_highlight && edit_options.cursor_beyond_eol && edit->mark1 != edit->mark2 && edit->over_col == 0 && edit->buffer.curs1 == edit_buffer_get_current_bol (&edit->buffer)) return; #ifdef HAVE_CHARSET if (edit->utf8) { edit_buffer_get_prev_utf (&edit->buffer, edit->buffer.curs1, &char_length); if (char_length < 1) char_length = 1; } #endif if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit->over_col--; else edit_cursor_move (edit, -char_length); } /* --------------------------------------------------------------------------------------------- */ /** Up or down cursor moving. direction = TRUE - move up = FALSE - move down */ static void edit_move_updown (WEdit * edit, long lines, gboolean do_scroll, gboolean direction) { long p; long l = direction ? edit->buffer.curs_line : edit->buffer.lines - edit->buffer.curs_line; if (lines > l) lines = l; if (lines == 0) return; if (lines > 1) edit->force |= REDRAW_PAGE; if (do_scroll) { if (direction) edit_scroll_upward (edit, lines); else edit_scroll_downward (edit, lines); } p = edit_buffer_get_current_bol (&edit->buffer); p = direction ? edit_buffer_get_backward_offset (&edit->buffer, p, lines) : edit_buffer_get_forward_offset (&edit->buffer, p, lines, 0); edit_cursor_move (edit, p - edit->buffer.curs1); edit_move_to_prev_col (edit, p); #ifdef HAVE_CHARSET /* search start of current multibyte char (like CJK) */ if (edit->buffer.curs1 > 0 && edit->buffer.curs1 + 1 < edit->buffer.size && edit_buffer_get_current_byte (&edit->buffer) >= 256) { edit_right_char_move_cmd (edit); edit_left_char_move_cmd (edit); } #endif edit->search_start = edit->buffer.curs1; edit->found_len = 0; } /* --------------------------------------------------------------------------------------------- */ static void edit_right_delete_word (WEdit * edit) { while (edit->buffer.curs1 < edit->buffer.size) { int c1, c2; c1 = edit_delete (edit, TRUE); if (c1 == '\n') break; c2 = edit_buffer_get_current_byte (&edit->buffer); if (c2 == '\n') break; if ((isspace (c1) == 0) != (isspace (c2) == 0)) break; if ((my_type_of (c1) & my_type_of (c2)) == 0) break; } } /* --------------------------------------------------------------------------------------------- */ static void edit_left_delete_word (WEdit * edit) { while (edit->buffer.curs1 > 0) { int c1, c2; c1 = edit_backspace (edit, TRUE); if (c1 == '\n') break; c2 = edit_buffer_get_previous_byte (&edit->buffer); if (c2 == '\n') break; if ((isspace (c1) == 0) != (isspace (c2) == 0)) break; if ((my_type_of (c1) & my_type_of (c2)) == 0) break; } } /* --------------------------------------------------------------------------------------------- */ /** the start column position is not recorded, and hence does not undo as it happed. But who would notice. */ static void edit_do_undo (WEdit * edit) { long ac; long count = 0; edit->undo_stack_disable = 1; /* don't record undo's onto undo stack! */ edit->over_col = 0; while ((ac = edit_pop_undo_action (edit)) < KEY_PRESS) { off_t b; switch ((int) ac) { case STACK_BOTTOM: goto done_undo; case CURS_RIGHT: edit_cursor_move (edit, 1); break; case CURS_LEFT: edit_cursor_move (edit, -1); break; case BACKSPACE: case BACKSPACE_BR: edit_backspace (edit, TRUE); break; case DELCHAR: case DELCHAR_BR: edit_delete (edit, TRUE); break; case COLUMN_ON: edit->column_highlight = 1; break; case COLUMN_OFF: edit->column_highlight = 0; break; default: break; } if (ac >= 256 && ac < 512) edit_insert_ahead (edit, ac - 256); if (ac >= 0 && ac < 256) edit_insert (edit, ac); if (ac >= MARK_1 - 2 && ac < MARK_2 - 2) { edit->mark1 = ac - MARK_1; b = edit_buffer_get_bol (&edit->buffer, edit->mark1); edit->column1 = (long) edit_move_forward3 (edit, b, 0, edit->mark1); } if (ac >= MARK_2 - 2 && ac < MARK_CURS - 2) { edit->mark2 = ac - MARK_2; b = edit_buffer_get_bol (&edit->buffer, edit->mark2); edit->column2 = (long) edit_move_forward3 (edit, b, 0, edit->mark2); } else if (ac >= MARK_CURS - 2 && ac < KEY_PRESS) { edit->end_mark_curs = ac - MARK_CURS; } if (count++) edit->force |= REDRAW_PAGE; /* more than one pop usually means something big */ } if (edit->start_display > ac - KEY_PRESS) { edit->start_line -= edit_buffer_count_lines (&edit->buffer, ac - KEY_PRESS, edit->start_display); edit->force |= REDRAW_PAGE; } else if (edit->start_display < ac - KEY_PRESS) { edit->start_line += edit_buffer_count_lines (&edit->buffer, edit->start_display, ac - KEY_PRESS); edit->force |= REDRAW_PAGE; } edit->start_display = ac - KEY_PRESS; /* see push and pop above */ edit_update_curs_row (edit); done_undo: edit->undo_stack_disable = 0; } /* --------------------------------------------------------------------------------------------- */ static void edit_do_redo (WEdit * edit) { long ac; long count = 0; if (edit->redo_stack_reset) return; edit->over_col = 0; while ((ac = edit_pop_redo_action (edit)) < KEY_PRESS) { off_t b; switch ((int) ac) { case STACK_BOTTOM: goto done_redo; case CURS_RIGHT: edit_cursor_move (edit, 1); break; case CURS_LEFT: edit_cursor_move (edit, -1); break; case BACKSPACE: edit_backspace (edit, TRUE); break; case DELCHAR: edit_delete (edit, TRUE); break; case COLUMN_ON: edit->column_highlight = 1; break; case COLUMN_OFF: edit->column_highlight = 0; break; default: break; } if (ac >= 256 && ac < 512) edit_insert_ahead (edit, ac - 256); if (ac >= 0 && ac < 256) edit_insert (edit, ac); if (ac >= MARK_1 - 2 && ac < MARK_2 - 2) { edit->mark1 = ac - MARK_1; b = edit_buffer_get_bol (&edit->buffer, edit->mark1); edit->column1 = (long) edit_move_forward3 (edit, b, 0, edit->mark1); } else if (ac >= MARK_2 - 2 && ac < KEY_PRESS) { edit->mark2 = ac - MARK_2; b = edit_buffer_get_bol (&edit->buffer, edit->mark2); edit->column2 = (long) edit_move_forward3 (edit, b, 0, edit->mark2); } /* more than one pop usually means something big */ if (count++ != 0) edit->force |= REDRAW_PAGE; } if (edit->start_display > ac - KEY_PRESS) { edit->start_line -= edit_buffer_count_lines (&edit->buffer, ac - KEY_PRESS, edit->start_display); edit->force |= REDRAW_PAGE; } else if (edit->start_display < ac - KEY_PRESS) { edit->start_line += edit_buffer_count_lines (&edit->buffer, edit->start_display, ac - KEY_PRESS); edit->force |= REDRAW_PAGE; } edit->start_display = ac - KEY_PRESS; /* see push and pop above */ edit_update_curs_row (edit); done_redo: ; } /* --------------------------------------------------------------------------------------------- */ static void edit_group_undo (WEdit * edit) { long ac = KEY_PRESS; long cur_ac = KEY_PRESS; while (ac != STACK_BOTTOM && ac == cur_ac) { cur_ac = get_prev_undo_action (edit); edit_do_undo (edit); ac = get_prev_undo_action (edit); /* exit from cycle if edit_options.group_undo is not set, * and make single UNDO operation */ if (!edit_options.group_undo) ac = STACK_BOTTOM; } } /* --------------------------------------------------------------------------------------------- */ static void edit_delete_to_line_end (WEdit * edit) { while (edit_buffer_get_current_byte (&edit->buffer) != '\n' && edit->buffer.curs2 != 0) edit_delete (edit, TRUE); } /* --------------------------------------------------------------------------------------------- */ static void edit_delete_to_line_begin (WEdit * edit) { while (edit_buffer_get_previous_byte (&edit->buffer) != '\n' && edit->buffer.curs1 != 0) edit_backspace (edit, TRUE); } /* --------------------------------------------------------------------------------------------- */ static gboolean is_aligned_on_a_tab (WEdit * edit) { long curs_col; edit_update_curs_col (edit); curs_col = edit->curs_col % (TAB_SIZE * space_width); return (curs_col == 0 || curs_col == (HALF_TAB_SIZE * space_width)); } /* --------------------------------------------------------------------------------------------- */ static gboolean right_of_four_spaces (WEdit * edit) { int i; int ch = 0; for (i = 1; i <= HALF_TAB_SIZE; i++) ch |= edit_buffer_get_byte (&edit->buffer, edit->buffer.curs1 - i); return (ch == ' ' && is_aligned_on_a_tab (edit)); } /* --------------------------------------------------------------------------------------------- */ static gboolean left_of_four_spaces (WEdit * edit) { int i, ch = 0; for (i = 0; i < HALF_TAB_SIZE; i++) ch |= edit_buffer_get_byte (&edit->buffer, edit->buffer.curs1 + i); return (ch == ' ' && is_aligned_on_a_tab (edit)); } /* --------------------------------------------------------------------------------------------- */ static void edit_auto_indent (WEdit * edit) { off_t p; p = edit->buffer.curs1; /* use the previous line as a template */ p = edit_buffer_get_backward_offset (&edit->buffer, p, 1); /* copy the leading whitespace of the line */ while (TRUE) { /* no range check - the line _is_ \n-terminated */ char c; c = edit_buffer_get_byte (&edit->buffer, p++); if (!whitespace (c)) break; edit_insert (edit, c); } } /* --------------------------------------------------------------------------------------------- */ static inline void edit_double_newline (WEdit * edit) { edit_insert (edit, '\n'); if (edit_buffer_get_current_byte (&edit->buffer) == '\n' || edit_buffer_get_byte (&edit->buffer, edit->buffer.curs1 - 2) == '\n') return; edit->force |= REDRAW_PAGE; edit_insert (edit, '\n'); } /* --------------------------------------------------------------------------------------------- */ static void insert_spaces_tab (WEdit * edit, gboolean half) { long i; edit_update_curs_col (edit); i = TAB_SIZE * space_width; if (half) i /= 2; if (i != 0) for (i = ((edit->curs_col / i) + 1) * i - edit->curs_col; i > 0; i -= space_width) edit_insert (edit, ' '); } /* --------------------------------------------------------------------------------------------- */ static inline void edit_tab_cmd (WEdit * edit) { if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)) { /* insert a half tab (usually four spaces) unless there is a half tab already behind, then delete it and insert a full tab. */ if (edit_options.fill_tabs_with_spaces || !right_of_four_spaces (edit)) insert_spaces_tab (edit, TRUE); else { int i; for (i = 1; i <= HALF_TAB_SIZE; i++) edit_backspace (edit, TRUE); edit_insert (edit, '\t'); } } else if (edit_options.fill_tabs_with_spaces) insert_spaces_tab (edit, FALSE); else edit_insert (edit, '\t'); } /* --------------------------------------------------------------------------------------------- */ static void check_and_wrap_line (WEdit * edit) { off_t curs; if (!edit_options.typewriter_wrap) return; edit_update_curs_col (edit); if (edit->curs_col < edit_options.word_wrap_line_length) return; curs = edit->buffer.curs1; while (TRUE) { int c; curs--; c = edit_buffer_get_byte (&edit->buffer, curs); if (c == '\n' || curs <= 0) { edit_insert (edit, '\n'); return; } if (whitespace (c)) { off_t current = edit->buffer.curs1; edit_cursor_move (edit, curs - edit->buffer.curs1 + 1); edit_insert (edit, '\n'); edit_cursor_move (edit, current - edit->buffer.curs1 + 1); return; } } } /* --------------------------------------------------------------------------------------------- */ /** this find the matching bracket in either direction, and sets edit->bracket * * @param edit editor object * @param in_screen search only on the current screen * @param furthest_bracket_search count of the bytes for search * * @return position of the found bracket (-1 if no match) */ static off_t edit_get_bracket (WEdit * edit, gboolean in_screen, unsigned long furthest_bracket_search) { const char *const b = "{}{[][()(", *p; int i = 1, inc = -1, c, d, n = 0; unsigned long j = 0; off_t q; edit_update_curs_row (edit); c = edit_buffer_get_current_byte (&edit->buffer); p = strchr (b, c); /* not on a bracket at all */ if (p == NULL || *p == '\0') return -1; /* the matching bracket */ d = p[1]; /* going left or right? */ if (strchr ("{[(", c) != NULL) inc = 1; /* no limit */ if (furthest_bracket_search == 0) furthest_bracket_search--; /* ULONG_MAX */ for (q = edit->buffer.curs1 + inc;; q += inc) { int a; /* out of buffer? */ if (q >= edit->buffer.size || q < 0) break; a = edit_buffer_get_byte (&edit->buffer, q); /* don't want to eat CPU */ if (j++ > furthest_bracket_search) break; /* out of screen? */ if (in_screen) { if (q < edit->start_display) break; /* count lines if searching downward */ if (inc > 0 && a == '\n') if (n++ >= WIDGET (edit)->rect.lines - edit->curs_row) /* out of screen */ break; } /* count bracket depth */ i += (a == c) - (a == d); /* return if bracket depth is zero */ if (i == 0) return q; } /* no match */ return -1; } /* --------------------------------------------------------------------------------------------- */ static inline void edit_goto_matching_bracket (WEdit * edit) { off_t q; q = edit_get_bracket (edit, 0, 0); if (q >= 0) { edit->bracket = edit->buffer.curs1; edit->force |= REDRAW_PAGE; edit_cursor_move (edit, q - edit->buffer.curs1); } } /* --------------------------------------------------------------------------------------------- */ static void edit_move_block_to_right (WEdit * edit) { off_t start_mark, end_mark; long cur_bol, start_bol; if (!eval_marks (edit, &start_mark, &end_mark)) return; start_bol = edit_buffer_get_bol (&edit->buffer, start_mark); cur_bol = edit_buffer_get_bol (&edit->buffer, end_mark - 1); do { off_t b; edit_cursor_move (edit, cur_bol - edit->buffer.curs1); if (!edit_line_is_blank (edit, edit->buffer.curs_line)) { if (edit_options.fill_tabs_with_spaces) insert_spaces_tab (edit, edit_options.fake_half_tabs); else edit_insert (edit, '\t'); b = edit_buffer_get_bol (&edit->buffer, cur_bol); edit_cursor_move (edit, b - edit->buffer.curs1); } if (cur_bol == 0) break; cur_bol = edit_buffer_get_bol (&edit->buffer, cur_bol - 1); } while (cur_bol >= start_bol); edit->force |= REDRAW_PAGE; } /* --------------------------------------------------------------------------------------------- */ static void edit_move_block_to_left (WEdit * edit) { off_t start_mark, end_mark; off_t cur_bol, start_bol; if (!eval_marks (edit, &start_mark, &end_mark)) return; start_bol = edit_buffer_get_bol (&edit->buffer, start_mark); cur_bol = edit_buffer_get_bol (&edit->buffer, end_mark - 1); do { int del_tab_width; int next_char; edit_cursor_move (edit, cur_bol - edit->buffer.curs1); del_tab_width = edit_options.fake_half_tabs ? HALF_TAB_SIZE : TAB_SIZE; next_char = edit_buffer_get_current_byte (&edit->buffer); if (next_char == '\t') edit_delete (edit, TRUE); else if (next_char == ' ') { int i; for (i = 0; i < del_tab_width; i++) { if (next_char == ' ') edit_delete (edit, TRUE); next_char = edit_buffer_get_current_byte (&edit->buffer); } } if (cur_bol == 0) break; cur_bol = edit_buffer_get_bol (&edit->buffer, cur_bol - 1); } while (cur_bol >= start_bol); edit->force |= REDRAW_PAGE; } /* --------------------------------------------------------------------------------------------- */ /** * prints at the cursor * @return number of chars printed */ static size_t edit_print_string (WEdit * e, const char *s) { size_t i; for (i = 0; s[i] != '\0'; i++) edit_execute_cmd (e, CK_InsertChar, (unsigned char) s[i]); e->force |= REDRAW_COMPLETELY; edit_update_screen (e); return i; } /* --------------------------------------------------------------------------------------------- */ static off_t edit_insert_column_from_file (WEdit * edit, int file, off_t * start_pos, off_t * end_pos, long *col1, long *col2) { off_t cursor; long col; off_t blocklen = -1, width = 0; unsigned char *data; cursor = edit->buffer.curs1; col = edit_get_col (edit); data = g_malloc0 (TEMP_BUF_LEN); while ((blocklen = mc_read (file, (char *) data, TEMP_BUF_LEN)) > 0) { off_t i; char *pn; pn = strchr ((char *) data, '\n'); width = pn == NULL ? blocklen : pn - (char *) data; for (i = 0; i < blocklen; i++) { if (data[i] != '\n') edit_insert (edit, data[i]); else { /* fill in and move to next line */ long l; off_t p; if (edit_buffer_get_current_byte (&edit->buffer) != '\n') for (l = width - (edit_get_col (edit) - col); l > 0; l -= space_width) edit_insert (edit, ' '); for (p = edit->buffer.curs1;; p++) { if (p == edit->buffer.size) { edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1); edit_insert_ahead (edit, '\n'); p++; break; } if (edit_buffer_get_byte (&edit->buffer, p) == '\n') { p++; break; } } edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1); for (l = col - edit_get_col (edit); l >= space_width; l -= space_width) edit_insert (edit, ' '); } } } *col1 = col; *col2 = col + width; *start_pos = cursor; *end_pos = edit->buffer.curs1; edit_cursor_move (edit, cursor - edit->buffer.curs1); g_free (data); return blocklen; } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /** User edit menu, like user menu (F2) but only in editor. */ void edit_user_menu (WEdit * edit, const char *menu_file, int selected_entry) { char *block_file; gboolean mark; off_t curs; off_t start_mark, end_mark; struct stat status; vfs_path_t *block_file_vpath; block_file = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE); block_file_vpath = vfs_path_from_str (block_file); curs = edit->buffer.curs1; mark = eval_marks (edit, &start_mark, &end_mark); if (mark) edit_save_block (edit, block_file, start_mark, end_mark); /* run shell scripts from menu */ if (user_menu_cmd (CONST_WIDGET (edit), menu_file, selected_entry) && (mc_stat (block_file_vpath, &status) == 0) && (status.st_size != 0)) { gboolean rc = FALSE; FILE *fd; /* i.e. we have marked block */ if (mark) rc = edit_block_delete_cmd (edit); if (!rc) { off_t ins_len; ins_len = edit_insert_file (edit, block_file_vpath); if (mark && ins_len > 0) edit_set_markers (edit, start_mark, start_mark + ins_len, 0, 0); } /* truncate block file */ fd = fopen (block_file, "w"); if (fd != NULL) fclose (fd); } g_free (block_file); vfs_path_free (block_file_vpath, TRUE); edit_cursor_move (edit, curs - edit->buffer.curs1); edit->force |= REDRAW_PAGE; widget_draw (WIDGET (edit)); } /* --------------------------------------------------------------------------------------------- */ char * edit_get_write_filter (const vfs_path_t * write_name_vpath, const vfs_path_t * filename_vpath) { int i; const char *write_name; char *write_name_quoted; char *p = NULL; i = edit_find_filter (filename_vpath); if (i < 0) return NULL; write_name = vfs_path_get_last_path_str (write_name_vpath); write_name_quoted = name_quote (write_name, FALSE); if (write_name_quoted != NULL) { p = g_strdup_printf (all_filters[i].write, write_name_quoted); g_free (write_name_quoted); } return p; } /* --------------------------------------------------------------------------------------------- */ /** * @param edit editor object * @param f value of stream file * @return the length of the file */ off_t edit_write_stream (WEdit * edit, FILE * f) { long i; if (edit->lb == LB_ASIS) { for (i = 0; i < edit->buffer.size; i++) if (fputc (edit_buffer_get_byte (&edit->buffer, i), f) < 0) break; return i; } /* change line breaks */ for (i = 0; i < edit->buffer.size; i++) { unsigned char c; c = edit_buffer_get_byte (&edit->buffer, i); if (!(c == '\n' || c == '\r')) { /* not line break */ if (fputc (c, f) < 0) return i; } else { /* (c == '\n' || c == '\r') */ unsigned char c1; c1 = edit_buffer_get_byte (&edit->buffer, i + 1); /* next char */ switch (edit->lb) { case LB_UNIX: /* replace "\r\n" or '\r' to '\n' */ /* put one line break unconditionally */ if (fputc ('\n', f) < 0) return i; i++; /* 2 chars are processed */ if (c == '\r' && c1 == '\n') /* Windows line break; go to the next char */ break; if (c == '\r' && c1 == '\r') { /* two Macintosh line breaks; put second line break */ if (fputc ('\n', f) < 0) return i; break; } if (fputc (c1, f) < 0) return i; break; case LB_WIN: /* replace '\n' or '\r' to "\r\n" */ /* put one line break unconditionally */ if (fputc ('\r', f) < 0 || fputc ('\n', f) < 0) return i; if (c == '\r' && c1 == '\n') /* Windows line break; go to the next char */ i++; break; case LB_MAC: /* replace "\r\n" or '\n' to '\r' */ /* put one line break unconditionally */ if (fputc ('\r', f) < 0) return i; i++; /* 2 chars are processed */ if (c == '\r' && c1 == '\n') /* Windows line break; go to the next char */ break; if (c == '\n' && c1 == '\n') { /* two Windows line breaks; put second line break */ if (fputc ('\r', f) < 0) return i; break; } if (fputc (c1, f) < 0) return i; break; case LB_ASIS: /* default without changes */ default: break; } } } return edit->buffer.size; } /* --------------------------------------------------------------------------------------------- */ gboolean is_break_char (char c) { return (isspace (c) || strchr ("{}[]()<>=|/\\!?~-+`'\",.;:#$%^&*", c) != NULL); } /* --------------------------------------------------------------------------------------------- */ /** inserts a file at the cursor, returns count of inserted bytes on success */ off_t edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath) { char *p; off_t current; off_t ins_len = 0; p = edit_get_filter (filename_vpath); current = edit->buffer.curs1; if (p != NULL) { FILE *f; f = (FILE *) popen (p, "r"); if (f != NULL) { edit_insert_stream (edit, f); /* Place cursor at the end of text selection */ if (!edit_options.cursor_after_inserted_block) { ins_len = edit->buffer.curs1 - current; edit_cursor_move (edit, -ins_len); } if (pclose (f) > 0) { char *errmsg; errmsg = g_strdup_printf (_("Error reading from pipe: %s"), p); edit_error_dialog (_("Error"), errmsg); g_free (errmsg); ins_len = -1; } } else { char *errmsg; errmsg = g_strdup_printf (_("Cannot open pipe for reading: %s"), p); edit_error_dialog (_("Error"), errmsg); g_free (errmsg); ins_len = -1; } g_free (p); } else { int file; off_t blocklen; gboolean vertical_insertion = FALSE; char *buf; file = mc_open (filename_vpath, O_RDONLY | O_BINARY); if (file == -1) return -1; buf = g_malloc0 (TEMP_BUF_LEN); blocklen = mc_read (file, buf, sizeof (VERTICAL_MAGIC)); if (blocklen > 0) { /* if contain signature VERTICAL_MAGIC then it vertical block */ if (memcmp (buf, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC)) == 0) vertical_insertion = TRUE; else mc_lseek (file, 0, SEEK_SET); } if (vertical_insertion) { off_t mark1, mark2; long c1, c2; blocklen = edit_insert_column_from_file (edit, file, &mark1, &mark2, &c1, &c2); edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2); /* highlight inserted text then not persistent blocks */ if (!edit_options.persistent_selections && edit->modified) { if (!edit->column_highlight) edit_push_undo_action (edit, COLUMN_OFF); edit->column_highlight = 1; } } else { off_t i; while ((blocklen = mc_read (file, (char *) buf, TEMP_BUF_LEN)) > 0) { for (i = 0; i < blocklen; i++) edit_insert (edit, buf[i]); } /* highlight inserted text then not persistent blocks */ if (!edit_options.persistent_selections && edit->modified) { edit_set_markers (edit, edit->buffer.curs1, current, 0, 0); if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; } /* Place cursor at the end of text selection */ if (!edit_options.cursor_after_inserted_block) { ins_len = edit->buffer.curs1 - current; edit_cursor_move (edit, -ins_len); } } edit->force |= REDRAW_PAGE; g_free (buf); mc_close (file); if (blocklen != 0) ins_len = 0; } return ins_len; } /* --------------------------------------------------------------------------------------------- */ /** * Fill in the edit structure. Return NULL on failure. Pass edit as * NULL to allocate a new structure. * * If line is 0, try to restore saved position. Otherwise put the * cursor on that line and show it in the middle of the screen. */ WEdit * edit_init (WEdit * edit, const WRect * r, const vfs_path_t * filename_vpath, long line) { gboolean to_free = FALSE; auto_syntax = TRUE; /* Resetting to auto on every invocation */ edit_options.line_state_width = edit_options.line_state ? LINE_STATE_WIDTH : 0; if (edit != NULL) { gboolean fullscreen; WRect loc_prev; /* save some widget parameters */ fullscreen = edit->fullscreen; loc_prev = edit->loc_prev; edit_purge_widget (edit); /* restore saved parameters */ edit->fullscreen = fullscreen; edit->loc_prev = loc_prev; } else { Widget *w; edit = g_malloc0 (sizeof (WEdit)); to_free = TRUE; w = WIDGET (edit); widget_init (w, r, NULL, NULL); w->options |= WOP_SELECTABLE | WOP_TOP_SELECT | WOP_WANT_CURSOR; w->keymap = editor_map; w->ext_keymap = editor_x_map; edit->fullscreen = TRUE; edit_save_size (edit); } edit->drag_state = MCEDIT_DRAG_NONE; edit->stat1.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; edit->stat1.st_uid = getuid (); edit->stat1.st_gid = getgid (); edit->stat1.st_mtime = 0; edit->over_col = 0; edit->bracket = -1; edit->last_bracket = -1; edit->force |= REDRAW_PAGE; /* set file name before load file */ edit_set_filename (edit, filename_vpath); edit->undo_stack_size = START_STACK_SIZE; edit->undo_stack_size_mask = START_STACK_SIZE - 1; edit->undo_stack = g_malloc0 ((edit->undo_stack_size + 10) * sizeof (long)); edit->redo_stack_size = START_STACK_SIZE; edit->redo_stack_size_mask = START_STACK_SIZE - 1; edit->redo_stack = g_malloc0 ((edit->redo_stack_size + 10) * sizeof (long)); #ifdef HAVE_CHARSET edit->utf8 = FALSE; edit->converter = str_cnv_from_term; edit_set_codeset (edit); #endif if (!edit_load_file (edit)) { /* edit_load_file already gives an error message */ if (to_free) g_free (edit); return NULL; } edit->loading_done = 1; edit->modified = 0; edit->locked = 0; edit_load_syntax (edit, NULL, NULL); edit_get_syntax_color (edit, -1); /* load saved cursor position and/or boolmarks */ if ((line == 0) && edit_options.save_position) edit_load_position (edit, TRUE); else { edit_load_position (edit, FALSE); if (line <= 0) line = 1; edit_move_display (edit, line - 1); edit_move_to_line (edit, line - 1); } edit_load_macro_cmd (edit); return edit; } /* --------------------------------------------------------------------------------------------- */ /** Clear the edit struct, freeing everything in it. Return TRUE on success */ gboolean edit_clean (WEdit * edit) { if (edit == NULL) return FALSE; /* a stale lock, remove it */ if (edit->locked) (void) unlock_file (edit->filename_vpath); /* save cursor position */ if (edit_options.save_position) edit_save_position (edit); else if (edit->serialized_bookmarks != NULL) g_array_free (edit->serialized_bookmarks, TRUE); /* File specified on the mcedit command line and never saved */ if (edit->delete_file) unlink (vfs_path_get_last_path_str (edit->filename_vpath)); edit_free_syntax_rules (edit); book_mark_flush (edit, -1); edit_buffer_clean (&edit->buffer); g_free (edit->undo_stack); g_free (edit->redo_stack); vfs_path_free (edit->filename_vpath, TRUE); vfs_path_free (edit->dir_vpath, TRUE); edit_search_deinit (edit); #ifdef HAVE_CHARSET if (edit->converter != str_cnv_from_term) str_close_conv (edit->converter); #endif edit_purge_widget (edit); return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** * Load a new file into the editor and set line. If it fails, preserve the old file. * To do it, allocate a new widget, initialize it and, if the new file * was loaded, copy the data to the old widget. * * @return TRUE on success, FALSE on failure. */ gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line) { Widget *w = WIDGET (edit); WEdit *e; e = g_malloc0 (sizeof (WEdit)); *WIDGET (e) = *w; /* save some widget parameters */ e->fullscreen = edit->fullscreen; e->loc_prev = edit->loc_prev; if (edit_init (e, &w->rect, filename_vpath, line) == NULL) { g_free (e); return FALSE; } edit_clean (edit); memcpy (edit, e, sizeof (*edit)); g_free (e); return TRUE; } /* --------------------------------------------------------------------------------------------- */ #ifdef HAVE_CHARSET void edit_set_codeset (WEdit * edit) { const char *cp_id; cp_id = get_codepage_id (mc_global.source_codepage >= 0 ? mc_global.source_codepage : mc_global.display_codepage); if (cp_id != NULL) { GIConv conv; conv = str_crt_conv_from (cp_id); if (conv != INVALID_CONV) { if (edit->converter != str_cnv_from_term) str_close_conv (edit->converter); edit->converter = conv; } } if (cp_id != NULL) edit->utf8 = str_isutf8 (cp_id); } #endif /* --------------------------------------------------------------------------------------------- */ /** * Recording stack for undo: * The following is an implementation of a compressed stack. Identical * pushes are recorded by a negative prefix indicating the number of times the * same char was pushed. This saves space for repeated curs-left or curs-right * delete etc. * * eg: * * pushed: stored: * * a * b a * b -3 * b b * c --> -4 * c c * c d * c * d * * If the stack long int is 0-255 it represents a normal insert (from a backspace), * 256-512 is an insert ahead (from a delete), If it is between 600 and 700 it is one * of the cursor functions define'd in edit-impl.h. 1000 through 700'000'000 is to * set edit->mark1 position. 700'000'000 through 1400'000'000 is to set edit->mark2 * position. * * The only way the cursor moves or the buffer is changed is through the routines: * insert, backspace, insert_ahead, delete, and cursor_move. * These record the reverse undo movements onto the stack each time they are * called. * * Each key press results in a set of actions (insert; delete ...). So each time * a key is pressed the current position of start_display is pushed as * KEY_PRESS + start_display. Then for undoing, we pop until we get to a number * over KEY_PRESS. We then assign this number less KEY_PRESS to start_display. So undo * tracks scrolling and key actions exactly. (KEY_PRESS is about (2^31) * (2/3) = 1400'000'000) * * * * @param edit editor object * @param c code of the action */ void edit_push_undo_action (WEdit * edit, long c) { unsigned long sp = edit->undo_stack_pointer; unsigned long spm1; /* first enlarge the stack if necessary */ if (sp > edit->undo_stack_size - 10) { /* say */ if (max_undo < 256) max_undo = 256; if (edit->undo_stack_size < (unsigned long) max_undo) { long *t; t = g_realloc (edit->undo_stack, (edit->undo_stack_size * 2 + 10) * sizeof (long)); if (t != NULL) { edit->undo_stack = t; edit->undo_stack_size <<= 1; edit->undo_stack_size_mask = edit->undo_stack_size - 1; } } } spm1 = (edit->undo_stack_pointer - 1) & edit->undo_stack_size_mask; if (edit->undo_stack_disable) { edit_push_redo_action (edit, KEY_PRESS); edit_push_redo_action (edit, c); return; } if (edit->redo_stack_reset) edit->redo_stack_bottom = edit->redo_stack_pointer = 0; if (edit->undo_stack_bottom != sp && spm1 != edit->undo_stack_bottom && ((sp - 2) & edit->undo_stack_size_mask) != edit->undo_stack_bottom) { long d; if (edit->undo_stack[spm1] < 0) { d = edit->undo_stack[(sp - 2) & edit->undo_stack_size_mask]; if (d == c && edit->undo_stack[spm1] > -1000000000) { if (c < KEY_PRESS) /* --> no need to push multiple do-nothings */ edit->undo_stack[spm1]--; return; } } else { d = edit->undo_stack[spm1]; if (d == c) { if (c >= KEY_PRESS) return; /* --> no need to push multiple do-nothings */ edit->undo_stack[sp] = -2; goto check_bottom; } } } edit->undo_stack[sp] = c; check_bottom: edit->undo_stack_pointer = (edit->undo_stack_pointer + 1) & edit->undo_stack_size_mask; /* if the sp wraps round and catches the undo_stack_bottom then erase * the first set of actions on the stack to make space - by moving * undo_stack_bottom forward one "key press" */ c = (edit->undo_stack_pointer + 2) & edit->undo_stack_size_mask; if ((unsigned long) c == edit->undo_stack_bottom || (((unsigned long) c + 1) & edit->undo_stack_size_mask) == edit->undo_stack_bottom) do { edit->undo_stack_bottom = (edit->undo_stack_bottom + 1) & edit->undo_stack_size_mask; } while (edit->undo_stack[edit->undo_stack_bottom] < KEY_PRESS && edit->undo_stack_bottom != edit->undo_stack_pointer); /*If a single key produced enough pushes to wrap all the way round then we would notice that the [undo_stack_bottom] does not contain KEY_PRESS. The stack is then initialised: */ if (edit->undo_stack_pointer != edit->undo_stack_bottom && edit->undo_stack[edit->undo_stack_bottom] < KEY_PRESS) { edit->undo_stack_bottom = edit->undo_stack_pointer = 0; } } /* --------------------------------------------------------------------------------------------- */ void edit_push_redo_action (WEdit * edit, long c) { unsigned long sp = edit->redo_stack_pointer; unsigned long spm1; /* first enlarge the stack if necessary */ if (sp > edit->redo_stack_size - 10) { /* say */ if (max_undo < 256) max_undo = 256; if (edit->redo_stack_size < (unsigned long) max_undo) { long *t; t = g_realloc (edit->redo_stack, (edit->redo_stack_size * 2 + 10) * sizeof (long)); if (t != NULL) { edit->redo_stack = t; edit->redo_stack_size <<= 1; edit->redo_stack_size_mask = edit->redo_stack_size - 1; } } } spm1 = (edit->redo_stack_pointer - 1) & edit->redo_stack_size_mask; if (edit->redo_stack_bottom != sp && spm1 != edit->redo_stack_bottom && ((sp - 2) & edit->redo_stack_size_mask) != edit->redo_stack_bottom) { long d; if (edit->redo_stack[spm1] < 0) { d = edit->redo_stack[(sp - 2) & edit->redo_stack_size_mask]; if (d == c && edit->redo_stack[spm1] > -1000000000) { if (c < KEY_PRESS) /* --> no need to push multiple do-nothings */ edit->redo_stack[spm1]--; return; } } else { d = edit->redo_stack[spm1]; if (d == c) { if (c >= KEY_PRESS) return; /* --> no need to push multiple do-nothings */ edit->redo_stack[sp] = -2; goto redo_check_bottom; } } } edit->redo_stack[sp] = c; redo_check_bottom: edit->redo_stack_pointer = (edit->redo_stack_pointer + 1) & edit->redo_stack_size_mask; /* if the sp wraps round and catches the redo_stack_bottom then erase * the first set of actions on the stack to make space - by moving * redo_stack_bottom forward one "key press" */ c = (edit->redo_stack_pointer + 2) & edit->redo_stack_size_mask; if ((unsigned long) c == edit->redo_stack_bottom || (((unsigned long) c + 1) & edit->redo_stack_size_mask) == edit->redo_stack_bottom) do { edit->redo_stack_bottom = (edit->redo_stack_bottom + 1) & edit->redo_stack_size_mask; } while (edit->redo_stack[edit->redo_stack_bottom] < KEY_PRESS && edit->redo_stack_bottom != edit->redo_stack_pointer); /* * If a single key produced enough pushes to wrap all the way round then * we would notice that the [redo_stack_bottom] does not contain KEY_PRESS. * The stack is then initialised: */ if (edit->redo_stack_pointer != edit->redo_stack_bottom && edit->redo_stack[edit->redo_stack_bottom] < KEY_PRESS) edit->redo_stack_bottom = edit->redo_stack_pointer = 0; } /* --------------------------------------------------------------------------------------------- */ /** Basic low level single character buffer alterations and movements at the cursor. */ void edit_insert (WEdit * edit, int c) { /* first we must update the position of the display window */ if (edit->buffer.curs1 < edit->start_display) { edit->start_display++; if (c == '\n') edit->start_line++; } /* Mark file as modified, unless the file hasn't been fully loaded */ if (edit->loading_done) edit_modification (edit); /* now we must update some info on the file and check if a redraw is required */ if (c == '\n') { book_mark_inc (edit, edit->buffer.curs_line); edit->buffer.curs_line++; edit->buffer.lines++; edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR; } /* save the reverse command onto the undo stack */ /* ordinary char and not space */ if (c > 32) edit_push_undo_action (edit, BACKSPACE); else edit_push_undo_action (edit, BACKSPACE_BR); /* update markers */ edit->mark1 += (edit->mark1 > edit->buffer.curs1) ? 1 : 0; edit->mark2 += (edit->mark2 > edit->buffer.curs1) ? 1 : 0; edit->last_get_rule += (edit->last_get_rule > edit->buffer.curs1) ? 1 : 0; edit_buffer_insert (&edit->buffer, c); } /* --------------------------------------------------------------------------------------------- */ /** same as edit_insert and move left */ void edit_insert_ahead (WEdit * edit, int c) { if (edit->buffer.curs1 < edit->start_display) { edit->start_display++; if (c == '\n') edit->start_line++; } edit_modification (edit); if (c == '\n') { book_mark_inc (edit, edit->buffer.curs_line); edit->buffer.lines++; edit->force |= REDRAW_AFTER_CURSOR; } /* ordinary char and not space */ if (c > 32) edit_push_undo_action (edit, DELCHAR); else edit_push_undo_action (edit, DELCHAR_BR); edit->mark1 += (edit->mark1 >= edit->buffer.curs1) ? 1 : 0; edit->mark2 += (edit->mark2 >= edit->buffer.curs1) ? 1 : 0; edit->last_get_rule += (edit->last_get_rule >= edit->buffer.curs1) ? 1 : 0; edit_buffer_insert_ahead (&edit->buffer, c); } /* --------------------------------------------------------------------------------------------- */ void edit_insert_over (WEdit * edit) { long i; for (i = 0; i < edit->over_col; i++) edit_insert (edit, ' '); edit->over_col = 0; } /* --------------------------------------------------------------------------------------------- */ int edit_delete (WEdit * edit, gboolean byte_delete) { int p = 0; int char_length = 1; int i; if (edit->buffer.curs2 == 0) return 0; #ifdef HAVE_CHARSET /* if byte_delete == TRUE then delete only one byte not multibyte char */ if (edit->utf8 && !byte_delete) { edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &char_length); if (char_length < 1) char_length = 1; } #else (void) byte_delete; #endif if (edit->mark2 != edit->mark1) edit_push_markers (edit); for (i = 1; i <= char_length; i++) { if (edit->mark1 > edit->buffer.curs1) { edit->mark1--; edit->end_mark_curs--; } if (edit->mark2 > edit->buffer.curs1) edit->mark2--; if (edit->last_get_rule > edit->buffer.curs1) edit->last_get_rule--; p = edit_buffer_delete (&edit->buffer); edit_push_undo_action (edit, p + 256); } edit_modification (edit); if (p == '\n') { book_mark_dec (edit, edit->buffer.curs_line); edit->buffer.lines--; edit->force |= REDRAW_AFTER_CURSOR; } if (edit->buffer.curs1 < edit->start_display) { edit->start_display--; if (p == '\n') edit->start_line--; } return p; } /* --------------------------------------------------------------------------------------------- */ int edit_backspace (WEdit * edit, gboolean byte_delete) { int p = 0; int char_length = 1; int i; if (edit->buffer.curs1 == 0) return 0; if (edit->mark2 != edit->mark1) edit_push_markers (edit); #ifdef HAVE_CHARSET if (edit->utf8 && !byte_delete) { edit_buffer_get_prev_utf (&edit->buffer, edit->buffer.curs1, &char_length); if (char_length < 1) char_length = 1; } #else (void) byte_delete; #endif for (i = 1; i <= char_length; i++) { if (edit->mark1 >= edit->buffer.curs1) { edit->mark1--; edit->end_mark_curs--; } if (edit->mark2 >= edit->buffer.curs1) edit->mark2--; if (edit->last_get_rule >= edit->buffer.curs1) edit->last_get_rule--; p = edit_buffer_backspace (&edit->buffer); edit_push_undo_action (edit, p); } edit_modification (edit); if (p == '\n') { book_mark_dec (edit, edit->buffer.curs_line); edit->buffer.curs_line--; edit->buffer.lines--; edit->force |= REDRAW_AFTER_CURSOR; } if (edit->buffer.curs1 < edit->start_display) { edit->start_display--; if (p == '\n') edit->start_line--; } return p; } /* --------------------------------------------------------------------------------------------- */ /** moves the cursor right or left: increment positive or negative respectively */ void edit_cursor_move (WEdit * edit, off_t increment) { if (increment < 0) { for (; increment < 0 && edit->buffer.curs1 != 0; increment++) { int c; edit_push_undo_action (edit, CURS_RIGHT); c = edit_buffer_get_previous_byte (&edit->buffer); edit_buffer_insert_ahead (&edit->buffer, c); c = edit_buffer_backspace (&edit->buffer); if (c == '\n') { edit->buffer.curs_line--; edit->force |= REDRAW_LINE_BELOW; } } } else { for (; increment > 0 && edit->buffer.curs2 != 0; increment--) { int c; edit_push_undo_action (edit, CURS_LEFT); c = edit_buffer_get_current_byte (&edit->buffer); edit_buffer_insert (&edit->buffer, c); c = edit_buffer_delete (&edit->buffer); if (c == '\n') { edit->buffer.curs_line++; edit->force |= REDRAW_LINE_ABOVE; } } } } /* --------------------------------------------------------------------------------------------- */ /* If cols is zero this returns the count of columns from current to upto. */ /* If upto is zero returns index of cols across from current. */ off_t edit_move_forward3 (const WEdit * edit, off_t current, long cols, off_t upto) { off_t p, q; long col; if (upto != 0) { q = upto; cols = -10; } else q = edit->buffer.size + 2; for (col = 0, p = current; p < q; p++) { int c, orig_c; if (cols != -10) { if (col == cols) return p; if (col > cols) return p - 1; } orig_c = c = edit_buffer_get_byte (&edit->buffer, p); #ifdef HAVE_CHARSET if (edit->utf8) { int utf_ch; int char_length = 1; utf_ch = edit_buffer_get_utf (&edit->buffer, p, &char_length); if (mc_global.utf8_display) { if (char_length > 1) col -= char_length - 1; if (g_unichar_iswide (utf_ch)) col++; } else if (char_length > 1 && g_unichar_isprint (utf_ch)) col -= char_length - 1; } c = convert_to_display_c (c); #endif if (c == '\n') return (upto != 0 ? (off_t) col : p); if (c == '\t') col += TAB_SIZE - col % TAB_SIZE; else if ((c < 32 || c == 127) && (orig_c == c #ifdef HAVE_CHARSET || (!mc_global.utf8_display && !edit->utf8) #endif )) /* '\r' is shown as ^M, so we must advance 2 characters */ /* Caret notation for control characters */ col += 2; else col++; } return (off_t) col; } /* --------------------------------------------------------------------------------------------- */ /** returns the current offset of the cursor from the beginning of a file */ off_t edit_get_cursor_offset (const WEdit * edit) { return edit->buffer.curs1; } /* --------------------------------------------------------------------------------------------- */ /** returns the current column position of the cursor */ long edit_get_col (const WEdit * edit) { off_t b; b = edit_buffer_get_current_bol (&edit->buffer); return (long) edit_move_forward3 (edit, b, 0, edit->buffer.curs1); } /* --------------------------------------------------------------------------------------------- */ /* Scrolling functions */ /* --------------------------------------------------------------------------------------------- */ void edit_update_curs_row (WEdit * edit) { edit->curs_row = edit->buffer.curs_line - edit->start_line; } /* --------------------------------------------------------------------------------------------- */ void edit_update_curs_col (WEdit * edit) { off_t b; b = edit_buffer_get_current_bol (&edit->buffer); edit->curs_col = (long) edit_move_forward3 (edit, b, 0, edit->buffer.curs1); } /* --------------------------------------------------------------------------------------------- */ long edit_get_curs_col (const WEdit * edit) { return edit->curs_col; } /* --------------------------------------------------------------------------------------------- */ /** moves the display start position up by i lines */ void edit_scroll_upward (WEdit * edit, long i) { long lines_above = edit->start_line; if (i > lines_above) i = lines_above; if (i != 0) { edit->start_line -= i; edit->start_display = edit_buffer_get_backward_offset (&edit->buffer, edit->start_display, i); edit->force |= REDRAW_PAGE; edit->force &= (0xfff - REDRAW_CHAR_ONLY); } edit_update_curs_row (edit); } /* --------------------------------------------------------------------------------------------- */ void edit_scroll_downward (WEdit * edit, long i) { long lines_below; lines_below = edit->buffer.lines - edit->start_line - (WIDGET (edit)->rect.lines - 1); if (lines_below > 0) { if (i > lines_below) i = lines_below; edit->start_line += i; edit->start_display = edit_buffer_get_forward_offset (&edit->buffer, edit->start_display, i, 0); edit->force |= REDRAW_PAGE; edit->force &= (0xfff - REDRAW_CHAR_ONLY); } edit_update_curs_row (edit); } /* --------------------------------------------------------------------------------------------- */ void edit_scroll_right (WEdit * edit, long i) { edit->force |= REDRAW_PAGE; edit->force &= (0xfff - REDRAW_CHAR_ONLY); edit->start_col -= i; } /* --------------------------------------------------------------------------------------------- */ void edit_scroll_left (WEdit * edit, long i) { if (edit->start_col) { edit->start_col += i; if (edit->start_col > 0) edit->start_col = 0; edit->force |= REDRAW_PAGE; edit->force &= (0xfff - REDRAW_CHAR_ONLY); } } /* --------------------------------------------------------------------------------------------- */ /* high level cursor movement commands */ /* --------------------------------------------------------------------------------------------- */ void edit_move_to_prev_col (WEdit * edit, off_t p) { long prev = edit->prev_col; long over = edit->over_col; off_t b; edit_cursor_move (edit, edit_move_forward3 (edit, p, prev + edit->over_col, 0) - edit->buffer.curs1); if (edit_options.cursor_beyond_eol) { off_t e; long line_len; b = edit_buffer_get_current_bol (&edit->buffer); e = edit_buffer_get_current_eol (&edit->buffer); line_len = (long) edit_move_forward3 (edit, b, 0, e); if (line_len < prev + edit->over_col) { edit->over_col = prev + over - line_len; edit->prev_col = line_len; edit->curs_col = line_len; } else { edit->over_col = 0; edit->prev_col = edit->curs_col; edit->curs_col = prev + over; } } else { edit->over_col = 0; if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer)) { long fake_half_tabs; edit_update_curs_col (edit); fake_half_tabs = HALF_TAB_SIZE * space_width; if (fake_half_tabs != 0 && edit->curs_col % fake_half_tabs != 0) { long q; q = edit->curs_col; edit->curs_col -= (edit->curs_col % fake_half_tabs); p = edit_buffer_get_current_bol (&edit->buffer); b = edit_move_forward3 (edit, p, edit->curs_col, 0); edit_cursor_move (edit, b - edit->buffer.curs1); if (!left_of_four_spaces (edit)) { b = edit_move_forward3 (edit, p, q, 0); edit_cursor_move (edit, b - edit->buffer.curs1); } } } } } /* --------------------------------------------------------------------------------------------- */ /** check whether line in editor is blank or not * * @param edit editor object * @param line number of line * * @return TRUE if line in blank, FALSE otherwise */ gboolean edit_line_is_blank (WEdit * edit, long line) { return is_blank (&edit->buffer, edit_find_line (edit, line)); } /* --------------------------------------------------------------------------------------------- */ /** move cursor to line 'line' */ void edit_move_to_line (WEdit * e, long line) { if (line < e->buffer.curs_line) edit_move_up (e, e->buffer.curs_line - line, FALSE); else edit_move_down (e, line - e->buffer.curs_line, FALSE); edit_scroll_screen_over_cursor (e); } /* --------------------------------------------------------------------------------------------- */ /** scroll window so that first visible line is 'line' */ void edit_move_display (WEdit * e, long line) { if (line < e->start_line) edit_scroll_upward (e, e->start_line - line); else edit_scroll_downward (e, line - e->start_line); } /* --------------------------------------------------------------------------------------------- */ /** save markers onto undo stack */ void edit_push_markers (WEdit * edit) { edit_push_undo_action (edit, MARK_1 + edit->mark1); edit_push_undo_action (edit, MARK_2 + edit->mark2); edit_push_undo_action (edit, MARK_CURS + edit->end_mark_curs); } /* --------------------------------------------------------------------------------------------- */ void edit_set_markers (WEdit * edit, off_t m1, off_t m2, long c1, long c2) { edit->mark1 = m1; edit->mark2 = m2; edit->column1 = c1; edit->column2 = c2; } /* --------------------------------------------------------------------------------------------- */ /** if mark2 is -1 then marking is from mark1 to the cursor. Otherwise its between the markers. This handles this. Returns FALSE if no text is marked. */ gboolean eval_marks (WEdit * edit, off_t * start_mark, off_t * end_mark) { long end_mark_curs; if (edit->mark1 == edit->mark2) { *start_mark = *end_mark = 0; edit->column2 = edit->column1 = 0; return FALSE; } if (edit->end_mark_curs < 0) end_mark_curs = edit->buffer.curs1; else end_mark_curs = edit->end_mark_curs; if (edit->mark2 >= 0) { *start_mark = MIN (edit->mark1, edit->mark2); *end_mark = MAX (edit->mark1, edit->mark2); } else { *start_mark = MIN (edit->mark1, end_mark_curs); *end_mark = MAX (edit->mark1, end_mark_curs); edit->column2 = edit->curs_col + edit->over_col; } if (edit->column_highlight && ((edit->mark1 > end_mark_curs && edit->column1 < edit->column2) || (edit->mark1 < end_mark_curs && edit->column1 > edit->column2))) { off_t start_bol, start_eol; off_t end_bol, end_eol; long col1, col2; off_t diff1, diff2; start_bol = edit_buffer_get_bol (&edit->buffer, *start_mark); start_eol = edit_buffer_get_eol (&edit->buffer, start_bol - 1) + 1; end_bol = edit_buffer_get_bol (&edit->buffer, *end_mark); end_eol = edit_buffer_get_eol (&edit->buffer, *end_mark); col1 = MIN (edit->column1, edit->column2); col2 = MAX (edit->column1, edit->column2); diff1 = edit_move_forward3 (edit, start_bol, col2, 0) - edit_move_forward3 (edit, start_bol, col1, 0); diff2 = edit_move_forward3 (edit, end_bol, col2, 0) - edit_move_forward3 (edit, end_bol, col1, 0); *start_mark -= diff1; *end_mark += diff2; *start_mark = MAX (*start_mark, start_eol); *end_mark = MIN (*end_mark, end_eol); } return TRUE; } /* --------------------------------------------------------------------------------------------- */ /** highlight marker toggle */ void edit_mark_cmd (WEdit * edit, gboolean unmark) { edit_push_markers (edit); if (unmark) { edit_set_markers (edit, 0, 0, 0, 0); edit->force |= REDRAW_PAGE; } else if (edit->mark2 >= 0) { edit->end_mark_curs = -1; edit_set_markers (edit, edit->buffer.curs1, -1, edit->curs_col + edit->over_col, edit->curs_col + edit->over_col); edit->force |= REDRAW_PAGE; } else { edit->end_mark_curs = edit->buffer.curs1; edit_set_markers (edit, edit->mark1, edit->buffer.curs1, edit->column1, edit->curs_col + edit->over_col); } } /* --------------------------------------------------------------------------------------------- */ /** highlight the word under cursor */ void edit_mark_current_word_cmd (WEdit * edit) { long pos; for (pos = edit->buffer.curs1; pos != 0; pos--) { int c1, c2; c1 = edit_buffer_get_byte (&edit->buffer, pos); c2 = edit_buffer_get_byte (&edit->buffer, pos - 1); if (!isspace (c1) && isspace (c2)) break; if ((my_type_of (c1) & my_type_of (c2)) == 0) break; } edit->mark1 = pos; for (; pos < edit->buffer.size; pos++) { int c1, c2; c1 = edit_buffer_get_byte (&edit->buffer, pos); c2 = edit_buffer_get_byte (&edit->buffer, pos + 1); if (!isspace (c1) && isspace (c2)) break; if ((my_type_of (c1) & my_type_of (c2)) == 0) break; } edit->mark2 = MIN (pos + 1, edit->buffer.size); edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR; } /* --------------------------------------------------------------------------------------------- */ void edit_mark_current_line_cmd (WEdit * edit) { edit->mark1 = edit_buffer_get_current_bol (&edit->buffer); edit->mark2 = edit_buffer_get_current_eol (&edit->buffer); edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR; } /* --------------------------------------------------------------------------------------------- */ void edit_delete_line (WEdit * edit) { /* * Delete right part of the line. * Note that edit_buffer_get_byte() returns '\n' when byte position is * beyond EOF. */ while (edit_buffer_get_current_byte (&edit->buffer) != '\n') (void) edit_delete (edit, TRUE); /* * Delete '\n' char. * Note that edit_delete() will not corrupt anything if called while * cursor position is EOF. */ (void) edit_delete (edit, TRUE); /* * Delete left part of the line. * Note, that edit_buffer_get_byte() returns '\n' when byte position is < 0. */ while (edit_buffer_get_previous_byte (&edit->buffer) != '\n') (void) edit_backspace (edit, TRUE); } /* --------------------------------------------------------------------------------------------- */ void edit_push_key_press (WEdit * edit) { edit_push_undo_action (edit, KEY_PRESS + edit->start_display); if (edit->mark2 == -1) { edit_push_undo_action (edit, MARK_1 + edit->mark1); edit_push_undo_action (edit, MARK_CURS + edit->end_mark_curs); } } /* --------------------------------------------------------------------------------------------- */ void edit_find_bracket (WEdit * edit) { edit->bracket = edit_get_bracket (edit, 1, 10000); if (edit->last_bracket != edit->bracket) edit->force |= REDRAW_PAGE; edit->last_bracket = edit->bracket; } /* --------------------------------------------------------------------------------------------- */ /** * This executes a command as though the user initiated it through a key * press. Callback with MSG_KEY as a message calls this after * translating the key press. This function can be used to pass any * command to the editor. Note that the screen wouldn't update * automatically. Either of command or char_for_insertion must be * passed as -1. Commands are executed, and char_for_insertion is * inserted at the cursor. */ void edit_execute_key_command (WEdit * edit, long command, int char_for_insertion) { if (command == CK_MacroStartRecord || command == CK_RepeatStartRecord || (macro_index < 0 && (command == CK_MacroStartStopRecord || command == CK_RepeatStartStopRecord))) { macro_index = 0; edit->force |= REDRAW_CHAR_ONLY | REDRAW_LINE; return; } if (macro_index != -1) { edit->force |= REDRAW_COMPLETELY; if (command == CK_MacroStopRecord || command == CK_MacroStartStopRecord) { edit_store_macro_cmd (edit); macro_index = -1; return; } if (command == CK_RepeatStopRecord || command == CK_RepeatStartStopRecord) { edit_repeat_macro_cmd (edit); macro_index = -1; return; } } if (macro_index >= 0 && macro_index < MAX_MACRO_LENGTH - 1) { record_macro_buf[macro_index].action = command; record_macro_buf[macro_index++].ch = char_for_insertion; } /* record the beginning of a set of editing actions initiated by a key press */ if (command != CK_Undo && command != CK_ExtendedKeyMap) edit_push_key_press (edit); edit_execute_cmd (edit, command, char_for_insertion); if (edit->column_highlight) edit->force |= REDRAW_PAGE; } /* --------------------------------------------------------------------------------------------- */ /** This executes a command at a lower level than macro recording. It also does not push a key_press onto the undo stack. This means that if it is called many times, a single undo command will undo all of them. It also does not check for the Undo command. */ void edit_execute_cmd (WEdit * edit, long command, int char_for_insertion) { WRect *w = &WIDGET (edit)->rect; if (command == CK_WindowFullscreen) { edit_toggle_fullscreen (edit); return; } /* handle window state */ if (edit_handle_move_resize (edit, command)) return; edit->force |= REDRAW_LINE; /* The next key press will unhighlight the found string, so update * the whole page */ if (edit->found_len || edit->column_highlight) edit->force |= REDRAW_PAGE; switch (command) { /* a mark command with shift-arrow */ case CK_MarkLeft: case CK_MarkRight: case CK_MarkToWordBegin: case CK_MarkToWordEnd: case CK_MarkToHome: case CK_MarkToEnd: case CK_MarkUp: case CK_MarkDown: case CK_MarkPageUp: case CK_MarkPageDown: case CK_MarkToFileBegin: case CK_MarkToFileEnd: case CK_MarkToPageBegin: case CK_MarkToPageEnd: case CK_MarkScrollUp: case CK_MarkScrollDown: case CK_MarkParagraphUp: case CK_MarkParagraphDown: /* a mark command with alt-arrow */ case CK_MarkColumnPageUp: case CK_MarkColumnPageDown: case CK_MarkColumnLeft: case CK_MarkColumnRight: case CK_MarkColumnUp: case CK_MarkColumnDown: case CK_MarkColumnScrollUp: case CK_MarkColumnScrollDown: case CK_MarkColumnParagraphUp: case CK_MarkColumnParagraphDown: edit->column_highlight = 0; if (edit->highlight == 0 || (edit->mark2 != -1 && edit->mark1 != edit->mark2)) { edit_mark_cmd (edit, TRUE); /* clear */ edit_mark_cmd (edit, FALSE); /* marking on */ } edit->highlight = 1; break; /* any other command */ default: if (edit->highlight) edit_mark_cmd (edit, FALSE); /* clear */ edit->highlight = 0; } /* first check for undo */ if (command == CK_Undo) { edit->redo_stack_reset = 0; edit_group_undo (edit); edit->found_len = 0; edit->prev_col = edit_get_col (edit); edit->search_start = edit->buffer.curs1; return; } /* check for redo */ if (command == CK_Redo) { edit->redo_stack_reset = 0; edit_do_redo (edit); edit->found_len = 0; edit->prev_col = edit_get_col (edit); edit->search_start = edit->buffer.curs1; return; } edit->redo_stack_reset = 1; /* An ordinary key press */ if (char_for_insertion >= 0) { /* if non persistent selection and text selected */ if (!edit_options.persistent_selections && edit->mark1 != edit->mark2) edit_block_delete_cmd (edit); if (edit->overwrite) { /* remove char only one time, after input first byte, multibyte chars */ #ifdef HAVE_CHARSET if (!mc_global.utf8_display || edit->charpoint == 0) #endif if (edit_buffer_get_current_byte (&edit->buffer) != '\n') edit_delete (edit, FALSE); } if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit_insert_over (edit); #ifdef HAVE_CHARSET /** Encode 8-bit input as UTF-8, if display (locale) is *not* UTF-8, *but* source encoding *is* set to UTF-8; see ticket #3843 for the details. */ if (char_for_insertion > 127 && str_isutf8 (get_codepage_id (mc_global.source_codepage)) && !mc_global.utf8_display) { unsigned char str[UTF8_CHAR_LEN + 1]; size_t i; int res; res = g_unichar_to_utf8 (char_for_insertion, (char *) str); if (res == 0) { str[0] = '.'; str[1] = '\0'; } else str[res] = '\0'; for (i = 0; i <= UTF8_CHAR_LEN && str[i] != '\0'; i++) { char_for_insertion = str[i]; edit_insert (edit, char_for_insertion); } } else #endif edit_insert (edit, char_for_insertion); if (edit_options.auto_para_formatting) { format_paragraph (edit, FALSE); edit->force |= REDRAW_PAGE; } else check_and_wrap_line (edit); edit->found_len = 0; edit->prev_col = edit_get_col (edit); edit->search_start = edit->buffer.curs1; edit_find_bracket (edit); return; } switch (command) { case CK_TopOnScreen: case CK_BottomOnScreen: case CK_Top: case CK_Bottom: case CK_PageUp: case CK_PageDown: case CK_Home: case CK_End: case CK_Up: case CK_Down: case CK_Left: case CK_Right: case CK_WordLeft: case CK_WordRight: if (!edit_options.persistent_selections && edit->mark2 >= 0) { if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; edit_mark_cmd (edit, TRUE); } break; default: break; } switch (command) { case CK_TopOnScreen: case CK_BottomOnScreen: case CK_MarkToPageBegin: case CK_MarkToPageEnd: case CK_Up: case CK_Down: case CK_WordLeft: case CK_WordRight: case CK_MarkToWordBegin: case CK_MarkToWordEnd: case CK_MarkUp: case CK_MarkDown: case CK_MarkColumnUp: case CK_MarkColumnDown: if (edit->mark2 == -1) break; /*marking is following the cursor: may need to highlight a whole line */ MC_FALLTHROUGH; case CK_Left: case CK_Right: case CK_MarkLeft: case CK_MarkRight: edit->force |= REDRAW_CHAR_ONLY; break; default: break; } /* basic cursor key commands */ switch (command) { case CK_BackSpace: /* if non persistent selection and text selected */ if (!edit_options.persistent_selections && edit->mark1 != edit->mark2) edit_block_delete_cmd (edit); else if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit->over_col--; else if (edit_options.backspace_through_tabs && is_in_indent (&edit->buffer)) { while (edit_buffer_get_previous_byte (&edit->buffer) != '\n' && edit->buffer.curs1 > 0) edit_backspace (edit, TRUE); } else if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer) && right_of_four_spaces (edit)) { int i; for (i = 0; i < HALF_TAB_SIZE; i++) edit_backspace (edit, TRUE); } else edit_backspace (edit, FALSE); break; case CK_Delete: /* if non persistent selection and text selected */ if (!edit_options.persistent_selections && edit->mark1 != edit->mark2) edit_block_delete_cmd (edit); else { if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit_insert_over (edit); if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer) && left_of_four_spaces (edit)) { int i; for (i = 1; i <= HALF_TAB_SIZE; i++) edit_delete (edit, TRUE); } else edit_delete (edit, FALSE); } break; case CK_DeleteToWordBegin: edit->over_col = 0; edit_left_delete_word (edit); break; case CK_DeleteToWordEnd: if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit_insert_over (edit); edit_right_delete_word (edit); break; case CK_DeleteLine: edit_delete_line (edit); break; case CK_DeleteToHome: edit_delete_to_line_begin (edit); break; case CK_DeleteToEnd: edit_delete_to_line_end (edit); break; case CK_Enter: edit->over_col = 0; if (edit_options.auto_para_formatting) { edit_double_newline (edit); if (edit_options.return_does_auto_indent && !bracketed_pasting_in_progress) edit_auto_indent (edit); format_paragraph (edit, FALSE); } else { edit_insert (edit, '\n'); if (edit_options.return_does_auto_indent && !bracketed_pasting_in_progress) edit_auto_indent (edit); } break; case CK_Return: edit_insert (edit, '\n'); break; case CK_MarkColumnPageUp: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_PageUp: case CK_MarkPageUp: edit_move_up (edit, w->lines - 1, TRUE); break; case CK_MarkColumnPageDown: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_PageDown: case CK_MarkPageDown: edit_move_down (edit, w->lines - 1, TRUE); break; case CK_MarkColumnLeft: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_Left: case CK_MarkLeft: if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer) && right_of_four_spaces (edit)) { if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit->over_col--; else edit_cursor_move (edit, -HALF_TAB_SIZE); edit->force &= (0xFFF - REDRAW_CHAR_ONLY); } else edit_left_char_move_cmd (edit); break; case CK_MarkColumnRight: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_Right: case CK_MarkRight: if (edit_options.fake_half_tabs && is_in_indent (&edit->buffer) && left_of_four_spaces (edit)) { edit_cursor_move (edit, HALF_TAB_SIZE); edit->force &= (0xFFF - REDRAW_CHAR_ONLY); } else edit_right_char_move_cmd (edit); break; case CK_TopOnScreen: case CK_MarkToPageBegin: edit_begin_page (edit); break; case CK_BottomOnScreen: case CK_MarkToPageEnd: edit_end_page (edit); break; case CK_WordLeft: case CK_MarkToWordBegin: edit->over_col = 0; edit_left_word_move_cmd (edit); break; case CK_WordRight: case CK_MarkToWordEnd: edit->over_col = 0; edit_right_word_move_cmd (edit); break; case CK_MarkColumnUp: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_Up: case CK_MarkUp: edit_move_up (edit, 1, FALSE); break; case CK_MarkColumnDown: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_Down: case CK_MarkDown: edit_move_down (edit, 1, FALSE); break; case CK_MarkColumnParagraphUp: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_ParagraphUp: case CK_MarkParagraphUp: edit_move_up_paragraph (edit, FALSE); break; case CK_MarkColumnParagraphDown: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_ParagraphDown: case CK_MarkParagraphDown: edit_move_down_paragraph (edit, FALSE); break; case CK_MarkColumnScrollUp: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_ScrollUp: case CK_MarkScrollUp: edit_move_up (edit, 1, TRUE); break; case CK_MarkColumnScrollDown: edit->column_highlight = 1; MC_FALLTHROUGH; case CK_ScrollDown: case CK_MarkScrollDown: edit_move_down (edit, 1, TRUE); break; case CK_Home: case CK_MarkToHome: edit_cursor_to_bol (edit); break; case CK_End: case CK_MarkToEnd: edit_cursor_to_eol (edit); break; case CK_Tab: /* if text marked shift block */ if (edit->mark1 != edit->mark2 && !edit_options.persistent_selections) { if (edit->mark2 < 0) edit_mark_cmd (edit, FALSE); edit_move_block_to_right (edit); } else { if (edit_options.cursor_beyond_eol) edit_insert_over (edit); edit_tab_cmd (edit); if (edit_options.auto_para_formatting) { format_paragraph (edit, FALSE); edit->force |= REDRAW_PAGE; } else check_and_wrap_line (edit); } break; case CK_InsertOverwrite: edit->overwrite = !edit->overwrite; break; case CK_Mark: if (edit->mark2 >= 0) { if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; } edit_mark_cmd (edit, FALSE); break; case CK_MarkColumn: if (!edit->column_highlight) edit_push_undo_action (edit, COLUMN_OFF); edit->column_highlight = 1; edit_mark_cmd (edit, FALSE); break; case CK_MarkAll: edit_set_markers (edit, 0, edit->buffer.size, 0, 0); edit->force |= REDRAW_PAGE; break; case CK_Unmark: if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; edit_mark_cmd (edit, TRUE); break; case CK_MarkWord: if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; edit_mark_current_word_cmd (edit); break; case CK_MarkLine: if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; edit_mark_current_line_cmd (edit); break; case CK_Bookmark: book_mark_clear (edit, edit->buffer.curs_line, BOOK_MARK_FOUND_COLOR); if (book_mark_query_color (edit, edit->buffer.curs_line, BOOK_MARK_COLOR)) book_mark_clear (edit, edit->buffer.curs_line, BOOK_MARK_COLOR); else book_mark_insert (edit, edit->buffer.curs_line, BOOK_MARK_COLOR); break; case CK_BookmarkFlush: book_mark_flush (edit, BOOK_MARK_COLOR); book_mark_flush (edit, BOOK_MARK_FOUND_COLOR); edit->force |= REDRAW_PAGE; break; case CK_BookmarkNext: if (edit->book_mark != NULL) { edit_book_mark_t *p; p = book_mark_find (edit, edit->buffer.curs_line); if (p->next != NULL) { p = p->next; if (p->line >= edit->start_line + w->lines || p->line < edit->start_line) edit_move_display (edit, p->line - w->lines / 2); edit_move_to_line (edit, p->line); } } break; case CK_BookmarkPrev: if (edit->book_mark != NULL) { edit_book_mark_t *p; p = book_mark_find (edit, edit->buffer.curs_line); while (p->line == edit->buffer.curs_line) if (p->prev != NULL) p = p->prev; if (p->line >= 0) { if (p->line >= edit->start_line + w->lines || p->line < edit->start_line) edit_move_display (edit, p->line - w->lines / 2); edit_move_to_line (edit, p->line); } } break; case CK_Top: case CK_MarkToFileBegin: edit_move_to_top (edit); break; case CK_Bottom: case CK_MarkToFileEnd: edit_move_to_bottom (edit); break; case CK_Copy: if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit_insert_over (edit); edit_block_copy_cmd (edit); break; case CK_Remove: edit_block_delete_cmd (edit); break; case CK_Move: edit_block_move_cmd (edit); break; case CK_BlockShiftLeft: if (edit->mark1 != edit->mark2) edit_move_block_to_left (edit); break; case CK_BlockShiftRight: if (edit->mark1 != edit->mark2) edit_move_block_to_right (edit); break; case CK_Store: edit_copy_to_X_buf_cmd (edit); break; case CK_Cut: edit_cut_to_X_buf_cmd (edit); break; case CK_Paste: /* if non persistent selection and text selected */ if (!edit_options.persistent_selections && edit->mark1 != edit->mark2) edit_block_delete_cmd (edit); if (edit_options.cursor_beyond_eol && edit->over_col > 0) edit_insert_over (edit); edit_paste_from_X_buf_cmd (edit); if (!edit_options.persistent_selections && edit->mark2 >= 0) { if (edit->column_highlight) edit_push_undo_action (edit, COLUMN_ON); edit->column_highlight = 0; edit_mark_cmd (edit, TRUE); } break; case CK_History: edit_paste_from_history (edit); break; case CK_SaveAs: edit_save_as_cmd (edit); break; case CK_Save: edit_save_confirm_cmd (edit); break; case CK_BlockSave: edit_save_block_cmd (edit); break; case CK_InsertFile: edit_insert_file_cmd (edit); break; case CK_FilePrev: edit_load_back_cmd (edit); break; case CK_FileNext: edit_load_forward_cmd (edit); break; case CK_SyntaxChoose: edit_syntax_dialog (edit); break; case CK_Search: edit_search_cmd (edit, FALSE); break; case CK_SearchContinue: edit_search_cmd (edit, TRUE); break; case CK_Replace: edit_replace_cmd (edit, FALSE); break; case CK_ReplaceContinue: edit_replace_cmd (edit, TRUE); break; case CK_Complete: /* if text marked shift block */ if (edit->mark1 != edit->mark2 && !edit_options.persistent_selections) edit_move_block_to_left (edit); else edit_complete_word_cmd (edit); break; case CK_Find: edit_get_match_keyword_cmd (edit); break; #ifdef HAVE_ASPELL case CK_SpellCheckCurrentWord: edit_suggest_current_word (edit); break; case CK_SpellCheck: edit_spellcheck_file (edit); break; case CK_SpellCheckSelectLang: edit_set_spell_lang (); break; #endif case CK_Date: { char s[BUF_MEDIUM]; /* fool gcc to prevent a Y2K warning */ char time_format[] = "_c"; time_format[0] = '%'; FMT_LOCALTIME_CURRENT (s, sizeof (s), time_format); edit_print_string (edit, s); edit->force |= REDRAW_PAGE; } break; case CK_Goto: edit_goto_cmd (edit); break; case CK_ParagraphFormat: format_paragraph (edit, TRUE); edit->force |= REDRAW_PAGE; break; case CK_MacroDelete: edit_delete_macro_cmd (edit); break; case CK_MatchBracket: edit_goto_matching_bracket (edit); break; case CK_UserMenu: edit_user_menu (edit, NULL, -1); break; case CK_Sort: edit_sort_cmd (edit); break; case CK_ExternalCommand: edit_ext_cmd (edit); break; case CK_EditMail: edit_mail_dialog (edit); break; #ifdef HAVE_CHARSET case CK_SelectCodepage: edit_select_codepage_cmd (edit); break; #endif case CK_InsertLiteral: edit_insert_literal_cmd (edit); break; case CK_MacroStartStopRecord: edit_begin_end_macro_cmd (edit); break; case CK_RepeatStartStopRecord: edit_begin_end_repeat_cmd (edit); break; case CK_ExtendedKeyMap: WIDGET (edit)->ext_mode = TRUE; break; default: break; } /* CK_PipeBlock */ if ((command / CK_PipeBlock (0)) == 1) edit_block_process_cmd (edit, command - CK_PipeBlock (0)); /* keys which must set the col position, and the search vars */ switch (command) { case CK_Search: case CK_SearchContinue: case CK_Replace: case CK_ReplaceContinue: case CK_Complete: edit->prev_col = edit_get_col (edit); break; case CK_Up: case CK_MarkUp: case CK_MarkColumnUp: case CK_Down: case CK_MarkDown: case CK_MarkColumnDown: case CK_PageUp: case CK_MarkPageUp: case CK_MarkColumnPageUp: case CK_PageDown: case CK_MarkPageDown: case CK_MarkColumnPageDown: case CK_Top: case CK_MarkToFileBegin: case CK_Bottom: case CK_MarkToFileEnd: case CK_ParagraphUp: case CK_MarkParagraphUp: case CK_MarkColumnParagraphUp: case CK_ParagraphDown: case CK_MarkParagraphDown: case CK_MarkColumnParagraphDown: case CK_ScrollUp: case CK_MarkScrollUp: case CK_MarkColumnScrollUp: case CK_ScrollDown: case CK_MarkScrollDown: case CK_MarkColumnScrollDown: edit->search_start = edit->buffer.curs1; edit->found_len = 0; break; default: edit->found_len = 0; edit->prev_col = edit_get_col (edit); edit->search_start = edit->buffer.curs1; } edit_find_bracket (edit); if (edit_options.auto_para_formatting) { switch (command) { case CK_BackSpace: case CK_Delete: case CK_DeleteToWordBegin: case CK_DeleteToWordEnd: case CK_DeleteToHome: case CK_DeleteToEnd: format_paragraph (edit, FALSE); edit->force |= REDRAW_PAGE; break; default: break; } } } /* --------------------------------------------------------------------------------------------- */ void edit_stack_init (void) { for (edit_stack_iterator = 0; edit_stack_iterator < MAX_HISTORY_MOVETO; edit_stack_iterator++) { edit_history_moveto[edit_stack_iterator].filename_vpath = NULL; edit_history_moveto[edit_stack_iterator].line = -1; } edit_stack_iterator = 0; } /* --------------------------------------------------------------------------------------------- */ void edit_stack_free (void) { for (edit_stack_iterator = 0; edit_stack_iterator < MAX_HISTORY_MOVETO; edit_stack_iterator++) vfs_path_free (edit_history_moveto[edit_stack_iterator].filename_vpath, TRUE); } /* --------------------------------------------------------------------------------------------- */ /** move i lines */ void edit_move_up (WEdit * edit, long i, gboolean do_scroll) { edit_move_updown (edit, i, do_scroll, TRUE); } /* --------------------------------------------------------------------------------------------- */ /** move i lines */ void edit_move_down (WEdit * edit, long i, gboolean do_scroll) { edit_move_updown (edit, i, do_scroll, FALSE); } /* --------------------------------------------------------------------------------------------- */