/* $Header: /nw/tony/src/stevie/src/RCS/undo.c,v 1.7 89/08/06 09:51:06 tony Exp $ * * Undo facility * * The routines in this file comprise a general undo facility for use * throughout the rest of the editor. The routine u_save() is called * before each edit operation to save the current contents of the lines * to be editted. Later, u_undo() can be called to return those lines * to their original state. The routine u_clear() should be called * whenever a new file is going to be editted to clear the undo buffer. */ #include "stevie.h" /* * The next two variables mark the boundaries of the changed section * of the file. Lines BETWEEN the lower and upper bounds are changed * and originally contained the lines pointed to by u_lines. To undo * the last change, insert the lines in u_lines between the lower and * upper bounds. */ static LINE *u_lbound = NULL; /* line just prior to first changed line */ static LINE *u_ubound = NULL; /* line just after the last changed line */ static LINE *u_lline = NULL; /* bounds of the saved lines */ static LINE *u_uline = NULL; static int u_col; static bool_t u_valid = FALSE; /* is the undo buffer valid */ /* * Local forward declarations */ static LINE *copyline(); static void u_lsave(); static void u_lfree(); /* * u_save(l, u) - save the current contents of part of the file * * The lines between 'l' and 'u' are about to be changed. This routine * saves their current contents into the undo buffer. The range l to u * is not inclusive because when we do an open, for example, there aren't * any lines in between. If no lines are to be saved, then l->next == u. */ void u_save(l, u) LINE *l, *u; { LINE *nl; /* copy of the current line */ /* * If l or u is null, there's an error. We don't return an * indication to the caller. They should find the problem * while trying to perform whatever edit is being requested * (e.g. a join on the last line). */ if (l == NULL || u == NULL) return; u_clear(); /* clear the buffer, first */ u_lsave(l, u); /* save to the "line undo" buffer, if needed */ u_lbound = l; u_ubound = u; if (l->next != u) { /* there are lines in the middle */ l = l->next; u = u->prev; u_lline = nl = copyline(l); /* copy the first line */ while (l != u) { nl->next = copyline(l->next); nl->next->prev = nl; l = l->next; nl = nl->next; } u_uline = nl; } else u_lline = u_uline = NULL; u_valid = TRUE; u_col = Cursvcol; } /* * u_saveline() - save the current line in the undo buffer */ void u_saveline() { u_save(Curschar->linep->prev, Curschar->linep->next); } /* * u_undo() - effect an 'undo' operation * * The last edit is undone by restoring the modified section of the file * to its original state. The lines we're going to trash are copied to * the undo buffer so that even an 'undo' can be undone. Rings the bell * if the undo buffer is empty. */ void u_undo() { LINE *tl, *tu; if (!u_valid) { beep(); return; } /* * Get the first line of the thing we're undoing on the screen. */ Curschar->linep = u_lbound->next; Curschar->index = 0; /* for now */ if (Curschar->linep == Fileend->linep) Curschar->linep = Curschar->linep->prev; cursupdate(); /* * Save pointers to what's in the file now. */ if (u_lbound->next != u_ubound) { /* there are lines to get */ tl = u_lbound->next; tu = u_ubound->prev; tl->prev = NULL; tu->next = NULL; } else tl = tu = NULL; /* no lines between bounds */ /* * Link the undo buffer into the right place in the file. */ if (u_lline != NULL) { /* there are lines in the undo buf */ /* * If the top line of the screen is being undone, we need to * fix up Topchar to point to the new line that will be there. */ if (u_lbound->next == Topchar->linep) Topchar->linep = u_lline; u_lbound->next = u_lline; u_lline->prev = u_lbound; u_ubound->prev = u_uline; u_uline->next = u_ubound; } else { /* no lines... link the bounds */ if (u_lbound->next == Topchar->linep) Topchar->linep = u_ubound; if (u_lbound == Filetop->linep) Topchar->linep = u_ubound; u_lbound->next = u_ubound; u_ubound->prev = u_lbound; } /* * If we swapped the top line, patch up Filemem appropriately. */ if (u_lbound == Filetop->linep) Filemem->linep = Filetop->linep->next; /* * Now save the old stuff in the undo buffer. */ u_lline = tl; u_uline = tu; renum(); /* have to renumber everything */ /* * Put the cursor on the first line of the 'undo' region. */ Curschar->linep = u_lbound->next; Curschar->index = 0; if (Curschar->linep == Fileend->linep) Curschar->linep = Curschar->linep->prev; *Curschar = *coladvance(Curschar, u_col); cursupdate(); updatescreen(); /* now show the change */ u_lfree(); /* clear the "line undo" buffer */ } /* * u_clear() - clear the undo buffer * * This routine is called to clear the undo buffer at times when the * pointers are about to become invalid, such as when a new file is * about to be editted. */ void u_clear() { LINE *l, *nextl; if (!u_valid) /* nothing to do */ return; for (l = u_lline; l != NULL ;l = nextl) { nextl = l->next; free(l->s); free((char *)l); } u_lbound = u_ubound = u_lline = u_uline = NULL; u_valid = FALSE; } /* * The following functions and data implement the "line undo" feature * performed by the 'U' command. */ static LINE *u_line; /* pointer to the line we last saved */ static LINE *u_lcopy = NULL; /* local copy of the original line */ /* * u_lfree() - free the line save buffer */ static void u_lfree() { if (u_lcopy != NULL) { free(u_lcopy->s); free((char *)u_lcopy); u_lcopy = NULL; } u_line = NULL; } /* * u_lsave() - save the current line if necessary */ static void u_lsave(l, u) LINE *l, *u; { if (l->next != u->prev) { /* not changing exactly one line */ u_lfree(); return; } if (l->next == u_line) /* more edits on the same line */ return; u_lfree(); u_line = l->next; u_lcopy = copyline(l->next); } /* * u_lundo() - undo the current line (the 'U' command) */ void u_lundo() { if (u_lcopy != NULL) { free(Curschar->linep->s); Curschar->linep->s = u_lcopy->s; Curschar->linep->size = u_lcopy->size; free((char *)u_lcopy); } else beep(); Curschar->index = 0; cursupdate(); updatescreen(); /* now show the change */ u_lcopy = NULL; /* can't undo this kind of undo */ u_line = NULL; } /* * u_lcheck() - clear the "line undo" buffer if we've moved to a new line */ void u_lcheck() { if (Curschar->linep != u_line) u_lfree(); } /* * copyline(l) - copy the given line, and return a pointer to the copy */ static LINE * copyline(l) LINE *l; { LINE *nl; /* the new line */ nl = newline(strlen(l->s)); strcpy(nl->s, l->s); return nl; }