[http://incrtcl.sourceforge.net/iwidgets/iwidgets/timeentry.gif] Docs can be found at http://incrtcl.sourceforge.net/iwidgets/iwidgets/timeentry.html and http://purl.org/tcl/home/man/iwidgets3.0/timeentry.n.html ---- This is a spinbox widget that only allows valid times to be entered. I tried to make this as much like the spinbox in the Windows "Date and Time" dialog. This is only for a 24 hour clock, but it wouldn´t take too much modification to support a 12 hour format. There´s probably an easier way to do this, so please edit this page if you know of one. -Paul Walton proc validateTime {spinbox originalString newString index adjust} { # Check if the colons are present. if { [string match {*:*:*} $newString] == 0 } { return 0 } # Split up each field into its own variable. set time [split $newString :] set hour [lindex $time 0] set minutes [lindex $time 1] set seconds [lindex $time 2] # Check if valid digits were entered in each field. if { $hour != "" && ![string match {[0-9]} $hour] && ![string match {[0-1][0-9]} $hour] && ![string match {2[0-3]} $hour] } { #invalid hour return 0 } if { $minutes != "" && ![string match {[0-9]} $minutes] && ![string match {[0-5][0-9]} $minutes] } { #invalid minutes return 0 } if { $seconds != "" && ![string match {[0-9]} $seconds] && ![string match {[0-5][0-9]} $seconds] } { #invalid seconds return 0 } # Adjust the index position of the cursor appropiately. set diff [expr { [string length $hour:$minutes:$seconds] - [string length $originalString] }] set newIndex $index if { $diff > 0 } { incr newIndex $diff } switch -- $adjust { up {set adjust 1} down {set adjust -1} default {set adjust 0} } set indexColon1 [string first {:} $newString] set indexColon2 [string last {:} $newString] if { $index <= $indexColon1 } { set field hour if { $adjust != 0 } { if { $hour == "" } { set hour 0 } scan $hour %d hour incr hour $adjust if { $hour == 24 } { set hour 0 } if { $hour == -1 } { set hour 23 } } } if { $index > $indexColon1 && $index <= $indexColon2 } { set field minutes if { $adjust != 0 } { if { $minutes == "" } { set minutes 0 } scan $minutes %d minutes incr minutes $adjust if { $minutes == 60 } { set minutes 0 } if { $minutes == -1 } { set minutes 59 } } } if { $index > $indexColon2 } { set field seconds if { $adjust != 0 } { if { $seconds == "" } { set seconds 0 } scan $seconds %d seconds incr seconds $adjust if { $seconds == 60 } { set seconds 0 } if { $seconds == -1 } { set seconds 59 } } } if { $index == -1 } { set field none } if { $field != "hour" || $adjust != 0 } { set increaseBy [expr {abs([string length $hour]-2)}] set hour [string repeat 0 $increaseBy]$hour incr newIndex $increaseBy } if { $field != "minutes" || $adjust != 0 } { set increaseBy [expr {abs([string length $minutes]-2)}] set minutes [string repeat 0 $increaseBy]$minutes if { $indexColon2 < $index } { incr newIndex $increaseBy } } if { $field != "seconds" || $adjust != 0 } { set increaseBy [expr {abs([string length $seconds]-2)}] set seconds [string repeat 0 $increaseBy]$seconds } if { $adjust != 0 } { switch -- $field { hour {set newIndex 2} minutes {set newIndex [string last {:} $hour:$minutes:$seconds]} seconds {set newIndex end} } } # Write the validated data with possible corrections to the spinbox. $spinbox configure -validate none $spinbox delete 0 end $spinbox insert 0 $hour:$minutes:$seconds $spinbox icursor $newIndex after idle [list $spinbox configure -validate all] return 0 } spinbox .spin -width 8 -validatecommand {validateTime %W %s %P %i ""} -validate all -command {validateTime %W %s %s [%W index insert] %d} .spin insert 0 00:00:00 pack .spin [MG] Nov 10th 2005 - I found a few problems with the code above, most notably that the spinner only effects the seconds. The simplest way for doing this seems to be using a constructed list of valid values: for {set h 0} {$h <= 23} {incr h} { set hour [format "%02d" $h] for {set m 0} {$m <= 59} {incr m} { set minute [format "%02d" $m] for {set s 0} {$s <= 59} {incr s} { set second [format "%02d" $s] lappend values "$hour:$minute:$second" } } } Though that doesn't do validation if you type text into the box. I also have the code below, which works well, with the one exception that the spinbox buttons don't do a bloody thing. I only discovered that after I'd written all the validation code, though, so thought I'd post it anyway - hopefully someone can spot why they don't work (but even if not, the code works great with an [entry] widget instead of a [spinbox]). I may even get it working myself, another time, when it's not 6am ;) proc validateTimeMG {W V v S s P i d} { if { $d == "-1"} { return 1; } elseif { ![string match {[0-9]} $S] || [string range $s $i $i] == ":"} { return 0; } if { $d == "0" } { set new [string replace $s $i $i 0] } else { set new [string replace $s $i $i $S] } set parts [split $new :] foreach {hours minutes seconds} $parts {break} foreach x {hours minutes seconds} { set $x [format %2d [set $x]] } set num "$hours$minutes$seconds" if { $num > 235959 || $hours > 23 || $minutes > 59 || $seconds > 59} { return 0; } upvar 1 [$W cget -textvariable] textvar if { $d == "0" } { set textvar [string replace $s $i $i 0] $W config -validate $v } elseif { $d == "1" } { if { [string range $s $i $i] == ":" } { return 0; } else { set textvar $new $W config -validate $v set cursor [expr {$i + 1}] if { [string range $s $cursor $cursor] == ":" } { incr cursor } $W icursor $cursor } } return 0; } set myvar "15:23:48" pack [spinbox .s -validatecommand [validateTimeMG %W %V %v %S %s %P %i %d] -validate all -textvariable myvar] ---- [Category Command], a part of [incr Widgets]