Files
questshock/modding/hacked/ss1/edit/undoable/cmd/Stack.go
T

101 lines
2.6 KiB
Go

package cmd
import "github.com/inkyblackness/hacked/ss1/world"
type stackEntry struct {
link *stackEntry
cmd Command
}
// Stack describes a list of commands. The stack allows to sequentially
// undo and redo stacked commands.
// It essentially stores two lists: a list of commands to undo, and
// another of commands to redo.
// Modifying stack functions will panic if they are called while already in use.
type Stack struct {
lockedBy string
undoList *stackEntry
redoList *stackEntry
}
// Perform executes the given command and puts it on the stack
// if the command was successful.
// This function also clears the list of commands to be redone.
func (stack *Stack) Perform(cmd Command, modder world.Modder) error {
stack.lock("Perform")
defer stack.unlock()
err := cmd.Do(modder)
if err != nil {
return err
}
stack.undoList = &stackEntry{link: stack.undoList, cmd: cmd}
stack.redoList = nil
return nil
}
// CanUndo returns true if there is at least one more command that can be undone.
func (stack *Stack) CanUndo() bool {
return stack.undoList != nil
}
// Undo attempts to undo the previous command on the list.
// If there is no further command to undo, nothing happens.
// An error is returned if the command failed. In this case, the stack is
// unchanged and a further attempt to undo will try the same command again.
func (stack *Stack) Undo(modder world.Modder) error {
stack.lock("Undo")
defer stack.unlock()
if stack.undoList == nil {
return nil
}
entry := stack.undoList
err := entry.cmd.Undo(modder)
if err != nil {
return err
}
stack.undoList = entry.link
entry.link = stack.redoList
stack.redoList = entry
return nil
}
// CanRedo returns true if there is at least one more command that can be redone.
func (stack *Stack) CanRedo() bool {
return stack.redoList != nil
}
// Redo attempts to perform the next command on the redo list.
// If there is no further command to redo, nothing happens.
// An error is returned if the command failed. In this case, the stack is
// unchanged and a further attempt to redo will try the same command again.
func (stack *Stack) Redo(modder world.Modder) error {
stack.lock("Redo")
defer stack.unlock()
if stack.redoList == nil {
return nil
}
entry := stack.redoList
err := entry.cmd.Do(modder)
if err != nil {
return err
}
stack.redoList = entry.link
entry.link = stack.undoList
stack.undoList = entry
return nil
}
func (stack *Stack) lock(by string) {
if stack.lockedBy != "" {
panic("Stack already in use by <" + stack.lockedBy + ">")
}
stack.lockedBy = by
}
func (stack *Stack) unlock() {
stack.lockedBy = ""
}