1 /* -*- mode: C; mode: fold; -*- */
2 /* Copyright (c) 1992, 1998 John E. Davis
3  * This file is part of JED editor library source.
4  *
5  * You may distribute this file under the terms the GNU General Public
6  * License.  See the file COPYING for more information.
7  */
8 #include "config.h"
9 #include "jed-feat.h"
10 
11 #include <stdio.h>
12 #include "buffer.h"
13 #include "undo.h"
14 #include "ins.h"
15 #include "line.h"
16 #include "misc.h"
17 #include "kanji.h"
18 
19 
20 #define LAST_UNDO CBuf->undo->Last_Undo
21 #define FIRST_UNDO CBuf->undo->First_Undo
22 #define UNDO_RING CBuf->undo->Undo_Ring
23 
24 #ifdef UNDO_HAS_REDO
25 # define CURRENT_UNDO CBuf->undo->Current_Undo
26 #endif
27 
28 static int Undo_In_Progress = 0;
29 int Undo_Buf_Unch_Flag;	       /* 1 if buffer prev not modified */
30 
31 #ifdef UNDO_HAS_REDO
32 #define DONT_RECORD_UNDO  (!(CBuf->flags & UNDO_ENABLED)\
33 			   || (CBuf->undo == NULL))
34 #else
35 #define DONT_RECORD_UNDO  (!(CBuf->flags & UNDO_ENABLED)\
36 			   || (CBuf->undo == NULL) || Undo_In_Progress)
37 #endif
38 
39 #define UNDO_BD_FLAG 0x8000
40 #define UNDO_UNCHANGED_FLAG 0x4000
41 
42 #ifdef UNDO_HAS_REDO
43 # define IS_UNDO_BD (CURRENT_UNDO->type & UNDO_BD_FLAG)
44 #else
45 # define IS_UNDO_BD (LAST_UNDO->type & UNDO_BD_FLAG)
46 #endif
47 
prepare_next_undo(void)48 static void prepare_next_undo(void) /*{{{*/
49 {
50    LAST_UNDO++;
51    if (LAST_UNDO >= UNDO_RING + MAX_UNDOS) LAST_UNDO = UNDO_RING;
52    if (LAST_UNDO == FIRST_UNDO)
53      {
54 	FIRST_UNDO++;
55 #ifdef UNDO_HAS_REDO
56 	if (FIRST_UNDO >= UNDO_RING + MAX_UNDOS) FIRST_UNDO = UNDO_RING;
57 #endif
58      }
59 #ifdef UNDO_HAS_REDO
60    if (LAST_UNDO == CURRENT_UNDO)      /*  undo-ring overflow will trash  */
61      CURRENT_UNDO = NULL;	       /*  further undos  */
62 #else
63    if (FIRST_UNDO >= UNDO_RING + MAX_UNDOS) FIRST_UNDO = UNDO_RING;
64 #endif
65 
66 
67    LAST_UNDO->type = 0;
68    LAST_UNDO->misc = 0;
69 }
70 
71 /*}}}*/
72 
73 #ifdef UNDO_HAS_REDO
74 /*  Returns True if there is still  undo info to be processed. */
75 #define MORE_UNDO_INFO (CURRENT_UNDO && (CURRENT_UNDO->type))
76 #endif
77 
record_deletion(unsigned char * p,int n)78 void record_deletion (unsigned char *p, int n) /*{{{*/
79 {
80    int misc;
81    unsigned char *pb;
82 
83    if (DONT_RECORD_UNDO || (n == 0)) return;
84 
85    while (1)
86      {
87 	if (((LAST_UNDO->type & 0xFF) == 0)
88 	    || ((LAST_UNDO->type & CDELETE)
89 		&& (LAST_UNDO->linenum == LineNum + CBuf->nup)
90 		&& (LAST_UNDO->point == Point)))
91 	  {
92 	     if (LAST_UNDO->type != 0) misc = LAST_UNDO->misc; else misc = 0;
93 	     pb = LAST_UNDO->buf + misc;
94 	     while (((misc < 8) && n && !iskanji(*p)) || ((misc < 7) && (1 < n) && iskanji(*p)))
95 	       {
96 		  if(iskanji(*p))
97 		    {
98 		       *pb++ = *p++;
99 		       misc++;
100 		       n--;
101 		    }
102 		  *pb++ = *p++;
103 		  misc++;
104 		  n--;
105 	       }
106 	     LAST_UNDO->misc = misc;
107 	     LAST_UNDO->type |= CDELETE;
108 	     LAST_UNDO->linenum = LineNum + CBuf->nup;
109 	     LAST_UNDO->point = Point;
110 	     if (Undo_Buf_Unch_Flag) LAST_UNDO->type |= UNDO_UNCHANGED_FLAG;
111 	     Undo_Buf_Unch_Flag = 0;
112 	  }
113 
114 	if (n == 0)
115 	  break;
116 
117 	prepare_next_undo();
118      }
119 
120    if (Undo_Buf_Unch_Flag) LAST_UNDO->type |= UNDO_UNCHANGED_FLAG;
121 #ifdef UNDO_HAS_REDO
122    set_current_undo ();
123 #endif
124 }
125 
126 /*}}}*/
127 
undo(void)128 int undo (void) /*{{{*/
129 {
130    int line;
131    CHECK_READ_ONLY
132    if (!(CBuf->flags & UNDO_ENABLED))
133      {
134 	msg_error("Undo not enabled for this buffer.");
135 	return(0);
136      }
137    else if ((CBuf->undo == NULL)
138 #ifdef UNDO_HAS_REDO
139 	    || (0 == MORE_UNDO_INFO)
140 #else
141 	    || (LAST_UNDO->type == 0)
142 #endif
143 	    )
144      {
145 	msg_error("No more undo information.");
146 	return(0);
147      }
148    Undo_In_Progress = 1;
149 
150    do
151      {
152 #ifdef UNDO_HAS_REDO
153 	line = (int) CURRENT_UNDO->linenum;
154 #else
155 	line = (int) LAST_UNDO->linenum;
156 #endif
157 	if ((line <= (int) CBuf->nup) || ((unsigned int) line > CBuf->nup + Max_LineNum))
158 	  {
159 	     msg_error("Next undo lies outside visible buffer.");
160 	     break;
161 	  }
162 	line -= CBuf->nup;
163 	goto_line(&line);
164 #ifdef UNDO_HAS_REDO
165 	Point = CURRENT_UNDO->point;
166 #else
167 	Point = LAST_UNDO->point;
168 #endif
169 
170 #ifdef UNDO_HAS_REDO
171 	switch (CURRENT_UNDO->type & 0xFF)
172 #else
173 	switch (LAST_UNDO->type & 0xFF)
174 #endif
175 	  {
176 #ifdef UNDO_HAS_REDO
177 	   case CDELETE: ins_chars(CURRENT_UNDO->buf, CURRENT_UNDO->misc);
178 	     /* Point = CURRENT_UNDO->point; */
179 #else
180 	   case CDELETE: ins_chars(LAST_UNDO->buf, LAST_UNDO->misc);
181 	     /* Point = LAST_UNDO->point; */
182 #endif
183 	     break;
184 
185 #ifdef UNDO_HAS_REDO
186 	   case CINSERT: deln(&CURRENT_UNDO->misc);
187 #else
188 	   case CINSERT: deln(&LAST_UNDO->misc);
189 #endif
190 	     break;
191 
192 	   case NLINSERT: del(); break;
193 
194 	   default: return(1);
195 	  }
196 
197 #ifdef UNDO_HAS_REDO
198 	if (CURRENT_UNDO == NULL) break;
199 	/*  no more undo info after overflow */
200 
201 	/*  above calls to ins_chars/deln/del may overflow the undo-ring */
202 	if (CURRENT_UNDO->type & UNDO_UNCHANGED_FLAG)
203 	  {
204 	     mark_buffer_modified (0);
205 	  }
206 
207 	if (CURRENT_UNDO == FIRST_UNDO)
208 	  {
209 	     CURRENT_UNDO = NULL ;
210 	     break ;			/*  no more undo info  */
211 	  }
212 
213 	CURRENT_UNDO--;
214 	if (CURRENT_UNDO < UNDO_RING) CURRENT_UNDO = UNDO_RING + MAX_UNDOS - 1;
215 #else
216 	if (LAST_UNDO->type & UNDO_UNCHANGED_FLAG)
217 	  {
218 	     mark_buffer_modified (0);
219 	  }
220 
221 	if (LAST_UNDO == FIRST_UNDO) LAST_UNDO->type = 0;
222 	else
223 	  {
224 	     LAST_UNDO--;
225 	     if (LAST_UNDO < UNDO_RING) LAST_UNDO = UNDO_RING + MAX_UNDOS - 1;
226 	  }
227 #endif
228      }
229 #ifdef UNDO_HAS_REDO
230    while ((!IS_UNDO_BD) && (CURRENT_UNDO->type));
231 #else
232    while ((!IS_UNDO_BD) && (LAST_UNDO->type));
233 #endif
234 
235    message("Undo!");
236    Undo_In_Progress = 0;
237    return(1);
238 }
239 
240 /*}}}*/
241 
record_insertion(int n)242 void record_insertion(int n) /*{{{*/
243 {
244    if (DONT_RECORD_UNDO || !n) return;
245 
246    if ((Undo_Buf_Unch_Flag) && (LAST_UNDO->type))
247      {
248 	prepare_next_undo ();
249      }
250 
251    if (LAST_UNDO->type == 0)
252      {
253 	LAST_UNDO->misc = n;
254 	LAST_UNDO->point = Point;
255      }
256    else if ((LAST_UNDO->type & CINSERT) && (LAST_UNDO->linenum == LineNum + CBuf->nup)
257 	    && (LAST_UNDO->point + LAST_UNDO->misc == Point)
258 	    && !(LAST_UNDO->misc == 32 && n == 1 && iskanji(CLine->data[Point]) && !iskanji2nd(CLine->data, Point)))
259      {
260 	LAST_UNDO->misc += n;
261      }
262    else
263      {
264 	prepare_next_undo();
265 	LAST_UNDO->point = Point;
266 	LAST_UNDO->misc = n;
267      }
268 
269    LAST_UNDO->type |= CINSERT;
270    if (Undo_Buf_Unch_Flag) LAST_UNDO->type |= UNDO_UNCHANGED_FLAG;
271    LAST_UNDO->linenum = LineNum + CBuf->nup;
272 #ifdef UNDO_HAS_REDO
273    set_current_undo ();
274 #endif
275 }
276 
277 /*}}}*/
278 
record_newline_insertion()279 void record_newline_insertion() /*{{{*/
280 {
281    if (DONT_RECORD_UNDO) return;
282    if (LAST_UNDO->type != 0) prepare_next_undo();
283    if (Undo_Buf_Unch_Flag) LAST_UNDO->type |= UNDO_UNCHANGED_FLAG;
284    LAST_UNDO->point = Point;
285    LAST_UNDO->misc = 0;
286    LAST_UNDO->type |= NLINSERT;
287    LAST_UNDO->linenum = LineNum + CBuf->nup;
288 #ifdef UNDO_HAS_REDO
289    set_current_undo ();
290 #endif
291 }
292 
293 /*}}}*/
294 
delete_undo_ring(Buffer * b)295 void delete_undo_ring(Buffer *b) /*{{{*/
296 {
297    SLfree ((char *)b->undo);
298 }
299 
300 /*}}}*/
301 
create_undo_ring()302 void create_undo_ring() /*{{{*/
303 {
304    Undo_Type *ur;
305    Undo_Object_Type *uo;
306    int n;
307 
308    if (NULL == (ur = (Undo_Type *) SLmalloc (sizeof(Undo_Type))))
309      {
310 	msg_error("Unable to malloc space for undo!");
311 	return;
312      }
313    CBuf->undo = ur;
314    uo = ur->Undo_Ring;
315    ur->Last_Undo = ur->First_Undo = uo;
316 #ifdef UNDO_HAS_REDO
317    ur->Current_Undo = NULL;
318 #endif
319 
320    n = MAX_UNDOS;
321    while (n--)
322      {
323 	uo->type = 0;
324 	uo++;
325      }
326 }
327 
328 /*}}}*/
329 
mark_undo_boundary(Buffer * b)330 void mark_undo_boundary (Buffer *b) /*{{{*/
331 {
332    Buffer *s = CBuf;
333 
334    CBuf = b;
335 
336    if (!DONT_RECORD_UNDO && (LAST_UNDO->type != 0))
337      {
338 	LAST_UNDO->type |= UNDO_BD_FLAG;
339      }
340    CBuf = s;
341 }
342 
343 /*}}}*/
344 
unmark_undo_boundary(Buffer * b)345 void unmark_undo_boundary (Buffer *b) /*{{{*/
346 {
347 #if 1
348    (void) b;
349 #else
350    Buffer *s = CBuf;
351 
352    CBuf = b;
353 
354    if (!DONT_RECORD_UNDO && (LAST_UNDO->type != 0))
355      {
356 	LAST_UNDO->type &= ~UNDO_BD_FLAG;
357      }
358    CBuf = s;
359 #endif
360 }
361 
362 /*}}}*/
363 
364 #ifdef UNDO_HAS_REDO
set_current_undo()365 void set_current_undo()		/*  Called when text is inserted, deleted,  */ /*{{{*/
366 {				/*  or the abort key is pressed.  */
367    if ((!Undo_In_Progress) && CBuf->undo)
368      {
369 	CURRENT_UNDO = LAST_UNDO ;
370      }
371 }
372 
373 /*}}}*/
374 
update_undo_unchanged(void)375 void update_undo_unchanged (void) /*{{{*/
376 /* Update unchange flags after a buffer save.  The reason for this is
377  * that after a save, undoing on the buffer does not affect the disk file.
378  */
379 {
380    Undo_Object_Type *uo;
381    int n;
382 
383    if (DONT_RECORD_UNDO) return;
384 
385    uo = UNDO_RING ;
386    n = MAX_UNDOS;
387    while (n--)
388      {
389 	uo->type &= ~UNDO_UNCHANGED_FLAG;
390 	uo++;
391      }
392 
393    CURRENT_UNDO = LAST_UNDO ;
394 }
395 
396 /*}}}*/
397 #endif
398 
399 
400