1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32
33 #include "undo.h"
34 #include "textBuf.h"
35 #include "text.h"
36 #include "nedit.h"
37 #include "search.h"
38 #include "window.h"
39 #include "file.h"
40 #include "userCmds.h"
41 #include "preferences.h"
42 #include "../util/nedit_malloc.h"
43
44 #include <string.h>
45 #include <sys/param.h>
46
47 #include <Xm/Xm.h>
48 #include <Xm/Text.h>
49
50 #ifdef HAVE_DEBUG_H
51 #include "../debug.h"
52 #endif
53
54
55 #define FORWARD 1
56 #define REVERSE 2
57
58 static void addUndoItem(WindowInfo *window, UndoInfo *undo);
59 static void addRedoItem(WindowInfo *window, UndoInfo *redo);
60 static void removeUndoItem(WindowInfo *window);
61 static void removeRedoItem(WindowInfo *window);
62 static void appendDeletedText(WindowInfo *window,
const char *deletedText,
63 int deletedLen,
int direction);
64 static void trimUndoList(WindowInfo *window,
int maxLength);
65 static int determineUndoType(
int nInserted,
int nDeleted);
66 static void freeUndoRecord(UndoInfo *undo);
67
68 static void doUndo(WindowInfo *window,
int isBatch,
size_t *cursors,
int cursorIndex)
69 {
70 UndoInfo *undo = window->undo;
71 int restoredTextLength;
72
73
74 if (undo ==
NULL)
75 return;
76
77
78
79
80
81
82 undo->inUndo = True;
83
84
85 BufReplace(window->buffer, undo->startPos, undo->endPos,
86 (undo->oldText !=
NULL ? undo->oldText :
""));
87
88 restoredTextLength = undo->oldText !=
NULL ? strlen(undo->oldText) :
0;
89 int diff = restoredTextLength;
90 if(diff ==
0) {
91 diff = undo->startPos - undo->endPos;
92 }
93 if (!window->buffer->primary.selected || GetPrefUndoModifiesSelection()) {
94 size_t newPos = undo->startPos + restoredTextLength;
95 if(!isBatch) {
96
97
98 TextSetCursorPos(window->lastFocus, newPos);
99 }
else {
100 cursors[cursorIndex] = newPos;
101 for(
int i=cursorIndex-
1;i>=
0;i--) {
102 cursors[i] += diff;
103 }
104 }
105 }
106
107 if (GetPrefUndoModifiesSelection() && !isBatch) {
108 if (restoredTextLength >
0) {
109 BufSelect(window->buffer, undo->startPos, undo->startPos +
110 restoredTextLength);
111 }
112 else {
113 BufUnselect(window->buffer);
114 }
115 }
116 MakeSelectionVisible(window, window->lastFocus);
117
118
119
120
121
122 if (undo->restoresToSaved) {
123 SetWindowModified(window, False);
124 RemoveBackupFile(window);
125 }
126
127
128 removeUndoItem(window);
129 }
130
131 void Undo(WindowInfo *window) {
132 int numOp = window->undo->numOp;
133 int undoCount =
1;
134 int isBatch =
0;
135 if(numOp >
0) {
136 undoCount = numOp;
137 isBatch =
1;
138 }
139
140 size_t *cursors =
NULL;
141 int cursorIndex =
0;
142 TextChangeCursors(window->lastFocus,
0,
0);
143 if(!isBatch) {
144 TextClearMultiCursors(window->lastFocus);
145 }
else {
146 cursors = NEditCalloc(
sizeof(
size_t), numOp);
147 }
148
149 window->undo_op_batch_size = numOp;
150 for(
int i=
0;i<undoCount;i++) {
151 doUndo(window, isBatch, cursors, cursorIndex++);
152 }
153
154 if(cursors) {
155 TextSetCursors(window->lastFocus, cursors, numOp);
156 NEditFree(cursors);
157 }
158 }
159
160 static void doRedo(WindowInfo *window,
int isBatch,
size_t *cursors,
int cursorIndex)
161 {
162 UndoInfo *redo = window->redo;
163 int restoredTextLength;
164
165
166 if (window->redo ==
NULL) {
167 return;
168 }
169
170
171
172
173 redo->inUndo = True;
174
175
176 BufReplace(window->buffer, redo->startPos, redo->endPos,
177 (redo->oldText !=
NULL ? redo->oldText :
""));
178
179 restoredTextLength = redo->oldText !=
NULL ? strlen(redo->oldText) :
0;
180 if (!window->buffer->primary.selected || GetPrefUndoModifiesSelection()) {
181
182
183 int newpos = redo->startPos + restoredTextLength;
184 if(!isBatch) {
185 TextSetCursorPos(window->lastFocus, newpos);
186 }
else {
187
188 cursors[cursorIndex] = redo->startPos + restoredTextLength;
189 }
190 }
191 if (!isBatch && GetPrefUndoModifiesSelection()) {
192 if (restoredTextLength >
0) {
193 BufSelect(window->buffer, redo->startPos, redo->startPos +
194 restoredTextLength);
195 }
196 else {
197 BufUnselect(window->buffer);
198 }
199 }
200 MakeSelectionVisible(window, window->lastFocus);
201
202
203
204
205
206 if (redo->restoresToSaved) {
207 SetWindowModified(window, False);
208 RemoveBackupFile(window);
209 }
210
211
212 removeRedoItem(window);
213 }
214
215 void Redo(WindowInfo *window)
216 {
217 UndoInfo *redo = window->redo;
218
219 if (window->redo ==
NULL)
220 return;
221
222 int numOp = redo->numOp;
223 int redoCount =
1;
224 int isBatch =
0;
225 size_t *cursors =
NULL;
226 int cursorIndex =
0;
227 if(numOp >
0) {
228 redoCount = numOp;
229 isBatch =
1;
230 cursors = NEditCalloc(
sizeof(
size_t), numOp);
231 }
232
233 TextChangeCursors(window->lastFocus,
0,
0);
234 window->undo_op_batch_size = numOp;
235 for(
int i=
0;i<redoCount;i++) {
236 doRedo(window, isBatch, cursors, cursorIndex++);
237 }
238
239 if(cursors) {
240 TextSetCursors(window->lastFocus, cursors, numOp);
241 NEditFree(cursors);
242 }
243 }
244
245
246
247
248
249
250
251
252
253
254 void SaveUndoInformation(WindowInfo *window,
int pos,
int nInserted,
255 int nDeleted,
const char *deletedText)
256 {
257 int newType, oldType;
258 UndoInfo *u, *undo = window->undo;
259 int isUndo = (undo !=
NULL && undo->inUndo);
260 int isRedo = (window->redo !=
NULL && window->redo->inUndo);
261 int numOp = window->undo_op_batch_size;
262
263
264
265
266 if (!(isUndo || isRedo) && window->redo !=
NULL)
267 ClearRedoList(window);
268
269
270
271 newType = determineUndoType(nInserted, nDeleted);
272 if (newType ==
UNDO_NOOP)
273 return;
274 oldType = (undo ==
NULL || isUndo) ?
UNDO_NOOP : undo->type;
275
276
277
278
279
280
281
282
283
284
285
286 if (window->fileChanged && !window->undo_batch_begin) {
287
288
289 if ( ((oldType ==
ONE_CHAR_INSERT || oldType ==
ONE_CHAR_REPLACE)
290 && newType ==
ONE_CHAR_INSERT) && (pos == undo->endPos)) {
291 undo->endPos++;
292 window->autoSaveCharCount++;
293 return;
294 }
295
296
297 if ((oldType ==
ONE_CHAR_REPLACE && newType ==
ONE_CHAR_REPLACE) &&
298 (pos == undo->endPos)) {
299 appendDeletedText(window, deletedText, nDeleted,
FORWARD);
300 undo->endPos++;
301 window->autoSaveCharCount++;
302 return;
303 }
304
305
306 if ((oldType==
ONE_CHAR_DELETE && newType==
ONE_CHAR_DELETE) &&
307 (pos==undo->startPos)) {
308 appendDeletedText(window, deletedText, nDeleted,
FORWARD);
309 return;
310 }
311
312
313 if ((oldType==
ONE_CHAR_DELETE && newType==
ONE_CHAR_DELETE) &&
314 (pos == undo->startPos-
1)) {
315 appendDeletedText(window, deletedText, nDeleted,
REVERSE);
316 undo->startPos--;
317 undo->endPos--;
318 return;
319 }
320 }
321
322
323
324
325
326 undo = (UndoInfo *)NEditMalloc(
sizeof(UndoInfo));
327 undo->oldLen =
0;
328 undo->oldText =
NULL;
329 undo->type = newType;
330 undo->inUndo = False;
331 undo->numOp = numOp;
332 undo->restoresToSaved = False;
333 undo->startPos = pos;
334 undo->endPos = pos + nInserted;
335
336
337 if (nDeleted >
0) {
338 undo->oldLen = nDeleted +
1;
339 undo->oldText = (
char*)NEditMalloc(nDeleted +
1);
340 strcpy(undo->oldText, deletedText);
341 }
342
343
344 window->autoSaveOpCount++;
345
346
347
348 if (!window->fileChanged) {
349 undo->restoresToSaved = True;
350 for (u=window->undo; u!=
NULL; u=u->next)
351 u->restoresToSaved = False;
352 for (u=window->redo; u!=
NULL; u=u->next)
353 u->restoresToSaved = False;
354 }
355
356
357
358
359 if (isUndo)
360 addRedoItem(window, undo);
361 else
362 addUndoItem(window, undo);
363 }
364
365
366
367
368
369
370
371 void ClearUndoList(WindowInfo *window)
372 {
373 while (window->undo !=
NULL)
374 removeUndoItem(window);
375 }
376 void ClearRedoList(WindowInfo *window)
377 {
378 while (window->redo !=
NULL)
379 removeRedoItem(window);
380 }
381
382
383
384
385
386
387 static void addUndoItem(WindowInfo *window, UndoInfo *undo)
388 {
389
390
391 if (window->undo ==
NULL) {
392 SetSensitive(window, window->undoItem, True);
393 SetBGMenuUndoSensitivity(window, True);
394 }
395
396
397 undo->next = window->undo;
398 window->undo = undo;
399
400
401 window->undoOpCount++;
402 window->undoMemUsed += undo->oldLen;
403
404
405 if (window->undoOpCount > GetPrefUndoOpLimit())
406 trimUndoList(window, GetPrefUndoOpTrimTo());
407 if (window->undoMemUsed > GetPrefUndoWorryLimit())
408 trimUndoList(window, GetPrefUndoWorryTrimTo());
409 if (window->undoMemUsed > GetPrefUndoPurgeLimit())
410 trimUndoList(window, GetPrefUndoPurgeTrimTo());
411 }
412
413
414
415
416 static void addRedoItem(WindowInfo *window, UndoInfo *redo)
417 {
418
419 if (window->redo ==
NULL) {
420 SetSensitive(window, window->redoItem, True);
421 SetBGMenuRedoSensitivity(window, True);
422 }
423
424
425 redo->next = window->redo;
426 window->redo = redo;
427 }
428
429
430
431
432 static void removeUndoItem(WindowInfo *window)
433 {
434 UndoInfo *undo = window->undo;
435
436 if (undo ==
NULL)
437 return;
438
439
440 window->undoOpCount--;
441 window->undoMemUsed -= undo->oldLen;
442
443
444 window->undo = undo->next;
445 freeUndoRecord(undo);
446
447
448 if (window->undo ==
NULL) {
449 SetSensitive(window, window->undoItem, False);
450 SetBGMenuUndoSensitivity(window, False);
451 }
452 }
453
454
455
456
457 static void removeRedoItem(WindowInfo *window)
458 {
459 UndoInfo *redo = window->redo;
460
461
462 window->redo = redo->next;
463 freeUndoRecord(redo);
464
465
466 if (window->redo ==
NULL) {
467 SetSensitive(window, window->redoItem, False);
468 SetBGMenuRedoSensitivity(window, False);
469 }
470 }
471
472
473
474
475
476
477
478 static void appendDeletedText(WindowInfo *window,
const char *deletedText,
479 int deletedLen,
int direction)
480 {
481 UndoInfo *undo = window->undo;
482 char *comboText;
483
484
485 comboText = (
char*)NEditMalloc(undo->oldLen + deletedLen);
486
487
488 if (direction ==
FORWARD) {
489 strcpy(comboText, undo->oldText);
490 strcat(comboText, deletedText);
491 }
else {
492 strcpy(comboText, deletedText);
493 strcat(comboText, undo->oldText);
494 }
495
496
497 window->undoMemUsed++;
498
499
500 NEditFree(undo->oldText);
501 undo->oldText = comboText;
502 undo->oldLen += deletedLen;
503 }
504
505
506
507
508
509 static void trimUndoList(WindowInfo *window,
int maxLength)
510 {
511 int i;
512 UndoInfo *u, *lastRec;
513
514 if (window->undo ==
NULL)
515 return;
516
517
518 for (i=
1, u=window->undo; i<maxLength && u!=
NULL; i++, u=u->next);
519 if (u ==
NULL)
520 return;
521
522
523 lastRec = u;
524 while (lastRec->next !=
NULL) {
525 u = lastRec->next;
526 lastRec->next = u->next;
527 window->undoOpCount--;
528 window->undoMemUsed -= u->oldLen;
529 freeUndoRecord(u);
530 }
531 }
532
533 static int determineUndoType(
int nInserted,
int nDeleted)
534 {
535 int textDeleted, textInserted;
536
537 textDeleted = (nDeleted >
0);
538 textInserted = (nInserted >
0);
539
540 if (textInserted && !textDeleted) {
541
542 if (nInserted ==
1)
543 return ONE_CHAR_INSERT;
544 else
545 return BLOCK_INSERT;
546 }
else if (textInserted && textDeleted) {
547
548 if (nInserted ==
1)
549 return ONE_CHAR_REPLACE;
550 else
551 return BLOCK_REPLACE;
552 }
else if (!textInserted && textDeleted) {
553
554 if (nDeleted ==
1)
555 return ONE_CHAR_DELETE;
556 else
557 return BLOCK_DELETE;
558 }
else {
559
560 return UNDO_NOOP;
561 }
562 }
563
564 static void freeUndoRecord(UndoInfo *undo)
565 {
566 if (undo ==
NULL)
567 return;
568
569 NEditFree(undo->oldText);
570 NEditFree(undo);
571 }
572