# undo.tcl
# Maintain an undo history

proc Undo_Reset {win} {
    upvar #0 Undo$win undo
    catch {unset undo}
}
proc Undo_Init {win} {
    upvar #0 Undo$win undo
    Undo_Reset $win
    set undo(end) 0		;# Index into the log for new stuff
    set undo(active) 0		;# True during Undo and Redo
}
proc Undo_Mark {win string} {
    Undo_Record $win _MARK_ $string
}

proc Undo_Record {win undoCmd redoCmd} {
    upvar #0 Undo$win undo
    if ![info exists undo] {
	return		;# Undo disabled
    }
    set undo([incr undo(end)]) [list $undoCmd $redoCmd]

    # Clear undo/redo pointers if not activly undoing or redoing

    if {! $undo(active)} {
	catch {unset undo(undo)}
	catch {unset undo(redo)}
    }
} 
proc Undo {win} {
    upvar #0 Undo$win undo
    if ![info exists undo] {
	return
    }
    if ![info exists undo(undo)] {
	set undo(undo) $undo(end)
    }
    while 1 {
	if {$undo(undo) <= 1} {
	    Status $win "There is nothing left to undo"
	    return
	}
	set cmd [lindex $undo([incr undo(undo) -1]) 0]
	if {[string match _MARK_* $cmd]} {
	    # Check for two MARKS in a rwo
	    if {$undo(undo) > 1} {
		set i [expr $undo(undo) - 1]
		set cmd [lindex $undo($i) 0]
		if {[string match _MARK_* $cmd]} {
		    set undo(undo) $i
		}
	    }
	    return
	}
	set undo(active) 1
	uplevel #0 $cmd
	set undo(active) 0
    }
}
proc Undo_RedoReset {win} {
    upvar #0 Undo$win undo
    if ![info exists undo] {
	return
    }
    set undo(redo) $undo(end)
}
proc Undo_Redo {win} {
    upvar #0 Undo$win undo
    if ![info exists undo] {
	return
    }
    if ![info exists undo(redo)] {
	if ![info exists undo(undo)] {
	    return
	}
	set undo(redo) $undo(undo)
    }
    while 1 {
	if ![info exists $undo($undo(redo))] {
	    return
	}
	set cmd [lindex $undo($undo(redo)) 1]
	incr undo(redo)
	if {[string match _MARK_* $cmd]} {
	    return
	}
	set undo(active) 1
	uplevel #0 $cmd
	set undo(active) 0
    }
}

proc Undo_Print {win} {
    upvar #0 Undo$win undo
    if ![info exists undo] {
	return
    }
    for {set i 1} {$i <= $undo(end)} {incr i} {
	puts [format "%3d %-30s || %s" $i [lindex $undo($i) 0] [lindex $undo($i) 1]]
    }
}
