[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] ---- Paul Walton: The spin buttons do (as well as keyboard arrows) effect the hours, minutes, and seconds, depending on where the insertion cursor is. This is also how the Windows "Date and Time" spinbox works. What other problems did you have with it? ---- [Category Command], a part of [incr Widgets]