*** empty log message ***
[midnight-commander.git] / edit / edit.c
blob7a733e73a8a1cb2c4fd94277096c0e7ed4ba55b3
1 /* editor low level data handling and cursor fundamentals.
3 Copyright (C) 1996, 1997 the Free Software Foundation
5 Authors: 1996, 1997 Paul Sheer
7 $Id$
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 02111-1307, USA.
25 #include <config.h>
26 #if defined(NEEDS_IO_H)
27 # include <io.h>
28 # define CR_LF_TRANSLATION
29 #endif
30 #include "edit.h"
32 #ifdef HAVE_CHARSET
33 #include "src/charsets.h"
34 #include "src/selcodepage.h"
35 #endif
38 what editor are we going to emulate? one of EDIT_KEY_EMULATION_NORMAL
39 or EDIT_KEY_EMULATION_EMACS
41 int edit_key_emulation = EDIT_KEY_EMULATION_NORMAL;
43 int option_word_wrap_line_length = 72;
44 int option_typewriter_wrap = 0;
45 int option_auto_para_formatting = 0;
46 int option_tab_spacing = 8;
47 int option_fill_tabs_with_spaces = 0;
48 int option_return_does_auto_indent = 1;
49 int option_backspace_through_tabs = 0;
50 int option_fake_half_tabs = 1;
51 int option_save_mode = 0;
52 int option_backup_ext_int = -1;
53 int option_max_undo = 32768;
55 int option_edit_right_extreme = 0;
56 int option_edit_left_extreme = 0;
57 int option_edit_top_extreme = 0;
58 int option_edit_bottom_extreme = 0;
60 char *option_whole_chars_search = "0123456789abcdefghijklmnopqrstuvwxyz_";
61 char *option_backup_ext = "~";
63 static struct selection selection = {0, 0};
64 static int current_selection = 0;
65 /* Note: selection.text = selection_history[current_selection].text */
66 static struct selection selection_history[NUM_SELECTION_HISTORY] =
68 {0, 0},
69 {0, 0},
70 {0, 0},
71 {0, 0},
72 {0, 0},
73 {0, 0},
74 {0, 0},
75 {0, 0},
76 {0, 0},
77 {0, 0}
80 static char *option_chars_move_whole_word =
81 "!=&|<>^~ !:;, !'!`!.?!\"!( !) !Aa0 !+-*/= |<> ![ !] !\\#! ";
85 * here's a quick sketch of the layout: (don't run this through indent.)
87 * (b1 is buffers1 and b2 is buffers2)
89 * |
90 * \0\0\0\0\0m e _ f i l e . \nf i n . \n|T h i s _ i s _ s o\0\0\0\0\0\0\0\0\0
91 * ______________________________________|______________________________________
92 * |
93 * ... | b2[2] | b2[1] | b2[0] | b1[0] | b1[1] | b1[2] | ...
94 * |-> |-> |-> |-> |-> |-> |
95 * |
96 * _<------------------------->|<----------------->_
97 * WEdit->curs2 | WEdit->curs1
98 * ^ | ^
99 * | ^|^ |
100 * cursor ||| cursor
101 * |||
102 * file end|||file beginning
107 * This_is_some_file
108 * fin.
114 returns a byte from any location in the file.
115 Returns '\n' if out of bounds.
118 static int push_action_disabled = 0;
120 #ifndef NO_INLINE_GETBYTE
122 int edit_get_byte (WEdit * edit, long byte_index)
124 unsigned long p;
125 if (byte_index >= (edit->curs1 + edit->curs2) || byte_index < 0)
126 return '\n';
128 if (byte_index >= edit->curs1) {
129 p = edit->curs1 + edit->curs2 - byte_index - 1;
130 return edit->buffers2[p >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (p & M_EDIT_BUF_SIZE) - 1];
131 } else {
132 return edit->buffers1[byte_index >> S_EDIT_BUF_SIZE][byte_index & M_EDIT_BUF_SIZE];
136 #endif
138 char *edit_get_buffer_as_text (WEdit * e)
140 int l, i;
141 char *t;
142 l = e->curs1 + e->curs2;
143 t = CMalloc (l + 1);
144 for (i = 0; i < l; i++)
145 t[i] = edit_get_byte (e, i);
146 t[l] = 0;
147 return t;
151 /* Note on CRLF->LF translation: */
153 #if MY_O_TEXT
154 #error "MY_O_TEXT is depreciated. CR_LF_TRANSLATION must be defined which does CR-LF translation internally. See note in source."
155 #endif
158 The edit_open_file (previously edit_load_file) function uses
159 init_dynamic_edit_buffers to load a file. This is unnecessary
160 since you can just as well fopen the file and insert the
161 characters one by one. The real reason for
162 init_dynamic_edit_buffers (besides allocating the buffers) is
163 as an optimisation - it uses raw block reads and inserts large
164 chunks at a time. It is hence extremely fast at loading files.
165 Where we might not want to use it is if we were doing
166 CRLF->LF translation or if we were reading from a pipe.
169 /* Initialisation routines */
171 /* returns 1 on error */
172 /* loads file OR text into buffers. Only one must be none-NULL. */
173 /* cursor set to start of file */
174 int init_dynamic_edit_buffers (WEdit * edit, const char *filename, const char *text)
176 long buf;
177 int j, file = -1, buf2;
179 for (j = 0; j <= MAXBUFF; j++) {
180 edit->buffers1[j] = NULL;
181 edit->buffers2[j] = NULL;
184 if (filename)
185 if ((file = mc_open (filename, O_RDONLY)) == -1) {
186 /* The file-name is printed after the ':' */
187 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
188 return 1;
190 edit->curs2 = edit->last_byte;
192 buf2 = edit->curs2 >> S_EDIT_BUF_SIZE;
194 edit->buffers2[buf2] = CMalloc (EDIT_BUF_SIZE);
196 if (filename) {
197 mc_read (file, (char *) edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE), edit->curs2 & M_EDIT_BUF_SIZE);
198 } else {
199 memcpy (edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE), text, edit->curs2 & M_EDIT_BUF_SIZE);
200 text += edit->curs2 & M_EDIT_BUF_SIZE;
203 for (buf = buf2 - 1; buf >= 0; buf--) {
204 edit->buffers2[buf] = CMalloc (EDIT_BUF_SIZE);
205 if (filename) {
206 mc_read (file, (char *) edit->buffers2[buf], EDIT_BUF_SIZE);
207 } else {
208 memcpy (edit->buffers2[buf], text, EDIT_BUF_SIZE);
209 text += EDIT_BUF_SIZE;
213 edit->curs1 = 0;
214 if (file != -1)
215 mc_close (file);
216 return 0;
219 /* detecting an error on save is easy: just check if every byte has been written. */
220 /* detecting an error on read, is not so easy 'cos there is not way to tell
221 whether you read everything or not. */
222 /* FIXME: add proper `triple_pipe_open' to read, write and check errors. */
223 static const struct edit_filters {
224 char *read, *write, *extension;
225 } all_filters[] = {
228 "bzip2 -cd %s 2>&1", "bzip2 > %s", ".bz2"
231 "gzip -cd %s 2>&1", "gzip > %s", ".gz"
234 "compress -cd %s 2>&1", "compress > %s", ".Z"
238 static int edit_find_filter (const char *filename)
240 int i, l;
241 if (!filename)
242 return -1;
243 l = strlen (filename);
244 for (i = 0; i < sizeof (all_filters) / sizeof (struct edit_filters); i++) {
245 int e;
246 e = strlen (all_filters[i].extension);
247 if (l > e)
248 if (!strcmp (all_filters[i].extension, filename + l - e))
249 return i;
251 return -1;
254 char *edit_get_filter (const char *filename)
256 int i, l;
257 char *p;
258 i = edit_find_filter (filename);
259 if (i < 0)
260 return 0;
261 l = strlen (filename);
262 p = malloc (strlen (all_filters[i].read) + l + 2);
263 sprintf (p, all_filters[i].read, filename);
264 return p;
267 char *edit_get_write_filter (char *writename, const char *filename)
269 int i, l;
270 char *p;
271 i = edit_find_filter (filename);
272 if (i < 0)
273 return 0;
274 l = strlen (writename);
275 p = malloc (strlen (all_filters[i].write) + l + 2);
276 sprintf (p, all_filters[i].write, writename);
277 return p;
280 #ifdef CR_LF_TRANSLATION
281 /* reads into buffer, replace \r\n with \n */
282 long edit_insert_stream (WEdit * edit, FILE * f)
284 int a = -1, b;
285 long i;
286 while ((b = fgetc (f))) {
287 if (a == '\r' && b == '\n') {
288 edit_insert (edit, '\n');
289 i++;
290 a = -1;
291 continue;
292 } else if (a >= 0) {
293 edit_insert (edit, a);
294 i++;
296 a = b;
298 if (a >= 0)
299 edit_insert (edit, a);
300 return i;
302 /* writes buffer, replaces, replace \n with \r\n */
303 long edit_write_stream (WEdit * edit, FILE * f)
305 long i;
306 int c;
307 for (i = 0; i < edit->last_byte; i++) {
308 c = edit_get_byte (edit, i);
309 if (c == '\n') {
310 if (fputc ('\r', f) < 0)
311 break;
313 if (fputc (c, f) < 0)
314 break;
316 return i;
318 #else /* CR_LF_TRANSLATION */
319 long edit_insert_stream (WEdit * edit, FILE * f)
321 int c;
322 long i = 0;
323 while ((c = fgetc (f)) >= 0) {
324 edit_insert (edit, c);
325 i++;
327 return i;
329 long edit_write_stream (WEdit * edit, FILE * f)
331 long i;
332 for (i = 0; i < edit->last_byte; i++)
333 if (fputc (edit_get_byte (edit, i), f) < 0)
334 break;
335 return i;
337 #endif /* CR_LF_TRANSLATION */
339 #define TEMP_BUF_LEN 1024
341 /* inserts a file at the cursor, returns 1 on success */
342 int edit_insert_file (WEdit * edit, const char *filename)
344 char *p;
345 if ((p = edit_get_filter (filename))) {
346 FILE *f;
347 long current = edit->curs1;
348 f = (FILE *) popen (p, "r");
349 if (f) {
350 edit_insert_stream (edit, f);
351 edit_cursor_move (edit, current - edit->curs1);
352 if (pclose (f) > 0) {
353 edit_error_dialog (_ (" Error "), catstrs (_ (" Error reading from pipe: "), p, " ", 0));
354 free (p);
355 return 0;
357 } else {
358 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open pipe for reading: "), p, " ", 0)));
359 free (p);
360 return 0;
362 free (p);
363 #ifdef CR_LF_TRANSLATION
364 } else {
365 FILE *f;
366 long current = edit->curs1;
367 f = fopen (filename, "r");
368 if (f) {
369 edit_insert_stream (edit, f);
370 edit_cursor_move (edit, current - edit->curs1);
371 if (fclose (f)) {
372 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Error reading file: "), filename, " ", 0)));
373 return 0;
375 } else {
376 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
377 return 0;
379 #else
380 } else {
381 int i, file, blocklen;
382 long current = edit->curs1;
383 unsigned char *buf;
384 if ((file = mc_open (filename, O_RDONLY)) == -1)
385 return 0;
386 buf = malloc (TEMP_BUF_LEN);
387 while ((blocklen = mc_read (file, (char *) buf, TEMP_BUF_LEN)) > 0) {
388 for (i = 0; i < blocklen; i++)
389 edit_insert (edit, buf[i]);
391 edit_cursor_move (edit, current - edit->curs1);
392 free (buf);
393 mc_close (file);
394 if (blocklen)
395 return 0;
396 #endif
398 return 1;
401 /* Open file and create it if necessary. Return 0 for success, 1 for error. */
402 static int check_file_access (WEdit *edit, const char *filename, struct stat *st)
404 int file;
405 int stat_ok = 0;
407 /* Try stat first to prevent getting stuck on pipes */
408 if (mc_stat ((char *) filename, st) == 0) {
409 stat_ok = 1;
412 /* Only regular files are allowed */
413 if (stat_ok && !S_ISREG (st->st_mode)) {
414 edit_error_dialog (_ (" Error "), catstrs (_ (" Not an ordinary file: "), filename, " ", 0));
415 return 1;
418 /* Open the file, create it if needed */
419 if ((file = mc_open (filename, O_RDONLY | O_CREAT, 0666)) < 0) {
420 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
421 return 1;
424 /* If the file has just been created, we don't have valid stat yet, so do it now */
425 if (!stat_ok && mc_fstat (file, st) < 0) {
426 mc_close (file);
427 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Cannot get size/permissions info on file: "), filename, " ", 0)));
428 return 1;
430 mc_close (file);
432 /* If it's a new file, delete it if it's not modified or saved */
433 if (!stat_ok && st->st_size == 0) {
434 edit->delete_file = 1;
437 if (st->st_size >= SIZE_LIMIT) {
438 /* The file-name is printed after the ':' */
439 edit_error_dialog (_ (" Error "), catstrs (_ (" File is too large: "), \
440 filename, _ (" \n Increase edit.h:MAXBUF and recompile the editor. "), 0));
441 return 1;
443 return 0;
446 /* returns 1 on error */
447 int edit_open_file (WEdit * edit, const char *filename, const char *text, unsigned long text_size)
449 struct stat st;
450 if (text) {
451 edit->last_byte = text_size;
452 filename = 0;
453 } else {
454 int r;
455 r = check_file_access (edit, filename, &st);
456 if (r)
457 return 1;
458 edit->stat1 = st;
459 #ifndef CR_LF_TRANSLATION
460 edit->last_byte = st.st_size;
461 #else
462 /* going to read the file into the buffer later byte by byte */
463 edit->last_byte = 0;
464 filename = 0;
465 text = "";
466 #endif
468 return init_dynamic_edit_buffers (edit, filename, text);
471 #define space_width 1
473 /* fills in the edit struct. returns 0 on fail. Pass edit as NULL for this */
474 WEdit *edit_init (WEdit * edit, int lines, int columns, const char *filename, const char *text, const char *dir, unsigned long text_size)
476 const char *f;
477 int to_free = 0;
478 int use_filter = 0;
480 if (!edit) {
481 #ifdef ENABLE_NLS
483 * Expand option_whole_chars_search by national letters using
484 * current locale
487 static char option_whole_chars_search_buf [256];
489 if (option_whole_chars_search_buf != option_whole_chars_search) {
490 int i;
491 int len = strlen (option_whole_chars_search);
493 strcpy (option_whole_chars_search_buf, option_whole_chars_search);
495 for (i = 1; i <= sizeof (option_whole_chars_search_buf); i++) {
496 if (islower (i) && !strchr (option_whole_chars_search, i)) {
497 option_whole_chars_search_buf [len++] = i;
501 option_whole_chars_search_buf [len] = 0;
502 option_whole_chars_search = option_whole_chars_search_buf;
504 #endif /* ENABLE_NLS */
505 edit = g_malloc (sizeof (WEdit));
506 memset (edit, 0, sizeof (WEdit));
507 to_free = 1;
509 memset (&(edit->from_here), 0, (unsigned long)&(edit->to_here) - (unsigned long)&(edit->from_here));
510 edit->num_widget_lines = lines;
511 edit->num_widget_columns = columns;
512 edit->stat1.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
513 edit->stat1.st_uid = getuid ();
514 edit->stat1.st_gid = getgid ();
515 edit->bracket = -1;
516 if (!dir)
517 dir = "";
518 f = filename;
519 if (filename) {
520 f = catstrs (dir, filename, 0);
522 if (edit_find_filter (f) < 0) {
523 #ifdef CR_LF_TRANSLATION
524 use_filter = 1;
525 #endif
526 if (edit_open_file (edit, f, text, text_size)) {
527 /* edit_load_file already gives an error message */
528 if (to_free)
529 g_free (edit);
530 return 0;
532 } else {
533 use_filter = 1;
534 if (edit_open_file (edit, 0, "", 0)) {
535 if (to_free)
536 g_free (edit);
537 return 0;
540 edit->force |= REDRAW_PAGE;
541 if (filename) {
542 filename = catstrs (dir, filename, 0);
543 edit_split_filename (edit, filename);
544 } else {
545 edit->filename = (char *) strdup ("");
546 edit->dir = (char *) strdup (dir);
548 edit->stack_size = START_STACK_SIZE;
549 edit->stack_size_mask = START_STACK_SIZE - 1;
550 edit->undo_stack = malloc ((edit->stack_size + 10) * sizeof (long));
551 edit->total_lines = edit_count_lines (edit, 0, edit->last_byte);
552 if (use_filter) {
553 push_action_disabled = 1;
554 if (check_file_access (edit, filename, &(edit->stat1))
555 || !edit_insert_file (edit, f))
557 edit_clean (edit);
558 if (to_free)
559 g_free (edit);
560 return 0;
562 /* FIXME: this should be an unmodification() function */
563 push_action_disabled = 0;
565 edit->modified = 0;
566 edit_load_syntax (edit, 0, 0);
568 int color;
569 edit_get_syntax_color (edit, -1, &color);
571 return edit;
574 /* clear the edit struct, freeing everything in it. returns 1 on success */
575 int edit_clean (WEdit * edit)
577 if (edit) {
578 int j = 0;
579 edit_free_syntax_rules (edit);
580 book_mark_flush (edit, -1);
581 for (; j <= MAXBUFF; j++) {
582 if (edit->buffers1[j] != NULL)
583 free (edit->buffers1[j]);
584 if (edit->buffers2[j] != NULL)
585 free (edit->buffers2[j]);
588 if (edit->undo_stack)
589 free (edit->undo_stack);
590 if (edit->filename)
591 free (edit->filename);
592 if (edit->dir)
593 free (edit->dir);
594 /* we don't want to clear the widget */
595 memset (&(edit->from_here), 0, (unsigned long)&(edit->to_here) - (unsigned long)&(edit->from_here));
597 /* Free temporary strings used in catstrs() */
598 freestrs();
600 return 1;
602 return 0;
606 /* returns 1 on success */
607 int edit_renew (WEdit * edit)
609 int lines = edit->num_widget_lines;
610 int columns = edit->num_widget_columns;
611 char *dir;
612 int retval = 1;
614 if (edit->dir)
615 dir = (char *) strdup (edit->dir);
616 else
617 dir = 0;
619 edit_clean (edit);
620 if (!edit_init (edit, lines, columns, 0, "", dir, 0))
621 retval = 0;
622 if (dir)
623 free (dir);
624 return retval;
627 /* returns 1 on success, if returns 0, the edit struct would have been free'd */
628 int edit_reload (WEdit * edit, const char *filename, const char *text, const char *dir, unsigned long text_size)
630 WEdit *e;
631 int lines = edit->num_widget_lines;
632 int columns = edit->num_widget_columns;
633 e = g_malloc (sizeof (WEdit));
634 memset (e, 0, sizeof (WEdit));
635 e->widget = edit->widget;
636 e->macro_i = -1;
637 if (!edit_init (e, lines, columns, filename, text, dir, text_size)) {
638 g_free (e);
639 return 0;
641 edit_clean (edit);
642 memcpy (edit, e, sizeof (WEdit));
643 g_free (e);
644 return 1;
649 Recording stack for undo:
650 The following is an implementation of a compressed stack. Identical
651 pushes are recorded by a negative prefix indicating the number of times the
652 same char was pushed. This saves space for repeated curs-left or curs-right
653 delete etc.
657 pushed: stored:
661 b -3
663 c --> -4
669 If the stack long int is 0-255 it represents a normal insert (from a backspace),
670 256-512 is an insert ahead (from a delete), If it is betwen 600 and 700 it is one
671 of the cursor functions #define'd in edit.h. 1000 through 700'000'000 is to
672 set edit->mark1 position. 700'000'000 through 1400'000'000 is to set edit->mark2
673 position.
675 The only way the cursor moves or the buffer is changed is through the routines:
676 insert, backspace, insert_ahead, delete, and cursor_move.
677 These record the reverse undo movements onto the stack each time they are
678 called.
680 Each key press results in a set of actions (insert; delete ...). So each time
681 a key is pressed the current position of start_display is pushed as
682 KEY_PRESS + start_display. Then for undoing, we pop until we get to a number
683 over KEY_PRESS. We then assign this number less KEY_PRESS to start_display. So undo
684 tracks scrolling and key actions exactly. (KEY_PRESS is about (2^31) * (2/3) = 1400'000'000)
688 void edit_push_action (WEdit * edit, long c,...)
690 unsigned long sp = edit->stack_pointer;
691 unsigned long spm1;
692 long *t;
693 /* first enlarge the stack if necessary */
694 if (sp > edit->stack_size - 10) { /* say */
695 if (option_max_undo < 256)
696 option_max_undo = 256;
697 if (edit->stack_size < option_max_undo) {
698 t = malloc ((edit->stack_size * 2 + 10) * sizeof (long));
699 if (t) {
700 memcpy (t, edit->undo_stack, sizeof (long) * edit->stack_size);
701 free (edit->undo_stack);
702 edit->undo_stack = t;
703 edit->stack_size <<= 1;
704 edit->stack_size_mask = edit->stack_size - 1;
708 spm1 = (edit->stack_pointer - 1) & edit->stack_size_mask;
709 if (push_action_disabled)
710 return;
712 #ifdef FAST_MOVE_CURSOR
713 if (c == CURS_LEFT_LOTS || c == CURS_RIGHT_LOTS) {
714 va_list ap;
715 edit->undo_stack[sp] = c == CURS_LEFT_LOTS ? CURS_LEFT : CURS_RIGHT;
716 edit->stack_pointer = (edit->stack_pointer + 1) & edit->stack_size_mask;
717 va_start (ap, c);
718 c = -(va_arg (ap, int));
719 va_end (ap);
720 } else
721 #endif /* ! FAST_MOVE_CURSOR */
722 if (edit->stack_bottom != sp
723 && spm1 != edit->stack_bottom
724 && ((sp - 2) & edit->stack_size_mask) != edit->stack_bottom) {
725 int d;
726 if (edit->undo_stack[spm1] < 0) {
727 d = edit->undo_stack[(sp - 2) & edit->stack_size_mask];
728 if (d == c) {
729 if (edit->undo_stack[spm1] > -1000000000) {
730 if (c < KEY_PRESS) /* --> no need to push multiple do-nothings */
731 edit->undo_stack[spm1]--;
732 return;
735 /* #define NO_STACK_CURSMOVE_ANIHILATION */
736 #ifndef NO_STACK_CURSMOVE_ANIHILATION
737 else if ((c == CURS_LEFT && d == CURS_RIGHT)
738 || (c == CURS_RIGHT && d == CURS_LEFT)) { /* a left then a right anihilate each other */
739 if (edit->undo_stack[spm1] == -2)
740 edit->stack_pointer = spm1;
741 else
742 edit->undo_stack[spm1]++;
743 return;
745 #endif
746 } else {
747 d = edit->undo_stack[spm1];
748 if (d == c) {
749 if (c >= KEY_PRESS)
750 return; /* --> no need to push multiple do-nothings */
751 edit->undo_stack[sp] = -2;
752 goto check_bottom;
754 #ifndef NO_STACK_CURSMOVE_ANIHILATION
755 else if ((c == CURS_LEFT && d == CURS_RIGHT)
756 || (c == CURS_RIGHT && d == CURS_LEFT)) { /* a left then a right anihilate each other */
757 edit->stack_pointer = spm1;
758 return;
760 #endif
763 edit->undo_stack[sp] = c;
764 check_bottom:
766 edit->stack_pointer = (edit->stack_pointer + 1) & edit->stack_size_mask;
768 /*if the sp wraps round and catches the stack_bottom then erase the first set of actions on the stack to make space - by moving stack_bottom forward one "key press" */
769 c = (edit->stack_pointer + 2) & edit->stack_size_mask;
770 if (c == edit->stack_bottom || ((c + 1) & edit->stack_size_mask) == edit->stack_bottom)
771 do {
772 edit->stack_bottom = (edit->stack_bottom + 1) & edit->stack_size_mask;
773 } while (edit->undo_stack[edit->stack_bottom] < KEY_PRESS && edit->stack_bottom != edit->stack_pointer);
775 /*If a single key produced enough pushes to wrap all the way round then we would notice that the [stack_bottom] does not contain KEY_PRESS. The stack is then initialised: */
776 if (edit->stack_pointer != edit->stack_bottom && edit->undo_stack[edit->stack_bottom] < KEY_PRESS)
777 edit->stack_bottom = edit->stack_pointer = 0;
781 TODO: if the user undos until the stack bottom, and the stack has not wrapped,
782 then the file should be as it was when he loaded up. Then set edit->modified to 0.
784 long pop_action (WEdit * edit)
786 long c;
787 unsigned long sp = edit->stack_pointer;
788 if (sp == edit->stack_bottom) {
789 return STACK_BOTTOM;
791 sp = (sp - 1) & edit->stack_size_mask;
792 if ((c = edit->undo_stack[sp]) >= 0) {
793 /* edit->undo_stack[sp] = '@'; */
794 edit->stack_pointer = (edit->stack_pointer - 1) & edit->stack_size_mask;
795 return c;
797 if (sp == edit->stack_bottom) {
798 return STACK_BOTTOM;
800 c = edit->undo_stack[(sp - 1) & edit->stack_size_mask];
801 if (edit->undo_stack[sp] == -2) {
802 /* edit->undo_stack[sp] = '@'; */
803 edit->stack_pointer = sp;
804 } else
805 edit->undo_stack[sp]++;
807 return c;
810 /* is called whenever a modification is made by one of the four routines below */
811 static inline void edit_modification (WEdit * edit)
813 edit->caches_valid = 0;
814 edit->modified = 1;
815 edit->screen_modified = 1;
819 Basic low level single character buffer alterations and movements at the cursor.
820 Returns char passed over, inserted or removed.
823 void edit_insert (WEdit * edit, int c)
825 /* check if file has grown to large */
826 if (edit->last_byte >= SIZE_LIMIT)
827 return;
829 /* first we must update the position of the display window */
830 if (edit->curs1 < edit->start_display) {
831 edit->start_display++;
832 if (c == '\n')
833 edit->start_line++;
835 /* now we must update some info on the file and check if a redraw is required */
836 if (c == '\n') {
837 if (edit->book_mark)
838 book_mark_inc (edit, edit->curs_line);
839 edit->curs_line++;
840 edit->total_lines++;
841 edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
843 /* tell that we've modified the file */
844 edit_modification (edit);
846 /* save the reverse command onto the undo stack */
847 edit_push_action (edit, BACKSPACE);
849 /* update markers */
850 edit->mark1 += (edit->mark1 > edit->curs1);
851 edit->mark2 += (edit->mark2 > edit->curs1);
852 edit->last_get_rule += (edit->last_get_rule > edit->curs1);
854 /* add a new buffer if we've reached the end of the last one */
855 if (!(edit->curs1 & M_EDIT_BUF_SIZE))
856 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
858 /* perfprm the insertion */
859 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE][edit->curs1 & M_EDIT_BUF_SIZE] = (unsigned char) c;
861 /* update file length */
862 edit->last_byte++;
864 /* update cursor position */
865 edit->curs1++;
869 /* same as edit_insert and move left */
870 void edit_insert_ahead (WEdit * edit, int c)
872 if (edit->last_byte >= SIZE_LIMIT)
873 return;
874 if (edit->curs1 < edit->start_display) {
875 edit->start_display++;
876 if (c == '\n')
877 edit->start_line++;
879 if (c == '\n') {
880 if (edit->book_mark)
881 book_mark_inc (edit, edit->curs_line);
882 edit->total_lines++;
883 edit->force |= REDRAW_AFTER_CURSOR;
885 edit_modification (edit);
886 edit_push_action (edit, DELCHAR);
888 edit->mark1 += (edit->mark1 >= edit->curs1);
889 edit->mark2 += (edit->mark2 >= edit->curs1);
890 edit->last_get_rule += (edit->last_get_rule >= edit->curs1);
892 if (!((edit->curs2 + 1) & M_EDIT_BUF_SIZE))
893 edit->buffers2[(edit->curs2 + 1) >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
894 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE) - 1] = c;
896 edit->last_byte++;
897 edit->curs2++;
901 int edit_delete (WEdit * edit)
903 int p;
904 if (!edit->curs2)
905 return 0;
907 edit->mark1 -= (edit->mark1 > edit->curs1);
908 edit->mark2 -= (edit->mark2 > edit->curs1);
909 edit->last_get_rule -= (edit->last_get_rule > edit->curs1);
911 p = edit->buffers2[(edit->curs2 - 1) >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - ((edit->curs2 - 1) & M_EDIT_BUF_SIZE) - 1];
913 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
914 free (edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE]);
915 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = NULL;
917 edit->last_byte--;
918 edit->curs2--;
920 if (p == '\n') {
921 if (edit->book_mark)
922 book_mark_dec (edit, edit->curs_line);
923 edit->total_lines--;
924 edit->force |= REDRAW_AFTER_CURSOR;
926 edit_push_action (edit, p + 256);
927 if (edit->curs1 < edit->start_display) {
928 edit->start_display--;
929 if (p == '\n')
930 edit->start_line--;
932 edit_modification (edit);
934 return p;
938 int edit_backspace (WEdit * edit)
940 int p;
941 if (!edit->curs1)
942 return 0;
944 edit->mark1 -= (edit->mark1 >= edit->curs1);
945 edit->mark2 -= (edit->mark2 >= edit->curs1);
946 edit->last_get_rule -= (edit->last_get_rule >= edit->curs1);
948 p = *(edit->buffers1[(edit->curs1 - 1) >> S_EDIT_BUF_SIZE] + ((edit->curs1 - 1) & M_EDIT_BUF_SIZE));
949 if (!((edit->curs1 - 1) & M_EDIT_BUF_SIZE)) {
950 free (edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE]);
951 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = NULL;
953 edit->last_byte--;
954 edit->curs1--;
956 if (p == '\n') {
957 if (edit->book_mark)
958 book_mark_dec (edit, edit->curs_line);
959 edit->curs_line--;
960 edit->total_lines--;
961 edit->force |= REDRAW_AFTER_CURSOR;
963 edit_push_action (edit, p);
965 if (edit->curs1 < edit->start_display) {
966 edit->start_display--;
967 if (p == '\n')
968 edit->start_line--;
970 edit_modification (edit);
972 return p;
975 #ifdef FAST_MOVE_CURSOR
977 static void memqcpy (WEdit * edit, unsigned char *dest, unsigned char *src, int n)
979 unsigned long next;
980 while ((next = (unsigned long) memccpy (dest, src, '\n', n))) {
981 edit->curs_line--;
982 next -= (unsigned long) dest;
983 n -= next;
984 src += next;
985 dest += next;
989 int edit_move_backward_lots (WEdit * edit, long increment)
991 int r, s, t;
992 unsigned char *p;
994 if (increment > edit->curs1)
995 increment = edit->curs1;
996 if (increment <= 0)
997 return -1;
998 edit_push_action (edit, CURS_RIGHT_LOTS, increment);
1000 t = r = EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE);
1001 if (r > increment)
1002 r = increment;
1003 s = edit->curs1 & M_EDIT_BUF_SIZE;
1005 p = 0;
1006 if (s > r) {
1007 memqcpy (edit, edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + t - r,
1008 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] + s - r, r);
1009 } else {
1010 if (s) {
1011 memqcpy (edit, edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + t - s,
1012 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE], s);
1013 p = edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE];
1014 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = 0;
1016 memqcpy (edit, edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + t - r,
1017 edit->buffers1[(edit->curs1 >> S_EDIT_BUF_SIZE) - 1] + EDIT_BUF_SIZE - (r - s), r - s);
1019 increment -= r;
1020 edit->curs1 -= r;
1021 edit->curs2 += r;
1022 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
1023 if (p)
1024 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = p;
1025 else
1026 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1027 } else {
1028 if (p)
1029 free (p);
1032 s = edit->curs1 & M_EDIT_BUF_SIZE;
1033 while (increment) {
1034 p = 0;
1035 r = EDIT_BUF_SIZE;
1036 if (r > increment)
1037 r = increment;
1038 t = s;
1039 if (r < t)
1040 t = r;
1041 memqcpy (edit,
1042 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + EDIT_BUF_SIZE - t,
1043 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] + s - t,
1045 if (r >= s) {
1046 if (t) {
1047 p = edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE];
1048 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = 0;
1050 memqcpy (edit,
1051 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + EDIT_BUF_SIZE - r,
1052 edit->buffers1[(edit->curs1 >> S_EDIT_BUF_SIZE) - 1] + EDIT_BUF_SIZE - (r - s),
1053 r - s);
1055 increment -= r;
1056 edit->curs1 -= r;
1057 edit->curs2 += r;
1058 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
1059 if (p)
1060 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = p;
1061 else
1062 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1063 } else {
1064 if (p)
1065 free (p);
1068 return edit_get_byte (edit, edit->curs1);
1071 #endif /* ! FAST_MOVE_CURSOR */
1073 /* moves the cursor right or left: increment positive or negative respectively */
1074 int edit_cursor_move (WEdit * edit, long increment)
1076 /* this is the same as a combination of two of the above routines, with only one push onto the undo stack */
1077 int c;
1079 #ifdef FAST_MOVE_CURSOR
1080 if (increment < -256) {
1081 edit->force |= REDRAW_PAGE;
1082 return edit_move_backward_lots (edit, -increment);
1084 #endif /* ! FAST_MOVE_CURSOR */
1086 if (increment < 0) {
1087 for (; increment < 0; increment++) {
1088 if (!edit->curs1)
1089 return -1;
1091 edit_push_action (edit, CURS_RIGHT);
1093 c = edit_get_byte (edit, edit->curs1 - 1);
1094 if (!((edit->curs2 + 1) & M_EDIT_BUF_SIZE))
1095 edit->buffers2[(edit->curs2 + 1) >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1096 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE) - 1] = c;
1097 edit->curs2++;
1098 c = edit->buffers1[(edit->curs1 - 1) >> S_EDIT_BUF_SIZE][(edit->curs1 - 1) & M_EDIT_BUF_SIZE];
1099 if (!((edit->curs1 - 1) & M_EDIT_BUF_SIZE)) {
1100 free (edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE]);
1101 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = NULL;
1103 edit->curs1--;
1104 if (c == '\n') {
1105 edit->curs_line--;
1106 edit->force |= REDRAW_LINE_BELOW;
1110 return c;
1111 } else if (increment > 0) {
1112 for (; increment > 0; increment--) {
1113 if (!edit->curs2)
1114 return -2;
1116 edit_push_action (edit, CURS_LEFT);
1118 c = edit_get_byte (edit, edit->curs1);
1119 if (!(edit->curs1 & M_EDIT_BUF_SIZE))
1120 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1121 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE][edit->curs1 & M_EDIT_BUF_SIZE] = c;
1122 edit->curs1++;
1123 c = edit->buffers2[(edit->curs2 - 1) >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - ((edit->curs2 - 1) & M_EDIT_BUF_SIZE) - 1];
1124 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
1125 free (edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE]);
1126 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = 0;
1128 edit->curs2--;
1129 if (c == '\n') {
1130 edit->curs_line++;
1131 edit->force |= REDRAW_LINE_ABOVE;
1134 return c;
1135 } else
1136 return -3;
1139 /* These functions return positions relative to lines */
1141 /* returns index of last char on line + 1 */
1142 long edit_eol (WEdit * edit, long current)
1144 if (current < edit->last_byte) {
1145 for (;; current++)
1146 if (edit_get_byte (edit, current) == '\n')
1147 break;
1148 } else
1149 return edit->last_byte;
1150 return current;
1153 /* returns index of first char on line */
1154 long edit_bol (WEdit * edit, long current)
1156 if (current > 0) {
1157 for (;; current--)
1158 if (edit_get_byte (edit, current - 1) == '\n')
1159 break;
1160 } else
1161 return 0;
1162 return current;
1166 int edit_count_lines (WEdit * edit, long current, int upto)
1168 int lines = 0;
1169 if (upto > edit->last_byte)
1170 upto = edit->last_byte;
1171 if (current < 0)
1172 current = 0;
1173 while (current < upto)
1174 if (edit_get_byte (edit, current++) == '\n')
1175 lines++;
1176 return lines;
1180 /* If lines is zero this returns the count of lines from current to upto. */
1181 /* If upto is zero returns index of lines forward current. */
1182 long edit_move_forward (WEdit * edit, long current, int lines, long upto)
1184 if (upto) {
1185 return edit_count_lines (edit, current, upto);
1186 } else {
1187 int next;
1188 if (lines < 0)
1189 lines = 0;
1190 while (lines--) {
1191 next = edit_eol (edit, current) + 1;
1192 if (next > edit->last_byte)
1193 break;
1194 else
1195 current = next;
1197 return current;
1202 /* Returns offset of 'lines' lines up from current */
1203 long edit_move_backward (WEdit * edit, long current, int lines)
1205 if (lines < 0)
1206 lines = 0;
1207 current = edit_bol (edit, current);
1208 while((lines--) && current != 0)
1209 current = edit_bol (edit, current - 1);
1210 return current;
1213 /* If cols is zero this returns the count of columns from current to upto. */
1214 /* If upto is zero returns index of cols across from current. */
1215 long edit_move_forward3 (WEdit * edit, long current, int cols, long upto)
1217 long p, q;
1218 int col = 0;
1220 if (upto) {
1221 q = upto;
1222 cols = -10;
1223 } else
1224 q = edit->last_byte + 2;
1226 for (col = 0, p = current; p < q; p++) {
1227 int c;
1228 if (cols != -10) {
1229 if (col == cols)
1230 return p;
1231 if (col > cols)
1232 return p - 1;
1234 c = edit_get_byte (edit, p);
1235 if (c == '\r')
1236 continue;
1237 else
1238 if (c == '\t')
1239 col += TAB_SIZE - col % TAB_SIZE;
1240 else
1241 col++;
1242 /*if(edit->nroff ... */
1243 if (c == '\n') {
1244 if (upto)
1245 return col;
1246 else
1247 return p;
1250 return col;
1253 /* returns the current column position of the cursor */
1254 int edit_get_col (WEdit * edit)
1256 return edit_move_forward3 (edit, edit_bol (edit, edit->curs1), 0, edit->curs1);
1260 /* Scrolling functions */
1262 void edit_update_curs_row (WEdit * edit)
1264 edit->curs_row = edit->curs_line - edit->start_line;
1267 void edit_update_curs_col (WEdit * edit)
1269 edit->curs_col = edit_move_forward3(edit, edit_bol(edit, edit->curs1), 0, edit->curs1);
1272 /*moves the display start position up by i lines */
1273 void edit_scroll_upward (WEdit * edit, unsigned long i)
1275 int lines_above = edit->start_line;
1276 if (i > lines_above)
1277 i = lines_above;
1278 if (i) {
1279 edit->start_line -= i;
1280 edit->start_display = edit_move_backward (edit, edit->start_display, i);
1281 edit->force |= REDRAW_PAGE;
1282 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1284 edit_update_curs_row (edit);
1288 /* returns 1 if could scroll, 0 otherwise */
1289 void edit_scroll_downward (WEdit * edit, int i)
1291 int lines_below;
1292 lines_below = edit->total_lines - edit->start_line - (edit->num_widget_lines - 1);
1293 if (lines_below > 0) {
1294 if (i > lines_below)
1295 i = lines_below;
1296 edit->start_line += i;
1297 edit->start_display = edit_move_forward (edit, edit->start_display, i, 0);
1298 edit->force |= REDRAW_PAGE;
1299 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1301 edit_update_curs_row (edit);
1304 void edit_scroll_right (WEdit * edit, int i)
1306 edit->force |= REDRAW_PAGE;
1307 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1308 edit->start_col -= i;
1311 void edit_scroll_left (WEdit * edit, int i)
1313 if (edit->start_col) {
1314 edit->start_col += i;
1315 if (edit->start_col > 0)
1316 edit->start_col = 0;
1317 edit->force |= REDRAW_PAGE;
1318 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1322 /* high level cursor movement commands */
1324 static int is_in_indent (WEdit *edit)
1326 long p = edit_bol (edit, edit->curs1);
1327 while (p < edit->curs1)
1328 if (!strchr (" \t", edit_get_byte (edit, p++)))
1329 return 0;
1330 return 1;
1333 static int left_of_four_spaces (WEdit *edit);
1335 void edit_move_to_prev_col (WEdit * edit, long p)
1337 edit_cursor_move (edit, edit_move_forward3 (edit, p, edit->prev_col, 0) - edit->curs1);
1339 if (is_in_indent (edit) && option_fake_half_tabs) {
1340 edit_update_curs_col (edit);
1341 if (space_width)
1342 if (edit->curs_col % (HALF_TAB_SIZE * space_width)) {
1343 int q = edit->curs_col;
1344 edit->curs_col -= (edit->curs_col % (HALF_TAB_SIZE * space_width));
1345 p = edit_bol (edit, edit->curs1);
1346 edit_cursor_move (edit, edit_move_forward3 (edit, p, edit->curs_col, 0) - edit->curs1);
1347 if (!left_of_four_spaces (edit))
1348 edit_cursor_move (edit, edit_move_forward3 (edit, p, q, 0) - edit->curs1);
1354 /* move i lines */
1355 void edit_move_up (WEdit * edit, unsigned long i, int scroll)
1357 long p, l = edit->curs_line;
1359 if (i > l)
1360 i = l;
1361 if (i) {
1362 if (i > 1)
1363 edit->force |= REDRAW_PAGE;
1364 if (scroll)
1365 edit_scroll_upward (edit, i);
1367 p = edit_bol (edit, edit->curs1);
1368 edit_cursor_move (edit, (p = edit_move_backward (edit, p, i)) - edit->curs1);
1369 edit_move_to_prev_col (edit, p);
1371 edit->search_start = edit->curs1;
1372 edit->found_len = 0;
1376 int is_blank (WEdit * edit, long offset)
1378 long s, f;
1379 int c;
1380 s = edit_bol (edit, offset);
1381 f = edit_eol (edit, offset) - 1;
1382 while (s <= f) {
1383 c = edit_get_byte (edit, s++);
1384 if (!isspace (c))
1385 return 0;
1387 return 1;
1391 /* returns the offset of line i */
1392 long edit_find_line (WEdit * edit, int line)
1394 int i, j = 0;
1395 int m = 2000000000;
1396 if (!edit->caches_valid) {
1397 for (i = 0; i < N_LINE_CACHES; i++)
1398 edit->line_numbers[i] = edit->line_offsets[i] = 0;
1399 /* three offsets that we *know* are line 0 at 0 and these two: */
1400 edit->line_numbers[1] = edit->curs_line;
1401 edit->line_offsets[1] = edit_bol (edit, edit->curs1);
1402 edit->line_numbers[2] = edit->total_lines;
1403 edit->line_offsets[2] = edit_bol (edit, edit->last_byte);
1404 edit->caches_valid = 1;
1406 if (line >= edit->total_lines)
1407 return edit->line_offsets[2];
1408 if (line <= 0)
1409 return 0;
1410 /* find the closest known point */
1411 for (i = 0; i < N_LINE_CACHES; i++) {
1412 int n;
1413 n = abs (edit->line_numbers[i] - line);
1414 if (n < m) {
1415 m = n;
1416 j = i;
1419 if (m == 0)
1420 return edit->line_offsets[j]; /* know the offset exactly */
1421 if (m == 1 && j >= 3)
1422 i = j; /* one line different - caller might be looping, so stay in this cache */
1423 else
1424 i = 3 + (rand () % (N_LINE_CACHES - 3));
1425 if (line > edit->line_numbers[j])
1426 edit->line_offsets[i] = edit_move_forward (edit, edit->line_offsets[j], line - edit->line_numbers[j], 0);
1427 else
1428 edit->line_offsets[i] = edit_move_backward (edit, edit->line_offsets[j], edit->line_numbers[j] - line);
1429 edit->line_numbers[i] = line;
1430 return edit->line_offsets[i];
1433 int line_is_blank (WEdit * edit, long line)
1435 return is_blank (edit, edit_find_line (edit, line));
1438 /* moves up until a blank line is reached, or until just
1439 before a non-blank line is reached */
1440 static void edit_move_up_paragraph (WEdit * edit, int scroll)
1442 int i;
1443 if (edit->curs_line <= 1) {
1444 i = 0;
1445 } else {
1446 if (line_is_blank (edit, edit->curs_line)) {
1447 if (line_is_blank (edit, edit->curs_line - 1)) {
1448 for (i = edit->curs_line - 1; i; i--)
1449 if (!line_is_blank (edit, i)) {
1450 i++;
1451 break;
1453 } else {
1454 for (i = edit->curs_line - 1; i; i--)
1455 if (line_is_blank (edit, i))
1456 break;
1458 } else {
1459 for (i = edit->curs_line - 1; i; i--)
1460 if (line_is_blank (edit, i))
1461 break;
1464 edit_move_up (edit, edit->curs_line - i, scroll);
1467 /* move i lines */
1468 void edit_move_down (WEdit * edit, int i, int scroll)
1470 long p, l = edit->total_lines - edit->curs_line;
1472 if (i > l)
1473 i = l;
1474 if (i) {
1475 if (i > 1)
1476 edit->force |= REDRAW_PAGE;
1477 if (scroll)
1478 edit_scroll_downward (edit, i);
1479 p = edit_bol (edit, edit->curs1);
1480 edit_cursor_move (edit, (p = edit_move_forward (edit, p, i, 0)) - edit->curs1);
1481 edit_move_to_prev_col (edit, p);
1483 edit->search_start = edit->curs1;
1484 edit->found_len = 0;
1488 /* moves down until a blank line is reached, or until just
1489 before a non-blank line is reached */
1490 static void edit_move_down_paragraph (WEdit * edit, int scroll)
1492 int i;
1493 if (edit->curs_line >= edit->total_lines - 1) {
1494 i = edit->total_lines;
1495 } else {
1496 if (line_is_blank (edit, edit->curs_line)) {
1497 if (line_is_blank (edit, edit->curs_line + 1)) {
1498 for (i = edit->curs_line + 1; i; i++)
1499 if (!line_is_blank (edit, i) || i > edit->total_lines) {
1500 i--;
1501 break;
1503 } else {
1504 for (i = edit->curs_line + 1; i; i++)
1505 if (line_is_blank (edit, i) || i >= edit->total_lines)
1506 break;
1508 } else {
1509 for (i = edit->curs_line + 1; i; i++)
1510 if (line_is_blank (edit, i) || i >= edit->total_lines)
1511 break;
1514 edit_move_down (edit, i - edit->curs_line, scroll);
1517 static void edit_begin_page (WEdit *edit)
1519 edit_update_curs_row (edit);
1520 edit_move_up (edit, edit->curs_row, 0);
1523 static void edit_end_page (WEdit *edit)
1525 edit_update_curs_row (edit);
1526 edit_move_down (edit, edit->num_widget_lines - edit->curs_row - 1, 0);
1530 /* goto beginning of text */
1531 static void edit_move_to_top (WEdit * edit)
1533 if (edit->curs_line) {
1534 edit_cursor_move (edit, -edit->curs1);
1535 edit_move_to_prev_col (edit, 0);
1536 edit->force |= REDRAW_PAGE;
1537 edit->search_start = 0;
1538 edit_update_curs_row(edit);
1543 /* goto end of text */
1544 static void edit_move_to_bottom (WEdit * edit)
1546 if (edit->curs_line < edit->total_lines) {
1547 edit_cursor_move (edit, edit->curs2);
1548 edit->start_display = edit->last_byte;
1549 edit->start_line = edit->total_lines;
1550 edit_update_curs_row(edit);
1551 edit_scroll_upward (edit, edit->num_widget_lines - 1);
1552 edit->force |= REDRAW_PAGE;
1556 /* goto beginning of line */
1557 static void edit_cursor_to_bol (WEdit * edit)
1559 edit_cursor_move (edit, edit_bol (edit, edit->curs1) - edit->curs1);
1560 edit->search_start = edit->curs1;
1561 edit->prev_col = edit_get_col (edit);
1564 /* goto end of line */
1565 static void edit_cursor_to_eol (WEdit * edit)
1567 edit_cursor_move (edit, edit_eol (edit, edit->curs1) - edit->curs1);
1568 edit->search_start = edit->curs1;
1569 edit->prev_col = edit_get_col (edit);
1572 /* move cursor to line 'line' */
1573 void edit_move_to_line (WEdit * e, long line)
1575 if(line < e->curs_line)
1576 edit_move_up (e, e->curs_line - line, 0);
1577 else
1578 edit_move_down (e, line - e->curs_line, 0);
1579 edit_scroll_screen_over_cursor (e);
1582 /* scroll window so that first visible line is 'line' */
1583 void edit_move_display (WEdit * e, long line)
1585 if(line < e->start_line)
1586 edit_scroll_upward (e, e->start_line - line);
1587 else
1588 edit_scroll_downward (e, line - e->start_line);
1591 /* save markers onto undo stack */
1592 void edit_push_markers (WEdit * edit)
1594 edit_push_action (edit, MARK_1 + edit->mark1);
1595 edit_push_action (edit, MARK_2 + edit->mark2);
1598 void free_selections (void)
1600 int i;
1601 for (i = 0; i < NUM_SELECTION_HISTORY; i++)
1602 if (selection_history[i].text) {
1603 free (selection_history[i].text);
1604 selection_history[i].text = 0;
1605 selection_history[i].len = 0;
1607 current_selection = 0;
1610 /* return -1 on nothing to store or error, zero otherwise */
1611 void edit_get_selection (WEdit * edit)
1613 long start_mark, end_mark;
1614 if (eval_marks (edit, &start_mark, &end_mark))
1615 return;
1616 if (selection_history[current_selection].len < 4096) /* large selections should not be held -- to save memory */
1617 current_selection = (current_selection + 1) % NUM_SELECTION_HISTORY;
1618 selection_history[current_selection].len = end_mark - start_mark;
1619 if (selection_history[current_selection].text)
1620 free (selection_history[current_selection].text);
1621 selection_history[current_selection].text = malloc (selection_history[current_selection].len + 1);
1622 if (!selection_history[current_selection].text) {
1623 selection_history[current_selection].text = malloc (1);
1624 *selection_history[current_selection].text = 0;
1625 selection_history[current_selection].len = 0;
1626 } else {
1627 unsigned char *p = selection_history[current_selection].text;
1628 for (; start_mark < end_mark; start_mark++)
1629 *p++ = edit_get_byte (edit, start_mark);
1630 *p = 0;
1632 selection.text = selection_history[current_selection].text;
1633 selection.len = selection_history[current_selection].len;
1636 void edit_set_markers (WEdit * edit, long m1, long m2, int c1, int c2)
1638 edit->mark1 = m1;
1639 edit->mark2 = m2;
1640 edit->column1 = c1;
1641 edit->column2 = c2;
1645 /* highlight marker toggle */
1646 void edit_mark_cmd (WEdit * edit, int unmark)
1648 edit_push_markers (edit);
1649 if (unmark) {
1650 edit_set_markers (edit, 0, 0, 0, 0);
1651 edit->force |= REDRAW_PAGE;
1652 } else {
1653 if (edit->mark2 >= 0) {
1654 edit_set_markers (edit, edit->curs1, -1, edit->curs_col, edit->curs_col);
1655 edit->force |= REDRAW_PAGE;
1656 } else
1657 edit_set_markers (edit, edit->mark1, edit->curs1, edit->column1, edit->curs_col);
1661 static unsigned long my_type_of (int c)
1663 int x, r = 0;
1664 char *p, *q;
1665 if (!c)
1666 return 0;
1667 if (c == '!') {
1668 if (*option_chars_move_whole_word == '!')
1669 return 2;
1670 return 0x80000000UL;
1672 if (isupper (c))
1673 c = 'A';
1674 else if (islower (c))
1675 c = 'a';
1676 else if (isalpha (c))
1677 c = 'a';
1678 else if (isdigit (c))
1679 c = '0';
1680 else if (isspace (c))
1681 c = ' ';
1682 q = strchr (option_chars_move_whole_word, c);
1683 if (!q)
1684 return 0xFFFFFFFFUL;
1685 do {
1686 for (x = 1, p = option_chars_move_whole_word; p < q; p++)
1687 if (*p == '!')
1688 x <<= 1;
1689 r |= x;
1690 } while ((q = strchr (q + 1, c)));
1691 return r;
1694 void edit_left_word_move (WEdit * edit, int s)
1696 for (;;) {
1697 int c1, c2;
1698 edit_cursor_move (edit, -1);
1699 if (!edit->curs1)
1700 break;
1701 c1 = edit_get_byte (edit, edit->curs1 - 1);
1702 c2 = edit_get_byte (edit, edit->curs1);
1703 if (!(my_type_of (c1) & my_type_of (c2)))
1704 break;
1705 if (isspace (c1) && !isspace (c2))
1706 break;
1707 if (s)
1708 if (!isspace (c1) && isspace (c2))
1709 break;
1713 static void edit_left_word_move_cmd (WEdit * edit)
1715 edit_left_word_move (edit, 0);
1716 edit->force |= REDRAW_PAGE;
1719 void edit_right_word_move (WEdit * edit, int s)
1721 for (;;) {
1722 int c1, c2;
1723 edit_cursor_move (edit, 1);
1724 if (edit->curs1 >= edit->last_byte)
1725 break;
1726 c1 = edit_get_byte (edit, edit->curs1 - 1);
1727 c2 = edit_get_byte (edit, edit->curs1);
1728 if (!(my_type_of (c1) & my_type_of (c2)))
1729 break;
1730 if (isspace (c1) && !isspace (c2))
1731 break;
1732 if (s)
1733 if (!isspace (c1) && isspace (c2))
1734 break;
1738 static void edit_right_word_move_cmd (WEdit * edit)
1740 edit_right_word_move (edit, 0);
1741 edit->force |= REDRAW_PAGE;
1745 static void edit_right_delete_word (WEdit * edit)
1747 int c1, c2;
1748 for (;;) {
1749 if (edit->curs1 >= edit->last_byte)
1750 break;
1751 c1 = edit_delete (edit);
1752 c2 = edit_get_byte (edit, edit->curs1);
1753 if ((isspace (c1) == 0) != (isspace (c2) == 0))
1754 break;
1755 if (!(my_type_of (c1) & my_type_of (c2)))
1756 break;
1760 static void edit_left_delete_word (WEdit * edit)
1762 int c1, c2;
1763 for (;;) {
1764 if (edit->curs1 <= 0)
1765 break;
1766 c1 = edit_backspace (edit);
1767 c2 = edit_get_byte (edit, edit->curs1 - 1);
1768 if ((isspace (c1) == 0) != (isspace (c2) == 0))
1769 break;
1770 if (!(my_type_of (c1) & my_type_of (c2)))
1771 break;
1776 the start column position is not recorded, and hence does not
1777 undo as it happed. But who would notice.
1779 void edit_do_undo (WEdit * edit)
1781 long ac;
1782 long count = 0;
1784 push_action_disabled = 1; /* don't record undo's onto undo stack! */
1786 while ((ac = pop_action (edit)) < KEY_PRESS) {
1787 switch ((int) ac) {
1788 case STACK_BOTTOM:
1789 goto done_undo;
1790 case CURS_RIGHT:
1791 edit_cursor_move (edit, 1);
1792 break;
1793 case CURS_LEFT:
1794 edit_cursor_move (edit, -1);
1795 break;
1796 case BACKSPACE:
1797 edit_backspace (edit);
1798 break;
1799 case DELCHAR:
1800 edit_delete (edit);
1801 break;
1802 case COLUMN_ON:
1803 column_highlighting = 1;
1804 break;
1805 case COLUMN_OFF:
1806 column_highlighting = 0;
1807 break;
1809 if (ac >= 256 && ac < 512)
1810 edit_insert_ahead (edit, ac - 256);
1811 if (ac >= 0 && ac < 256)
1812 edit_insert (edit, ac);
1814 if (ac >= MARK_1 - 2 && ac < MARK_2 - 2) {
1815 edit->mark1 = ac - MARK_1;
1816 edit->column1 = edit_move_forward3 (edit, edit_bol (edit, edit->mark1), 0, edit->mark1);
1817 } else if (ac >= MARK_2 - 2 && ac < KEY_PRESS) {
1818 edit->mark2 = ac - MARK_2;
1819 edit->column2 = edit_move_forward3 (edit, edit_bol (edit, edit->mark2), 0, edit->mark2);
1821 if (count++)
1822 edit->force |= REDRAW_PAGE; /* more than one pop usually means something big */
1825 if (edit->start_display > ac - KEY_PRESS) {
1826 edit->start_line -= edit_count_lines (edit, ac - KEY_PRESS, edit->start_display);
1827 edit->force |= REDRAW_PAGE;
1828 } else if (edit->start_display < ac - KEY_PRESS) {
1829 edit->start_line += edit_count_lines (edit, edit->start_display, ac - KEY_PRESS);
1830 edit->force |= REDRAW_PAGE;
1832 edit->start_display = ac - KEY_PRESS; /* see push and pop above */
1833 edit_update_curs_row (edit);
1835 done_undo:;
1836 push_action_disabled = 0;
1839 static void edit_delete_to_line_end (WEdit * edit)
1841 while (edit_get_byte (edit, edit->curs1) != '\n') {
1842 if (!edit->curs2)
1843 break;
1844 edit_delete (edit);
1848 static void edit_delete_to_line_begin (WEdit * edit)
1850 while (edit_get_byte (edit, edit->curs1 - 1) != '\n') {
1851 if (!edit->curs1)
1852 break;
1853 edit_backspace (edit);
1857 void edit_delete_line (WEdit * edit)
1859 int c;
1860 do {
1861 c = edit_delete (edit);
1862 } while (c != '\n' && c);
1863 do {
1864 c = edit_backspace (edit);
1865 } while (c != '\n' && c);
1866 if (c)
1867 edit_insert (edit, '\n');
1870 static void insert_spaces_tab (WEdit * edit, int half)
1872 int i;
1873 edit_update_curs_col (edit);
1874 i = ((edit->curs_col / (option_tab_spacing * space_width / (half + 1))) + 1) * (option_tab_spacing * space_width / (half + 1)) - edit->curs_col;
1875 while (i > 0) {
1876 edit_insert (edit, ' ');
1877 i -= space_width;
1881 static int is_aligned_on_a_tab (WEdit * edit)
1883 edit_update_curs_col (edit);
1884 if ((edit->curs_col % (TAB_SIZE * space_width)) && edit->curs_col % (TAB_SIZE * space_width) != (HALF_TAB_SIZE * space_width))
1885 return 0; /* not alligned on a tab */
1886 return 1;
1889 static int right_of_four_spaces (WEdit *edit)
1891 int i, ch = 0;
1892 for (i = 1; i <= HALF_TAB_SIZE; i++)
1893 ch |= edit_get_byte (edit, edit->curs1 - i);
1894 if (ch == ' ')
1895 return is_aligned_on_a_tab (edit);
1896 return 0;
1899 static int left_of_four_spaces (WEdit *edit)
1901 int i, ch = 0;
1902 for (i = 0; i < HALF_TAB_SIZE; i++)
1903 ch |= edit_get_byte (edit, edit->curs1 + i);
1904 if (ch == ' ')
1905 return is_aligned_on_a_tab (edit);
1906 return 0;
1909 int edit_indent_width (WEdit * edit, long p)
1911 long q = p;
1912 while (strchr ("\t ", edit_get_byte (edit, q)) && q < edit->last_byte - 1) /* move to the end of the leading whitespace of the line */
1913 q++;
1914 return edit_move_forward3 (edit, p, 0, q); /* count the number of columns of indentation */
1917 void edit_insert_indent (WEdit * edit, int indent)
1919 if (!option_fill_tabs_with_spaces) {
1920 while (indent >= TAB_SIZE) {
1921 edit_insert (edit, '\t');
1922 indent -= TAB_SIZE;
1925 while (indent-- > 0)
1926 edit_insert (edit, ' ');
1929 void edit_auto_indent (WEdit * edit, int extra, int no_advance)
1931 long p;
1932 int indent;
1933 p = edit->curs1;
1934 while (isspace (edit_get_byte (edit, p - 1)) && p > 0) /* move back/up to a line with text */
1935 p--;
1936 indent = edit_indent_width (edit, edit_bol (edit, p));
1937 if (edit->curs_col < indent && no_advance)
1938 indent = edit->curs_col;
1939 edit_insert_indent (edit, indent + (option_fake_half_tabs ? HALF_TAB_SIZE : TAB_SIZE) * space_width * extra);
1942 static void edit_double_newline (WEdit * edit)
1944 edit_insert (edit, '\n');
1945 if (edit_get_byte (edit, edit->curs1) == '\n')
1946 return;
1947 if (edit_get_byte (edit, edit->curs1 - 2) == '\n')
1948 return;
1949 edit->force |= REDRAW_PAGE;
1950 edit_insert (edit, '\n');
1953 static void edit_tab_cmd (WEdit * edit)
1955 int i;
1957 if (option_fake_half_tabs) {
1958 if (is_in_indent (edit)) {
1959 /*insert a half tab (usually four spaces) unless there is a
1960 half tab already behind, then delete it and insert a
1961 full tab. */
1962 if (!option_fill_tabs_with_spaces && right_of_four_spaces (edit)) {
1963 for (i = 1; i <= HALF_TAB_SIZE; i++)
1964 edit_backspace (edit);
1965 edit_insert (edit, '\t');
1966 } else {
1967 insert_spaces_tab (edit, 1);
1969 return;
1972 if (option_fill_tabs_with_spaces) {
1973 insert_spaces_tab (edit, 0);
1974 } else {
1975 edit_insert (edit, '\t');
1977 return;
1980 void format_paragraph (WEdit * edit, int force);
1982 static void check_and_wrap_line (WEdit * edit)
1984 int curs, c;
1985 if (!option_typewriter_wrap)
1986 return;
1987 edit_update_curs_col (edit);
1988 if (edit->curs_col < option_word_wrap_line_length)
1989 return;
1990 curs = edit->curs1;
1991 for (;;) {
1992 curs--;
1993 c = edit_get_byte (edit, curs);
1994 if (c == '\n' || curs <= 0) {
1995 edit_insert (edit, '\n');
1996 return;
1998 if (c == ' ' || c == '\t') {
1999 int current = edit->curs1;
2000 edit_cursor_move (edit, curs - edit->curs1 + 1);
2001 edit_insert (edit, '\n');
2002 edit_cursor_move (edit, current - edit->curs1 + 1);
2003 return;
2008 void edit_execute_macro (WEdit * edit, struct macro macro[], int n);
2010 int edit_translate_key (WEdit * edit, unsigned int x_keycode, long x_key, int x_state, int *cmd, int *ch)
2012 int command = -1;
2013 int char_for_insertion = -1;
2015 #include "edit_key_translator.c"
2017 *cmd = command;
2018 *ch = char_for_insertion;
2020 if((command == -1 || command == 0) && char_for_insertion == -1) /* unchanged, key has no function here */
2021 return 0;
2022 return 1;
2025 void edit_push_key_press (WEdit * edit)
2027 edit_push_action (edit, KEY_PRESS + edit->start_display);
2028 if (edit->mark2 == -1)
2029 edit_push_action (edit, MARK_1 + edit->mark1);
2032 /* this find the matching bracket in either direction, and sets edit->bracket */
2033 static long edit_get_bracket (WEdit * edit, int in_screen, unsigned long furthest_bracket_search)
2035 const char *b = "{}{[][()(", *p;
2036 int i = 1, a, inc = -1, c, d, n = 0;
2037 unsigned long j = 0;
2038 long q;
2039 edit_update_curs_row (edit);
2040 c = edit_get_byte (edit, edit->curs1);
2041 p = strchr (b, c);
2042 /* no limit */
2043 if (!furthest_bracket_search)
2044 furthest_bracket_search--;
2045 /* not on a bracket at all */
2046 if (!p)
2047 return -1;
2048 /* the matching bracket */
2049 d = p[1];
2050 /* going left or right? */
2051 if (strchr ("{[(", c))
2052 inc = 1;
2053 for (q = edit->curs1 + inc;; q += inc) {
2054 /* out of buffer? */
2055 if (q >= edit->last_byte || q < 0)
2056 break;
2057 a = edit_get_byte (edit, q);
2058 /* don't want to eat CPU */
2059 if (j++ > furthest_bracket_search)
2060 break;
2061 /* out of screen? */
2062 if (in_screen) {
2063 if (q < edit->start_display)
2064 break;
2065 /* count lines if searching downward */
2066 if (inc > 0 && a == '\n')
2067 if (n++ >= edit->num_widget_lines - edit->curs_row) /* out of screen */
2068 break;
2070 /* count bracket depth */
2071 i += (a == c) - (a == d);
2072 /* return if bracket depth is zero */
2073 if (!i)
2074 return q;
2076 /* no match */
2077 return -1;
2080 static long last_bracket = -1;
2082 static void edit_find_bracket (WEdit * edit)
2084 edit->bracket = edit_get_bracket (edit, 1, 10000);
2085 if (last_bracket != edit->bracket)
2086 edit->force |= REDRAW_PAGE;
2087 last_bracket = edit->bracket;
2090 static void edit_goto_matching_bracket (WEdit *edit)
2092 long q;
2093 q = edit_get_bracket (edit, 0, 0);
2094 if (q < 0)
2095 return;
2096 edit->bracket = edit->curs1;
2097 edit->force |= REDRAW_PAGE;
2098 edit_cursor_move (edit, q - edit->curs1);
2101 /* this executes a command as though the user initiated it through a key press. */
2102 /* callback with WIDGET_KEY as a message calls this after translating the key
2103 press */
2104 /* this can be used to pass any command to the editor. Same as sendevent with
2105 msg = WIDGET_COMMAND and par = command except the screen wouldn't update */
2106 /* one of command or char_for_insertion must be passed as -1 */
2107 /* commands are executed, and char_for_insertion is inserted at the cursor */
2108 /* returns 0 if the command is a macro that was not found, 1 otherwise */
2109 int edit_execute_key_command (WEdit * edit, int command, int char_for_insertion)
2111 int r;
2112 if (command == CK_Begin_Record_Macro) {
2113 edit->macro_i = 0;
2114 edit->force |= REDRAW_CHAR_ONLY | REDRAW_LINE;
2115 return command;
2117 if (command == CK_End_Record_Macro && edit->macro_i != -1) {
2118 edit->force |= REDRAW_COMPLETELY;
2119 edit_save_macro_cmd (edit, edit->macro, edit->macro_i);
2120 edit->macro_i = -1;
2121 return command;
2123 if (edit->macro_i >= 0 && edit->macro_i < MAX_MACRO_LENGTH - 1) {
2124 edit->macro[edit->macro_i].command = command;
2125 edit->macro[edit->macro_i++].ch = char_for_insertion;
2127 /* record the beginning of a set of editing actions initiated by a key press */
2128 if (command != CK_Undo)
2129 edit_push_key_press (edit);
2131 r = edit_execute_cmd (edit, command, char_for_insertion);
2132 if (column_highlighting)
2133 edit->force |= REDRAW_PAGE;
2135 return r;
2138 static const char * const shell_cmd[] = SHELL_COMMANDS_i
2139 void edit_mail_dialog (WEdit * edit);
2142 This executes a command at a lower level than macro recording.
2143 It also does not push a key_press onto the undo stack. This means
2144 that if it is called many times, a single undo command will undo
2145 all of them. It also does not check for the Undo command.
2146 Returns 0 if the command is a macro that was not found, 1
2147 otherwise.
2149 int edit_execute_cmd (WEdit * edit, int command, int char_for_insertion)
2151 int result = 1;
2152 edit->force |= REDRAW_LINE;
2153 if (edit->found_len || column_highlighting)
2154 /* the next key press will unhighlight the found string, so update whole page */
2155 edit->force |= REDRAW_PAGE;
2157 if (command / 100 == 6) { /* a highlight command like shift-arrow */
2158 column_highlighting = 0;
2159 if (!edit->highlight || (edit->mark2 != -1 && edit->mark1 != edit->mark2)) {
2160 edit_mark_cmd (edit, 1); /* clear */
2161 edit_mark_cmd (edit, 0); /* marking on */
2163 edit->highlight = 1;
2164 } else { /* any other command */
2165 if (edit->highlight)
2166 edit_mark_cmd (edit, 0); /* clear */
2167 edit->highlight = 0;
2170 /* first check for undo */
2171 if (command == CK_Undo) {
2172 edit_do_undo (edit);
2173 edit->found_len = 0;
2174 edit->prev_col = edit_get_col (edit);
2175 edit->search_start = edit->curs1;
2176 return 1;
2178 /* An ordinary key press */
2179 if (char_for_insertion >= 0) {
2180 if (edit->overwrite) {
2181 if (edit_get_byte (edit, edit->curs1) != '\n')
2182 edit_delete (edit);
2184 edit_insert (edit, char_for_insertion);
2185 if (option_auto_para_formatting) {
2186 format_paragraph (edit, 0);
2187 edit->force |= REDRAW_PAGE;
2188 } else
2189 check_and_wrap_line (edit);
2190 edit->found_len = 0;
2191 edit->prev_col = edit_get_col (edit);
2192 edit->search_start = edit->curs1;
2193 edit_find_bracket (edit);
2194 edit_check_spelling (edit);
2195 return 1;
2197 switch (command) {
2198 case CK_Begin_Page:
2199 case CK_End_Page:
2200 case CK_Begin_Page_Highlight:
2201 case CK_End_Page_Highlight:
2202 case CK_Word_Left:
2203 case CK_Word_Right:
2204 case CK_Up:
2205 case CK_Down:
2206 case CK_Word_Left_Highlight:
2207 case CK_Word_Right_Highlight:
2208 case CK_Up_Highlight:
2209 case CK_Down_Highlight:
2210 if (edit->mark2 == -1)
2211 break; /*marking is following the cursor: may need to highlight a whole line */
2212 case CK_Left:
2213 case CK_Right:
2214 case CK_Left_Highlight:
2215 case CK_Right_Highlight:
2216 edit->force |= REDRAW_CHAR_ONLY;
2219 /* basic cursor key commands */
2220 switch (command) {
2221 case CK_BackSpace:
2222 if (option_backspace_through_tabs && is_in_indent (edit)) {
2223 while (edit_get_byte (edit, edit->curs1 - 1) != '\n'
2224 && edit->curs1 > 0)
2225 edit_backspace (edit);
2226 break;
2227 } else {
2228 if (option_fake_half_tabs) {
2229 int i;
2230 if (is_in_indent (edit) && right_of_four_spaces (edit)) {
2231 for (i = 0; i < HALF_TAB_SIZE; i++)
2232 edit_backspace (edit);
2233 break;
2237 edit_backspace (edit);
2238 break;
2239 case CK_Delete:
2240 if (option_fake_half_tabs) {
2241 int i;
2242 if (is_in_indent (edit) && left_of_four_spaces (edit)) {
2243 for (i = 1; i <= HALF_TAB_SIZE; i++)
2244 edit_delete (edit);
2245 break;
2248 edit_delete (edit);
2249 break;
2250 case CK_Delete_Word_Left:
2251 edit_left_delete_word (edit);
2252 break;
2253 case CK_Delete_Word_Right:
2254 edit_right_delete_word (edit);
2255 break;
2256 case CK_Delete_Line:
2257 edit_delete_line (edit);
2258 break;
2259 case CK_Delete_To_Line_End:
2260 edit_delete_to_line_end (edit);
2261 break;
2262 case CK_Delete_To_Line_Begin:
2263 edit_delete_to_line_begin (edit);
2264 break;
2265 case CK_Enter:
2266 if (option_auto_para_formatting) {
2267 edit_double_newline (edit);
2268 if (option_return_does_auto_indent)
2269 edit_auto_indent (edit, 0, 1);
2270 format_paragraph (edit, 0);
2271 } else {
2272 edit_insert (edit, '\n');
2273 if (option_return_does_auto_indent) {
2274 edit_auto_indent (edit, 0, 1);
2277 break;
2278 case CK_Return:
2279 edit_insert (edit, '\n');
2280 break;
2282 case CK_Page_Up:
2283 case CK_Page_Up_Highlight:
2284 edit_move_up (edit, edit->num_widget_lines - 1, 1);
2285 break;
2286 case CK_Page_Down:
2287 case CK_Page_Down_Highlight:
2288 edit_move_down (edit, edit->num_widget_lines - 1, 1);
2289 break;
2290 case CK_Left:
2291 case CK_Left_Highlight:
2292 if (option_fake_half_tabs) {
2293 if (is_in_indent (edit) && right_of_four_spaces (edit)) {
2294 edit_cursor_move (edit, -HALF_TAB_SIZE);
2295 edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
2296 break;
2299 edit_cursor_move (edit, -1);
2300 break;
2301 case CK_Right:
2302 case CK_Right_Highlight:
2303 if (option_fake_half_tabs) {
2304 if (is_in_indent (edit) && left_of_four_spaces (edit)) {
2305 edit_cursor_move (edit, HALF_TAB_SIZE);
2306 edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
2307 break;
2310 edit_cursor_move (edit, 1);
2311 break;
2312 case CK_Begin_Page:
2313 case CK_Begin_Page_Highlight:
2314 edit_begin_page (edit);
2315 break;
2316 case CK_End_Page:
2317 case CK_End_Page_Highlight:
2318 edit_end_page (edit);
2319 break;
2320 case CK_Word_Left:
2321 case CK_Word_Left_Highlight:
2322 edit_left_word_move_cmd (edit);
2323 break;
2324 case CK_Word_Right:
2325 case CK_Word_Right_Highlight:
2326 edit_right_word_move_cmd (edit);
2327 break;
2328 case CK_Up:
2329 case CK_Up_Highlight:
2330 edit_move_up (edit, 1, 0);
2331 break;
2332 case CK_Down:
2333 case CK_Down_Highlight:
2334 edit_move_down (edit, 1, 0);
2335 break;
2336 case CK_Paragraph_Up:
2337 case CK_Paragraph_Up_Highlight:
2338 edit_move_up_paragraph (edit, 0);
2339 break;
2340 case CK_Paragraph_Down:
2341 case CK_Paragraph_Down_Highlight:
2342 edit_move_down_paragraph (edit, 0);
2343 break;
2344 case CK_Scroll_Up:
2345 case CK_Scroll_Up_Highlight:
2346 edit_move_up (edit, 1, 1);
2347 break;
2348 case CK_Scroll_Down:
2349 case CK_Scroll_Down_Highlight:
2350 edit_move_down (edit, 1, 1);
2351 break;
2352 case CK_Home:
2353 case CK_Home_Highlight:
2354 edit_cursor_to_bol (edit);
2355 break;
2356 case CK_End:
2357 case CK_End_Highlight:
2358 edit_cursor_to_eol (edit);
2359 break;
2361 case CK_Tab:
2362 edit_tab_cmd (edit);
2363 if (option_auto_para_formatting) {
2364 format_paragraph (edit, 0);
2365 edit->force |= REDRAW_PAGE;
2366 } else
2367 check_and_wrap_line (edit);
2368 break;
2370 case CK_Toggle_Insert:
2371 edit->overwrite = (edit->overwrite == 0);
2372 break;
2374 case CK_Mark:
2375 if (edit->mark2 >= 0) {
2376 if (column_highlighting)
2377 edit_push_action (edit, COLUMN_ON);
2378 column_highlighting = 0;
2380 edit_mark_cmd (edit, 0);
2381 break;
2382 case CK_Column_Mark:
2383 if (!column_highlighting)
2384 edit_push_action (edit, COLUMN_OFF);
2385 column_highlighting = 1;
2386 edit_mark_cmd (edit, 0);
2387 break;
2388 case CK_Unmark:
2389 if (column_highlighting)
2390 edit_push_action (edit, COLUMN_ON);
2391 column_highlighting = 0;
2392 edit_mark_cmd (edit, 1);
2393 break;
2395 case CK_Toggle_Bookmark:
2396 book_mark_clear (edit, edit->curs_line, BOOK_MARK_FOUND_COLOR);
2397 if (book_mark_query_color (edit, edit->curs_line, BOOK_MARK_COLOR))
2398 book_mark_clear (edit, edit->curs_line, BOOK_MARK_COLOR);
2399 else
2400 book_mark_insert (edit, edit->curs_line, BOOK_MARK_COLOR);
2401 break;
2402 case CK_Flush_Bookmarks:
2403 book_mark_flush (edit, BOOK_MARK_COLOR);
2404 book_mark_flush (edit, BOOK_MARK_FOUND_COLOR);
2405 edit->force |= REDRAW_PAGE;
2406 break;
2407 case CK_Next_Bookmark:
2408 if (edit->book_mark) {
2409 struct _book_mark *p;
2410 p = (struct _book_mark *) book_mark_find (edit, edit->curs_line);
2411 if (p->next) {
2412 p = p->next;
2413 if (p->line >= edit->start_line + edit->num_widget_lines || p->line < edit->start_line)
2414 edit_move_display (edit, p->line - edit->num_widget_lines / 2);
2415 edit_move_to_line (edit, p->line);
2418 break;
2419 case CK_Prev_Bookmark:
2420 if (edit->book_mark) {
2421 struct _book_mark *p;
2422 p = (struct _book_mark *) book_mark_find (edit, edit->curs_line);
2423 while (p->line == edit->curs_line)
2424 if (p->prev)
2425 p = p->prev;
2426 if (p->line >= 0) {
2427 if (p->line >= edit->start_line + edit->num_widget_lines || p->line < edit->start_line)
2428 edit_move_display (edit, p->line - edit->num_widget_lines / 2);
2429 edit_move_to_line (edit, p->line);
2432 break;
2434 case CK_Beginning_Of_Text:
2435 case CK_Beginning_Of_Text_Highlight:
2436 edit_move_to_top (edit);
2437 break;
2438 case CK_End_Of_Text:
2439 case CK_End_Of_Text_Highlight:
2440 edit_move_to_bottom (edit);
2441 break;
2443 case CK_Copy:
2444 edit_block_copy_cmd (edit);
2445 break;
2446 case CK_Remove:
2447 edit_block_delete_cmd (edit);
2448 break;
2449 case CK_Move:
2450 edit_block_move_cmd (edit);
2451 break;
2453 case CK_XStore:
2454 edit_copy_to_X_buf_cmd (edit);
2455 break;
2456 case CK_XCut:
2457 edit_cut_to_X_buf_cmd (edit);
2458 break;
2459 case CK_XPaste:
2460 edit_paste_from_X_buf_cmd (edit);
2461 break;
2462 case CK_Selection_History:
2463 edit_paste_from_history (edit);
2464 break;
2466 case CK_Save_As:
2467 edit_save_as_cmd (edit);
2468 break;
2469 case CK_Save:
2470 edit_save_confirm_cmd (edit);
2471 break;
2472 case CK_Load:
2473 edit_load_cmd (edit);
2474 break;
2475 case CK_Save_Block:
2476 edit_save_block_cmd (edit);
2477 break;
2478 case CK_Insert_File:
2479 edit_insert_file_cmd (edit);
2480 break;
2482 case CK_Find:
2483 edit_search_cmd (edit, 0);
2484 break;
2485 case CK_Find_Again:
2486 edit_search_cmd (edit, 1);
2487 break;
2488 case CK_Replace:
2489 edit_replace_cmd (edit, 0);
2490 break;
2491 case CK_Replace_Again:
2492 edit_replace_cmd (edit, 1);
2493 break;
2494 case CK_Complete_Word:
2495 edit_complete_word_cmd (edit);
2496 break;
2498 case CK_Exit:
2499 edit_quit_cmd (edit);
2500 break;
2501 case CK_New:
2502 edit_new_cmd (edit);
2503 break;
2505 case CK_Help:
2506 edit_help_cmd (edit);
2507 break;
2509 case CK_Refresh:
2510 edit_refresh_cmd (edit);
2511 break;
2513 case CK_Date:{
2514 time_t t;
2515 #ifdef HAVE_STRFTIME
2516 char s[1024];
2517 static const char time_format[] = "%c";
2518 #endif
2519 time (&t);
2520 #ifdef HAVE_STRFTIME
2521 strftime (s, sizeof (s), time_format, localtime (&t));
2522 edit_print_string (edit, s);
2523 #else
2524 edit_print_string (edit, ctime (&t));
2525 #endif
2526 edit->force |= REDRAW_PAGE;
2527 break;
2529 case CK_Goto:
2530 edit_goto_cmd (edit);
2531 break;
2532 case CK_Paragraph_Format:
2533 format_paragraph (edit, 1);
2534 edit->force |= REDRAW_PAGE;
2535 break;
2536 case CK_Delete_Macro:
2537 edit_delete_macro_cmd (edit);
2538 break;
2539 case CK_Match_Bracket:
2540 edit_goto_matching_bracket (edit);
2541 break;
2542 case CK_User_Menu:
2543 if (edit_one_file) {
2544 message (1, MSG_ERROR, _("User menu available only in mcedit invoked from mc"));
2545 break;
2547 else
2548 user_menu (edit);
2549 break;
2550 case CK_Sort:
2551 edit_sort_cmd (edit);
2552 break;
2553 case CK_Mail:
2554 edit_mail_dialog (edit);
2555 break;
2556 case CK_Shell:
2557 view_other_cmd ();
2558 break;
2560 /* These commands are not handled and must be handled by the user application */
2561 #if 0
2562 case CK_Sort:
2563 case CK_Mail:
2564 case CK_Find_File:
2565 case CK_Ctags:
2566 case CK_Terminal:
2567 case CK_Terminal_App:
2568 #endif
2569 case CK_Complete:
2570 case CK_Cancel:
2571 case CK_Save_Desktop:
2572 case CK_New_Window:
2573 case CK_Cycle:
2574 case CK_Save_And_Quit:
2575 case CK_Check_Save_And_Quit:
2576 case CK_Run_Another:
2577 case CK_Debug_Start:
2578 case CK_Debug_Stop:
2579 case CK_Debug_Toggle_Break:
2580 case CK_Debug_Clear:
2581 case CK_Debug_Next:
2582 case CK_Debug_Step:
2583 case CK_Debug_Back_Trace:
2584 case CK_Debug_Continue:
2585 case CK_Debug_Enter_Command:
2586 case CK_Debug_Until_Curser:
2587 result = 0;
2588 break;
2589 case CK_Menu:
2590 result = 0;
2591 break;
2594 /* CK_Pipe_Block */
2595 if ((command / 1000) == 1) /* a shell command */
2596 edit_block_process_cmd (edit, shell_cmd[command - 1000], 1);
2597 if (command > CK_Macro (0) && command <= CK_Last_Macro) { /* a macro command */
2598 struct macro m[MAX_MACRO_LENGTH];
2599 int nm;
2600 if ((result = edit_load_macro_cmd (edit, m, &nm, command - 2000)))
2601 edit_execute_macro (edit, m, nm);
2604 /* keys which must set the col position, and the search vars */
2605 switch (command) {
2606 case CK_Find:
2607 case CK_Find_Again:
2608 case CK_Replace:
2609 case CK_Replace_Again:
2610 case CK_Complete_Word:
2611 edit->prev_col = edit_get_col (edit);
2612 return 1;
2613 break;
2614 case CK_Up:
2615 case CK_Up_Highlight:
2616 case CK_Down:
2617 case CK_Down_Highlight:
2618 case CK_Page_Up:
2619 case CK_Page_Up_Highlight:
2620 case CK_Page_Down:
2621 case CK_Page_Down_Highlight:
2622 case CK_Beginning_Of_Text:
2623 case CK_Beginning_Of_Text_Highlight:
2624 case CK_End_Of_Text:
2625 case CK_End_Of_Text_Highlight:
2626 case CK_Paragraph_Up:
2627 case CK_Paragraph_Up_Highlight:
2628 case CK_Paragraph_Down:
2629 case CK_Paragraph_Down_Highlight:
2630 case CK_Scroll_Up:
2631 case CK_Scroll_Up_Highlight:
2632 case CK_Scroll_Down:
2633 case CK_Scroll_Down_Highlight:
2634 edit->search_start = edit->curs1;
2635 edit->found_len = 0;
2636 edit_find_bracket (edit);
2637 edit_check_spelling (edit);
2638 return 1;
2639 break;
2640 default:
2641 edit->found_len = 0;
2642 edit->prev_col = edit_get_col (edit);
2643 edit->search_start = edit->curs1;
2645 edit_find_bracket (edit);
2646 edit_check_spelling (edit);
2648 if (option_auto_para_formatting) {
2649 switch (command) {
2650 case CK_BackSpace:
2651 case CK_Delete:
2652 case CK_Delete_Word_Left:
2653 case CK_Delete_Word_Right:
2654 case CK_Delete_To_Line_End:
2655 case CK_Delete_To_Line_Begin:
2656 format_paragraph (edit, 0);
2657 edit->force |= REDRAW_PAGE;
2660 return result;
2664 /* either command or char_for_insertion must be passed as -1 */
2665 /* returns 0 if command is a macro that was not found, 1 otherwise */
2666 int edit_execute_command (WEdit * edit, int command, int char_for_insertion)
2668 int r;
2669 r = edit_execute_cmd (edit, command, char_for_insertion);
2670 edit_update_screen (edit);
2671 return r;
2674 void edit_execute_macro (WEdit * edit, struct macro macro[], int n)
2676 int i = 0;
2677 edit->force |= REDRAW_PAGE;
2678 for (; i < n; i++) {
2679 edit_execute_cmd (edit, macro[i].command, macro[i].ch);
2681 edit_update_screen (edit);
2684 /* User edit menu, like user menu (F2) but only in editor. */
2685 void
2686 user_menu (WEdit * edit)
2688 FILE *fd;
2689 int nomark;
2690 struct stat status;
2691 long start_mark, end_mark;
2692 char *block_file = catstrs (home_dir, BLOCK_FILE, 0);
2693 int rc = 0;
2695 nomark = eval_marks (edit, &start_mark, &end_mark);
2696 if (!nomark) /* remember marked or not */
2697 edit_save_block (edit, block_file, start_mark, end_mark);
2699 /* run shell scripts from menu */
2700 user_menu_cmd (edit);
2702 if (mc_stat (block_file, &status) != 0 || !status.st_size) {
2703 /* no block messages */
2704 return;
2707 if (!nomark) {
2708 /* i.e. we have marked block */
2709 rc = edit_block_delete_cmd (edit);
2712 if (!rc) {
2713 edit_insert_file (edit, block_file);
2716 /* truncate block file */
2717 if ((fd = fopen (block_file, "w"))) {
2718 fclose (fd);
2721 edit_refresh_cmd (edit);
2722 edit->force |= REDRAW_COMPLETELY;
2723 return;