302 lines
8.3 KiB
C
302 lines
8.3 KiB
C
/* $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;
|
|
}
|