home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2000 December
/
PCWorld_2000-12_cd.bin
/
Komunikace
/
Comanche
/
comanche.exe
/
lib
/
iwidgets3.0.0
/
scripts
/
timefield.itk
< prev
next >
Wrap
Text File
|
1999-02-24
|
29KB
|
976 lines
#
# Timefield
# ----------------------------------------------------------------------
# Implements a time entry field with adjustable built-in intelligence
# levels.
# ----------------------------------------------------------------------
# AUTHOR: John A. Tucker E-mail: jatucker@austin.dsccc.com
#
# @(#) $Id: timefield.itk,v 1.1 1998/07/27 18:53:20 stanton Exp $
# ----------------------------------------------------------------------
# Copyright (c) 1997 DSC Technologies Corporation
# ======================================================================
# Permission to use, copy, modify, distribute and license this software
# and its documentation for any purpose, and without fee or written
# agreement with DSC, is hereby granted, provided that the above copyright
# notice appears in all copies and that both the copyright notice and
# warranty disclaimer below appear in supporting documentation, and that
# the names of DSC Technologies Corporation or DSC Communications
# Corporation not be used in advertising or publicity pertaining to the
# software without specific, written prior permission.
#
# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL
# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
# SOFTWARE.
# ======================================================================
#
# Use option database to override default resources of base classes.
#
option add *Timefield.justify center widgetDefault
#
# Usual options.
#
itk::usual Timefield {
keep -background -borderwidth -cursor -foreground -highlightcolor \
-highlightthickness -labelfont -textbackground -textfont
}
# ------------------------------------------------------------------
# TIMEFIELD
# ------------------------------------------------------------------
class iwidgets::Timefield {
inherit iwidgets::Labeledwidget
constructor {args} {}
itk_option define -childsitepos childSitePos Position e
itk_option define -command command Command {}
itk_option define -seconds seconds Seconds on
itk_option define -format format Format civilian
itk_option define -iq iq Iq high
public {
method get {{format "-string"}}
method isvalid {}
method show {{time "now"}}
}
protected {
method _backwardCivilian {}
method _backwardMilitary {}
method _focusIn {}
method _forwardCivilian {}
method _forwardMilitary {}
method _keyPress {char sym state}
method _moveField {direction}
method _setField {field}
method _whichField {}
method _toggleAmPm {}
variable _cfield hour
variable _formatString "%r"
variable _fields {}
variable _numFields 4
variable _forward {}
variable _backward {}
common _militaryFields {hour minute second}
common _civilianFields {hour minute second ampm}
common _timeVar
}
}
#
# Provide a lowercased access method for the timefield class.
#
proc iwidgets::timefield {pathName args} {
uplevel iwidgets::Timefield $pathName $args
}
# ------------------------------------------------------------------
# CONSTRUCTOR
# ------------------------------------------------------------------
body iwidgets::Timefield::constructor {args} {
component hull configure -borderwidth 0
set _timeVar($this) ""
#
# Create an entry field for entering the time.
#
itk_component add time {
entry $itk_interior.time \
-textvariable [scope _timeVar($this)]
} {
keep -borderwidth -cursor -exportselection \
-foreground -highlightcolor -highlightthickness \
-insertbackground -justify -relief
rename -font -textfont textFont Font
rename -highlightbackground -background background Background
rename -background -textbackground textBackground Background
}
#
# Create the child site widget.
#
itk_component add -protected dfchildsite {
frame $itk_interior.dfchildsite
}
set itk_interior $itk_component(dfchildsite)
#
# Add timefield event bindings for focus in and keypress events.
#
bind $itk_component(time) <FocusIn> [code $this _focusIn]
bind $itk_component(time) <KeyPress> [code $this _keyPress %A %K %s]
#
# Disable some mouse button event bindings:
# Button Motion
# Double-Clicks
# Triple-Clicks
# Button2
#
bind $itk_component(time) <Button1-Motion> break
bind $itk_component(time) <Button2-Motion> break
bind $itk_component(time) <Double-Button> break
bind $itk_component(time) <Triple-Button> break
bind $itk_component(time) <2> break
#
# Initialize the widget based on the command line options.
#
eval itk_initialize $args
#
# Initialize the time to the current time.
#
show
}
# ------------------------------------------------------------------
# OPTIONS
# ------------------------------------------------------------------
# ------------------------------------------------------------------
# OPTION: -childsitepos
#
# Specifies the position of the child site in the widget. Valid
# locations are n, s, e, and w.
# ------------------------------------------------------------------
configbody iwidgets::Timefield::childsitepos {
set parent [winfo parent $itk_component(time)]
switch $itk_option(-childsitepos) {
n {
grid $itk_component(dfchildsite) -row 0 -column 0 -sticky ew
grid $itk_component(time) -row 1 -column 0 -sticky nsew
grid rowconfigure $parent 0 -weight 0
grid rowconfigure $parent 1 -weight 1
grid columnconfigure $parent 0 -weight 1
grid columnconfigure $parent 1 -weight 0
}
e {
grid $itk_component(dfchildsite) -row 0 -column 1 -sticky ns
grid $itk_component(time) -row 0 -column 0 -sticky nsew
grid rowconfigure $parent 0 -weight 1
grid rowconfigure $parent 1 -weight 0
grid columnconfigure $parent 0 -weight 1
grid columnconfigure $parent 1 -weight 0
}
s {
grid $itk_component(dfchildsite) -row 1 -column 0 -sticky ew
grid $itk_component(time) -row 0 -column 0 -sticky nsew
grid rowconfigure $parent 0 -weight 1
grid rowconfigure $parent 1 -weight 0
grid columnconfigure $parent 0 -weight 1
grid columnconfigure $parent 1 -weight 0
}
w {
grid $itk_component(dfchildsite) -row 0 -column 0 -sticky ns
grid $itk_component(time) -row 0 -column 1 -sticky nsew
grid rowconfigure $parent 0 -weight 1
grid rowconfigure $parent 1 -weight 0
grid columnconfigure $parent 0 -weight 0
grid columnconfigure $parent 1 -weight 1
}
default {
error "bad childsite option\
\"$itk_option(-childsitepos)\":\
should be n, e, s, or w"
}
}
}
# ------------------------------------------------------------------
# OPTION: -command
#
# Command invoked upon detection of return key press event.
# ------------------------------------------------------------------
configbody iwidgets::Timefield::command {}
# ------------------------------------------------------------------
# OPTION: -iq
#
# Specifies the level of intelligence to be shown in the actions
# taken by the time field during the processing of keypress events.
# Valid settings include high or low. With a high iq,
# the time prevents the user from typing in an invalid time. For
# example, if the current time is 05/31/1997 and the user changes
# the hour to 04, then the minute will be instantly modified for them
# to be 30. In addition, leap seconds are fully taken into account.
# A setting of low iq instructs the widget to do no validity checking
# at all during time entry. With a low iq level, it is assumed that
# the validity will be determined at a later time using the time's
# isvalid command.
# ------------------------------------------------------------------
configbody iwidgets::Timefield::iq {
switch $itk_option(-iq) {
high - low {
}
default {
error "bad iq option \"$itk_option(-iq)\": should be high or low"
}
}
}
# ------------------------------------------------------------------
# OPTION: -format
#
# Specifies the time format displayed in the entry widget.
# ------------------------------------------------------------------
configbody iwidgets::Timefield::format {
switch $itk_option(-format) {
civilian {
set _backward _backwardCivilian
set _forward _forwardCivilian
set _fields $_civilianFields
set _numFields 4
set _formatString "%r"
$itk_component(time) config -width 11
}
military {
set _backward _backwardMilitary
set _forward _forwardMilitary
set _fields $_militaryFields
set _numFields 3
set _formatString "%T"
$itk_component(time) config -width 8
}
default {
error "bad iq option \"$itk_option(-iq)\":\
should be civilian or military"
}
}
#
# Update the current contents of the entry field to reflect
# the configured format.
#
show $_timeVar($this)
}
# ------------------------------------------------------------------
# METHODS
# ------------------------------------------------------------------
# ------------------------------------------------------------------
# PUBLIC METHOD: get ?format?
#
# Return the current contents of the timefield in one of two formats
# string or as an integer clock value using the -string and -clicks
# options respectively. The default is by string. Reference the
# clock command for more information on obtaining times and their
# formats.
# ------------------------------------------------------------------
body iwidgets::Timefield::get {{format "-string"}} {
switch -- $format {
"-string" {
return $_timeVar($this)
}
"-clicks" {
return [::clock scan $_timeVar($this)]
}
default {
error "bad format option \"$format\":\
should be -string or -clicks"
}
}
}
# ------------------------------------------------------------------
# PUBLIC METHOD: show time
#
# Changes the currently displayed time to be that of the time
# argument. The time may be specified either as a string or an
# integer clock value. Reference the clock command for more
# information on obtaining times and their formats.
# ------------------------------------------------------------------
body iwidgets::Timefield::show {{time "now"}} {
if {$time == {}} {
set time "now"
}
switch -regexp -- $time {
{^now$} {
set seconds [::clock seconds]
}
{^[0-9]+$} {
if { [catch {::clock format $time}] } {
error "bad time: \"$time\", must be a valid time \
string, clock clicks value or the keyword now"
}
set seconds $time
}
default {
if {[catch {set seconds [::clock scan $time]}]} {
error "bad time: \"$time\", must be a valid time \
string, clock clicks value or the keyword now"
}
}
}
set _timeVar($this) [::clock format $seconds -format $_formatString]
}
# ------------------------------------------------------------------
# PUBLIC METHOD: isvalid
#
# Returns a boolean indication of the validity of the currently
# displayed time value. For example, 09:59::59 is valid whereas
# 26:59:59 is invalid.
# ------------------------------------------------------------------
body iwidgets::Timefield::isvalid {} {
return [expr ([catch {::clock scan $_timeVar($this)}] == 0)]
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _focusIn
#
# This method is bound to the <FocusIn> event. It resets the
# insert cursor and field settings to be back to their last known
# positions.
# ------------------------------------------------------------------
body iwidgets::Timefield::_focusIn {} {
_setField $_cfield
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _keyPress
#
# This method is the workhorse of the class. It is bound to the
# <KeyPress> event and controls the processing of all key strokes.
# ------------------------------------------------------------------
body iwidgets::Timefield::_keyPress {char sym state} {
#
# Determine which field we are in currently. This is needed
# since the user may have moved to this position via a mouse
# selection and so it would not be in the position we last
# knew it to be.
#
set _cfield [_whichField ]
#
# Set up a few basic variables we'll be needing throughout the
# rest of the method such as the position of the insert cursor
# and the currently displayed minute, hour, and second.
#
set inValid 0
set icursor [$itk_component(time) index insert]
set lastField [lindex $_fields end]
set prevtime $_timeVar($this)
regexp {^([0-9])([0-9]):([0-9])([0-9]):([0-9])([0-9]).*$} \
$_timeVar($this) dummy \
hour1 hour2 minute1 minute2 second1 second2
set hour "$hour1$hour2"
set minute "$minute1$minute2"
set second "$second1$second2"
#
# Process numeric keystrokes. This involes a fair amount of
# processing with step one being to check and make sure we
# aren't attempting to insert more that 6 characters. If
# so ring the bell and break.
#
if {![catch {expr int($char)}]} {
# If we are currently in the hour field then we process the
# number entered based on the cursor position. If we are at
# at the first position and our iq is low, then accept any
# input.
#
# if the current format is military, then
# validate the hour field which can be [00 - 23]
#
switch $_cfield {
hour {
if {$itk_option(-iq) == "low"} {
$itk_component(time) delete $icursor
$itk_component(time) insert $icursor $char
} elseif {$itk_option(-format) == "military"} {
if {$icursor == 0} {
#
# if the digit is less than 2, then
# the second hour digit is valid for 0-9
#
if {$char < 2} {
$itk_component(time) delete 0 1
$itk_component(time) insert 0 $char
#
# if the digit is equal to 2, then
# the second hour digit is valid for 0-3
#
} elseif {$char == 2} {
$itk_component(time) delete 0 1
$itk_component(time) insert 0 $char
if {$hour2 > 3} {
$itk_component(time) delete 1 2
$itk_component(time) insert 1 "0"
$itk_component(time) icursor 1
}
#
# if the digit is greater than 2, then
# set the first hour digit to 0 and the
# second hour digit to the value.
#
} elseif {$char > 2} {
$itk_component(time) delete 0 2
$itk_component(time) insert 0 "0$char"
set icursor 1
} else {
set inValid 1
}
#
# if the insertion cursor is for the second hour digit, then
# format is military, then it can only be valid if the first
# hour digit is less than 2 or the new digit is less than 4
#
} else {
if {$hour1 < 2 || $char < 4} {
$itk_component(time) delete 1 2
$itk_component(time) insert 1 $char
} else {
set inValid 1
}
}
#
# The format is civilian, so we need to
# validate the hour field which can be [01 - 12]
#
} else {
if {$icursor == 0} {
#
# if the digit is 0, then
# the second hour digit is valid for 1-9
# so just insert it.
#
if {$char == 0 && $hour2 != 0} {
$itk_component(time) delete 0 1
$itk_component(time) insert 0 $char
#
# if the digit is equal to 1, then
# the second hour digit is valid for 0-2
#
} elseif {$char == 1} {
$itk_component(time) delete 0 1
$itk_component(time) insert 0 $char
if {$hour2 > 2} {
$itk_component(time) delete 1 2
$itk_component(time) insert 1 0
set icursor 1
}
#
# if the digit is greater than 1, then
# set the first hour digit to 0 and the
# second hour digit to the value.
#
} elseif {$char > 1} {
$itk_component(time) delete 0 2
$itk_component(time) insert 0 "0$char"
set icursor 1
} else {
set inValid 1
}
#
# The insertion cursor is at the second hour digit, so
# it can only be valid if the firs thour digit is 0
# or the new digit is less than or equal to 2
#
} else {
if {$hour1 == 0 || $char <= 2} {
$itk_component(time) delete 1 2
$itk_component(time) insert 1 $char
} else {
set inValid 1
}
}
}
if {$inValid} {
bell
} elseif {$icursor == 1} {
_setField minute
}
}
minute {
if {$itk_option(-iq) == "low" || $char < 6 || $icursor == 4} {
$itk_component(time) delete $icursor
$itk_component(time) insert $icursor $char
} elseif {$itk_option(-iq) == "high"} {
if {$char > 5} {
$itk_component(time) delete 3 5
$itk_component(time) insert 3 "0$char"
set icursor 4
}
}
if {$icursor == 4} {
_setField second
}
}
second {
if {$itk_option(-iq) == "low" || $char < 6 || $icursor == 7} {
$itk_component(time) delete $icursor
$itk_component(time) insert $icursor $char
} elseif {$itk_option(-iq) == "high"} {
if {$char > 5} {
$itk_component(time) delete 6 8
$itk_component(time) insert 6 "0$char"
set icursor 7
}
}
if {$icursor == 7} {
_moveField forward
}
}
}
return -code break
}
#
# Process the plus and the up arrow keys. They both yield the same
# effect, they increment the minute by one.
#
switch $sym {
p - P {
if {$itk_option(-format) == "civilian"} {
$itk_component(time) delete 9 10
$itk_component(time) insert 9 P
_setField hour
}
}
a - A {
if {$itk_option(-format) == "civilian"} {
$itk_component(time) delete 9 10
$itk_component(time) insert 9 A
_setField hour
}
}
plus - Up {
if {$_cfield == "ampm"} {
_toggleAmPm
} else {
set newclicks [::clock scan "$prevtime 1 $_cfield"]
set newtime [::clock format $newclicks -format $_formatString]
set _timeVar($this) $newtime
}
return -code continue
}
minus - Down {
#
# Process the minus and the down arrow keys which decrement the value
# of the field in which the cursor is currently positioned.
#
if {$_cfield == "ampm"} {
_toggleAmPm
} else {
set newclicks [::clock scan "$prevtime 1 $_cfield ago"]
set newtime [::clock format $newclicks -format $_formatString]
set _timeVar($this) $newtime
}
return -code continue
}
Tab {
#
# A tab key moves the "hour:minute:second" field forward by one unless
# the current field is the second. In that case we'll let tab
# do what is supposed to and pass the focus onto the next widget.
#
if {$state == 0} {
if {($itk_option(-format) == "civilian" && $_cfield == $lastField)} {
_setField hour
return -code continue
}
_moveField forward
#
# A ctrl-tab key moves the hour:minute:second field backwards by one
# unless the current field is the hour. In that case we'll let
# tab take the focus to a previous widget.
#
} elseif {$state == 4} {
if {$_cfield == "hour"} {
_setField hour
return -code continue
}
_moveField backward
}
}
Right {
#
# A right arrow key moves the insert cursor to the right one.
#
$_forward
}
Left - BackSpace - Delete {
#
# A left arrow, backspace, or delete key moves the insert cursor
# to the left one. This is what you expect for the left arrow
# and since the whole widget always operates in overstrike mode,
# it makes the most sense for backspace and delete to do the same.
#
$_backward
}
Return {
#
# A Return key invokes the optionally specified command option.
#
uplevel #0 $itk_option(-command)
}
default {
}
}
return -code break
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _toggleAmPm
#
# Internal method which toggles the displayed time
# between "AM" and "PM" when format is "civilian".
# ------------------------------------------------------------------
body iwidgets::Timefield::_toggleAmPm {} {
set firstChar [string index $_timeVar($this) 9]
$itk_component(time) delete 9 10
$itk_component(time) insert 9 [expr {($firstChar == "A") ? "P" : "A"}]
$itk_component(time) icursor 9
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _setField field
#
# Adjusts the current field to be that of the argument, setting the
# insert cursor appropriately.
# ------------------------------------------------------------------
body iwidgets::Timefield::_setField {field} {
# Move the position of the cursor to the first character of the
# field given by the argument:
#
# Field First Character Index
# ----- ---------------------
# hour 0
# minute 3
# second 6
# ampm 9
#
switch $field {
hour {
$itk_component(time) icursor 0
}
minute {
$itk_component(time) icursor 3
}
second {
$itk_component(time) icursor 6
}
ampm {
if {$itk_option(-format) == "military"} {
error "bad field: \"$field\", must be hour, minute or second"
}
$itk_component(time) icursor 9
}
default {
if {$itk_option(-format) == "military"} {
error "bad field: \"$field\", must be hour, minute or second"
} else {
error "bad field: \"$field\", must be hour, minute, second or ampm"
}
}
}
set _cfield $field
return $_cfield
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _moveField
#
# Moves the cursor one field forward or backward.
# ------------------------------------------------------------------
body iwidgets::Timefield::_moveField {direction} {
# Since the value "_fields" list variable is always either value:
# military => {hour minute second}
# civilian => {hour minute second ampm}
#
# the index of the previous or next field index can be determined
# by subtracting or adding 1 to current the index, respectively.
#
set index [lsearch $_fields $_cfield]
expr {($direction == "forward") ? [incr index] : [incr index -1]}
if {$index == $_numFields} {
set index 0
} elseif {$index < 0} {
set index [expr $_numFields-1]
}
_setField [lindex $_fields $index]
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _whichField
#
# Returns the current field that the cursor is positioned within.
# ------------------------------------------------------------------
body iwidgets::Timefield::_whichField {} {
# Return the current field based on the position of the cursor.
#
# Field Index
# ----- -----
# hour 0,1
# minute 3,4
# second 6,7
# ampm 9,10
#
set icursor [$itk_component(time) index insert]
switch $icursor {
0 - 1 {
set _cfield hour
}
3 - 4 {
set _cfield minute
}
6 - 7 {
set _cfield second
}
9 - 10 {
set _cfield ampm
}
}
return $_cfield
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _forwardCivilian
#
# Internal method which moves the cursor forward by one character
# jumping over the slashes and wrapping.
# ------------------------------------------------------------------
body iwidgets::Timefield::_forwardCivilian {} {
#
# If the insertion cursor is at the second digit
# of either the hour, minute or second field, then
# move the cursor to the first digit of the right-most field.
#
# else move the insertion cursor right one character
#
set icursor [$itk_component(time) index insert]
switch $icursor {
1 {
_setField minute
}
4 {
_setField second
}
7 {
_setField ampm
}
9 - 10 {
_setField hour
}
default {
$itk_component(time) icursor [expr $icursor+1]
}
}
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _forwardMilitary
#
# Internal method which moves the cursor forward by one character
# jumping over the slashes and wrapping.
# ------------------------------------------------------------------
body iwidgets::Timefield::_forwardMilitary {} {
#
# If the insertion cursor is at the second digit of either
# the hour, minute or second field, then move the cursor to
# the first digit of the right-most field.
#
# else move the insertion cursor right one character
#
set icursor [$itk_component(time) index insert]
switch $icursor {
1 {
_setField minute
}
4 {
_setField second
}
7 {
_setField hour
}
default {
$itk_component(time) icursor [expr $icursor+1]
}
}
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _backwardCivilian
#
# Internal method which moves the cursor backward by one character
# jumping over the ":" and wrapping.
# ------------------------------------------------------------------
body iwidgets::Timefield::_backwardCivilian {} {
#
# If the insertion cursor is at the first character
# of either the minute or second field or at the ampm
# field, then move the cursor to the second character
# of the left-most field.
#
# else if the insertion cursor is at the first digit of the
# hour field, then move the cursor to the first character
# of the ampm field.
#
# else move the insertion cursor left one character
#
set icursor [$itk_component(time) index insert]
switch $icursor {
9 {
_setField second
$itk_component(time) icursor 7
}
6 {
_setField minute
$itk_component(time) icursor 4
}
3 {
_setField hour
$itk_component(time) icursor 1
}
0 {
_setField ampm
$itk_component(time) icursor 9
}
default {
$itk_component(time) icursor [expr $icursor-1]
}
}
}
# ------------------------------------------------------------------
# PROTECTED METHOD: _backwardMilitary
#
# Internal method which moves the cursor backward by one character
# jumping over the slashes and wrapping.
# ------------------------------------------------------------------
body iwidgets::Timefield::_backwardMilitary {} {
#
# If the insertion cursor is at the first digit of either
# the minute or second field, then move the cursor to the
# second character of the left-most field.
#
# else if the insertion cursor is at the first digit of the
# hour field, then move the cursor to the second digit
# of the second field.
#
# else move the insertion cursor left one character
#
set icursor [$itk_component(time) index insert]
switch $icursor {
6 {
_setField minute
$itk_component(time) icursor 4
}
3 {
_setField hour
$itk_component(time) icursor 1
}
0 {
_setField second
$itk_component(time) icursor 7
}
default {
$itk_component(time) icursor [expr $icursor-1]
}
}
}