This is undo.c in view mode; [Download] [Up]
/* Undo/redo system management functions. Copyright (C) 1993 Sebastiano Vigna This file is part of ne, the nice editor. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding! */ #include "ne.h" /* How many undo steps we (re)allocate whenever we need more. */ #define STD_UNDO_STEP_SIZE (1024) /* How many undo stream bytes we (re)allocate whenever we need more. */ #define STD_UNDO_STREAM_SIZE (16*1024) /* This is the main function for recording an undo step (though it should be called through add_undo_step). It adds to the given undo buffer an undo step with given line, position and length, possibly enlarging the undo step buffer. The redo stream is reset. Note that this function is transparent with respect to the various positive/negative conventions about len and pos. */ static int cat_undo_step(undo_buffer *ub, int line, int pos, int len) { undo_step *ud; if (!ub) return(FALSE); assert_undo_buffer(ub); if (ub->cur_step >= ub->steps_size) { if (ud = realloc(ub->steps, (ub->steps_size+STD_UNDO_STEP_SIZE)*sizeof(undo_step))) { ub->steps_size += STD_UNDO_STEP_SIZE; ub->steps = ud; } else return(OUT_OF_MEMORY); } ub->steps[ub->cur_step].line = line; ub->steps[ub->cur_step].pos = pos; ub->steps[ub->cur_step].len = len; ub->last_step = ++ub->cur_step; ub->last_stream = ub->cur_stream; reset_stream(&ub->redo); return(0); } /* This function activates the chaining feature of the undo system. Any operations recorded between start_undo_chain() and end_undo_chain() will be undone or redone as a single entity. These calls can be nested, since a nesting index keeps track of multiple calls. */ void start_undo_chain(buffer *b) { assert_buffer(b); assert(b->undo.cur_step == 0 || b->link_undos || b->undo.steps[b->undo.cur_step-1].pos>=0); b->link_undos++; } /* See the comments to the previous function. */ void end_undo_chain(buffer *b) { assert_undo_buffer(&b->undo); if (--b->link_undos) return; if (b->undo.cur_step && b->undo.steps[b->undo.cur_step-1].pos < 0) b->undo.steps[b->undo.cur_step-1].pos = -(1+b->undo.steps[b->undo.cur_step-1].pos); } /* This function is the external interface to the undo recording system. It takes care of recording a position of -pos-1 if the undo linking feature is in use. A positive len records an insertion, a negative len records a deletion. When an insertion is recorded, len characters have to be added to the undo stream with add_to_undo_stream(). */ int add_undo_step(buffer *b, int line, int pos, int len) { return(cat_undo_step(&b->undo, line, b->link_undos ? -pos-1 : pos, len)); } /* This function adds to the undo stream a block of len characters pointed to by p. */ int add_to_undo_stream(undo_buffer *ub, const char *p, int len) { assert(len>0); assert(ub != NULL); assert(ub->cur_step && ub->steps[ub->cur_step-1].len>0); if (!ub) return(-1); assert_undo_buffer(ub); if (!ub->cur_step || ub->steps[ub->cur_step-1].len<0) return(-1); if (ub->cur_stream+len >= ub->streams_size) { char *new_stream; if (new_stream = realloc(ub->streams, (ub->streams_size+STD_UNDO_STREAM_SIZE+len))) { ub->streams_size += STD_UNDO_STREAM_SIZE+len; ub->streams = new_stream; } else return(OUT_OF_MEMORY); } memcpy(&ub->streams[ub->cur_stream], p, len); ub->last_stream = (ub->cur_stream += len); return(0); } /* This function resets the undo buffer. All the previous undo steps are lost. */ void reset_undo_buffer(undo_buffer *ub) { ub->cur_step = ub->last_step = ub->cur_stream = ub->last_stream = ub->steps_size = ub->streams_size = 0; free(ub->streams); free(ub->steps); ub->streams = NULL; ub->steps = NULL; reset_stream(&ub->redo); } /* This function undoes the current undo step, which is the last one, if no undo has still be done, or an intermediate one, if some undo has already been done. */ int undo(buffer *b) { if (!b) return(-1); assert_buffer(b); if (b->undo.cur_step == 0) return(NOTHING_TO_UNDO); /* Important! insert_stream() and delete_stream() do different things while undoing or redoing. */ b->undoing = 1; do { b->undo.cur_step--; if (b->undo.steps[b->undo.cur_step].len) { goto_line(b, b->undo.steps[b->undo.cur_step].line); goto_pos(b, b->undo.steps[b->undo.cur_step].pos >= 0 ? b->undo.steps[b->undo.cur_step].pos : -(1+b->undo.steps[b->undo.cur_step].pos)); if (b->undo.steps[b->undo.cur_step].len<0) { delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, -b->undo.steps[b->undo.cur_step].len); } else { insert_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, b->undo.streams + (b->undo.cur_stream -= b->undo.steps[b->undo.cur_step].len), b->undo.steps[b->undo.cur_step].len); } } } while(b->undo.cur_step && b->undo.steps[b->undo.cur_step-1].pos<0); b->undoing = 0; return(0); } /* This function redoes the last step undone. */ int redo(buffer *b) { if (!b) return(-1); assert_buffer(b); if (b->undo.cur_step == b->undo.last_step) return(NOTHING_TO_REDO); /* Important! insert_stream() and delete_stream() do different things while undoing or redoing. */ b->redoing = 1; do { if (b->undo.steps[b->undo.cur_step].len) { goto_line(b, b->undo.steps[b->undo.cur_step].line); goto_pos(b, b->undo.steps[b->undo.cur_step].pos >= 0 ? b->undo.steps[b->undo.cur_step].pos : -(1+b->undo.steps[b->undo.cur_step].pos)); if (b->undo.steps[b->undo.cur_step].len<0) insert_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, b->undo.redo.stream+(b->undo.redo.len += b->undo.steps[b->undo.cur_step].len-1), -b->undo.steps[b->undo.cur_step].len+1); else { delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, b->undo.steps[b->undo.cur_step].len-1); b->undo.cur_stream += b->undo.steps[b->undo.cur_step].len; } } b->undo.cur_step++; } while(b->undo.steps[b->undo.cur_step-1].pos<0); b->redoing = 0; return(0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.