An entry with a history

Summary

Richard Suchenwirth 2000-12-21: This was originally part of my not-yet wikified sandbox, then isolated out into Turtleshell, but since it is a pretty self-contained thingy, here it comes by itself: a history mechanism for entry widgets, now in its own namespace.

Description

On creating an entry, you just prepend the word(s) history::for:

history::for entry .e -textvar foo

Your entry will be created with all its args, and in addition you get the following key bindings:

  • Cursor up: move one item up in history, display
  • Cursor down: move one item down, display (empty after last)
  • Page down: move immediately to end of history (i.e. empty)
  • Return: append entry content to end of history, if not empty or identical with last

NB: When the user adds another binding (e.g. for <Return>), he must start it with a + sign, otherwise the history binding gets lost. But I wanted to concentrate all in the history::for command, which does the creation and the bindings, so user-defined bindings come after history's. Each entry's history is kept in a separate variable history::$widgetname, so you can have more than one around.

namespace eval history {
    proc add? {w} {
        variable $w
        variable n$w
        upvar 0 $w hist
        set s [set ::[$w cget -textvariable]]
        if {$s == ""} return
        if [string compare $s [lindex $hist end]] {
            lappend hist $s
            set n$w [llength $hist]
        }
    }
    proc move {w where} {
        variable $w
        variable n$w
        upvar 0 $w hist
        incr n$w $where
        if {[set n$w]<0} {set n$w 0}
        if {[set n$w]>=[llength $hist]+1} {
            set n$w [llength $hist]
        }
        set ::[$w cget -textvar] [lindex $hist [set n$w]]
    }
    proc for {type name args} {
        switch -- $type {
            entry {
                uplevel $type $name $args
                bind $name <Up> {history::move %W -1}
                bind $name <Down> {history::move %W 1}
                bind $name <Next> {history::move %W 99999}
                bind $name <Return> {history::add? %W}
                variable $name {}
                variable n$name 0
            }
            default {error "usage: history::for entry <w> <args>"}
        }
    }
}

Test and demo code:

history::for entry .1 -textvar foo
history::for entry .2 -textvar bar
pack .1 .2
bind .1 <Return> {+ puts [string tolower $foo];set foo ""}
bind .2 <Return> {+ puts [string toupper $bar]; set bar ""}

Also see GWM Undo and Redo undoable widgets for a general method of adding a history and undo/redo functionality to all widgets (not just entry widgets but tbuttons, menus...).