1
1
mc/src/editor/edit.c
Andrew Borodin c5924af3c4 Indentation.
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
2013-07-05 09:20:40 +04:00

3968 строки
113 KiB
C

/*
Editor low level data handling and cursor fundamentals.
Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
2007, 2008, 2009, 2010, 2011, 2012, 2013
The Free Software Foundation, Inc.
Written by:
Paul Sheer 1996, 1997
Ilia Maslakov <il.smind@gmail.com> 2009, 2010, 2011
Andrew Borodin <aborodin@vmail.ru> 2012, 2013
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 <http://www.gnu.org/licenses/>.
*/
/** \file
* \brief Source: editor low level data handling and cursor fundamentals
* \author Paul Sheer
* \date 1996, 1997
*/
#include <config.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <stdint.h> /* UINTMAX_MAX */
#include <stdlib.h>
#include <fcntl.h>
#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/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/filemanager/usermenu.h" /* user_menu_cmd() */
#include "src/setup.h" /* option_tab_spacing */
#include "src/learn.h" /* learn_keys */
#include "src/keybind-defaults.h"
#include "edit-impl.h"
#include "editwidget.h"
#ifdef HAVE_ASPELL
#include "spell.h"
#endif
/*** global variables ****************************************************************************/
int option_word_wrap_line_length = DEFAULT_WRAP_LINE_LENGTH;
int option_typewriter_wrap = 0;
int option_auto_para_formatting = 0;
int option_fill_tabs_with_spaces = 0;
int option_return_does_auto_indent = 1;
int option_backspace_through_tabs = 0;
int option_fake_half_tabs = 1;
int option_save_mode = EDIT_QUICK_SAVE;
int option_save_position = 1;
int option_max_undo = 32768;
int option_persistent_selections = 1;
int option_cursor_beyond_eol = 0;
int option_line_state = 0;
int option_line_state_width = 0;
gboolean option_cursor_after_inserted_block = FALSE;
int option_edit_right_extreme = 0;
int option_edit_left_extreme = 0;
int option_edit_top_extreme = 0;
int option_edit_bottom_extreme = 0;
int enable_show_tabs_tws = 1;
int option_check_nl_at_eof = 0;
int option_group_undo = 0;
int show_right_margin = 0;
char *option_backup_ext = NULL;
char *option_filesize_threshold = NULL;
unsigned int edit_stack_iterator = 0;
edit_stack_type edit_history_moveto[MAX_HISTORY_MOVETO];
/* magic sequense 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 ****************************************************************/
/*** 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"},
{ "lzma -cd %s 2>&1", "lzma > %s", ".lzma" },
{ "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 option_filesize_default_threshold = 64 * 1024 * 1024; /* 64 MB */
/* --------------------------------------------------------------------------------------------- */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* 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;
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;
}
ret = (edit_buffer_read_file (buf, file, buf->size) == buf->size);
if (ret)
buf->lines = edit_buffer_count_lines (buf, 0, buf->size);
else
{
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)
{
size_t i, l, e;
if (filename_vpath == NULL)
return -1;
l = strlen (vfs_path_as_str (filename_vpath));
for (i = 0; i < G_N_ELEMENTS (all_filters); i++)
{
e = strlen (all_filters[i].extension);
if (l > e)
if (!strcmp (all_filters[i].extension, vfs_path_as_str (filename_vpath) + l - e))
return i;
}
return -1;
}
/* --------------------------------------------------------------------------------------------- */
static char *
edit_get_filter (const vfs_path_t * filename_vpath)
{
int i;
char *p, *quoted_name;
i = edit_find_filter (filename_vpath);
if (i < 0)
return NULL;
quoted_name = name_quote (vfs_path_as_str (filename_vpath), 0);
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 = 0;
while ((c = fgetc (f)) >= 0)
{
edit_insert (edit, c);
i++;
}
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 (option_filesize_threshold, &err);
if (err)
threshold = option_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"));
g_free (errmsg);
errmsg = NULL;
if (act == 1)
ret = FALSE;
}
cleanup:
if (errmsg != NULL)
{
edit_error_dialog (_("Error"), errmsg);
g_free (errmsg);
ret = FALSE;
}
if (!ret)
(void) mc_close (file);
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 in the file */
static void
edit_load_position (WEdit * edit)
{
long line, column;
off_t offset;
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);
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;
}
book_mark_restore (edit, BOOK_MARK_COLOR);
edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
edit_move_display (edit, line - (WIDGET (edit)->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 && !edit->delete_file)
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) - 1;
while (s <= f)
{
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, 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 = abs (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_move_forward (&edit->buffer, edit->line_offsets[j],
line - edit->line_numbers[j], 0);
else
edit->line_offsets[i] =
edit_buffer_move_backward (&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, 0);
}
/* --------------------------------------------------------------------------------------------- */
static void
edit_end_page (WEdit * edit)
{
edit_update_curs_row (edit);
edit_move_down (edit, WIDGET (edit)->lines - edit->curs_row - 1, 0);
}
/* --------------------------------------------------------------------------------------------- */
/** 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, 0);
edit->start_display = edit->buffer.size;
edit->start_line = edit->buffer.lines;
edit_scroll_upward (edit, WIDGET (edit)->lines - 1);
edit->force |= REDRAW_PAGE;
}
}
/* --------------------------------------------------------------------------------------------- */
/** goto beginning of line */
static void
edit_cursor_to_bol (WEdit * edit)
{
edit_cursor_move (edit, edit_buffer_get_current_bol (&edit->buffer) - 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)
{
edit_cursor_move (edit, edit_buffer_get_current_eol (&edit->buffer) - 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)
{
int x, r = 0;
const char *p, *q;
const char option_chars_move_whole_word[] =
"!=&|<>^~ !:;, !'!`!.?!\"!( !) !{ !} !Aa0 !+-*/= |<> ![ !] !\\#! ";
if (c == 0)
return 0;
if (c == '!')
{
if (*option_chars_move_whole_word == '!')
return 2;
return 0x80000000UL;
}
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 (option_chars_move_whole_word, c);
if (!q)
return 0xFFFFFFFFUL;
do
{
for (x = 1, p = option_chars_move_whole_word; p < q; p++)
if (*p == '!')
x <<= 1;
r |= x;
}
while ((q = strchr (q + 1, c)));
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);
c2 = edit_buffer_get_current_byte (&edit->buffer);
if (c1 == '\n' || 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);
c2 = edit_buffer_get_current_byte (&edit->buffer);
if (c1 == '\n' || 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 cw = 1;
int c;
#ifdef HAVE_CHARSET
if (edit->utf8)
{
c = edit_buffer_get_utf (&edit->buffer, edit->buffer.curs1, &cw);
if (cw < 1)
cw = 1;
}
else
#endif
c = edit_buffer_get_current_byte (&edit->buffer);
if (option_cursor_beyond_eol && c == '\n')
edit->over_col++;
else
edit_cursor_move (edit, cw);
}
/* --------------------------------------------------------------------------------------------- */
static void
edit_left_char_move_cmd (WEdit * edit)
{
int cw = 1;
if (edit->column_highlight
&& option_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, &cw);
if (cw < 1)
cw = 1;
}
#endif
if (option_cursor_beyond_eol && edit->over_col > 0)
edit->over_col--;
else
edit_cursor_move (edit, -cw);
}
/* --------------------------------------------------------------------------------------------- */
/** 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_move_backward (&edit->buffer, p, lines) :
edit_buffer_move_forward (&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);
c2 = edit_buffer_get_current_byte (&edit->buffer);
if (c1 == '\n' || 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);
c2 = edit_buffer_get_previous_byte (&edit->buffer);
if (c1 == '\n' || 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)
{
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;
}
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;
edit->column1 =
(long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, edit->mark1),
0, edit->mark1);
}
if (ac >= MARK_2 - 2 && ac < MARK_CURS - 2)
{
edit->mark2 = ac - MARK_2;
edit->column2 =
(long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, edit->mark2),
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)
{
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;
}
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;
edit->column1 =
(long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, edit->mark1),
0, edit->mark1);
}
else if (ac >= MARK_2 - 2 && ac < KEY_PRESS)
{
edit->mark2 = ac - MARK_2;
edit->column2 =
(long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, edit->mark2),
0, edit->mark2);
}
/* more than one pop usually means something big */
if (count++)
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 option_group_undo is not set,
* and make single UNDO operation
*/
if (!option_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, 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;
char c;
p = edit->buffer.curs1;
/* use the previous line as a template */
p = edit_buffer_move_backward (&edit->buffer, p, 1);
/* copy the leading whitespace of the line */
while (TRUE)
{ /* no range check - the line _is_ \n-terminated */
c = edit_buffer_get_byte (&edit->buffer, p++);
if (c != ' ' && c != '\t')
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 = option_tab_spacing * space_width;
if (half)
i /= 2;
if (i != 0)
{
i = ((edit->curs_col / i) + 1) * i - edit->curs_col;
while (i > 0)
{
edit_insert (edit, ' ');
i -= space_width;
}
}
}
/* --------------------------------------------------------------------------------------------- */
static inline void
edit_tab_cmd (WEdit * edit)
{
if (option_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 (option_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 (option_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;
int c;
if (!option_typewriter_wrap)
return;
edit_update_curs_col (edit);
if (edit->curs_col < option_word_wrap_line_length)
return;
curs = edit->buffer.curs1;
while (TRUE)
{
curs--;
c = edit_buffer_get_byte (&edit->buffer, curs);
if (c == '\n' || curs <= 0)
{
edit_insert (edit, '\n');
return;
}
if (c == ' ' || c == '\t')
{
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 seach 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, a, 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);
/* no limit */
if (!furthest_bracket_search)
furthest_bracket_search--;
/* not on a bracket at all */
if (p == NULL)
return -1;
/* the matching bracket */
d = p[1];
/* going left or right? */
if (strchr ("{[(", c))
inc = 1;
for (q = edit->buffer.curs1 + inc;; q += inc)
{
/* 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)->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
{
edit_cursor_move (edit, cur_bol - edit->buffer.curs1);
if (!edit_line_is_blank (edit, edit->buffer.curs_line))
{
if (option_fill_tabs_with_spaces)
insert_spaces_tab (edit, option_fake_half_tabs);
else
edit_insert (edit, '\t');
edit_cursor_move (edit,
edit_buffer_get_bol (&edit->buffer, cur_bol) - 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;
int i;
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);
if (option_fake_half_tabs)
del_tab_width = HALF_TAB_SIZE;
else
del_tab_width = option_tab_spacing;
next_char = edit_buffer_get_current_byte (&edit->buffer);
if (next_char == '\t')
edit_delete (edit, TRUE);
else if (next_char == ' ')
for (i = 1; 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 = 0;
while (s[i] != '\0')
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;
int 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
user_menu (WEdit * edit, const char *menu_file, int selected_entry)
{
char *block_file;
int nomark;
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_BLOCK_FILE);
block_file_vpath = vfs_path_from_str (block_file);
curs = edit->buffer.curs1;
nomark = eval_marks (edit, &start_mark, &end_mark);
if (nomark == 0)
edit_save_block (edit, block_file, start_mark, end_mark);
/* run shell scripts from menu */
if (user_menu_cmd (edit, menu_file, selected_entry)
&& (mc_stat (block_file_vpath, &status) == 0) && (status.st_size != 0))
{
int rc = 0;
FILE *fd;
/* i.e. we have marked block */
if (nomark == 0)
rc = edit_block_delete_cmd (edit);
if (rc == 0)
{
off_t ins_len;
ins_len = edit_insert_file (edit, block_file_vpath);
if (nomark == 0 && 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);
edit_cursor_move (edit, curs - edit->buffer.curs1);
edit->force |= REDRAW_PAGE;
widget_redraw (WIDGET (edit));
}
/* --------------------------------------------------------------------------------------------- */
char *
edit_get_write_filter (const vfs_path_t * write_name_vpath, const vfs_path_t * filename_vpath)
{
int i;
char *p, *writename;
const vfs_path_element_t *path_element;
i = edit_find_filter (filename_vpath);
if (i < 0)
return NULL;
path_element = vfs_path_get_by_index (write_name_vpath, -1);
writename = name_quote (path_element->path, 0);
p = g_strdup_printf (all_filters[i].write, writename);
g_free (writename);
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 */
break;
}
}
}
return edit->buffer.size;
}
/* --------------------------------------------------------------------------------------------- */
gboolean
is_break_char (char c)
{
return (isspace (c) || strchr ("{}[]()<>=|/\\!?~-+`'\",.;:#$%^&*", c));
}
/* --------------------------------------------------------------------------------------------- */
/** 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 (!option_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;
int vertical_insertion = 0;
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 = 1;
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 (!option_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 (!option_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 (!option_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, int y, int x, int lines, int cols, const vfs_path_t * filename_vpath,
long line)
{
gboolean to_free = FALSE;
option_auto_syntax = 1; /* Resetting to auto on every invokation */
option_line_state_width = option_line_state ? LINE_STATE_WIDTH : 0;
if (edit != NULL)
{
/* save some widget parameters */
gboolean fullscreen = edit->fullscreen;
int y_prev = edit->y_prev;
int x_prev = edit->x_prev;
int lines_prev = edit->lines_prev;
int cols_prev = edit->cols_prev;
edit_purge_widget (edit);
/* restore saved parameters */
edit->fullscreen = fullscreen;
edit->y_prev = y_prev;
edit->x_prev = x_prev;
edit->lines_prev = lines_prev;
edit->cols_prev = cols_prev;
}
else
{
edit = g_malloc0 (sizeof (WEdit));
to_free = TRUE;
widget_init (WIDGET (edit), y, x, lines, cols, NULL, NULL);
edit->fullscreen = TRUE;
edit_save_size (edit);
}
edit->drag_state = MCEDIT_DRAG_NORMAL;
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 */
if ((line == 0) && option_save_position)
edit_load_position (edit);
else
{
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)
edit->locked = unlock_file (edit->filename_vpath);
/* save cursor position */
if (option_save_position)
edit_save_position (edit);
else if (edit->serialized_bookmarks != NULL)
edit->serialized_bookmarks = (GArray *) 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);
vfs_path_free (edit->dir_vpath);
mc_search_free (edit->search);
edit->search = NULL;
#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->y_prev = edit->y_prev;
e->x_prev = edit->x_prev;
e->lines_prev = edit->lines_prev;
e->cols_prev = edit->cols_prev;
if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line) == NULL)
{
g_free (e);
return FALSE;
}
edit_clean (edit);
memcpy (edit, e, sizeof (WEdit));
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 betwen 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;
long *t;
/* first enlarge the stack if necessary */
if (sp > edit->undo_stack_size - 10)
{ /* say */
if (option_max_undo < 256)
option_max_undo = 256;
if (edit->undo_stack_size < (unsigned long) option_max_undo)
{
t = g_realloc (edit->undo_stack, (edit->undo_stack_size * 2 + 10) * sizeof (long));
if (t)
{
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)
{
int 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;
long *t;
/* first enlarge the stack if necessary */
if (sp > edit->redo_stack_size - 10)
{ /* say */
if (option_max_undo < 256)
option_max_undo = 256;
if (edit->redo_stack_size < (unsigned long) option_max_undo)
{
t = g_realloc (edit->redo_stack, (edit->redo_stack_size * 2 + 10) * sizeof (long));
if (t)
{
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)
{
int 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);
/* update file length */
edit->buffer.size++;
}
/* --------------------------------------------------------------------------------------------- */
/** 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);
edit->buffer.size++;
}
/* --------------------------------------------------------------------------------------------- */
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 cw = 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, &cw);
if (cw < 1)
cw = 1;
}
#else
(void) byte_delete;
#endif
if (edit->mark2 != edit->mark1)
edit_push_markers (edit);
for (i = 1; i <= cw; 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->buffer.size--;
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 cw = 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, &cw);
if (cw < 1)
cw = 1;
}
#else
(void) byte_delete;
#endif
for (i = 1; i <= cw; 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->buffer.size--;
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 cw = 1;
utf_ch = edit_buffer_get_utf (&edit->buffer, p, &cw);
if (mc_global.utf8_display)
{
if (cw > 1)
col -= cw - 1;
if (g_unichar_iswide (utf_ch))
col++;
}
else if (cw > 1 && g_unichar_isprint (utf_ch))
col -= cw - 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 column position of the cursor */
long
edit_get_col (const WEdit * edit)
{
return (long) edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 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)
{
edit->curs_col = (long) edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer),
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_move_backward (&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)->lines - 1);
if (lines_below > 0)
{
if (i > lines_below)
i = lines_below;
edit->start_line += i;
edit->start_display = edit_buffer_move_forward (&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;
edit_cursor_move (edit,
edit_move_forward3 (edit, p, prev + edit->over_col, 0) - edit->buffer.curs1);
if (option_cursor_beyond_eol)
{
long line_len;
line_len = (long) edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
edit_buffer_get_current_eol (&edit->buffer));
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->curs_col = prev + over;
edit->prev_col = edit->curs_col;
edit->over_col = 0;
}
}
else
{
edit->over_col = 0;
if (option_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)
{
int q;
q = edit->curs_col;
edit->curs_col -= (edit->curs_col % fake_half_tabs);
p = edit_buffer_get_current_bol (&edit->buffer);
edit_cursor_move (edit,
edit_move_forward3 (edit, p, edit->curs_col,
0) - edit->buffer.curs1);
if (!left_of_four_spaces (edit))
edit_cursor_move (edit,
edit_move_forward3 (edit, p, q, 0) - 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, 0);
else
edit_move_down (e, line - e->buffer.curs_line, 0);
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;
}
/* --------------------------------------------------------------------------------------------- */
/** 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, unsigned 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, unsigned long command, int char_for_insertion)
{
Widget *w = WIDGET (edit);
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 (!option_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 (option_cursor_beyond_eol && edit->over_col > 0)
edit_insert_over (edit);
#ifdef HAVE_CHARSET
if (char_for_insertion > 255 && !mc_global.utf8_display)
{
unsigned char str[6 + 1];
size_t i = 0;
int res;
res = g_unichar_to_utf8 (char_for_insertion, (char *) str);
if (res == 0)
{
str[0] = '.';
str[1] = '\0';
}
else
{
str[res] = '\0';
}
while (str[i] != 0 && i <= 6)
{
char_for_insertion = str[i];
edit_insert (edit, char_for_insertion);
i++;
}
}
else
#endif
edit_insert (edit, char_for_insertion);
if (option_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 (!option_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);
}
}
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 */
case CK_Left:
case CK_Right:
case CK_MarkLeft:
case CK_MarkRight:
edit->force |= REDRAW_CHAR_ONLY;
}
/* basic cursor key commands */
switch (command)
{
case CK_BackSpace:
/* if non persistent selection and text selected */
if (!option_persistent_selections && edit->mark1 != edit->mark2)
edit_block_delete_cmd (edit);
else if (option_cursor_beyond_eol && edit->over_col > 0)
edit->over_col--;
else if (option_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 (option_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 (!option_persistent_selections && edit->mark1 != edit->mark2)
edit_block_delete_cmd (edit);
else
{
if (option_cursor_beyond_eol && edit->over_col > 0)
edit_insert_over (edit);
if (option_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 (option_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 (option_auto_para_formatting)
{
edit_double_newline (edit);
if (option_return_does_auto_indent)
edit_auto_indent (edit);
format_paragraph (edit, FALSE);
}
else
{
edit_insert (edit, '\n');
if (option_return_does_auto_indent)
edit_auto_indent (edit);
}
break;
case CK_Return:
edit_insert (edit, '\n');
break;
case CK_MarkColumnPageUp:
edit->column_highlight = 1;
case CK_PageUp:
case CK_MarkPageUp:
edit_move_up (edit, w->lines - 1, 1);
break;
case CK_MarkColumnPageDown:
edit->column_highlight = 1;
case CK_PageDown:
case CK_MarkPageDown:
edit_move_down (edit, w->lines - 1, 1);
break;
case CK_MarkColumnLeft:
edit->column_highlight = 1;
case CK_Left:
case CK_MarkLeft:
if (option_fake_half_tabs && is_in_indent (&edit->buffer) && right_of_four_spaces (edit))
{
if (option_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;
case CK_Right:
case CK_MarkRight:
if (option_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;
case CK_Up:
case CK_MarkUp:
edit_move_up (edit, 1, 0);
break;
case CK_MarkColumnDown:
edit->column_highlight = 1;
case CK_Down:
case CK_MarkDown:
edit_move_down (edit, 1, 0);
break;
case CK_MarkColumnParagraphUp:
edit->column_highlight = 1;
case CK_ParagraphUp:
case CK_MarkParagraphUp:
edit_move_up_paragraph (edit, 0);
break;
case CK_MarkColumnParagraphDown:
edit->column_highlight = 1;
case CK_ParagraphDown:
case CK_MarkParagraphDown:
edit_move_down_paragraph (edit, 0);
break;
case CK_MarkColumnScrollUp:
edit->column_highlight = 1;
case CK_ScrollUp:
case CK_MarkScrollUp:
edit_move_up (edit, 1, 1);
break;
case CK_MarkColumnScrollDown:
edit->column_highlight = 1;
case CK_ScrollDown:
case CK_MarkScrollDown:
edit_move_down (edit, 1, 1);
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 && !option_persistent_selections)
{
if (edit->mark2 < 0)
edit_mark_cmd (edit, FALSE);
edit_move_block_to_right (edit);
}
else
{
if (option_cursor_beyond_eol)
edit_insert_over (edit);
edit_tab_cmd (edit);
if (option_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 (option_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 (!option_persistent_selections && edit->mark1 != edit->mark2)
edit_block_delete_cmd (edit);
if (option_cursor_beyond_eol && edit->over_col > 0)
edit_insert_over (edit);
edit_paste_from_X_buf_cmd (edit);
if (!option_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, 0);
break;
case CK_ReplaceContinue:
edit_replace_cmd (edit, 1);
break;
case CK_Complete:
/* if text marked shift block */
if (edit->mark1 != edit->mark2 && !option_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:
user_menu (edit, NULL, -1);
break;
case CK_Sort:
edit_sort_cmd (edit);
break;
case CK_ExternalCommand:
edit_ext_cmd (edit);
break;
case CK_Mail:
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:
edit->extmod = 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 (option_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;
}
}
}
/* --------------------------------------------------------------------------------------------- */
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);
}
/* --------------------------------------------------------------------------------------------- */
/** 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);
}
/* --------------------------------------------------------------------------------------------- */