revert between 56095 -> 55830 in arch
[AROS.git] / workbench / classes / zune / texteditor / mcc / UndoFunctions.c
blob29a69fa2afca2e1ec323f76c7da490bb3f155714
1 /***************************************************************************
3 TextEditor.mcc - Textediting MUI Custom Class
4 Copyright (C) 1997-2000 Allan Odgaard
5 Copyright (C) 2005-2014 TextEditor.mcc Open Source Team
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 TextEditor class Support Site: http://www.sf.net/projects/texteditor-mcc
19 $Id$
21 ***************************************************************************/
23 #include <string.h>
25 #include <clib/alib_protos.h>
27 #include <proto/exec.h>
28 #include <proto/intuition.h>
30 #include "private.h"
31 #include "Debug.h"
33 /// FreeUndoStep()
34 // free the memory occupated by an undo step
35 static void FreeUndoStep(struct InstData *data, ULONG index)
37 struct UserAction *step = &data->undoSteps[index];
39 ENTER();
41 D(DBF_UNDO, "free undo step %ld", index);
43 if(step->type == ET_DELETEBLOCK || step->type == ET_DELETEBLOCK_NOMOVE || step->type == ET_PASTEBLOCK)
45 if(step->clip != NULL)
47 FreeVecPooled(data->mypool, step->clip);
48 // clear the pointer
49 step->clip = NULL;
51 // forget about the type of undo
52 step->type = ET_NONE;
55 LEAVE();
58 ///
59 /// Undo()
60 BOOL Undo(struct InstData *data)
62 BOOL success = FALSE;
64 ENTER();
66 D(DBF_UNDO, "before maxUndoSteps=%ld nextUndoStep=%ld usedUndoSteps=%ld", data->maxUndoSteps, data->nextUndoStep, data->usedUndoSteps);
68 // check if there is something in the undo buffer available
69 if(isFlagClear(data->flags, FLG_ReadOnly) && data->nextUndoStep > 0)
71 struct UserAction *action;
72 BOOL moveCursor = TRUE;
74 if(Enabled(data))
76 data->blockinfo.enabled = FALSE;
77 MarkText(data, data->blockinfo.startx, data->blockinfo.startline, data->blockinfo.stopx, data->blockinfo.stopline);
80 // as the redo operation automatically
81 // becomes available when undo is used we just
82 // check here if we didn't yet set RedoAvailable
83 // as we only want to set it once
84 if(data->nextUndoStep == data->usedUndoSteps)
85 set(data->object, MUIA_TextEditor_RedoAvailable, TRUE);
87 // go one step back
88 data->nextUndoStep--;
89 action = &data->undoSteps[data->nextUndoStep];
91 // if(data->actualline != LineNode(data, buffer->y) || data->CPos_X != buffer->x)
92 SetCursor(data, data->CPos_X, data->actualline, FALSE);
94 data->CPos_X = action->x;
95 data->actualline = LineNode(data, action->y);
96 ScrollIntoDisplay(data);
98 // perform the saved undo action
99 switch(action->type)
101 case ET_PASTECHAR:
103 D(DBF_UNDO, "undo PASTECHAR");
104 action->del.character = data->actualline->line.Contents[data->CPos_X];
105 action->del.style = GetStyle(data->CPos_X, data->actualline);
106 action->del.flow = data->actualline->line.Flow;
107 action->del.separator = data->actualline->line.Separator;
108 action->del.highlight = data->actualline->line.Highlight;
109 RemoveChars(data, data->CPos_X, data->actualline, 1);
111 break;
113 case ET_DELETECHAR:
115 D(DBF_UNDO, "undo DELETECHAR");
116 PasteChars(data, data->CPos_X, data->actualline, 1, (char *)&action->del.character, action);
118 break;
120 case ET_SPLITLINE:
122 D(DBF_UNDO, "undo SPLITLINE");
123 MergeLines(data, data->actualline);
125 break;
127 case ET_MERGELINES:
129 D(DBF_UNDO, "undo MERGELINES");
130 SplitLine(data, data->CPos_X, data->actualline, FALSE, action);
132 break;
134 case ET_BACKSPACEMERGE:
136 D(DBF_UNDO, "undo BACKSPACEMARGE");
137 SplitLine(data, data->CPos_X, data->actualline, TRUE, action);
139 break;
141 case ET_PASTEBLOCK:
143 struct marking block =
145 TRUE,
146 LineNode(data, action->y),
147 action->x,
148 LineNode(data, action->blk.y),
149 action->blk.x
151 STRPTR clip = GetBlock(data, &block);
153 D(DBF_UNDO, "undo PASTEBLOCK");
154 CutBlock2(data, CUTF_CUT|CUTF_UPDATE, &block);
155 action->clip = clip;
157 break;
159 case ET_DELETEBLOCK_NOMOVE:
161 D(DBF_UNDO, "undo DELETEBLOCK_NOMOVE");
162 moveCursor = FALSE;
164 // fall through...
166 case ET_DELETEBLOCK:
168 struct Hook *oldhook;
170 D(DBF_UNDO, "undo DELETEBLOCK");
171 oldhook = data->ImportHook;
172 data->ImportHook = &ImPlainHook;
173 InsertText(data, action->clip, moveCursor);
174 data->ImportHook = oldhook;
175 FreeVecPooled(data->mypool, action->clip);
176 // clear the pointer
177 action->clip = NULL;
179 action->blk.x = data->CPos_X;
180 action->blk.y = LineNr(data, data->actualline);
182 if(moveCursor == FALSE)
184 data->CPos_X = action->x;
185 data->actualline = LineNode(data, action->y);
188 break;
190 default:
191 // nothing to do
192 break;
195 ScrollIntoDisplay(data);
197 if(isFlagSet(data->flags, FLG_Active))
198 SetCursor(data, data->CPos_X, data->actualline, TRUE);
200 // if there are no further undo levels we
201 // have to set UndoAvailable to FALSE
202 if(data->nextUndoStep == 0)
204 set(data->object, MUIA_TextEditor_UndoAvailable, FALSE);
206 if(isFlagClear(data->flags, FLG_UndoLost))
207 data->HasChanged = FALSE;
210 success = TRUE;
212 else
214 DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NothingToUndo);
217 D(DBF_UNDO, "after maxUndoSteps=%ld nextUndoStep=%ld usedUndoSteps=%ld", data->maxUndoSteps, data->nextUndoStep, data->usedUndoSteps);
220 RETURN(success);
221 return success;
225 /// Redo()
226 BOOL Redo(struct InstData *data)
228 BOOL success = FALSE;
230 ENTER();
232 D(DBF_UNDO, "before maxUndoSteps=%ld nextUndoStep=%ld usedUndoSteps=%ld", data->maxUndoSteps, data->nextUndoStep, data->usedUndoSteps);
234 // check if there something to redo at all
235 if(isFlagClear(data->flags, FLG_ReadOnly) && data->nextUndoStep < data->usedUndoSteps)
237 struct UserAction *action;
239 if(Enabled(data))
241 data->blockinfo.enabled = FALSE;
242 MarkText(data, data->blockinfo.startx, data->blockinfo.startline, data->blockinfo.stopx, data->blockinfo.stopline);
245 // in case nextUndoStep is equal zero then we have to
246 // set the undoavailable attribute to true to signal
247 // others that undo is available
248 if(data->nextUndoStep == 0)
249 set(data->object, MUIA_TextEditor_UndoAvailable, TRUE);
251 action = &data->undoSteps[data->nextUndoStep];
252 data->nextUndoStep++;
254 // if(data->actualline != LineNode(data, buffer->y) || data->CPos_X != buffer->x)
255 SetCursor(data, data->CPos_X, data->actualline, FALSE);
256 data->CPos_X = action->x;
257 data->actualline = LineNode(data, action->y);
258 ScrollIntoDisplay(data);
260 switch(action->type)
262 case ET_PASTECHAR:
264 D(DBF_UNDO, "redo PASTECHAR");
265 PasteChars(data, data->CPos_X, data->actualline, 1, (char *)&action->del.character, action);
266 data->CPos_X++;
268 break;
270 case ET_DELETECHAR:
272 D(DBF_UNDO, "redo DELETECHAR");
273 RemoveChars(data, data->CPos_X, data->actualline, 1);
275 break;
277 case ET_SPLITLINE:
279 D(DBF_UNDO, "redo SPLITLINE");
280 SplitLine(data, data->CPos_X, data->actualline, TRUE, NULL);
282 break;
284 case ET_MERGELINES:
285 case ET_BACKSPACEMERGE:
287 D(DBF_UNDO, "redo MERGELINES/BACKSPACEMERGE");
288 MergeLines(data, data->actualline);
290 break;
292 case ET_PASTEBLOCK:
294 struct Hook *oldhook = data->ImportHook;
296 D(DBF_UNDO, "redo PASTEBLOCK");
297 data->ImportHook = &ImPlainHook;
298 InsertText(data, action->clip, TRUE);
299 data->ImportHook = oldhook;
300 FreeVecPooled(data->mypool, action->clip);
301 // clear the pointer
302 action->clip = NULL;
304 action->blk.x = data->CPos_X;
305 action->blk.y = LineNr(data, data->actualline);
307 break;
309 case ET_DELETEBLOCK_NOMOVE:
310 case ET_DELETEBLOCK:
312 struct marking block =
314 TRUE,
315 LineNode(data, action->y),
316 action->x,
317 LineNode(data, action->blk.y),
318 action->blk.x
320 STRPTR clip = GetBlock(data, &block);
322 D(DBF_UNDO, "redo DELETEBLOCK/DELETEBLOCK_NOMOVE");
323 CutBlock2(data, CUTF_CUT|CUTF_UPDATE, &block);
324 action->clip = clip;
326 break;
328 default:
329 // nothing to do
330 break;
333 ScrollIntoDisplay(data);
335 if(isFlagSet(data->flags, FLG_Active))
336 SetCursor(data, data->CPos_X, data->actualline, TRUE);
338 // if nextUndoStep == usedUndoSteps this signals that we
339 // don't have any things to redo anymore.
340 if(data->nextUndoStep == data->usedUndoSteps)
341 set(data->object, MUIA_TextEditor_RedoAvailable, FALSE);
343 success = TRUE;
345 else
347 DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NothingToRedo);
350 D(DBF_UNDO, "after maxUndoSteps=%ld nextUndoStep=%ld usedUndoSteps=%ld", data->maxUndoSteps, data->nextUndoStep, data->usedUndoSteps);
352 RETURN(success);
353 return success;
357 /// AddToUndoBuffer()
358 BOOL AddToUndoBuffer(struct InstData *data, enum EventType eventtype, void *eventData)
360 ENTER();
362 D(DBF_UNDO, "before maxUndoSteps=%ld nextUndoStep=%ld usedUndoSteps=%ld", data->maxUndoSteps, data->nextUndoStep, data->usedUndoSteps);
364 if(isFlagClear(data->flags, FLG_ReadOnly) && data->maxUndoSteps > 0)
366 struct UserAction *action;
367 BOOL success = TRUE;
369 // check if there is still enough space in our undo buffer
370 // and if not we clean it up one entry
371 if(data->nextUndoStep == data->maxUndoSteps)
373 // free the oldest stored action and forget about it
374 D(DBF_UNDO, "undo buffer is full, loose the oldest step");
375 FreeUndoStep(data, 0);
376 data->nextUndoStep--;
377 data->usedUndoSteps--;
379 // shift all remaining actions one step to the front
380 memmove(&data->undoSteps[0], &data->undoSteps[1], sizeof(data->undoSteps[0]) * data->maxUndoSteps);
382 // signal the user that something in the undo buffer was lost
383 setFlag(data->flags, FLG_UndoLost);
385 else
387 ULONG i;
389 // adding something new to the undo buffer will erase all previously
390 // performed redo's
391 for(i = data->nextUndoStep; i < data->usedUndoSteps; i++)
393 D(DBF_UNDO, "free not yet redone step %ld", i);
394 FreeUndoStep(data, i);
396 data->usedUndoSteps = data->nextUndoStep;
399 action = &data->undoSteps[data->nextUndoStep];
401 // clear any previous data
402 memset(action, 0, sizeof(*action));
404 action->x = data->CPos_X;
405 action->y = LineNr(data, data->actualline);
406 action->type = eventtype;
408 switch(eventtype)
410 case ET_BACKSPACEMERGE:
411 case ET_MERGELINES:
413 struct line_node *next = GetNextLine(data->actualline);
415 D(DBF_UNDO, "add undo MERGELINES/BACKSPACEMERGE");
416 action->del.highlight = next->line.Highlight;
417 action->del.flow = next->line.Flow;
418 action->del.separator = next->line.Separator;
420 break;
422 case ET_DELETECHAR:
424 STRPTR str = (STRPTR)eventData;
426 D(DBF_UNDO, "add undo DELETECHAR");
427 action->del.character = str[0];
428 action->del.style = GetStyle(data->CPos_X, data->actualline);
429 action->del.flow = data->actualline->line.Flow;
430 action->del.separator = data->actualline->line.Separator;
431 action->del.highlight = data->actualline->line.Highlight;
433 break;
435 case ET_PASTEBLOCK:
437 struct marking *block = (struct marking *)eventData;
439 D(DBF_UNDO, "add undo PASTEBLOCK");
440 action->x = block->startx;
441 action->y = LineNr(data, block->startline);
442 action->blk.x = block->stopx;
443 action->blk.y = LineNr(data, block->stopline);
445 break;
447 case ET_DELETEBLOCK:
448 case ET_DELETEBLOCK_NOMOVE:
450 STRPTR text;
451 struct marking *block = (struct marking *)eventData;
453 D(DBF_UNDO, "add undo DELETEBLOCK");
454 if((text = GetBlock(data, block)) != NULL)
456 action->x = block->startx;
457 action->y = LineNr(data, block->startline);
458 action->clip = text;
460 if(eventtype == ET_DELETEBLOCK && isFlagSet(data->flags, FLG_FreezeCrsr))
461 action->type = ET_DELETEBLOCK_NOMOVE;
463 else
465 success = FALSE;
468 break;
470 default:
472 // nothing to do
474 break;
477 if(success == TRUE)
479 // adding the undo step was successful, update the variables
480 // advance the index for the next undo step
481 data->nextUndoStep++;
482 // and count this new step
483 data->usedUndoSteps++;
485 else
487 // something went wrong, invoke the error method
488 DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NotEnoughUndoMem);
491 // trigger possible notifications, no matter if the action succeeded or not,
492 // because the undo/redo situation might have changed
493 SetAttrs(data->object, MUIA_TextEditor_RedoAvailable, data->nextUndoStep < data->usedUndoSteps,
494 MUIA_TextEditor_UndoAvailable, data->usedUndoSteps != 0,
495 TAG_DONE);
498 D(DBF_UNDO, "after maxUndoSteps=%ld nextUndoStep=%ld usedUndoSteps=%ld", data->maxUndoSteps, data->nextUndoStep, data->usedUndoSteps);
500 RETURN(TRUE);
501 return(TRUE);
505 /// ResetUndoBuffer()
506 void ResetUndoBuffer(struct InstData *data)
508 ENTER();
510 if(data->maxUndoSteps != 0 && data->undoSteps != NULL)
512 ULONG i;
514 for(i = 0; i < data->usedUndoSteps; i++)
515 FreeUndoStep(data, i);
517 data->usedUndoSteps = 0;
518 data->nextUndoStep = 0;
520 // trigger possible notifications
521 SetAttrs(data->object, MUIA_TextEditor_RedoAvailable, FALSE,
522 MUIA_TextEditor_UndoAvailable, FALSE,
523 TAG_DONE);
526 LEAVE();
530 /// ResizeUndoBuffer()
531 void ResizeUndoBuffer(struct InstData *data, ULONG newMaxUndoSteps)
533 ENTER();
535 if(data->maxUndoSteps != newMaxUndoSteps)
537 struct UserAction *newUndoSteps = NULL;
539 D(DBF_UNDO, "resizing undo buffer from %ld to %ld steps", data->maxUndoSteps, newMaxUndoSteps);
541 if(newMaxUndoSteps != 0)
543 ULONG oldSize;
544 ULONG newSize;
546 // calculate number of bytes from number of undo levels
547 oldSize = (data->maxUndoSteps * sizeof(struct UserAction));
548 newSize = (newMaxUndoSteps * sizeof(struct UserAction));
550 // allocate a new undo buffer
551 if((newUndoSteps = AllocVecPooled(data->mypool, newSize)) != NULL)
553 if(data->undoSteps != NULL)
555 // copy over the old undo steps
556 memcpy(newUndoSteps, data->undoSteps, MIN(oldSize, newSize));
561 if(data->undoSteps != NULL)
563 ULONG i;
565 // free the steps which don't fit into the new buffer anymore
566 for(i = newMaxUndoSteps; i < data->maxUndoSteps; i++)
567 FreeUndoStep(data, i);
569 // free the old buffer
570 FreeVecPooled(data->mypool, data->undoSteps);
573 // reset everything to the new values
574 data->undoSteps = newUndoSteps;
575 data->maxUndoSteps = newMaxUndoSteps;
576 data->usedUndoSteps = MIN(data->usedUndoSteps, newMaxUndoSteps);
577 data->nextUndoStep = MIN(data->nextUndoStep, newMaxUndoSteps);
579 // trigger possible notifications
580 SetAttrs(data->object, MUIA_TextEditor_RedoAvailable, data->nextUndoStep < data->usedUndoSteps,
581 MUIA_TextEditor_UndoAvailable, data->usedUndoSteps != 0,
582 TAG_DONE);
585 LEAVE();
589 /// FreeUndoBuffer()
590 void FreeUndoBuffer(struct InstData *data)
592 ENTER();
594 if(data->undoSteps != NULL)
596 ULONG i;
598 for(i = 0; i < data->usedUndoSteps; i++)
599 FreeUndoStep(data, i);
601 FreeVecPooled(data->mypool, data->undoSteps);
602 data->undoSteps = NULL;
605 LEAVE();