1#
2# Timefield
3# ----------------------------------------------------------------------
4# Implements a time entry field with adjustable built-in intelligence
5# levels.
6# ----------------------------------------------------------------------
7#   AUTHOR:  John A. Tucker          E-mail: jatucker@austin.dsccc.com
8#
9#   @(#) $Id: timefield.itk,v 1.6 2001/08/17 19:05:44 smithc Exp $
10# ----------------------------------------------------------------------
11#            Copyright (c) 1997 DSC Technologies Corporation
12# ======================================================================
13# Permission to use, copy, modify, distribute and license this software
14# and its documentation for any purpose, and without fee or written
15# agreement with DSC, is hereby granted, provided that the above copyright
16# notice appears in all copies and that both the copyright notice and
17# warranty disclaimer below appear in supporting documentation, and that
18# the names of DSC Technologies Corporation or DSC Communications
19# Corporation not be used in advertising or publicity pertaining to the
20# software without specific, written prior permission.
21#
22# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
24# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
25# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
26# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL
27# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
28# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
29# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
30# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31# SOFTWARE.
32# ======================================================================
33
34#
35# Use option database to override default resources of base classes.
36#
37option add *Timefield.justify center widgetDefault
38
39
40#
41# Usual options.
42#
43itk::usual Timefield {
44    keep -background -borderwidth -cursor -foreground -highlightcolor \
45       -highlightthickness -labelfont -textbackground -textfont
46}
47
48# ------------------------------------------------------------------
49#                               TIMEFIELD
50# ------------------------------------------------------------------
51itcl::class iwidgets::Timefield {
52
53    inherit iwidgets::Labeledwidget
54
55    constructor {args} {}
56
57    itk_option define -childsitepos childSitePos Position e
58    itk_option define -command command Command {}
59    itk_option define -seconds seconds Seconds on
60    itk_option define -format format Format civilian
61    itk_option define -iq iq Iq high
62    itk_option define -gmt gmt GMT no
63    itk_option define -state state State normal
64
65    public {
66      method get {{format "-string"}}
67      method isvalid {}
68      method show {{time "now"}}
69    }
70
71    protected {
72      method _backwardCivilian {}
73      method _backwardMilitary {}
74      method _focusIn {}
75      method _forwardCivilian {}
76      method _forwardMilitary {}
77      method _keyPress {char sym state}
78      method _moveField {direction}
79      method _setField {field}
80      method _whichField {}
81      method _toggleAmPm {}
82
83      variable _cfield hour
84      variable _formatString "%r"
85      variable _fields {}
86      variable _numFields 4
87      variable _forward {}
88      variable _backward {}
89      variable _timeVar ""
90
91      common _militaryFields {hour minute second}
92      common _civilianFields {hour minute second ampm}
93    }
94}
95
96#
97# Provide a lowercased access method for the timefield class.
98#
99proc iwidgets::timefield {pathName args} {
100    uplevel iwidgets::Timefield $pathName $args
101}
102
103# ------------------------------------------------------------------
104#                        CONSTRUCTOR
105# ------------------------------------------------------------------
106itcl::body iwidgets::Timefield::constructor {args} {
107    component hull configure -borderwidth 0
108
109    #
110    # Create an entry field for entering the time.
111    #
112    itk_component add time {
113      entry $itk_interior.time
114    } {
115      keep -borderwidth -cursor -exportselection \
116          -foreground -highlightcolor -highlightthickness \
117          -insertbackground -justify -relief -textvariable
118
119      rename -font -textfont textFont Font
120      rename -highlightbackground -background background Background
121      rename -background -textbackground textBackground Background
122    }
123
124    #
125    # Create the child site widget.
126    #
127    itk_component add -protected dfchildsite {
128      frame $itk_interior.dfchildsite
129    }
130    set itk_interior $itk_component(dfchildsite)
131
132    #
133    # Add timefield event bindings for focus in and keypress events.
134    #
135    bind $itk_component(time) <FocusIn>   [itcl::code $this _focusIn]
136    bind $itk_component(time) <KeyPress>  [itcl::code $this _keyPress %A %K %s]
137    bind $itk_component(time) <1> "focus $itk_component(time); break"
138
139    #
140    # Disable some mouse button event bindings:
141    #   Button Motion
142    #   Double-Clicks
143    #   Triple-Clicks
144    #   Button2
145    #
146    bind $itk_component(time) <Button1-Motion>	break
147    bind $itk_component(time) <Button2-Motion>	break
148    bind $itk_component(time) <Double-Button>	break
149    bind $itk_component(time) <Triple-Button>	break
150    bind $itk_component(time) <2>		break
151
152    #
153    # Initialize the widget based on the command line options.
154    #
155    eval itk_initialize $args
156
157    #
158    # Initialize the time to the current time.
159    #
160    show
161}
162
163# ------------------------------------------------------------------
164#                             OPTIONS
165# ------------------------------------------------------------------
166
167# ------------------------------------------------------------------
168# OPTION: -childsitepos
169#
170# Specifies the position of the child site in the widget.  Valid
171# locations are n, s, e, and w.
172# ------------------------------------------------------------------
173itcl::configbody iwidgets::Timefield::childsitepos {
174    set parent [winfo parent $itk_component(time)]
175
176    switch $itk_option(-childsitepos) {
177      n {
178          grid $itk_component(dfchildsite) -row 0 -column 0 -sticky ew
179          grid $itk_component(time) -row 1 -column 0 -sticky nsew
180
181          grid rowconfigure $parent 0 -weight 0
182          grid rowconfigure $parent 1 -weight 1
183          grid columnconfigure $parent 0 -weight 1
184          grid columnconfigure $parent 1 -weight 0
185      }
186
187      e {
188          grid $itk_component(dfchildsite) -row 0 -column 1 -sticky ns
189          grid $itk_component(time) -row 0 -column 0 -sticky nsew
190
191          grid rowconfigure $parent 0 -weight 1
192          grid rowconfigure $parent 1 -weight 0
193          grid columnconfigure $parent 0 -weight 1
194          grid columnconfigure $parent 1 -weight 0
195      }
196
197      s {
198          grid $itk_component(dfchildsite) -row 1 -column 0 -sticky ew
199          grid $itk_component(time) -row 0 -column 0 -sticky nsew
200
201          grid rowconfigure $parent 0 -weight 1
202          grid rowconfigure $parent 1 -weight 0
203          grid columnconfigure $parent 0 -weight 1
204          grid columnconfigure $parent 1 -weight 0
205      }
206
207      w {
208          grid $itk_component(dfchildsite) -row 0 -column 0 -sticky ns
209          grid $itk_component(time) -row 0 -column 1 -sticky nsew
210
211          grid rowconfigure $parent 0 -weight 1
212          grid rowconfigure $parent 1 -weight 0
213          grid columnconfigure $parent 0 -weight 0
214          grid columnconfigure $parent 1 -weight 1
215      }
216
217      default {
218          error "bad childsite option\
219                \"$itk_option(-childsitepos)\":\
220                should be n, e, s, or w"
221      }
222    }
223}
224
225# ------------------------------------------------------------------
226# OPTION: -command
227#
228# Command invoked upon detection of return key press event.
229# ------------------------------------------------------------------
230itcl::configbody iwidgets::Timefield::command {}
231
232# ------------------------------------------------------------------
233# OPTION: -iq
234#
235# Specifies the level of intelligence to be shown in the actions
236# taken by the time field during the processing of keypress events.
237# Valid settings include high or low.  With a high iq,
238# the time prevents the user from typing in an invalid time.  For
239# example, if the current time is 05/31/1997 and the user changes
240# the hour to 04, then the minute will be instantly modified for them
241# to be 30.  In addition, leap seconds are fully taken into account.
242# A setting of low iq instructs the widget to do no validity checking
243# at all during time entry.  With a low iq level, it is assumed that
244# the validity will be determined at a later time using the time's
245# isvalid command.
246# ------------------------------------------------------------------
247itcl::configbody iwidgets::Timefield::iq {
248
249  switch $itk_option(-iq) {
250    high - low {
251
252    }
253    default {
254      error "bad iq option \"$itk_option(-iq)\": should be high or low"
255    }
256  }
257}
258
259# ------------------------------------------------------------------
260# OPTION: -format
261#
262# Specifies the time format displayed in the entry widget.
263# ------------------------------------------------------------------
264itcl::configbody iwidgets::Timefield::format {
265
266  switch $itk_option(-format) {
267    civilian {
268      set _backward _backwardCivilian
269      set _forward _forwardCivilian
270      set _fields $_civilianFields
271      set _numFields 4
272      set _formatString "%r"
273      $itk_component(time) config -width 11
274    }
275    military {
276      set _backward _backwardMilitary
277      set _forward _forwardMilitary
278      set _fields $_militaryFields
279      set _numFields 3
280      set _formatString "%T"
281      $itk_component(time) config -width 8
282    }
283    default {
284      error "bad iq option \"$itk_option(-iq)\":\
285             should be civilian or military"
286    }
287  }
288
289  #
290  # Update the current contents of the entry field to reflect
291  # the configured format.
292  #
293  show $_timeVar
294}
295
296# ------------------------------------------------------------------
297# OPTION: -gmt
298#
299# This option is used for GMT time.  Must be a boolean value.
300# ------------------------------------------------------------------
301itcl::configbody iwidgets::Timefield::gmt {
302  switch $itk_option(-gmt) {
303    0 - no - false - off { }
304    1 - yes - true - on { }
305    default {
306      error "bad gmt option \"$itk_option(-gmt)\": should be boolean"
307    }
308  }
309}
310
311# ------------------------------------------------------------------
312# OPTION: -state
313#
314# Disable the
315# ------------------------------------------------------------------
316itcl::configbody iwidgets::Timefield::state {
317  switch -- $itk_option(-state) {
318    normal {
319      $itk_component(time) configure -state normal
320    }
321    disabled {
322      focus $itk_component(hull)
323      $itk_component(time) configure -state disabled
324    }
325    default {
326      error "Invalid value for -state: $itk_option(-state).  Should be\
327        \"normal\" or \"disabled\"."
328    }
329  }
330}
331
332
333# ------------------------------------------------------------------
334#                            METHODS
335# ------------------------------------------------------------------
336
337# ------------------------------------------------------------------
338# PUBLIC METHOD: get ?format?
339#
340# Return the current contents of the timefield in one of two formats
341# string or as an integer clock value using the -string and -clicks
342# options respectively.  The default is by string.  Reference the
343# clock command for more information on obtaining times and their
344# formats.
345# ------------------------------------------------------------------
346itcl::body iwidgets::Timefield::get {{format "-string"}} {
347  set _timeVar [$itk_component(time) get]
348
349  switch -- $format {
350    "-string" {
351      return $_timeVar
352    }
353    "-clicks" {
354      return [::clock scan $_timeVar -gmt $itk_option(-gmt)]
355    }
356    default {
357      error "bad format option \"$format\":\
358               should be -string or -clicks"
359    }
360  }
361}
362
363# ------------------------------------------------------------------
364# PUBLIC METHOD: show time
365#
366# Changes the currently displayed time to be that of the time
367# argument.  The time may be specified either as a string or an
368# integer clock value.  Reference the clock command for more
369# information on obtaining times and their formats.
370# ------------------------------------------------------------------
371itcl::body iwidgets::Timefield::show {{time "now"}} {
372  set icursor [$itk_component(time) index insert]
373
374  if {$time == {}} {
375    set time "now"
376  }
377
378  switch -regexp -- $time {
379
380    {^now$} {
381      set seconds [::clock seconds]
382    }
383
384    {^[0-9]+$} {
385      if { [catch {::clock format $time -gmt $itk_option(-gmt)}] } {
386        error "bad time: \"$time\", must be a valid time \
387           string, clock clicks value or the keyword now"
388      }
389      set seconds $time
390    }
391
392    default {
393      if {[catch {set seconds [::clock scan $time -gmt $itk_option(-gmt)]}]} {
394        error "bad time: \"$time\", must be a valid time \
395           string, clock clicks value or the keyword now"
396      }
397    }
398  }
399
400  set _timeVar [::clock format $seconds -format $_formatString \
401    -gmt $itk_option(-gmt)]
402
403  $itk_component(time) delete 0 end
404  $itk_component(time) insert end $_timeVar
405  $itk_component(time) icursor $icursor
406
407  return $_timeVar
408}
409
410# ------------------------------------------------------------------
411# PUBLIC METHOD: isvalid
412#
413# Returns a boolean indication of the validity of the currently
414# displayed time value.  For example, 09:59::59 is valid whereas
415# 26:59:59 is invalid.
416# ------------------------------------------------------------------
417itcl::body iwidgets::Timefield::isvalid {} {
418  set _timeVar [$itk_component(time) get]
419  return [expr {([catch {::clock scan $_timeVar -gmt $itk_option(-gmt)}] == 0)}]
420}
421
422# ------------------------------------------------------------------
423# PROTECTED METHOD: _focusIn
424#
425# This method is bound to the <FocusIn> event.  It resets the
426# insert cursor and field settings to be back to their last known
427# positions.
428# ------------------------------------------------------------------
429itcl::body iwidgets::Timefield::_focusIn {} {
430  _setField $_cfield
431}
432
433# ------------------------------------------------------------------
434# PROTECTED METHOD: _keyPress
435#
436# This method is the workhorse of the class.  It is bound to the
437# <KeyPress> event and controls the processing of all key strokes.
438# ------------------------------------------------------------------
439itcl::body iwidgets::Timefield::_keyPress {char sym state} {
440
441  #
442  #  Determine which field we are in currently.  This is needed
443  # since the user may have moved to this position via a mouse
444  # selection and so it would not be in the position we last
445  # knew it to be.
446  #
447  set _cfield [_whichField ]
448
449  #
450  # Set up a few basic variables we'll be needing throughout the
451  # rest of the method such as the position of the insert cursor
452  # and the currently displayed minute, hour, and second.
453  #
454  set inValid 0
455  set icursor [$itk_component(time) index insert]
456  set lastField [lindex $_fields end]
457
458  set prevtime $_timeVar
459  regexp {^([0-9])([0-9]):([0-9])([0-9]):([0-9])([0-9]).*$} \
460        $_timeVar dummy \
461        hour1 hour2 minute1 minute2 second1 second2
462  set hour	"$hour1$hour2"
463  set minute	"$minute1$minute2"
464  set second	"$second1$second2"
465
466  #
467  # Process numeric keystrokes.  This involes a fair amount of
468  # processing with step one being to check and make sure we
469  # aren't attempting to insert more that 6 characters.  If
470  # so ring the bell and break.
471  #
472  if {![catch {expr {int($char)}}]} {
473
474    # If we are currently in the hour field then we process the
475    # number entered based on the cursor position.  If we are at
476    # at the first position and our iq is low, then accept any
477    # input.
478    #
479    # if the current format is military, then
480    # validate the hour field which can be [00 - 23]
481    #
482    switch $_cfield {
483      hour {
484        if {$itk_option(-iq) == "low"} {
485          $itk_component(time) delete $icursor
486          $itk_component(time) insert $icursor $char
487
488        } elseif {$itk_option(-format) == "military"} {
489          if {$icursor == 0}  {
490            #
491            # if the digit is less than 2, then
492            # the second hour digit is valid for 0-9
493            #
494            if {$char < 2} {
495              $itk_component(time) delete 0 1
496              $itk_component(time) insert 0 $char
497
498            #
499            # if the digit is equal to 2, then
500            # the second hour digit is valid for 0-3
501            #
502            } elseif {$char == 2} {
503              $itk_component(time) delete 0 1
504              $itk_component(time) insert 0 $char
505
506              if {$hour2 > 3} {
507                $itk_component(time) delete 1 2
508                $itk_component(time) insert 1 "0"
509                $itk_component(time) icursor 1
510              }
511
512            #
513            # if the digit is greater than 2, then
514            # set the first hour digit to 0 and the
515            # second hour digit to the value.
516            #
517            } elseif {$char > 2}  {
518              $itk_component(time) delete 0 2
519              $itk_component(time) insert 0 "0$char"
520              set icursor 1
521            } else {
522              set inValid 1
523            }
524
525          #
526          # if the insertion cursor is for the second hour digit, then
527          # format is military, then it can only be valid if the first
528          # hour digit is less than 2 or the new digit is less than 4
529          #
530          } else {
531            if {$hour1 < 2 || $char < 4} {
532              $itk_component(time) delete 1 2
533              $itk_component(time) insert 1 $char
534            } else {
535              set inValid 1
536            }
537          }
538
539        #
540        # The format is civilian, so we need to
541        # validate the hour field which can be [01 - 12]
542        #
543        } else {
544          if {$icursor == 0}  {
545            #
546            # if the digit is 0, then
547            #   the second hour digit is valid for 1-9
548            #   so just insert it.
549            #
550            if {$char == 0 && $hour2 != 0} {
551              $itk_component(time) delete 0 1
552              $itk_component(time) insert 0 $char
553
554            #
555            # if the digit is equal to 1, then
556            #   the second hour digit is valid for 0-2
557            #
558            } elseif {$char == 1} {
559              $itk_component(time) delete 0 1
560              $itk_component(time) insert 0 $char
561
562              if {$hour2 > 2} {
563                $itk_component(time) delete 1 2
564                $itk_component(time) insert 1 0
565                set icursor 1
566              }
567
568            #
569            # if the digit is greater than 1, then
570            #   set the first hour digit to 0 and the
571            #   second hour digit to the value.
572            #
573            } elseif {$char > 1}  {
574              $itk_component(time) delete 0 2
575              $itk_component(time) insert 0 "0$char"
576              set icursor 1
577
578            } else {
579              set inValid 1
580            }
581
582          #
583          # The insertion cursor is at the second hour digit, so
584          # it can only be valid if the firs thour digit is 0
585          # or the new digit is less than or equal to 2
586          #
587          } else {
588            if {$hour1 == 0 || $char <= 2} {
589              $itk_component(time) delete 1 2
590              $itk_component(time) insert 1 $char
591            } else {
592              set inValid 1
593            }
594          }
595        }
596
597        if {$inValid} {
598          bell
599        } elseif {$icursor == 1} {
600          _setField minute
601        }
602      }
603
604      minute {
605        if {$itk_option(-iq) == "low" || $char < 6 || $icursor == 4} {
606          $itk_component(time) delete $icursor
607          $itk_component(time) insert $icursor $char
608        } elseif {$itk_option(-iq) == "high"} {
609          if {$char > 5} {
610            $itk_component(time) delete 3 5
611            $itk_component(time) insert 3 "0$char"
612            set icursor 4
613          }
614        }
615
616        if {$icursor == 4} {
617          _setField second
618        }
619      }
620
621      second {
622        if {$itk_option(-iq) == "low" || $char < 6 || $icursor == 7} {
623          $itk_component(time) delete $icursor
624          $itk_component(time) insert $icursor $char
625
626        } elseif {$itk_option(-iq) == "high"} {
627          if {$char > 5} {
628            $itk_component(time) delete 6 8
629            $itk_component(time) insert 6 "0$char"
630            set icursor 7
631          }
632        }
633
634        if {$icursor == 7} {
635          _moveField forward
636        }
637      }
638    }
639
640    set _timeVar [$itk_component(time) get]
641    return -code break
642  }
643
644  #
645  # Process the plus and the up arrow keys.  They both yield the same
646  # effect, they increment the minute by one.
647  #
648  switch $sym {
649    p - P {
650      if {$itk_option(-format) == "civilian"} {
651        $itk_component(time) delete 9 10
652        $itk_component(time) insert 9 P
653        _setField hour
654      }
655    }
656
657    a - A {
658      if {$itk_option(-format) == "civilian"} {
659        $itk_component(time) delete 9 10
660        $itk_component(time) insert 9 A
661        _setField hour
662      }
663    }
664
665    plus - Up {
666      if {$_cfield == "ampm"} {
667        _toggleAmPm
668      } else {
669        set newclicks [::clock scan "$prevtime 1 $_cfield"]
670        show [::clock format $newclicks -format $_formatString]
671      }
672    }
673
674    minus - Down {
675      #
676      # Process the minus and the down arrow keys which decrement the value
677      # of the field in which the cursor is currently positioned.
678      #
679      if {$_cfield == "ampm"} {
680        _toggleAmPm
681      } else {
682        set newclicks [::clock scan "$prevtime 1 $_cfield ago"]
683        show [::clock format $newclicks -format $_formatString]
684      }
685    }
686
687    Tab {
688      #
689      # A tab key moves the "hour:minute:second" field forward by one unless
690      # the current field is the second.  In that case we'll let tab
691      # do what is supposed to and pass the focus onto the next widget.
692      #
693      if {$state == 0} {
694
695        if {($itk_option(-format) == "civilian" && $_cfield == $lastField)} {
696          _setField hour
697          return -code continue
698        }
699        _moveField forward
700
701      #
702      # A ctrl-tab key moves the hour:minute:second field backwards by one
703      # unless the current field is the hour.  In that case we'll let
704      # tab take the focus to a previous widget.
705      #
706      } elseif {$state == 4} {
707        if {$_cfield == "hour"} {
708          _setField hour
709          return -code continue
710        }
711        _moveField backward
712      }
713    }
714
715    Right {
716      #
717      # A right arrow key moves the insert cursor to the right one.
718      #
719      $_forward
720    }
721
722    Left - BackSpace - Delete {
723      #
724      # A left arrow, backspace, or delete key moves the insert cursor
725      # to the left one.  This is what you expect for the left arrow
726      # and since the whole widget always operates in overstrike mode,
727      # it makes the most sense for backspace and delete to do the same.
728      #
729      $_backward
730    }
731
732    Return {
733      #
734      # A Return key invokes the optionally specified command option.
735      #
736      uplevel #0 $itk_option(-command)
737    }
738
739    default {
740
741    }
742  }
743
744  return -code break
745}
746
747# ------------------------------------------------------------------
748# PROTECTED METHOD: _toggleAmPm
749#
750# Internal method which toggles the displayed time
751# between "AM" and "PM" when format is "civilian".
752# ------------------------------------------------------------------
753itcl::body iwidgets::Timefield::_toggleAmPm {} {
754  set firstChar  [string index $_timeVar 9]
755  $itk_component(time) delete 9 10
756  $itk_component(time) insert 9 [expr {($firstChar == "A") ? "P" : "A"}]
757  $itk_component(time) icursor 9
758  set _timeVar [$itk_component(time) get]
759}
760
761# ------------------------------------------------------------------
762# PROTECTED METHOD: _setField field
763#
764# Adjusts the current field to be that of the argument, setting the
765# insert cursor appropriately.
766# ------------------------------------------------------------------
767itcl::body iwidgets::Timefield::_setField {field} {
768
769  # Move the position of the cursor to the first character of the
770  # field given by the argument:
771  #
772  # Field   First Character Index
773  # -----   ---------------------
774  # hour    0
775  # minute  3
776  # second  6
777  # ampm    9
778  #
779  switch $field {
780    hour {
781      $itk_component(time) icursor 0
782    }
783    minute {
784      $itk_component(time) icursor 3
785    }
786    second {
787      $itk_component(time) icursor 6
788    }
789    ampm {
790      if {$itk_option(-format) == "military"} {
791        error "bad field: \"$field\", must be hour, minute or second"
792      }
793      $itk_component(time) icursor 9
794    }
795    default {
796      if {$itk_option(-format) == "military"} {
797        error "bad field: \"$field\", must be hour, minute or second"
798      } else {
799        error "bad field: \"$field\", must be hour, minute, second or ampm"
800      }
801    }
802  }
803
804  set _cfield $field
805
806  return $_cfield
807}
808
809# ------------------------------------------------------------------
810# PROTECTED METHOD: _moveField
811#
812# Moves the cursor one field forward or backward.
813# ------------------------------------------------------------------
814itcl::body iwidgets::Timefield::_moveField {direction} {
815
816  # Since the value "_fields" list variable is always either value:
817  #   military => {hour minute second}
818  #   civilian => {hour minute second ampm}
819  #
820  # the index of the previous or next field index can be determined
821  # by subtracting or adding 1 to current the index, respectively.
822  #
823  set index [lsearch $_fields $_cfield]
824  expr {($direction == "forward") ? [incr index] : [incr index -1]}
825
826  if {$index == $_numFields} {
827    set index 0
828  } elseif {$index < 0} {
829    set index [expr {$_numFields-1}]
830  }
831
832  _setField [lindex $_fields $index]
833}
834
835# ------------------------------------------------------------------
836# PROTECTED METHOD: _whichField
837#
838# Returns the current field that the cursor is positioned within.
839# ------------------------------------------------------------------
840itcl::body iwidgets::Timefield::_whichField {} {
841
842  # Return the current field based on the position of the cursor.
843  #
844  # Field   Index
845  # -----   -----
846  # hour    0,1
847  # minute  3,4
848  # second  6,7
849  # ampm    9,10
850  #
851  set icursor [$itk_component(time) index insert]
852  switch $icursor {
853    0 - 1 {
854      set _cfield hour
855    }
856    3 - 4 {
857      set _cfield minute
858    }
859    6 - 7 {
860      set _cfield second
861    }
862    9 - 10 {
863      set _cfield ampm
864    }
865  }
866
867  return $_cfield
868}
869
870# ------------------------------------------------------------------
871# PROTECTED METHOD: _forwardCivilian
872#
873# Internal method which moves the cursor forward by one character
874# jumping over the slashes and wrapping.
875# ------------------------------------------------------------------
876itcl::body iwidgets::Timefield::_forwardCivilian {} {
877
878  #
879  # If the insertion cursor is at the second digit
880  # of either the hour, minute or second field, then
881  # move the cursor to the first digit of the right-most field.
882  #
883  # else move the insertion cursor right one character
884  #
885  set icursor [$itk_component(time) index insert]
886  switch $icursor {
887    1 {
888      _setField minute
889    }
890    4 {
891      _setField second
892    }
893    7 {
894      _setField ampm
895    }
896    9 - 10 {
897      _setField hour
898    }
899    default {
900      $itk_component(time) icursor [expr {$icursor+1}]
901    }
902  }
903}
904
905# ------------------------------------------------------------------
906# PROTECTED METHOD: _forwardMilitary
907#
908# Internal method which moves the cursor forward by one character
909# jumping over the slashes and wrapping.
910# ------------------------------------------------------------------
911itcl::body iwidgets::Timefield::_forwardMilitary {} {
912
913  #
914  # If the insertion cursor is at the second digit of either
915  # the hour, minute or second field, then move the cursor to
916  # the first digit of the right-most field.
917  #
918  # else move the insertion cursor right one character
919  #
920  set icursor [$itk_component(time) index insert]
921  switch $icursor {
922    1 {
923      _setField minute
924    }
925    4 {
926      _setField second
927    }
928    7 {
929      _setField hour
930    }
931    default {
932      $itk_component(time) icursor [expr {$icursor+1}]
933    }
934  }
935}
936
937# ------------------------------------------------------------------
938# PROTECTED METHOD: _backwardCivilian
939#
940# Internal method which moves the cursor backward by one character
941# jumping over the ":" and wrapping.
942# ------------------------------------------------------------------
943itcl::body iwidgets::Timefield::_backwardCivilian {} {
944
945  #
946  # If the insertion cursor is at the first character
947  # of either the minute or second field or at the ampm
948  # field, then move the cursor to the second character
949  # of the left-most field.
950  #
951  # else if the insertion cursor is at the first digit of the
952  # hour field, then move the cursor to the first character
953  # of the ampm field.
954  #
955  # else move the insertion cursor left one character
956  #
957  set icursor [$itk_component(time) index insert]
958  switch $icursor {
959    9 {
960      _setField second
961      $itk_component(time) icursor 7
962    }
963    6 {
964      _setField minute
965      $itk_component(time) icursor 4
966    }
967    3 {
968      _setField hour
969      $itk_component(time) icursor 1
970    }
971    0 {
972      _setField ampm
973      $itk_component(time) icursor 9
974    }
975    default {
976      $itk_component(time) icursor [expr {$icursor-1}]
977    }
978  }
979}
980
981# ------------------------------------------------------------------
982# PROTECTED METHOD: _backwardMilitary
983#
984# Internal method which moves the cursor backward by one character
985# jumping over the slashes and wrapping.
986# ------------------------------------------------------------------
987itcl::body iwidgets::Timefield::_backwardMilitary {} {
988
989  #
990  # If the insertion cursor is at the first digit of either
991  # the minute or second field, then move the cursor to the
992  # second character of the left-most field.
993  #
994  # else if the insertion cursor is at the first digit of the
995  # hour field, then move the cursor to the second digit
996  # of the second field.
997  #
998  # else move the insertion cursor left one character
999  #
1000  set icursor [$itk_component(time) index insert]
1001  switch $icursor {
1002    6 {
1003      _setField minute
1004      $itk_component(time) icursor 4
1005    }
1006    3 {
1007      _setField hour
1008      $itk_component(time) icursor 1
1009    }
1010    0 {
1011      _setField second
1012      $itk_component(time) icursor 7
1013    }
1014    default {
1015      $itk_component(time) icursor [expr {$icursor-1}]
1016    }
1017  }
1018}
1019