132 lines
3.0 KiB
Go
132 lines
3.0 KiB
Go
|
package drawtool
|
||
|
|
||
|
// History manages a history of Strokes added to a drawing.
|
||
|
type History struct {
|
||
|
limit int
|
||
|
head *HistoryElement // oldest history element, top of linked list
|
||
|
tail *HistoryElement // newest element added to history
|
||
|
}
|
||
|
|
||
|
// HistoryElement is a doubly linked list of stroke history.
|
||
|
type HistoryElement struct {
|
||
|
stroke *Stroke
|
||
|
next *HistoryElement
|
||
|
previous *HistoryElement
|
||
|
}
|
||
|
|
||
|
// NewHistory initializes a History list.
|
||
|
func NewHistory(limit int) *History {
|
||
|
return &History{
|
||
|
limit: limit,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Reset clears the history.
|
||
|
func (h *History) Reset() {
|
||
|
h.head = nil
|
||
|
h.tail = nil
|
||
|
}
|
||
|
|
||
|
// Size returns the current size of the history list.
|
||
|
func (h *History) Size() int {
|
||
|
var (
|
||
|
size int
|
||
|
node = h.head
|
||
|
)
|
||
|
|
||
|
for node != nil {
|
||
|
size++
|
||
|
node = node.next
|
||
|
}
|
||
|
|
||
|
return size
|
||
|
}
|
||
|
|
||
|
// Latest returns the tail of the history (the most recent stroke). If you had
|
||
|
// recently called Undo, the latest stroke may still have a 'next' stroke.
|
||
|
// Returns nil if there was no stroke in history.
|
||
|
func (h *History) Latest() *Stroke {
|
||
|
if h.tail == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return h.tail.stroke
|
||
|
}
|
||
|
|
||
|
// Oldest returns the head of the history (the earliest stroke added). If the
|
||
|
// history size limit had been reached, the oldest stroke will creep along
|
||
|
// forward and not necessarily be the FIRST EVER stroke added.
|
||
|
func (h *History) Oldest() *Stroke {
|
||
|
if h.head == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return h.head.stroke
|
||
|
}
|
||
|
|
||
|
// AddStroke adds a stroke to the history, becoming the new tail at the end
|
||
|
// of the history data.
|
||
|
func (h *History) AddStroke(s *Stroke) {
|
||
|
var (
|
||
|
elem = &HistoryElement{
|
||
|
stroke: s,
|
||
|
}
|
||
|
tail = h.tail
|
||
|
)
|
||
|
|
||
|
// Make the current tail point to this one.
|
||
|
if tail != nil {
|
||
|
tail.next = elem
|
||
|
elem.previous = tail
|
||
|
}
|
||
|
|
||
|
// First stroke of the history? Make it the head of the linked list.
|
||
|
if h.head == nil {
|
||
|
h.head = elem
|
||
|
}
|
||
|
|
||
|
h.tail = elem
|
||
|
|
||
|
// Have we reached the history storage limit?
|
||
|
var size = h.Size()
|
||
|
if size > h.limit {
|
||
|
var node = h.tail
|
||
|
for i := 0; i < h.limit-1; i++ {
|
||
|
if node.previous == nil {
|
||
|
break
|
||
|
}
|
||
|
node = node.previous
|
||
|
}
|
||
|
h.head = node
|
||
|
h.head.previous = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Undo steps back a step in the history. This sets the current tail to point
|
||
|
// to the "tail - 1" element, but doesn't change the link of that element to
|
||
|
// its future value yet; so that you can Redo it. But if you add a new stroke
|
||
|
// from this state, it will overwrite the tail.next and invalidate the old
|
||
|
// history that came after, starting a new branch of history from that point on.
|
||
|
//
|
||
|
// Returns false if the undo failed (no earlier node to move to).
|
||
|
func (h *History) Undo() bool {
|
||
|
if h.tail == nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// if h.tail.previous == nil {
|
||
|
// return false
|
||
|
// }
|
||
|
|
||
|
h.tail = h.tail.previous
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Redo advances forwards after a recent Undo. Note that if you added new strokes
|
||
|
// after an Undo, the new tail has no next node to move to and Redo returns false.
|
||
|
func (h *History) Redo() bool {
|
||
|
if h.tail == nil || h.tail.next == nil {
|
||
|
return false
|
||
|
}
|
||
|
h.tail = h.tail.next
|
||
|
return true
|
||
|
}
|