101 lines
2.6 KiB
Go
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 = ""
|
|
}
|