my homework was late :-)
[midnight-commander.git] / gtkedit / edit.c
blob8e6545dcddf68a77aeca49d2775d4c3be0a41a51
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 #define _EDIT_C THIS_IS
27 #include <config.h>
28 #if defined(NEEDS_IO_H)
29 # include <io.h>
30 # include <fcntl.h>
31 # define CR_LF_TRANSLATION
32 #endif
33 #include "edit.h"
35 #ifdef SCO_FLAVOR
36 # include <sys/timeb.h>
37 #endif /* SCO_FLAVOR */
38 #include <time.h> /* for ctime() */
39 #if defined (HAVE_MAD) && ! defined (MIDNIGHT) && ! defined (GTK)
40 #include "mad.h"
41 #endif
43 #include <../src/dialog.h> /* MSG_ERROR */
45 extern char *edit_one_file;
49 * here's a quick sketch of the layout: (don't run this through indent.)
51 * (b1 is buffers1 and b2 is buffers2)
53 * |
54 * \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
55 * ______________________________________|______________________________________
56 * |
57 * ... | b2[2] | b2[1] | b2[0] | b1[0] | b1[1] | b1[2] | ...
58 * |-> |-> |-> |-> |-> |-> |
59 * |
60 * _<------------------------->|<----------------->_
61 * WEdit->curs2 | WEdit->curs1
62 * ^ | ^
63 * | ^|^ |
64 * cursor ||| cursor
65 * |||
66 * file end|||file beginning
67 * |
68 * |
70 * _
71 * This_is_some_file
72 * fin.
78 returns a byte from any location in the file.
79 Returns '\n' if out of bounds.
82 static int push_action_disabled = 0;
84 #ifndef NO_INLINE_GETBYTE
86 int edit_get_byte (WEdit * edit, long byte_index)
88 unsigned long p;
89 if (byte_index >= (edit->curs1 + edit->curs2) || byte_index < 0)
90 return '\n';
92 if (byte_index >= edit->curs1) {
93 p = edit->curs1 + edit->curs2 - byte_index - 1;
94 return edit->buffers2[p >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (p & M_EDIT_BUF_SIZE) - 1];
95 } else {
96 return edit->buffers1[byte_index >> S_EDIT_BUF_SIZE][byte_index & M_EDIT_BUF_SIZE];
100 #endif
102 char *edit_get_buffer_as_text (WEdit * e)
104 int l, i;
105 char *t;
106 l = e->curs1 + e->curs2;
107 t = CMalloc (l + 1);
108 for (i = 0; i < l; i++)
109 t[i] = edit_get_byte (e, i);
110 t[l] = 0;
111 return t;
115 /* Note on CRLF->LF translation: */
117 #if MY_O_TEXT
118 #error MY_O_TEXT is depreciated. CR_LF_TRANSLATION must be defined which does CR-LF translation internally. See note in source.
119 #endif
122 The edit_open_file (previously edit_load_file) function uses
123 init_dynamic_edit_buffers to load a file. This is unnecessary
124 since you can just as well fopen the file and insert the
125 characters one by one. The real reason for
126 init_dynamic_edit_buffers (besides allocating the buffers) is
127 as an optimisation - it uses raw block reads and inserts large
128 chunks at a time. It is hence extremely fast at loading files.
129 Where we might not want to use it is if we were doing
130 CRLF->LF translation or if we were reading from a pipe.
133 /* Initialisation routines */
135 /* returns 1 on error */
136 /* loads file OR text into buffers. Only one must be none-NULL. */
137 /* cursor set to start of file */
138 int init_dynamic_edit_buffers (WEdit * edit, const char *filename, const char *text)
140 long buf;
141 int j, file = -1, buf2;
143 for (j = 0; j <= MAXBUFF; j++) {
144 edit->buffers1[j] = NULL;
145 edit->buffers2[j] = NULL;
148 if (filename)
149 if ((file = open ((char *) filename, O_RDONLY)) == -1) {
150 /* The file-name is printed after the ':' */
151 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
152 return 1;
154 edit->curs2 = edit->last_byte;
156 buf2 = edit->curs2 >> S_EDIT_BUF_SIZE;
158 edit->buffers2[buf2] = CMalloc (EDIT_BUF_SIZE);
160 if (filename) {
161 read (file, (char *) edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE), edit->curs2 & M_EDIT_BUF_SIZE);
162 } else {
163 memcpy (edit->buffers2[buf2] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE), text, edit->curs2 & M_EDIT_BUF_SIZE);
164 text += edit->curs2 & M_EDIT_BUF_SIZE;
167 for (buf = buf2 - 1; buf >= 0; buf--) {
168 edit->buffers2[buf] = CMalloc (EDIT_BUF_SIZE);
169 if (filename) {
170 read (file, (char *) edit->buffers2[buf], EDIT_BUF_SIZE);
171 } else {
172 memcpy (edit->buffers2[buf], text, EDIT_BUF_SIZE);
173 text += EDIT_BUF_SIZE;
177 edit->curs1 = 0;
178 if (file != -1)
179 close (file);
180 return 0;
183 /* detecting an error on save is easy: just check if every byte has been written. */
184 /* detecting an error on read, is not so easy 'cos there is not way to tell
185 whether you read everything or not. */
186 /* FIXME: add proper `triple_pipe_open' to read, write and check errors. */
187 static const struct edit_filters {
188 char *read, *write, *extension;
189 } all_filters[] = {
192 "bzip2 -cd %s 2>&1", "bzip2 > %s", ".bz2"
195 "gzip -cd %s 2>&1", "gzip > %s", ".gz"
198 "compress -cd %s 2>&1", "compress > %s", ".Z"
202 static int edit_find_filter (const char *filename)
204 int i, l;
205 if (!filename)
206 return -1;
207 l = strlen (filename);
208 for (i = 0; i < sizeof (all_filters) / sizeof (struct edit_filters); i++) {
209 int e;
210 e = strlen (all_filters[i].extension);
211 if (l > e)
212 if (!strcmp (all_filters[i].extension, filename + l - e))
213 return i;
215 return -1;
218 char *edit_get_filter (const char *filename)
220 int i, l;
221 char *p;
222 i = edit_find_filter (filename);
223 if (i < 0)
224 return 0;
225 l = strlen (filename);
226 p = malloc (strlen (all_filters[i].read) + l + 2);
227 sprintf (p, all_filters[i].read, filename);
228 return p;
231 char *edit_get_write_filter (char *writename, const char *filename)
233 int i, l;
234 char *p;
235 i = edit_find_filter (filename);
236 if (i < 0)
237 return 0;
238 l = strlen (writename);
239 p = malloc (strlen (all_filters[i].write) + l + 2);
240 sprintf (p, all_filters[i].write, writename);
241 return p;
244 #ifdef CR_LF_TRANSLATION
245 /* reads into buffer, replace \r\n with \n */
246 long edit_insert_stream (WEdit * edit, FILE * f)
248 int a = -1, b;
249 long i;
250 while ((b = fgetc (f))) {
251 if (a == '\r' && b == '\n') {
252 edit_insert (edit, '\n');
253 i++;
254 a = -1;
255 continue;
256 } else if (a >= 0) {
257 edit_insert (edit, a);
258 i++;
260 a = b;
262 if (a >= 0)
263 edit_insert (edit, a);
264 return i;
266 /* writes buffer, replaces, replace \n with \r\n */
267 long edit_write_stream (WEdit * edit, FILE * f)
269 long i;
270 int c;
271 for (i = 0; i < edit->last_byte; i++) {
272 c = edit_get_byte (edit, i);
273 if (c == '\n') {
274 if (fputc ('\r', f) < 0)
275 break;
276 if (fputc ('\n', f) < 0)
277 break;
278 } else {
279 if (fputc (c, f) < 0)
280 break;
283 return i;
285 #else /* CR_LF_TRANSLATION */
286 long edit_insert_stream (WEdit * edit, FILE * f)
288 int c;
289 long i = 0;
290 while ((c = fgetc (f)) >= 0) {
291 edit_insert (edit, c);
292 i++;
294 return i;
296 long edit_write_stream (WEdit * edit, FILE * f)
298 long i;
299 for (i = 0; i < edit->last_byte; i++)
300 if (fputc (edit_get_byte (edit, i), f) < 0)
301 break;
302 return i;
304 #endif /* CR_LF_TRANSLATION */
306 #define TEMP_BUF_LEN 1024
308 /* inserts a file at the cursor, returns 1 on success */
309 int edit_insert_file (WEdit * edit, const char *filename)
311 char *p;
312 if ((p = edit_get_filter (filename))) {
313 FILE *f;
314 long current = edit->curs1;
315 f = (FILE *) popen (p, "r");
316 if (f) {
317 edit_insert_stream (edit, f);
318 edit_cursor_move (edit, current - edit->curs1);
319 if (pclose (f) > 0) {
320 edit_error_dialog (_ (" Error "), catstrs (_ (" Error reading from pipe: "), p, " ", 0));
321 free (p);
322 return 0;
324 } else {
325 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open pipe for reading: "), p, " ", 0)));
326 free (p);
327 return 0;
329 free (p);
330 #ifdef CR_LF_TRANSLATION
331 } else {
332 FILE *f;
333 long current = edit->curs1;
334 f = fopen (filename, "r");
335 if (f) {
336 edit_insert_stream (edit, f);
337 edit_cursor_move (edit, current - edit->curs1);
338 if (fclose (f)) {
339 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Error reading file: "), filename, " ", 0)));
340 return 0;
342 } else {
343 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
344 return 0;
346 #else
347 } else {
348 int i, file, blocklen;
349 long current = edit->curs1;
350 unsigned char *buf;
351 if ((file = open ((char *) filename, O_RDONLY)) == -1)
352 return 0;
353 buf = malloc (TEMP_BUF_LEN);
354 while ((blocklen = read (file, (char *) buf, TEMP_BUF_LEN)) > 0) {
355 for (i = 0; i < blocklen; i++)
356 edit_insert (edit, buf[i]);
358 edit_cursor_move (edit, current - edit->curs1);
359 free (buf);
360 close (file);
361 if (blocklen)
362 return 0;
363 #endif
365 return 1;
368 static int check_file_access (WEdit *edit, const char *filename, struct stat *st)
370 int file;
371 #if defined(MIDNIGHT) || defined(GTK)
372 if ((file = open ((char *) filename, O_RDONLY)) < 0) {
373 close (creat ((char *) filename, 0666));
374 if ((file = open ((char *) filename, O_RDONLY)) < 0) {
375 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
376 return 2;
379 #else
380 if ((file = open ((char *) filename, O_RDONLY)) < 0) {
381 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open file for reading: "), filename, " ", 0)));
382 return 1;
384 #endif
385 if (stat ((char *) filename, st) < 0) {
386 close (file);
387 /* The file-name is printed after the ':' */
388 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Cannot get size/permissions info on file: "), filename, " ", 0)));
389 return 1;
391 if (S_ISDIR (st->st_mode) || S_ISSOCK (st->st_mode)
392 || S_ISFIFO (st->st_mode)) {
393 close (file);
394 /* The file-name is printed after the ':' */
395 edit_error_dialog (_ (" Error "), catstrs (_ (" Not an ordinary file: "), filename, " ", 0));
396 return 1;
398 if (st->st_size >= SIZE_LIMIT) {
399 close (file);
400 /* The file-name is printed after the ':' */
401 edit_error_dialog (_ (" Error "), catstrs (_ (" File is too large: "), \
402 filename, _ (" \n Increase edit.h:MAXBUF and recompile the editor. "), 0));
403 return 1;
405 close (file);
406 return 0;
409 /* returns 1 on error */
410 int edit_open_file (WEdit * edit, const char *filename, const char *text, unsigned long text_size)
412 struct stat st;
413 if (text) {
414 edit->last_byte = text_size;
415 filename = 0;
416 } else {
417 int r;
418 r = check_file_access (edit, filename, &st);
419 #if defined(MIDNIGHT) || defined(GTK)
420 if (r == 2)
421 return edit->delete_file = 1;
422 #endif
423 if (r)
424 return 1;
425 edit->stat = st;
426 #ifndef CR_LF_TRANSLATION
427 edit->last_byte = st.st_size;
428 #else
429 /* going to read the file into the buffer later byte by byte */
430 edit->last_byte = 0;
431 filename = 0;
432 text = "";
433 #endif
435 return init_dynamic_edit_buffers (edit, filename, text);
438 #ifdef MIDNIGHT
439 #define space_width 1
440 #else
441 int space_width;
442 extern int option_long_whitespace;
444 void edit_set_space_width (int s)
446 space_width = s;
449 int (*edit_file_is_open) (char *) = 0;
451 #endif
453 /* fills in the edit struct. returns 0 on fail. Pass edit as NULL for this */
454 WEdit *edit_init (WEdit * edit, int lines, int columns, const char *filename, const char *text, const char *dir, unsigned long text_size)
456 char *f;
457 int to_free = 0;
458 int use_filter = 0;
459 #ifndef MIDNIGHT
460 if (option_long_whitespace)
461 edit_set_space_width (FONT_PER_CHAR[' '] * 2);
462 else
463 edit_set_space_width (FONT_PER_CHAR[' ']);
464 #endif
465 if (!edit) {
466 #ifdef ENABLE_NLS
468 * Expand option_whole_chars_search by national letters using
469 * current locale
472 static char option_whole_chars_search_buf [256];
474 if (option_whole_chars_search_buf != option_whole_chars_search) {
475 int i;
476 int len = strlen (option_whole_chars_search);
478 strcpy (option_whole_chars_search_buf, option_whole_chars_search);
480 for (i = 1; i <= sizeof (option_whole_chars_search_buf); i++) {
481 if (islower (i) && !strchr (option_whole_chars_search, i)) {
482 option_whole_chars_search_buf [len++] = i;
486 option_whole_chars_search_buf [len] = 0;
487 option_whole_chars_search = option_whole_chars_search_buf;
489 #endif /* ENABLE_NLS */
490 edit = malloc (sizeof (WEdit));
491 memset (edit, 0, sizeof (WEdit));
492 to_free = 1;
494 memset (&(edit->from_here), 0, (unsigned long) &(edit->to_here) - (unsigned long) &(edit->from_here));
495 #ifndef MIDNIGHT
496 edit->max_column = columns * FONT_MEAN_WIDTH;
497 #endif
498 edit->num_widget_lines = lines;
499 edit->num_widget_columns = columns;
500 edit->stat.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
501 edit->stat.st_uid = getuid ();
502 edit->stat.st_gid = getgid ();
503 edit->bracket = -1;
504 if (!dir)
505 dir = "";
506 f = (char *) filename;
507 if (filename) {
508 f = catstrs (dir, filename, 0);
509 #ifndef MIDNIGHT
510 if (edit_file_is_open)
511 if ((*edit_file_is_open) (f)) {
512 if (to_free)
513 free (edit);
514 return 0;
516 #endif
518 if (edit_find_filter (f) < 0) {
519 #ifdef CR_LF_TRANSLATION
520 use_filter = 1;
521 #endif
522 if (edit_open_file (edit, f, text, text_size)) {
523 /* edit_load_file already gives an error message */
524 if (to_free)
525 free (edit);
526 return 0;
528 } else {
529 use_filter = 1;
530 if (edit_open_file (edit, 0, "", 0)) {
531 if (to_free)
532 free (edit);
533 return 0;
536 edit->force |= REDRAW_PAGE;
537 if (filename) {
538 filename = catstrs (dir, filename, 0);
539 edit_split_filename (edit, (char *) filename);
540 } else {
541 edit->filename = (char *) strdup ("");
542 edit->dir = (char *) strdup (dir);
544 edit->stack_size = START_STACK_SIZE;
545 edit->stack_size_mask = START_STACK_SIZE - 1;
546 edit->undo_stack = malloc ((edit->stack_size + 10) * sizeof (long));
547 edit->total_lines = edit_count_lines (edit, 0, edit->last_byte);
548 if (use_filter) {
549 struct stat st;
550 push_action_disabled = 1;
551 if (check_file_access (edit, filename, &st)) {
552 edit_clean (edit);
553 if (to_free)
554 free (edit);
555 return 0;
557 edit->stat = st;
558 if (!edit_insert_file (edit, f)) {
559 edit_clean (edit);
560 if (to_free)
561 free (edit);
562 return 0;
564 /* FIXME: this should be an unmodification() function */
565 push_action_disabled = 0;
567 edit->modified = 0;
568 edit_load_syntax (edit, 0, 0);
570 int fg, bg;
571 edit_get_syntax_color (edit, -1, &fg, &bg);
573 return edit;
576 /* clear the edit struct, freeing everything in it. returns 1 on success */
577 int edit_clean (WEdit * edit)
579 if (edit) {
580 int j = 0;
581 edit_free_syntax_rules (edit);
582 book_mark_flush (edit, -1);
583 for (; j <= MAXBUFF; j++) {
584 if (edit->buffers1[j] != NULL)
585 free (edit->buffers1[j]);
586 if (edit->buffers2[j] != NULL)
587 free (edit->buffers2[j]);
590 if (edit->undo_stack)
591 free (edit->undo_stack);
592 if (edit->filename)
593 free (edit->filename);
594 if (edit->dir)
595 free (edit->dir);
596 /* we don't want to clear the widget */
597 memset (&(edit->from_here), 0, (unsigned long) &(edit->to_here) - (unsigned long) &(edit->from_here));
598 return 1;
600 return 0;
604 /* returns 1 on success */
605 int edit_renew (WEdit * edit)
607 int lines = edit->num_widget_lines;
608 int columns = edit->num_widget_columns;
609 char *dir;
611 if (edit->dir)
612 dir = (char *) strdup (edit->dir);
613 else
614 dir = 0;
616 edit_clean (edit);
617 if (!edit_init (edit, lines, columns, 0, "", dir, 0))
618 return 0;
619 return 1;
622 /* returns 1 on success, if returns 0, the edit struct would have been free'd */
623 int edit_reload (WEdit * edit, const char *filename, const char *text, const char *dir, unsigned long text_size)
625 WEdit *e;
626 int lines = edit->num_widget_lines;
627 int columns = edit->num_widget_columns;
628 e = malloc (sizeof (WEdit));
629 memset (e, 0, sizeof (WEdit));
630 e->widget = edit->widget;
631 e->macro_i = -1;
632 if (!edit_init (e, lines, columns, filename, text, dir, text_size)) {
633 free (e);
634 return 0;
636 edit_clean (edit);
637 memcpy (edit, e, sizeof (WEdit));
638 free (e);
639 return 1;
644 Recording stack for undo:
645 The following is an implementation of a compressed stack. Identical
646 pushes are recorded by a negative prefix indicating the number of times the
647 same char was pushed. This saves space for repeated curs-left or curs-right
648 delete etc.
652 pushed: stored:
656 b -3
658 c --> -4
664 If the stack long int is 0-255 it represents a normal insert (from a backspace),
665 256-512 is an insert ahead (from a delete), If it is betwen 600 and 700 it is one
666 of the cursor functions #define'd in edit.h. 1000 through 700'000'000 is to
667 set edit->mark1 position. 700'000'000 through 1400'000'000 is to set edit->mark2
668 position.
670 The only way the cursor moves or the buffer is changed is through the routines:
671 insert, backspace, insert_ahead, delete, and cursor_move.
672 These record the reverse undo movements onto the stack each time they are
673 called.
675 Each key press results in a set of actions (insert; delete ...). So each time
676 a key is pressed the current position of start_display is pushed as
677 KEY_PRESS + start_display. Then for undoing, we pop until we get to a number
678 over KEY_PRESS. We then assign this number less KEY_PRESS to start_display. So undo
679 tracks scrolling and key actions exactly. (KEY_PRESS is about (2^31) * (2/3) = 1400'000'000)
683 void edit_push_action (WEdit * edit, long c,...)
685 unsigned long sp = edit->stack_pointer;
686 unsigned long spm1;
687 long *t;
688 /* first enlarge the stack if necessary */
689 if (sp > edit->stack_size - 10) { /* say */
690 if (option_max_undo < 256)
691 option_max_undo = 256;
692 if (edit->stack_size < option_max_undo) {
693 t = malloc ((edit->stack_size * 2 + 10) * sizeof (long));
694 if (t) {
695 memcpy (t, edit->undo_stack, sizeof (long) * edit->stack_size);
696 free (edit->undo_stack);
697 edit->undo_stack = t;
698 edit->stack_size <<= 1;
699 edit->stack_size_mask = edit->stack_size - 1;
703 spm1 = (edit->stack_pointer - 1) & edit->stack_size_mask;
704 if (push_action_disabled)
705 return;
707 #ifdef FAST_MOVE_CURSOR
708 if (c == CURS_LEFT_LOTS || c == CURS_RIGHT_LOTS) {
709 va_list ap;
710 edit->undo_stack[sp] = c == CURS_LEFT_LOTS ? CURS_LEFT : CURS_RIGHT;
711 edit->stack_pointer = (edit->stack_pointer + 1) & edit->stack_size_mask;
712 va_start (ap, c);
713 c = -(va_arg (ap, int));
714 va_end (ap);
715 } else
716 #endif /* ! FAST_MOVE_CURSOR */
717 if (spm1 != edit->stack_bottom && ((sp - 2) & edit->stack_size_mask) != edit->stack_bottom) {
718 int d;
719 if (edit->undo_stack[spm1] < 0) {
720 d = edit->undo_stack[(sp - 2) & edit->stack_size_mask];
721 if (d == c) {
722 if (edit->undo_stack[spm1] > -1000000000) {
723 if (c < KEY_PRESS) /* --> no need to push multiple do-nothings */
724 edit->undo_stack[spm1]--;
725 return;
728 /* #define NO_STACK_CURSMOVE_ANIHILATION */
729 #ifndef NO_STACK_CURSMOVE_ANIHILATION
730 else if ((c == CURS_LEFT && d == CURS_RIGHT)
731 || (c == CURS_RIGHT && d == CURS_LEFT)) { /* a left then a right anihilate each other */
732 if (edit->undo_stack[spm1] == -2)
733 edit->stack_pointer = spm1;
734 else
735 edit->undo_stack[spm1]++;
736 return;
738 #endif
739 } else {
740 d = edit->undo_stack[spm1];
741 if (d == c) {
742 if (c >= KEY_PRESS)
743 return; /* --> no need to push multiple do-nothings */
744 edit->undo_stack[sp] = -2;
745 goto check_bottom;
747 #ifndef NO_STACK_CURSMOVE_ANIHILATION
748 else if ((c == CURS_LEFT && d == CURS_RIGHT)
749 || (c == CURS_RIGHT && d == CURS_LEFT)) { /* a left then a right anihilate each other */
750 edit->stack_pointer = spm1;
751 return;
753 #endif
756 edit->undo_stack[sp] = c;
757 check_bottom:
759 edit->stack_pointer = (edit->stack_pointer + 1) & edit->stack_size_mask;
761 /*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" */
762 c = (edit->stack_pointer + 2) & edit->stack_size_mask;
763 if (c == edit->stack_bottom || ((c + 1) & edit->stack_size_mask) == edit->stack_bottom)
764 do {
765 edit->stack_bottom = (edit->stack_bottom + 1) & edit->stack_size_mask;
766 } while (edit->undo_stack[edit->stack_bottom] < KEY_PRESS && edit->stack_bottom != edit->stack_pointer);
768 /*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: */
769 if (edit->stack_pointer != edit->stack_bottom && edit->undo_stack[edit->stack_bottom] < KEY_PRESS)
770 edit->stack_bottom = edit->stack_pointer = 0;
774 TODO: if the user undos until the stack bottom, and the stack has not wrapped,
775 then the file should be as it was when he loaded up. Then set edit->modified to 0.
777 long pop_action (WEdit * edit)
779 long c;
780 unsigned long sp = edit->stack_pointer;
781 if (sp == edit->stack_bottom) {
782 return STACK_BOTTOM;
784 sp = (sp - 1) & edit->stack_size_mask;
785 if ((c = edit->undo_stack[sp]) >= 0) {
786 /* edit->undo_stack[sp] = '@'; */
787 edit->stack_pointer = (edit->stack_pointer - 1) & edit->stack_size_mask;
788 return c;
790 if (sp == edit->stack_bottom) {
791 return STACK_BOTTOM;
793 c = edit->undo_stack[(sp - 1) & edit->stack_size_mask];
794 if (edit->undo_stack[sp] == -2) {
795 /* edit->undo_stack[sp] = '@'; */
796 edit->stack_pointer = sp;
797 } else
798 edit->undo_stack[sp]++;
800 return c;
803 /* is called whenever a modification is made by one of the four routines below */
804 static inline void edit_modification (WEdit * edit)
806 edit->caches_valid = 0;
807 edit->modified = 1;
808 edit->screen_modified = 1;
812 Basic low level single character buffer alterations and movements at the cursor.
813 Returns char passed over, inserted or removed.
816 void edit_insert (WEdit * edit, int c)
818 /* check if file has grown to large */
819 if (edit->last_byte >= SIZE_LIMIT)
820 return;
822 /* first we must update the position of the display window */
823 if (edit->curs1 < edit->start_display) {
824 edit->start_display++;
825 if (c == '\n')
826 edit->start_line++;
828 /* now we must update some info on the file and check if a redraw is required */
829 if (c == '\n') {
830 if (edit->book_mark)
831 book_mark_inc (edit, edit->curs_line);
832 edit->curs_line++;
833 edit->total_lines++;
834 edit->force |= REDRAW_LINE_ABOVE | REDRAW_AFTER_CURSOR;
836 /* tell that we've modified the file */
837 edit_modification (edit);
839 /* save the reverse command onto the undo stack */
840 edit_push_action (edit, BACKSPACE);
842 /* update markers */
843 edit->mark1 += (edit->mark1 > edit->curs1);
844 edit->mark2 += (edit->mark2 > edit->curs1);
845 edit->last_get_rule += (edit->last_get_rule > edit->curs1);
847 /* add a new buffer if we've reached the end of the last one */
848 if (!(edit->curs1 & M_EDIT_BUF_SIZE))
849 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
851 /* perfprm the insertion */
852 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE][edit->curs1 & M_EDIT_BUF_SIZE] = (unsigned char) c;
854 /* update file length */
855 edit->last_byte++;
857 /* update cursor position */
858 edit->curs1++;
862 /* same as edit_insert and move left */
863 void edit_insert_ahead (WEdit * edit, int c)
865 if (edit->last_byte >= SIZE_LIMIT)
866 return;
867 if (edit->curs1 < edit->start_display) {
868 edit->start_display++;
869 if (c == '\n')
870 edit->start_line++;
872 if (c == '\n') {
873 if (edit->book_mark)
874 book_mark_inc (edit, edit->curs_line);
875 edit->total_lines++;
876 edit->force |= REDRAW_AFTER_CURSOR;
878 edit_modification (edit);
879 edit_push_action (edit, DELETE);
881 edit->mark1 += (edit->mark1 >= edit->curs1);
882 edit->mark2 += (edit->mark2 >= edit->curs1);
883 edit->last_get_rule += (edit->last_get_rule >= edit->curs1);
885 if (!((edit->curs2 + 1) & M_EDIT_BUF_SIZE))
886 edit->buffers2[(edit->curs2 + 1) >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
887 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE) - 1] = c;
889 edit->last_byte++;
890 edit->curs2++;
894 int edit_delete (WEdit * edit)
896 int p;
897 if (!edit->curs2)
898 return 0;
900 edit->mark1 -= (edit->mark1 > edit->curs1);
901 edit->mark2 -= (edit->mark2 > edit->curs1);
902 edit->last_get_rule -= (edit->last_get_rule > edit->curs1);
904 p = edit->buffers2[(edit->curs2 - 1) >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - ((edit->curs2 - 1) & M_EDIT_BUF_SIZE) - 1];
906 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
907 free (edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE]);
908 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = NULL;
910 edit->last_byte--;
911 edit->curs2--;
913 if (p == '\n') {
914 if (edit->book_mark)
915 book_mark_dec (edit, edit->curs_line);
916 edit->total_lines--;
917 edit->force |= REDRAW_AFTER_CURSOR;
919 edit_push_action (edit, p + 256);
920 if (edit->curs1 < edit->start_display) {
921 edit->start_display--;
922 if (p == '\n')
923 edit->start_line--;
925 edit_modification (edit);
927 return p;
931 int edit_backspace (WEdit * edit)
933 int p;
934 if (!edit->curs1)
935 return 0;
937 edit->mark1 -= (edit->mark1 >= edit->curs1);
938 edit->mark2 -= (edit->mark2 >= edit->curs1);
939 edit->last_get_rule -= (edit->last_get_rule >= edit->curs1);
941 p = *(edit->buffers1[(edit->curs1 - 1) >> S_EDIT_BUF_SIZE] + ((edit->curs1 - 1) & M_EDIT_BUF_SIZE));
942 if (!((edit->curs1 - 1) & M_EDIT_BUF_SIZE)) {
943 free (edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE]);
944 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = NULL;
946 edit->last_byte--;
947 edit->curs1--;
949 if (p == '\n') {
950 if (edit->book_mark)
951 book_mark_dec (edit, edit->curs_line);
952 edit->curs_line--;
953 edit->total_lines--;
954 edit->force |= REDRAW_AFTER_CURSOR;
956 edit_push_action (edit, p);
958 if (edit->curs1 < edit->start_display) {
959 edit->start_display--;
960 if (p == '\n')
961 edit->start_line--;
963 edit_modification (edit);
965 return p;
968 #ifdef FAST_MOVE_CURSOR
970 static void memqcpy (WEdit * edit, unsigned char *dest, unsigned char *src, int n)
972 unsigned long next;
973 while ((next = (unsigned long) memccpy (dest, src, '\n', n))) {
974 edit->curs_line--;
975 next -= (unsigned long) dest;
976 n -= next;
977 src += next;
978 dest += next;
982 int edit_move_backward_lots (WEdit * edit, long increment)
984 int r, s, t;
985 unsigned char *p;
987 if (increment > edit->curs1)
988 increment = edit->curs1;
989 if (increment <= 0)
990 return -1;
991 edit_push_action (edit, CURS_RIGHT_LOTS, increment);
993 t = r = EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE);
994 if (r > increment)
995 r = increment;
996 s = edit->curs1 & M_EDIT_BUF_SIZE;
998 p = 0;
999 if (s > r) {
1000 memqcpy (edit, edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + t - r,
1001 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] + s - r, r);
1002 } else {
1003 if (s) {
1004 memqcpy (edit, edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + t - s,
1005 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE], s);
1006 p = edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE];
1007 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = 0;
1009 memqcpy (edit, edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + t - r,
1010 edit->buffers1[(edit->curs1 >> S_EDIT_BUF_SIZE) - 1] + EDIT_BUF_SIZE - (r - s), r - s);
1012 increment -= r;
1013 edit->curs1 -= r;
1014 edit->curs2 += r;
1015 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
1016 if (p)
1017 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = p;
1018 else
1019 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1020 } else {
1021 if (p)
1022 free (p);
1025 s = edit->curs1 & M_EDIT_BUF_SIZE;
1026 while (increment) {
1027 p = 0;
1028 r = EDIT_BUF_SIZE;
1029 if (r > increment)
1030 r = increment;
1031 t = s;
1032 if (r < t)
1033 t = r;
1034 memqcpy (edit,
1035 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + EDIT_BUF_SIZE - t,
1036 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] + s - t,
1038 if (r >= s) {
1039 if (t) {
1040 p = edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE];
1041 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = 0;
1043 memqcpy (edit,
1044 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] + EDIT_BUF_SIZE - r,
1045 edit->buffers1[(edit->curs1 >> S_EDIT_BUF_SIZE) - 1] + EDIT_BUF_SIZE - (r - s),
1046 r - s);
1048 increment -= r;
1049 edit->curs1 -= r;
1050 edit->curs2 += r;
1051 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
1052 if (p)
1053 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = p;
1054 else
1055 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1056 } else {
1057 if (p)
1058 free (p);
1061 return edit_get_byte (edit, edit->curs1);
1064 #endif /* ! FAST_MOVE_CURSOR */
1066 /* moves the cursor right or left: increment positive or negative respectively */
1067 int edit_cursor_move (WEdit * edit, long increment)
1069 /* this is the same as a combination of two of the above routines, with only one push onto the undo stack */
1070 int c;
1072 #ifdef FAST_MOVE_CURSOR
1073 if (increment < -256) {
1074 edit->force |= REDRAW_PAGE;
1075 return edit_move_backward_lots (edit, -increment);
1077 #endif /* ! FAST_MOVE_CURSOR */
1079 if (increment < 0) {
1080 for (; increment < 0; increment++) {
1081 if (!edit->curs1)
1082 return -1;
1084 edit_push_action (edit, CURS_RIGHT);
1086 c = edit_get_byte (edit, edit->curs1 - 1);
1087 if (!((edit->curs2 + 1) & M_EDIT_BUF_SIZE))
1088 edit->buffers2[(edit->curs2 + 1) >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1089 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE) - 1] = c;
1090 edit->curs2++;
1091 c = edit->buffers1[(edit->curs1 - 1) >> S_EDIT_BUF_SIZE][(edit->curs1 - 1) & M_EDIT_BUF_SIZE];
1092 if (!((edit->curs1 - 1) & M_EDIT_BUF_SIZE)) {
1093 free (edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE]);
1094 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = NULL;
1096 edit->curs1--;
1097 if (c == '\n') {
1098 edit->curs_line--;
1099 edit->force |= REDRAW_LINE_BELOW;
1103 return c;
1104 } else if (increment > 0) {
1105 for (; increment > 0; increment--) {
1106 if (!edit->curs2)
1107 return -2;
1109 edit_push_action (edit, CURS_LEFT);
1111 c = edit_get_byte (edit, edit->curs1);
1112 if (!(edit->curs1 & M_EDIT_BUF_SIZE))
1113 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE] = malloc (EDIT_BUF_SIZE);
1114 edit->buffers1[edit->curs1 >> S_EDIT_BUF_SIZE][edit->curs1 & M_EDIT_BUF_SIZE] = c;
1115 edit->curs1++;
1116 c = edit->buffers2[(edit->curs2 - 1) >> S_EDIT_BUF_SIZE][EDIT_BUF_SIZE - ((edit->curs2 - 1) & M_EDIT_BUF_SIZE) - 1];
1117 if (!(edit->curs2 & M_EDIT_BUF_SIZE)) {
1118 free (edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE]);
1119 edit->buffers2[edit->curs2 >> S_EDIT_BUF_SIZE] = 0;
1121 edit->curs2--;
1122 if (c == '\n') {
1123 edit->curs_line++;
1124 edit->force |= REDRAW_LINE_ABOVE;
1127 return c;
1128 } else
1129 return -3;
1132 /* These functions return positions relative to lines */
1134 /* returns index of last char on line + 1 */
1135 long edit_eol (WEdit * edit, long current)
1137 if (current < edit->last_byte) {
1138 for (;; current++)
1139 if (edit_get_byte (edit, current) == '\n')
1140 break;
1141 } else
1142 return edit->last_byte;
1143 return current;
1146 /* returns index of first char on line */
1147 long edit_bol (WEdit * edit, long current)
1149 if (current > 0) {
1150 for (;; current--)
1151 if (edit_get_byte (edit, current - 1) == '\n')
1152 break;
1153 } else
1154 return 0;
1155 return current;
1159 int edit_count_lines (WEdit * edit, long current, int upto)
1161 int lines = 0;
1162 if (upto > edit->last_byte)
1163 upto = edit->last_byte;
1164 if (current < 0)
1165 current = 0;
1166 while (current < upto)
1167 if (edit_get_byte (edit, current++) == '\n')
1168 lines++;
1169 return lines;
1173 /* If lines is zero this returns the count of lines from current to upto. */
1174 /* If upto is zero returns index of lines forward current. */
1175 long edit_move_forward (WEdit * edit, long current, int lines, long upto)
1177 if (upto) {
1178 return edit_count_lines (edit, current, upto);
1179 } else {
1180 int next;
1181 if (lines < 0)
1182 lines = 0;
1183 while (lines--) {
1184 next = edit_eol (edit, current) + 1;
1185 if (next > edit->last_byte)
1186 break;
1187 else
1188 current = next;
1190 return current;
1195 /* Returns offset of 'lines' lines up from current */
1196 long edit_move_backward (WEdit * edit, long current, int lines)
1198 if (lines < 0)
1199 lines = 0;
1200 current = edit_bol (edit, current);
1201 while((lines--) && current != 0)
1202 current = edit_bol (edit, current - 1);
1203 return current;
1206 #ifdef MIDNIGHT
1207 /* If cols is zero this returns the count of columns from current to upto. */
1208 /* If upto is zero returns index of cols across from current. */
1209 long edit_move_forward3 (WEdit * edit, long current, int cols, long upto)
1211 long p, q;
1212 int col = 0;
1214 if (upto) {
1215 q = upto;
1216 cols = -10;
1217 } else
1218 q = edit->last_byte + 2;
1220 for (col = 0, p = current; p < q; p++) {
1221 int c;
1222 if (cols != -10) {
1223 if (col == cols)
1224 return p;
1225 if (col > cols)
1226 return p - 1;
1228 c = edit_get_byte (edit, p);
1229 if (c == '\r')
1230 continue;
1231 else
1232 if (c == '\t')
1233 col += TAB_SIZE - col % TAB_SIZE;
1234 else
1235 col++;
1236 /*if(edit->nroff ... */
1237 if (c == '\n') {
1238 if (upto)
1239 return col;
1240 else
1241 return p;
1244 return col;
1246 #endif
1248 /* returns the current column position of the cursor */
1249 int edit_get_col (WEdit * edit)
1251 return edit_move_forward3 (edit, edit_bol (edit, edit->curs1), 0, edit->curs1);
1255 /* Scrolling functions */
1257 void edit_update_curs_row (WEdit * edit)
1259 edit->curs_row = edit->curs_line - edit->start_line;
1262 void edit_update_curs_col (WEdit * edit)
1264 edit->curs_col = edit_move_forward3(edit, edit_bol(edit, edit->curs1), 0, edit->curs1);
1267 /*moves the display start position up by i lines */
1268 void edit_scroll_upward (WEdit * edit, unsigned long i)
1270 int lines_above = edit->start_line;
1271 if (i > lines_above)
1272 i = lines_above;
1273 if (i) {
1274 edit->start_line -= i;
1275 edit->start_display = edit_move_backward (edit, edit->start_display, i);
1276 edit->force |= REDRAW_PAGE;
1277 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1278 #ifndef MIDNIGHT
1279 #endif
1281 edit_update_curs_row (edit);
1285 /* returns 1 if could scroll, 0 otherwise */
1286 void edit_scroll_downward (WEdit * edit, int i)
1288 int lines_below;
1289 lines_below = edit->total_lines - edit->start_line - (edit->num_widget_lines - 1);
1290 if (lines_below > 0) {
1291 if (i > lines_below)
1292 i = lines_below;
1293 edit->start_line += i;
1294 edit->start_display = edit_move_forward (edit, edit->start_display, i, 0);
1295 edit->force |= REDRAW_PAGE;
1296 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1297 #ifndef MIDNIGHT
1298 #endif
1300 edit_update_curs_row (edit);
1303 void edit_scroll_right (WEdit * edit, int i)
1305 edit->force |= REDRAW_PAGE;
1306 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1307 edit->start_col -= i;
1310 void edit_scroll_left (WEdit * edit, int i)
1312 if (edit->start_col) {
1313 edit->start_col += i;
1314 if (edit->start_col > 0)
1315 edit->start_col = 0;
1316 edit->force |= REDRAW_PAGE;
1317 edit->force &= (0xfff - REDRAW_CHAR_ONLY);
1321 /* high level cursor movement commands */
1323 static int is_in_indent (WEdit *edit)
1325 long p = edit_bol (edit, edit->curs1);
1326 while (p < edit->curs1)
1327 if (!strchr (" \t", edit_get_byte (edit, p++)))
1328 return 0;
1329 return 1;
1332 static int left_of_four_spaces (WEdit *edit);
1334 void edit_move_to_prev_col (WEdit * edit, long p)
1336 edit_cursor_move (edit, edit_move_forward3 (edit, p, edit->prev_col, 0) - edit->curs1);
1338 if (is_in_indent (edit) && option_fake_half_tabs) {
1339 edit_update_curs_col (edit);
1340 if (space_width)
1341 if (edit->curs_col % (HALF_TAB_SIZE * space_width)) {
1342 int q = edit->curs_col;
1343 edit->curs_col -= (edit->curs_col % (HALF_TAB_SIZE * space_width));
1344 p = edit_bol (edit, edit->curs1);
1345 edit_cursor_move (edit, edit_move_forward3 (edit, p, edit->curs_col, 0) - edit->curs1);
1346 if (!left_of_four_spaces (edit))
1347 edit_cursor_move (edit, edit_move_forward3 (edit, p, q, 0) - edit->curs1);
1353 /* move i lines */
1354 void edit_move_up (WEdit * edit, unsigned long i, int scroll)
1356 long p, l = edit->curs_line;
1358 if (i > l)
1359 i = l;
1360 if (i) {
1361 if (i > 1)
1362 edit->force |= REDRAW_PAGE;
1363 if (scroll)
1364 edit_scroll_upward (edit, i);
1366 p = edit_bol (edit, edit->curs1);
1367 edit_cursor_move (edit, (p = edit_move_backward (edit, p, i)) - edit->curs1);
1368 edit_move_to_prev_col (edit, p);
1370 edit->search_start = edit->curs1;
1371 edit->found_len = 0;
1375 int is_blank (WEdit * edit, long offset)
1377 long s, f;
1378 int c;
1379 s = edit_bol (edit, offset);
1380 f = edit_eol (edit, offset) - 1;
1381 while (s <= f) {
1382 c = edit_get_byte (edit, s++);
1383 if (!isspace (c))
1384 return 0;
1386 return 1;
1390 /* returns the offset of line i */
1391 long edit_find_line (WEdit * edit, int line)
1393 int i, j = 0;
1394 int m = 2000000000;
1395 if (!edit->caches_valid) {
1396 for (i = 0; i < N_LINE_CACHES; i++)
1397 edit->line_numbers[i] = edit->line_offsets[i] = 0;
1398 /* three offsets that we *know* are line 0 at 0 and these two: */
1399 edit->line_numbers[1] = edit->curs_line;
1400 edit->line_offsets[1] = edit_bol (edit, edit->curs1);
1401 edit->line_numbers[2] = edit->total_lines;
1402 edit->line_offsets[2] = edit_bol (edit, edit->last_byte);
1403 edit->caches_valid = 1;
1405 if (line >= edit->total_lines)
1406 return edit->line_offsets[2];
1407 if (line <= 0)
1408 return 0;
1409 /* find the closest known point */
1410 for (i = 0; i < N_LINE_CACHES; i++) {
1411 int n;
1412 n = abs (edit->line_numbers[i] - line);
1413 if (n < m) {
1414 m = n;
1415 j = i;
1418 if (m == 0)
1419 return edit->line_offsets[j]; /* know the offset exactly */
1420 if (m == 1 && j >= 3)
1421 i = j; /* one line different - caller might be looping, so stay in this cache */
1422 else
1423 i = 3 + (rand () % (N_LINE_CACHES - 3));
1424 if (line > edit->line_numbers[j])
1425 edit->line_offsets[i] = edit_move_forward (edit, edit->line_offsets[j], line - edit->line_numbers[j], 0);
1426 else
1427 edit->line_offsets[i] = edit_move_backward (edit, edit->line_offsets[j], edit->line_numbers[j] - line);
1428 edit->line_numbers[i] = line;
1429 return edit->line_offsets[i];
1432 int line_is_blank (WEdit * edit, long line)
1434 return is_blank (edit, edit_find_line (edit, line));
1437 /* moves up until a blank line is reached, or until just
1438 before a non-blank line is reached */
1439 static void edit_move_up_paragraph (WEdit * edit, int scroll)
1441 int i;
1442 if (edit->curs_line <= 1) {
1443 i = 0;
1444 } else {
1445 if (line_is_blank (edit, edit->curs_line)) {
1446 if (line_is_blank (edit, edit->curs_line - 1)) {
1447 for (i = edit->curs_line - 1; i; i--)
1448 if (!line_is_blank (edit, i)) {
1449 i++;
1450 break;
1452 } else {
1453 for (i = edit->curs_line - 1; i; i--)
1454 if (line_is_blank (edit, i))
1455 break;
1457 } else {
1458 for (i = edit->curs_line - 1; i; i--)
1459 if (line_is_blank (edit, i))
1460 break;
1463 edit_move_up (edit, edit->curs_line - i, scroll);
1466 /* move i lines */
1467 void edit_move_down (WEdit * edit, int i, int scroll)
1469 long p, l = edit->total_lines - edit->curs_line;
1471 if (i > l)
1472 i = l;
1473 if (i) {
1474 if (i > 1)
1475 edit->force |= REDRAW_PAGE;
1476 if (scroll)
1477 edit_scroll_downward (edit, i);
1478 p = edit_bol (edit, edit->curs1);
1479 edit_cursor_move (edit, (p = edit_move_forward (edit, p, i, 0)) - edit->curs1);
1480 edit_move_to_prev_col (edit, p);
1482 edit->search_start = edit->curs1;
1483 edit->found_len = 0;
1487 /* moves down until a blank line is reached, or until just
1488 before a non-blank line is reached */
1489 static void edit_move_down_paragraph (WEdit * edit, int scroll)
1491 int i;
1492 if (edit->curs_line >= edit->total_lines - 1) {
1493 i = edit->total_lines;
1494 } else {
1495 if (line_is_blank (edit, edit->curs_line)) {
1496 if (line_is_blank (edit, edit->curs_line + 1)) {
1497 for (i = edit->curs_line + 1; i; i++)
1498 if (!line_is_blank (edit, i) || i > edit->total_lines) {
1499 i--;
1500 break;
1502 } else {
1503 for (i = edit->curs_line + 1; i; i++)
1504 if (line_is_blank (edit, i) || i >= edit->total_lines)
1505 break;
1507 } else {
1508 for (i = edit->curs_line + 1; i; i++)
1509 if (line_is_blank (edit, i) || i >= edit->total_lines)
1510 break;
1513 edit_move_down (edit, i - edit->curs_line, scroll);
1516 static void edit_begin_page (WEdit *edit)
1518 edit_update_curs_row (edit);
1519 edit_move_up (edit, edit->curs_row, 0);
1522 static void edit_end_page (WEdit *edit)
1524 edit_update_curs_row (edit);
1525 edit_move_down (edit, edit->num_widget_lines - edit->curs_row - 1, 0);
1529 /* goto beginning of text */
1530 static void edit_move_to_top (WEdit * edit)
1532 if (edit->curs_line) {
1533 edit_cursor_move (edit, -edit->curs1);
1534 edit_move_to_prev_col (edit, 0);
1535 edit->force |= REDRAW_PAGE;
1536 edit->search_start = 0;
1537 edit_update_curs_row(edit);
1542 /* goto end of text */
1543 static void edit_move_to_bottom (WEdit * edit)
1545 if (edit->curs_line < edit->total_lines) {
1546 edit_cursor_move (edit, edit->curs2);
1547 edit->start_display = edit->last_byte;
1548 edit->start_line = edit->total_lines;
1549 edit_update_curs_row(edit);
1550 edit_scroll_upward (edit, edit->num_widget_lines - 1);
1551 edit->force |= REDRAW_PAGE;
1555 /* goto beginning of line */
1556 static void edit_cursor_to_bol (WEdit * edit)
1558 edit_cursor_move (edit, edit_bol (edit, edit->curs1) - edit->curs1);
1559 edit->search_start = edit->curs1;
1560 edit->prev_col = edit_get_col (edit);
1563 /* goto end of line */
1564 static void edit_cursor_to_eol (WEdit * edit)
1566 edit_cursor_move (edit, edit_eol (edit, edit->curs1) - edit->curs1);
1567 edit->search_start = edit->curs1;
1568 edit->prev_col = edit_get_col (edit);
1571 /* move cursor to line 'line' */
1572 void edit_move_to_line (WEdit * e, long line)
1574 if(line < e->curs_line)
1575 edit_move_up (e, e->curs_line - line, 0);
1576 else
1577 edit_move_down (e, line - e->curs_line, 0);
1578 edit_scroll_screen_over_cursor (e);
1581 /* scroll window so that first visible line is 'line' */
1582 void edit_move_display (WEdit * e, long line)
1584 if(line < e->start_line)
1585 edit_scroll_upward (e, e->start_line - line);
1586 else
1587 edit_scroll_downward (e, line - e->start_line);
1590 /* save markers onto undo stack */
1591 void edit_push_markers (WEdit * edit)
1593 edit_push_action (edit, MARK_1 + edit->mark1);
1594 edit_push_action (edit, MARK_2 + edit->mark2);
1597 void free_selections (void)
1599 int i;
1600 for (i = 0; i < NUM_SELECTION_HISTORY; i++)
1601 if (selection_history[i].text) {
1602 free (selection_history[i].text);
1603 selection_history[i].text = 0;
1604 selection_history[i].len = 0;
1606 current_selection = 0;
1609 /* return -1 on nothing to store or error, zero otherwise */
1610 void edit_get_selection (WEdit * edit)
1612 long start_mark, end_mark;
1613 if (eval_marks (edit, &start_mark, &end_mark))
1614 return;
1615 if (selection_history[current_selection].len < 4096) /* large selections should not be held -- to save memory */
1616 current_selection = (current_selection + 1) % NUM_SELECTION_HISTORY;
1617 selection_history[current_selection].len = end_mark - start_mark;
1618 if (selection_history[current_selection].text)
1619 free (selection_history[current_selection].text);
1620 selection_history[current_selection].text = malloc (selection_history[current_selection].len + 1);
1621 if (!selection_history[current_selection].text) {
1622 selection_history[current_selection].text = malloc (1);
1623 *selection_history[current_selection].text = 0;
1624 selection_history[current_selection].len = 0;
1625 } else {
1626 unsigned char *p = selection_history[current_selection].text;
1627 for (; start_mark < end_mark; start_mark++)
1628 *p++ = edit_get_byte (edit, start_mark);
1629 *p = 0;
1631 selection.text = selection_history[current_selection].text;
1632 selection.len = selection_history[current_selection].len;
1635 void edit_set_markers (WEdit * edit, long m1, long m2, int c1, int c2)
1637 edit->mark1 = m1;
1638 edit->mark2 = m2;
1639 edit->column1 = c1;
1640 edit->column2 = c2;
1644 /* highlight marker toggle */
1645 void edit_mark_cmd (WEdit * edit, int unmark)
1647 edit_push_markers (edit);
1648 if (unmark) {
1649 edit_set_markers (edit, 0, 0, 0, 0);
1650 edit->force |= REDRAW_PAGE;
1651 } else {
1652 if (edit->mark2 >= 0) {
1653 edit_set_markers (edit, edit->curs1, -1, edit->curs_col, edit->curs_col);
1654 edit->force |= REDRAW_PAGE;
1655 } else
1656 edit_set_markers (edit, edit->mark1, edit->curs1, edit->column1, edit->curs_col);
1660 static unsigned long my_type_of (int c)
1662 int x, r = 0;
1663 char *p, *q;
1664 if (!c)
1665 return 0;
1666 if (c == '!') {
1667 if (*option_chars_move_whole_word == '!')
1668 return 2;
1669 return 0x80000000UL;
1671 if (isupper (c))
1672 c = 'A';
1673 else if (islower (c))
1674 c = 'a';
1675 else if (isalpha (c))
1676 c = 'a';
1677 else if (isdigit (c))
1678 c = '0';
1679 else if (isspace (c))
1680 c = ' ';
1681 q = strchr (option_chars_move_whole_word, c);
1682 if (!q)
1683 return 0xFFFFFFFFUL;
1684 do {
1685 for (x = 1, p = option_chars_move_whole_word; (unsigned long) p < (unsigned long) q; p++)
1686 if (*p == '!')
1687 x <<= 1;
1688 r |= x;
1689 } while ((q = strchr (q + 1, c)));
1690 return r;
1693 void edit_left_word_move (WEdit * edit, int s)
1695 for (;;) {
1696 int c1, c2;
1697 edit_cursor_move (edit, -1);
1698 if (!edit->curs1)
1699 break;
1700 c1 = edit_get_byte (edit, edit->curs1 - 1);
1701 c2 = edit_get_byte (edit, edit->curs1);
1702 if (!(my_type_of (c1) & my_type_of (c2)))
1703 break;
1704 if (isspace (c1) && !isspace (c2))
1705 break;
1706 if (s)
1707 if (!isspace (c1) && isspace (c2))
1708 break;
1712 static void edit_left_word_move_cmd (WEdit * edit)
1714 edit_left_word_move (edit, 0);
1715 edit->force |= REDRAW_PAGE;
1718 void edit_right_word_move (WEdit * edit, int s)
1720 for (;;) {
1721 int c1, c2;
1722 edit_cursor_move (edit, 1);
1723 if (edit->curs1 >= edit->last_byte)
1724 break;
1725 c1 = edit_get_byte (edit, edit->curs1 - 1);
1726 c2 = edit_get_byte (edit, edit->curs1);
1727 if (!(my_type_of (c1) & my_type_of (c2)))
1728 break;
1729 if (isspace (c1) && !isspace (c2))
1730 break;
1731 if (s)
1732 if (!isspace (c1) && isspace (c2))
1733 break;
1737 static void edit_right_word_move_cmd (WEdit * edit)
1739 edit_right_word_move (edit, 0);
1740 edit->force |= REDRAW_PAGE;
1744 static void edit_right_delete_word (WEdit * edit)
1746 int c1, c2;
1747 for (;;) {
1748 if (edit->curs1 >= edit->last_byte)
1749 break;
1750 c1 = edit_delete (edit);
1751 c2 = edit_get_byte (edit, edit->curs1);
1752 if ((isspace (c1) == 0) != (isspace (c2) == 0))
1753 break;
1754 if (!(my_type_of (c1) & my_type_of (c2)))
1755 break;
1759 static void edit_left_delete_word (WEdit * edit)
1761 int c1, c2;
1762 for (;;) {
1763 if (edit->curs1 <= 0)
1764 break;
1765 c1 = edit_backspace (edit);
1766 c2 = edit_get_byte (edit, edit->curs1 - 1);
1767 if ((isspace (c1) == 0) != (isspace (c2) == 0))
1768 break;
1769 if (!(my_type_of (c1) & my_type_of (c2)))
1770 break;
1774 extern int column_highlighting;
1777 the start column position is not recorded, and hence does not
1778 undo as it happed. But who would notice.
1780 void edit_do_undo (WEdit * edit)
1782 long ac;
1783 long count = 0;
1785 push_action_disabled = 1; /* don't record undo's onto undo stack! */
1787 while ((ac = pop_action (edit)) < KEY_PRESS) {
1788 switch ((int) ac) {
1789 case STACK_BOTTOM:
1790 goto done_undo;
1791 case CURS_RIGHT:
1792 edit_cursor_move (edit, 1);
1793 break;
1794 case CURS_LEFT:
1795 edit_cursor_move (edit, -1);
1796 break;
1797 case BACKSPACE:
1798 edit_backspace (edit);
1799 break;
1800 case DELETE:
1801 edit_delete (edit);
1802 break;
1803 case COLUMN_ON:
1804 column_highlighting = 1;
1805 break;
1806 case COLUMN_OFF:
1807 column_highlighting = 0;
1808 break;
1810 if (ac >= 256 && ac < 512)
1811 edit_insert_ahead (edit, ac - 256);
1812 if (ac >= 0 && ac < 256)
1813 edit_insert (edit, ac);
1815 if (ac >= MARK_1 - 2 && ac < MARK_2 - 2) {
1816 edit->mark1 = ac - MARK_1;
1817 edit->column1 = edit_move_forward3 (edit, edit_bol (edit, edit->mark1), 0, edit->mark1);
1818 } else if (ac >= MARK_2 - 2 && ac < KEY_PRESS) {
1819 edit->mark2 = ac - MARK_2;
1820 edit->column2 = edit_move_forward3 (edit, edit_bol (edit, edit->mark2), 0, edit->mark2);
1822 if (count++)
1823 edit->force |= REDRAW_PAGE; /* more than one pop usually means something big */
1826 if (edit->start_display > ac - KEY_PRESS) {
1827 edit->start_line -= edit_count_lines (edit, ac - KEY_PRESS, edit->start_display);
1828 edit->force |= REDRAW_PAGE;
1829 } else if (edit->start_display < ac - KEY_PRESS) {
1830 edit->start_line += edit_count_lines (edit, edit->start_display, ac - KEY_PRESS);
1831 edit->force |= REDRAW_PAGE;
1833 edit->start_display = ac - KEY_PRESS; /* see push and pop above */
1834 edit_update_curs_row (edit);
1836 done_undo:;
1837 push_action_disabled = 0;
1840 static void edit_delete_to_line_end (WEdit * edit)
1842 while (edit_get_byte (edit, edit->curs1) != '\n') {
1843 if (!edit->curs2)
1844 break;
1845 edit_delete (edit);
1849 static void edit_delete_to_line_begin (WEdit * edit)
1851 while (edit_get_byte (edit, edit->curs1 - 1) != '\n') {
1852 if (!edit->curs1)
1853 break;
1854 edit_backspace (edit);
1858 void edit_delete_line (WEdit * edit)
1860 int c;
1861 do {
1862 c = edit_delete (edit);
1863 } while (c != '\n' && c);
1864 do {
1865 c = edit_backspace (edit);
1866 } while (c != '\n' && c);
1867 if (c)
1868 edit_insert (edit, '\n');
1871 static void insert_spaces_tab (WEdit * edit, int half)
1873 int i;
1874 edit_update_curs_col (edit);
1875 i = ((edit->curs_col / (option_tab_spacing * space_width / (half + 1))) + 1) * (option_tab_spacing * space_width / (half + 1)) - edit->curs_col;
1876 while (i > 0) {
1877 edit_insert (edit, ' ');
1878 i -= space_width;
1882 static int is_aligned_on_a_tab (WEdit * edit)
1884 edit_update_curs_col (edit);
1885 if ((edit->curs_col % (TAB_SIZE * space_width)) && edit->curs_col % (TAB_SIZE * space_width) != (HALF_TAB_SIZE * space_width))
1886 return 0; /* not alligned on a tab */
1887 return 1;
1890 static int right_of_four_spaces (WEdit *edit)
1892 int i, ch = 0;
1893 for (i = 1; i <= HALF_TAB_SIZE; i++)
1894 ch |= edit_get_byte (edit, edit->curs1 - i);
1895 if (ch == ' ')
1896 return is_aligned_on_a_tab (edit);
1897 return 0;
1900 static int left_of_four_spaces (WEdit *edit)
1902 int i, ch = 0;
1903 for (i = 0; i < HALF_TAB_SIZE; i++)
1904 ch |= edit_get_byte (edit, edit->curs1 + i);
1905 if (ch == ' ')
1906 return is_aligned_on_a_tab (edit);
1907 return 0;
1910 int edit_indent_width (WEdit * edit, long p)
1912 long q = p;
1913 while (strchr ("\t ", edit_get_byte (edit, q)) && q < edit->last_byte - 1) /* move to the end of the leading whitespace of the line */
1914 q++;
1915 return edit_move_forward3 (edit, p, 0, q); /* count the number of columns of indentation */
1918 void edit_insert_indent (WEdit * edit, int indent)
1920 #ifndef MIDNIGHT
1921 indent /= space_width;
1922 #endif
1923 if (!option_fill_tabs_with_spaces) {
1924 while (indent >= TAB_SIZE) {
1925 edit_insert (edit, '\t');
1926 indent -= TAB_SIZE;
1929 while (indent-- > 0)
1930 edit_insert (edit, ' ');
1933 void edit_auto_indent (WEdit * edit, int extra, int no_advance)
1935 long p;
1936 int indent;
1937 p = edit->curs1;
1938 while (isspace (edit_get_byte (edit, p - 1)) && p > 0) /* move back/up to a line with text */
1939 p--;
1940 indent = edit_indent_width (edit, edit_bol (edit, p));
1941 if (edit->curs_col < indent && no_advance)
1942 indent = edit->curs_col;
1943 edit_insert_indent (edit, indent + (option_fake_half_tabs ? HALF_TAB_SIZE : TAB_SIZE) * space_width * extra);
1946 static void edit_double_newline (WEdit * edit)
1948 edit_insert (edit, '\n');
1949 if (edit_get_byte (edit, edit->curs1) == '\n')
1950 return;
1951 if (edit_get_byte (edit, edit->curs1 - 2) == '\n')
1952 return;
1953 edit->force |= REDRAW_PAGE;
1954 edit_insert (edit, '\n');
1957 static void edit_tab_cmd (WEdit * edit)
1959 int i;
1961 if (option_fake_half_tabs) {
1962 if (is_in_indent (edit)) {
1963 /*insert a half tab (usually four spaces) unless there is a
1964 half tab already behind, then delete it and insert a
1965 full tab. */
1966 if (!option_fill_tabs_with_spaces && right_of_four_spaces (edit)) {
1967 for (i = 1; i <= HALF_TAB_SIZE; i++)
1968 edit_backspace (edit);
1969 edit_insert (edit, '\t');
1970 } else {
1971 insert_spaces_tab (edit, 1);
1973 return;
1976 if (option_fill_tabs_with_spaces) {
1977 insert_spaces_tab (edit, 0);
1978 } else {
1979 edit_insert (edit, '\t');
1981 return;
1984 void format_paragraph (WEdit * edit, int force);
1986 static void check_and_wrap_line (WEdit * edit)
1988 int curs, c;
1989 if (!option_typewriter_wrap)
1990 return;
1991 edit_update_curs_col (edit);
1992 #ifdef MIDNIGHT
1993 if (edit->curs_col < option_word_wrap_line_length)
1994 return;
1995 #else
1996 CPushFont ("editor", 0);
1997 c = FONT_MEAN_WIDTH;
1998 CPopFont ();
1999 if (edit->curs_col < option_word_wrap_line_length * c)
2000 return;
2001 #endif
2002 curs = edit->curs1;
2003 for (;;) {
2004 curs--;
2005 c = edit_get_byte (edit, curs);
2006 if (c == '\n' || curs <= 0) {
2007 edit_insert (edit, '\n');
2008 return;
2010 if (c == ' ' || c == '\t') {
2011 int current = edit->curs1;
2012 edit_cursor_move (edit, curs - edit->curs1 + 1);
2013 edit_insert (edit, '\n');
2014 edit_cursor_move (edit, current - edit->curs1 + 1);
2015 return;
2020 void edit_execute_macro (WEdit * edit, struct macro macro[], int n);
2022 /* either command or char_for_insertion must be passed as -1 */
2023 int edit_execute_cmd (WEdit * edit, int command, int char_for_insertion);
2025 #ifdef MIDNIGHT
2026 int edit_translate_key (WEdit * edit, unsigned int x_keycode, long x_key, int x_state, int *cmd, int *ch)
2028 int command = -1;
2029 int char_for_insertion = -1;
2031 #include "edit_key_translator.c"
2033 *cmd = command;
2034 *ch = char_for_insertion;
2036 if((command == -1 || command == 0) && char_for_insertion == -1) /* unchanged, key has no function here */
2037 return 0;
2038 return 1;
2040 #endif
2042 void edit_push_key_press (WEdit * edit)
2044 edit_push_action (edit, KEY_PRESS + edit->start_display);
2045 if (edit->mark2 == -1)
2046 edit_push_action (edit, MARK_1 + edit->mark1);
2049 /* this find the matching bracket in either direction, and sets edit->bracket */
2050 static long edit_get_bracket (WEdit * edit, int in_screen, unsigned long furthest_bracket_search)
2052 const char *b = "{}{[][()(", *p;
2053 int i = 1, a, inc = -1, c, d, n = 0;
2054 unsigned long j = 0;
2055 long q;
2056 edit_update_curs_row (edit);
2057 c = edit_get_byte (edit, edit->curs1);
2058 p = strchr (b, c);
2059 /* no limit */
2060 if (!furthest_bracket_search)
2061 furthest_bracket_search--;
2062 /* not on a bracket at all */
2063 if (!p)
2064 return -1;
2065 /* the matching bracket */
2066 d = p[1];
2067 /* going left or right? */
2068 if (strchr ("{[(", c))
2069 inc = 1;
2070 for (q = edit->curs1 + inc;; q += inc) {
2071 /* out of buffer? */
2072 if (q >= edit->last_byte || q < 0)
2073 break;
2074 a = edit_get_byte (edit, q);
2075 /* don't want to eat CPU */
2076 if (j++ > furthest_bracket_search)
2077 break;
2078 /* out of screen? */
2079 if (in_screen) {
2080 if (q < edit->start_display)
2081 break;
2082 /* count lines if searching downward */
2083 if (inc > 0 && a == '\n')
2084 if (n++ >= edit->num_widget_lines - edit->curs_row) /* out of screen */
2085 break;
2087 /* count bracket depth */
2088 i += (a == c) - (a == d);
2089 /* return if bracket depth is zero */
2090 if (!i)
2091 return q;
2093 /* no match */
2094 return -1;
2097 static long last_bracket = -1;
2099 static void edit_find_bracket (WEdit * edit)
2101 if (option_find_bracket) {
2102 edit->bracket = edit_get_bracket (edit, 1, 10000);
2103 if (last_bracket != edit->bracket)
2104 edit->force |= REDRAW_PAGE;
2105 last_bracket = edit->bracket;
2109 static void edit_goto_matching_bracket (WEdit *edit)
2111 long q;
2112 q = edit_get_bracket (edit, 0, 0);
2113 if (q < 0)
2114 return;
2115 edit->bracket = edit->curs1;
2116 edit->force |= REDRAW_PAGE;
2117 edit_cursor_move (edit, q - edit->curs1);
2120 /* this executes a command as though the user initiated it through a key press. */
2121 /* callback with WIDGET_KEY as a message calls this after translating the key
2122 press */
2123 /* this can be used to pass any command to the editor. Same as sendevent with
2124 msg = WIDGET_COMMAND and par = command except the screen wouldn't update */
2125 /* one of command or char_for_insertion must be passed as -1 */
2126 /* commands are executed, and char_for_insertion is inserted at the cursor */
2127 /* returns 0 if the command is a macro that was not found, 1 otherwise */
2128 int edit_execute_key_command (WEdit * edit, int command, int char_for_insertion)
2130 int r;
2131 if (command == CK_Begin_Record_Macro) {
2132 edit->macro_i = 0;
2133 edit->force |= REDRAW_CHAR_ONLY | REDRAW_LINE;
2134 return command;
2136 if (command == CK_End_Record_Macro && edit->macro_i != -1) {
2137 edit->force |= REDRAW_COMPLETELY;
2138 edit_save_macro_cmd (edit, edit->macro, edit->macro_i);
2139 edit->macro_i = -1;
2140 return command;
2142 if (edit->macro_i >= 0 && edit->macro_i < MAX_MACRO_LENGTH - 1) {
2143 edit->macro[edit->macro_i].command = command;
2144 edit->macro[edit->macro_i++].ch = char_for_insertion;
2146 /* record the beginning of a set of editing actions initiated by a key press */
2147 if (command != CK_Undo)
2148 edit_push_key_press (edit);
2150 r = edit_execute_cmd (edit, command, char_for_insertion);
2151 #ifdef GTK
2152 if (edit->stopped && edit->widget->destroy_me) {
2153 (*edit->widget->destroy_me) (edit->widget->destroy_me_user_data);
2154 return 0;
2156 #endif
2157 if (column_highlighting)
2158 edit->force |= REDRAW_PAGE;
2160 return r;
2163 #ifdef MIDNIGHT
2164 static const char *shell_cmd[] = SHELL_COMMANDS_i
2165 void edit_mail_dialog (WEdit * edit);
2167 #else
2168 static void (*user_commamd) (WEdit *, int) = 0;
2169 void edit_set_user_command (void (*func) (WEdit *, int))
2171 user_commamd = func;
2173 #endif
2176 This executes a command at a lower level than macro recording.
2177 It also does not push a key_press onto the undo stack. This means
2178 that if it is called many times, a single undo command will undo
2179 all of them. It also does not check for the Undo command.
2180 Returns 0 if the command is a macro that was not found, 1
2181 otherwise.
2183 int edit_execute_cmd (WEdit * edit, int command, int char_for_insertion)
2185 int result = 1;
2186 edit->force |= REDRAW_LINE;
2187 if (edit->found_len || column_highlighting)
2188 /* the next key press will unhighlight the found string, so update whole page */
2189 edit->force |= REDRAW_PAGE;
2191 if (command / 100 == 6) { /* a highlight command like shift-arrow */
2192 column_highlighting = 0;
2193 if (!edit->highlight || (edit->mark2 != -1 && edit->mark1 != edit->mark2)) {
2194 edit_mark_cmd (edit, 1); /* clear */
2195 edit_mark_cmd (edit, 0); /* marking on */
2197 edit->highlight = 1;
2198 } else { /* any other command */
2199 if (edit->highlight)
2200 edit_mark_cmd (edit, 0); /* clear */
2201 edit->highlight = 0;
2204 /* first check for undo */
2205 if (command == CK_Undo) {
2206 edit_do_undo (edit);
2207 edit->found_len = 0;
2208 edit->prev_col = edit_get_col (edit);
2209 edit->search_start = edit->curs1;
2210 return 1;
2212 /* An ordinary key press */
2213 if (char_for_insertion >= 0) {
2214 if (edit->overwrite) {
2215 if (edit_get_byte (edit, edit->curs1) != '\n')
2216 edit_delete (edit);
2218 edit_insert (edit, char_for_insertion);
2219 if (option_auto_para_formatting) {
2220 format_paragraph (edit, 0);
2221 edit->force |= REDRAW_PAGE;
2222 } else
2223 check_and_wrap_line (edit);
2224 edit->found_len = 0;
2225 edit->prev_col = edit_get_col (edit);
2226 edit->search_start = edit->curs1;
2227 edit_find_bracket (edit);
2228 edit_check_spelling (edit);
2229 return 1;
2231 switch (command) {
2232 case CK_Begin_Page:
2233 case CK_End_Page:
2234 case CK_Begin_Page_Highlight:
2235 case CK_End_Page_Highlight:
2236 case CK_Word_Left:
2237 case CK_Word_Right:
2238 case CK_Up:
2239 case CK_Down:
2240 case CK_Word_Left_Highlight:
2241 case CK_Word_Right_Highlight:
2242 case CK_Up_Highlight:
2243 case CK_Down_Highlight:
2244 if (edit->mark2 == -1)
2245 break; /*marking is following the cursor: may need to highlight a whole line */
2246 case CK_Left:
2247 case CK_Right:
2248 case CK_Left_Highlight:
2249 case CK_Right_Highlight:
2250 edit->force |= REDRAW_CHAR_ONLY;
2253 /* basic cursor key commands */
2254 switch (command) {
2255 case CK_BackSpace:
2256 if (option_backspace_through_tabs && is_in_indent (edit)) {
2257 while (edit_get_byte (edit, edit->curs1 - 1) != '\n'
2258 && edit->curs1 > 0)
2259 edit_backspace (edit);
2260 break;
2261 } else {
2262 if (option_fake_half_tabs) {
2263 int i;
2264 if (is_in_indent (edit) && right_of_four_spaces (edit)) {
2265 for (i = 0; i < HALF_TAB_SIZE; i++)
2266 edit_backspace (edit);
2267 break;
2271 edit_backspace (edit);
2272 break;
2273 case CK_Delete:
2274 if (option_fake_half_tabs) {
2275 int i;
2276 if (is_in_indent (edit) && left_of_four_spaces (edit)) {
2277 for (i = 1; i <= HALF_TAB_SIZE; i++)
2278 edit_delete (edit);
2279 break;
2282 edit_delete (edit);
2283 break;
2284 case CK_Delete_Word_Left:
2285 edit_left_delete_word (edit);
2286 break;
2287 case CK_Delete_Word_Right:
2288 edit_right_delete_word (edit);
2289 break;
2290 case CK_Delete_Line:
2291 edit_delete_line (edit);
2292 break;
2293 case CK_Delete_To_Line_End:
2294 edit_delete_to_line_end (edit);
2295 break;
2296 case CK_Delete_To_Line_Begin:
2297 edit_delete_to_line_begin (edit);
2298 break;
2299 case CK_Enter:
2300 if (option_auto_para_formatting) {
2301 edit_double_newline (edit);
2302 if (option_return_does_auto_indent)
2303 edit_auto_indent (edit, 0, 1);
2304 format_paragraph (edit, 0);
2305 } else {
2306 edit_insert (edit, '\n');
2307 if (option_return_does_auto_indent) {
2308 edit_auto_indent (edit, 0, 1);
2311 break;
2312 case CK_Return:
2313 edit_insert (edit, '\n');
2314 break;
2316 case CK_Page_Up:
2317 case CK_Page_Up_Highlight:
2318 edit_move_up (edit, edit->num_widget_lines - 1, 1);
2319 break;
2320 case CK_Page_Down:
2321 case CK_Page_Down_Highlight:
2322 edit_move_down (edit, edit->num_widget_lines - 1, 1);
2323 break;
2324 case CK_Left:
2325 case CK_Left_Highlight:
2326 if (option_fake_half_tabs) {
2327 if (is_in_indent (edit) && right_of_four_spaces (edit)) {
2328 edit_cursor_move (edit, -HALF_TAB_SIZE);
2329 edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
2330 break;
2333 edit_cursor_move (edit, -1);
2334 break;
2335 case CK_Right:
2336 case CK_Right_Highlight:
2337 if (option_fake_half_tabs) {
2338 if (is_in_indent (edit) && left_of_four_spaces (edit)) {
2339 edit_cursor_move (edit, HALF_TAB_SIZE);
2340 edit->force &= (0xFFF - REDRAW_CHAR_ONLY);
2341 break;
2344 edit_cursor_move (edit, 1);
2345 break;
2346 case CK_Begin_Page:
2347 case CK_Begin_Page_Highlight:
2348 edit_begin_page (edit);
2349 break;
2350 case CK_End_Page:
2351 case CK_End_Page_Highlight:
2352 edit_end_page (edit);
2353 break;
2354 case CK_Word_Left:
2355 case CK_Word_Left_Highlight:
2356 edit_left_word_move_cmd (edit);
2357 break;
2358 case CK_Word_Right:
2359 case CK_Word_Right_Highlight:
2360 edit_right_word_move_cmd (edit);
2361 break;
2362 case CK_Up:
2363 case CK_Up_Highlight:
2364 edit_move_up (edit, 1, 0);
2365 break;
2366 case CK_Down:
2367 case CK_Down_Highlight:
2368 edit_move_down (edit, 1, 0);
2369 break;
2370 case CK_Paragraph_Up:
2371 case CK_Paragraph_Up_Highlight:
2372 edit_move_up_paragraph (edit, 0);
2373 break;
2374 case CK_Paragraph_Down:
2375 case CK_Paragraph_Down_Highlight:
2376 edit_move_down_paragraph (edit, 0);
2377 break;
2378 case CK_Scroll_Up:
2379 case CK_Scroll_Up_Highlight:
2380 edit_move_up (edit, 1, 1);
2381 break;
2382 case CK_Scroll_Down:
2383 case CK_Scroll_Down_Highlight:
2384 edit_move_down (edit, 1, 1);
2385 break;
2386 case CK_Home:
2387 case CK_Home_Highlight:
2388 edit_cursor_to_bol (edit);
2389 break;
2390 case CK_End:
2391 case CK_End_Highlight:
2392 edit_cursor_to_eol (edit);
2393 break;
2395 case CK_Tab:
2396 edit_tab_cmd (edit);
2397 if (option_auto_para_formatting) {
2398 format_paragraph (edit, 0);
2399 edit->force |= REDRAW_PAGE;
2400 } else
2401 check_and_wrap_line (edit);
2402 break;
2404 case CK_Toggle_Insert:
2405 edit->overwrite = (edit->overwrite == 0);
2406 #ifndef MIDNIGHT
2407 #ifdef GTK
2408 /* *** */
2409 #else
2410 CSetCursorColor (edit->overwrite ? color_palette (24) : color_palette (19));
2411 #endif
2412 #endif
2413 break;
2415 case CK_Mark:
2416 if (edit->mark2 >= 0) {
2417 if (column_highlighting)
2418 edit_push_action (edit, COLUMN_ON);
2419 column_highlighting = 0;
2421 edit_mark_cmd (edit, 0);
2422 break;
2423 case CK_Column_Mark:
2424 if (!column_highlighting)
2425 edit_push_action (edit, COLUMN_OFF);
2426 column_highlighting = 1;
2427 edit_mark_cmd (edit, 0);
2428 break;
2429 case CK_Unmark:
2430 if (column_highlighting)
2431 edit_push_action (edit, COLUMN_ON);
2432 column_highlighting = 0;
2433 edit_mark_cmd (edit, 1);
2434 break;
2436 case CK_Toggle_Bookmark:
2437 book_mark_clear (edit, edit->curs_line, BOOK_MARK_FOUND_COLOR);
2438 if (book_mark_query_color (edit, edit->curs_line, BOOK_MARK_COLOR))
2439 book_mark_clear (edit, edit->curs_line, BOOK_MARK_COLOR);
2440 else
2441 book_mark_insert (edit, edit->curs_line, BOOK_MARK_COLOR);
2442 break;
2443 case CK_Flush_Bookmarks:
2444 book_mark_flush (edit, BOOK_MARK_COLOR);
2445 book_mark_flush (edit, BOOK_MARK_FOUND_COLOR);
2446 edit->force |= REDRAW_PAGE;
2447 break;
2448 case CK_Next_Bookmark:
2449 if (edit->book_mark) {
2450 struct _book_mark *p;
2451 p = (struct _book_mark *) book_mark_find (edit, edit->curs_line);
2452 if (p->next) {
2453 p = p->next;
2454 if (p->line >= edit->start_line + edit->num_widget_lines || p->line < edit->start_line)
2455 edit_move_display (edit, p->line - edit->num_widget_lines / 2);
2456 edit_move_to_line (edit, p->line);
2459 break;
2460 case CK_Prev_Bookmark:
2461 if (edit->book_mark) {
2462 struct _book_mark *p;
2463 p = (struct _book_mark *) book_mark_find (edit, edit->curs_line);
2464 while (p->line == edit->curs_line)
2465 if (p->prev)
2466 p = p->prev;
2467 if (p->line >= 0) {
2468 if (p->line >= edit->start_line + edit->num_widget_lines || p->line < edit->start_line)
2469 edit_move_display (edit, p->line - edit->num_widget_lines / 2);
2470 edit_move_to_line (edit, p->line);
2473 break;
2475 case CK_Beginning_Of_Text:
2476 case CK_Beginning_Of_Text_Highlight:
2477 edit_move_to_top (edit);
2478 break;
2479 case CK_End_Of_Text:
2480 case CK_End_Of_Text_Highlight:
2481 edit_move_to_bottom (edit);
2482 break;
2484 case CK_Copy:
2485 edit_block_copy_cmd (edit);
2486 break;
2487 case CK_Remove:
2488 edit_block_delete_cmd (edit);
2489 break;
2490 case CK_Move:
2491 edit_block_move_cmd (edit);
2492 break;
2494 case CK_XStore:
2495 edit_copy_to_X_buf_cmd (edit);
2496 break;
2497 case CK_XCut:
2498 edit_cut_to_X_buf_cmd (edit);
2499 break;
2500 case CK_XPaste:
2501 edit_paste_from_X_buf_cmd (edit);
2502 break;
2503 case CK_Selection_History:
2504 edit_paste_from_history (edit);
2505 break;
2507 case CK_Save_As:
2508 #ifndef MIDNIGHT
2509 /* if (COptionsOf (edit->widget) & EDITOR_NO_FILE) */
2510 if (edit->widget->options & EDITOR_NO_FILE)
2511 break;
2512 #endif
2513 edit_save_as_cmd (edit);
2514 break;
2515 case CK_Save:
2516 #ifndef MIDNIGHT
2517 if (COptionsOf (edit->widget) & EDITOR_NO_FILE)
2518 break;
2519 #endif
2520 edit_save_confirm_cmd (edit);
2521 break;
2522 case CK_Load:
2523 #ifndef MIDNIGHT
2524 if (COptionsOf (edit->widget) & EDITOR_NO_FILE)
2525 break;
2526 #endif
2527 edit_load_cmd (edit);
2528 break;
2529 case CK_Save_Block:
2530 edit_save_block_cmd (edit);
2531 break;
2532 case CK_Insert_File:
2533 edit_insert_file_cmd (edit);
2534 break;
2536 case CK_Find:
2537 edit_search_cmd (edit, 0);
2538 break;
2539 case CK_Find_Again:
2540 edit_search_cmd (edit, 1);
2541 break;
2542 case CK_Replace:
2543 edit_replace_cmd (edit, 0);
2544 break;
2545 case CK_Replace_Again:
2546 edit_replace_cmd (edit, 1);
2547 break;
2549 case CK_Exit:
2550 edit_quit_cmd (edit);
2551 break;
2552 case CK_New:
2553 edit_new_cmd (edit);
2554 break;
2556 case CK_Help:
2557 edit_help_cmd (edit);
2558 break;
2560 case CK_Refresh:
2561 edit_refresh_cmd (edit);
2562 break;
2564 case CK_Date:{
2565 time_t t;
2566 #ifdef HAVE_STRFTIME
2567 char s[1024];
2568 #endif
2569 time (&t);
2570 #ifdef HAVE_STRFTIME
2571 strftime (s, sizeof (s), "%c", localtime (&t));
2572 edit_printf (edit, s);
2573 #else
2574 edit_printf (edit, ctime (&t));
2575 #endif
2576 edit->force |= REDRAW_PAGE;
2577 break;
2579 case CK_Goto:
2580 edit_goto_cmd (edit);
2581 break;
2582 case CK_Paragraph_Format:
2583 format_paragraph (edit, 1);
2584 edit->force |= REDRAW_PAGE;
2585 break;
2586 case CK_Delete_Macro:
2587 edit_delete_macro_cmd (edit);
2588 break;
2589 case CK_Match_Bracket:
2590 edit_goto_matching_bracket (edit);
2591 break;
2592 case CK_User_Menu:
2593 if (edit_one_file) {
2594 message (1, MSG_ERROR, _("User menu available only in mcedit invoked from mc"));
2595 break;
2597 else
2598 user_menu (edit);
2599 break;
2600 #ifdef MIDNIGHT
2601 case CK_Sort:
2602 edit_sort_cmd (edit);
2603 break;
2604 case CK_Mail:
2605 edit_mail_dialog (edit);
2606 break;
2607 #endif
2609 /* These commands are not handled and must be handled by the user application */
2610 #ifndef MIDNIGHT
2611 case CK_Sort:
2612 case CK_Mail:
2613 case CK_Find_File:
2614 case CK_Ctags:
2615 case CK_Terminal:
2616 case CK_Terminal_App:
2617 #endif
2618 case CK_Complete:
2619 case CK_Cancel:
2620 case CK_Save_Desktop:
2621 case CK_New_Window:
2622 case CK_Cycle:
2623 case CK_Save_And_Quit:
2624 case CK_Check_Save_And_Quit:
2625 case CK_Run_Another:
2626 case CK_Debug_Start:
2627 case CK_Debug_Stop:
2628 case CK_Debug_Toggle_Break:
2629 case CK_Debug_Clear:
2630 case CK_Debug_Next:
2631 case CK_Debug_Step:
2632 case CK_Debug_Back_Trace:
2633 case CK_Debug_Continue:
2634 case CK_Debug_Enter_Command:
2635 case CK_Debug_Until_Curser:
2636 result = 0;
2637 break;
2638 case CK_Menu:
2639 #ifdef GTK
2640 if (edit->widget->menubar)
2641 gtk_menu_popup (GTK_MENU(
2642 (GTK_MENU_ITEM (g_list_nth_data (GTK_MENU_BAR (edit->widget->menubar)->menu_shell.children, 0)))->submenu
2643 ), 0, 0, 0, 0, 1, 0);
2644 result = 1;
2645 #else
2646 result = 0;
2647 #endif
2648 break;
2651 #ifdef MIDNIGHT
2652 /* CK_Pipe_Block */
2653 if ((command / 1000) == 1) /* a shell command */
2654 edit_block_process_cmd (edit, shell_cmd[command - 1000], 1);
2655 if (command > CK_Macro (0) && command <= CK_Last_Macro) { /* a macro command */
2656 struct macro m[MAX_MACRO_LENGTH];
2657 int nm;
2658 if ((result = edit_load_macro_cmd (edit, m, &nm, command - 2000)))
2659 edit_execute_macro (edit, m, nm);
2661 #else
2662 if (IS_USER_COMMAND (command)) /* a user defined command */
2663 if (user_commamd)
2664 (*user_commamd) (edit, command & 0xFFFF);
2665 if (IS_MACRO_COMMAND (command)) { /* a macro command */
2666 struct macro m[MAX_MACRO_LENGTH];
2667 int nm;
2668 if ((result = edit_load_macro_cmd (edit, m, &nm, command & 0xFFFF)))
2669 edit_execute_macro (edit, m, nm);
2671 #endif
2673 /* keys which must set the col position, and the search vars */
2674 switch (command) {
2675 case CK_Find:
2676 case CK_Find_Again:
2677 case CK_Replace:
2678 case CK_Replace_Again:
2679 edit->prev_col = edit_get_col (edit);
2680 return 1;
2681 break;
2682 case CK_Up:
2683 case CK_Up_Highlight:
2684 case CK_Down:
2685 case CK_Down_Highlight:
2686 case CK_Page_Up:
2687 case CK_Page_Up_Highlight:
2688 case CK_Page_Down:
2689 case CK_Page_Down_Highlight:
2690 case CK_Beginning_Of_Text:
2691 case CK_Beginning_Of_Text_Highlight:
2692 case CK_End_Of_Text:
2693 case CK_End_Of_Text_Highlight:
2694 case CK_Paragraph_Up:
2695 case CK_Paragraph_Up_Highlight:
2696 case CK_Paragraph_Down:
2697 case CK_Paragraph_Down_Highlight:
2698 case CK_Scroll_Up:
2699 case CK_Scroll_Up_Highlight:
2700 case CK_Scroll_Down:
2701 case CK_Scroll_Down_Highlight:
2702 edit->search_start = edit->curs1;
2703 edit->found_len = 0;
2704 edit_find_bracket (edit);
2705 edit_check_spelling (edit);
2706 return 1;
2707 break;
2708 default:
2709 edit->found_len = 0;
2710 edit->prev_col = edit_get_col (edit);
2711 edit->search_start = edit->curs1;
2713 edit_find_bracket (edit);
2714 edit_check_spelling (edit);
2716 if (option_auto_para_formatting) {
2717 switch (command) {
2718 case CK_BackSpace:
2719 case CK_Delete:
2720 case CK_Delete_Word_Left:
2721 case CK_Delete_Word_Right:
2722 case CK_Delete_To_Line_End:
2723 case CK_Delete_To_Line_Begin:
2724 format_paragraph (edit, 0);
2725 edit->force |= REDRAW_PAGE;
2728 return result;
2732 /* either command or char_for_insertion must be passed as -1 */
2733 /* returns 0 if command is a macro that was not found, 1 otherwise */
2734 int edit_execute_command (WEdit * edit, int command, int char_for_insertion)
2736 int r;
2737 r = edit_execute_cmd (edit, command, char_for_insertion);
2738 edit_update_screen (edit);
2739 return r;
2742 void edit_execute_macro (WEdit * edit, struct macro macro[], int n)
2744 int i = 0;
2745 edit->force |= REDRAW_PAGE;
2746 for (; i < n; i++) {
2747 edit_execute_cmd (edit, macro[i].command, macro[i].ch);
2749 edit_update_screen (edit);
2752 /* User edit menu, like user menu (F2) but only in editor. */
2753 void user_menu (WEdit *edit)
2755 FILE *fd;
2756 int nomark;
2757 struct stat status;
2758 long start_mark, end_mark;
2759 char *block_file = catstrs (home_dir, BLOCK_FILE, 0);
2760 char *error_file = catstrs (home_dir, ERROR_FILE, 0);
2761 int rc = 0;
2763 nomark = eval_marks (edit, &start_mark, &end_mark);
2764 if (! nomark) /* remember marked or not */
2765 edit_save_block (edit, block_file = catstrs (home_dir, BLOCK_FILE, 0),
2766 start_mark, end_mark);
2768 /* run shell scripts from menu */
2769 user_menu_cmd (edit);
2771 if (stat (error_file, &status) == 0) {
2772 if (!status.st_size) { /* no error messages */
2773 if (stat (block_file, &status) == 0)
2774 if (!status.st_size)
2775 return; /* no block messages */
2776 if (! nomark) /* i.e. we have marked block */
2777 rc = edit_block_delete_cmd(edit);
2778 if (!rc) {
2779 edit_cursor_to_bol (edit);
2780 edit_insert_file (edit, block_file);
2781 edit_cursor_to_eol (edit);
2782 if ((fd = fopen (block_file, "w"))) fclose(fd);
2784 } else { /* it is error */
2785 edit_cursor_to_bol (edit);
2786 edit_insert_file (edit, error_file);
2787 if ((fd = fopen (error_file, "w"))) fclose(fd);
2788 if ((fd = fopen (block_file, "w"))) fclose(fd);
2790 } else {
2791 edit_error_dialog ("",
2792 get_sys_error (catstrs (_ ("Error trying to stat file:"),
2793 error_file, 0)));
2794 return;
2796 edit_refresh_cmd (edit);
2797 edit->force |= REDRAW_COMPLETELY;
2798 return;