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 / datefield.itk < prev    next >
Text File  |  1999-02-24  |  26KB  |  855 lines

  1. #
  2. # Datefield
  3. # ----------------------------------------------------------------------
  4. # Implements a date entry field with adjustable built-in intelligence
  5. # levels.
  6. # ----------------------------------------------------------------------
  7. #   AUTHOR:  Mark L. Ulferts          E-mail: mulferts@austin.dsccc.com
  8. #
  9. #   @(#) $Id: datefield.itk,v 1.1 1998/07/27 18:53:03 stanton 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. # DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
  22. # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
  23. # INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
  24. # AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
  25. # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
  26. # DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
  27. # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
  28. # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
  29. # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
  30. # SOFTWARE.
  31. # ======================================================================
  32.  
  33. #
  34. # Usual options.
  35. #
  36. itk::usual Datefield {
  37.     keep -background -borderwidth -cursor -foreground -highlightcolor \
  38.      -highlightthickness -labelfont -textbackground -textfont
  39. }
  40.  
  41. # ------------------------------------------------------------------
  42. #                               DATEFIELD
  43. # ------------------------------------------------------------------
  44. class iwidgets::Datefield {
  45.     inherit iwidgets::Labeledwidget 
  46.     
  47.     constructor {args} {}
  48.  
  49.     itk_option define -childsitepos childSitePos Position e
  50.     itk_option define -command command Command {}
  51.     itk_option define -iq iq Iq high
  52.     
  53.     public method get {{format "-string"}}
  54.     public method isvalid {}
  55.     public method show {{date now}}
  56.  
  57.     protected method _backward {}
  58.     protected method _focusIn {}
  59.     protected method _forward {}
  60.     protected method _keyPress {char sym state}
  61.     protected method _lastDay {month year}
  62.     protected method _moveField {direction}
  63.     protected method _setField {field}
  64.     protected method _whichField {}
  65.  
  66.     protected variable _cfield "month"
  67.     protected variable _fields {month day year}
  68. }
  69.  
  70. #
  71. # Provide a lowercased access method for the datefield class.
  72. proc ::iwidgets::datefield {pathName args} {
  73.     uplevel ::iwidgets::Datefield $pathName $args
  74. }
  75.  
  76. #
  77. # Use option database to override default resources of base classes.
  78. #
  79. option add *Datefield.justify center widgetDefault
  80.  
  81. # ------------------------------------------------------------------
  82. #                        CONSTRUCTOR
  83. # ------------------------------------------------------------------
  84. body iwidgets::Datefield::constructor {args} {
  85.     component hull configure -borderwidth 0
  86.     
  87.     #
  88.     # Create an entry field for entering the date.
  89.     #
  90.     itk_component add date {
  91.     entry $itk_interior.date -width 10
  92.     } {
  93.     keep -borderwidth -cursor -exportselection \
  94.         -foreground -highlightcolor -highlightthickness \
  95.         -insertbackground -justify -relief -state
  96.     
  97.     rename -font -textfont textFont Font
  98.     rename -highlightbackground -background background Background
  99.     rename -background -textbackground textBackground Background
  100.     }
  101.  
  102.     #
  103.     # Create the child site widget.
  104.     #
  105.     itk_component add -protected dfchildsite {
  106.     frame $itk_interior.dfchildsite
  107.     } 
  108.     set itk_interior $itk_component(dfchildsite)
  109.     
  110.     #
  111.     # Add datefield event bindings for focus in and keypress events.
  112.     #
  113.     bind $itk_component(date) <FocusIn> [code $this _focusIn]
  114.     bind $itk_component(date) <KeyPress> [code $this _keyPress %A %K %s]
  115.     
  116.     #
  117.     # Disable some mouse button event bindings:
  118.     #   Button Motion
  119.     #   Double-Clicks
  120.     #   Triple-Clicks
  121.     #   Button2
  122.     #
  123.     bind $itk_component(date) <Button1-Motion>    break
  124.     bind $itk_component(date) <Button2-Motion>    break
  125.     bind $itk_component(date) <Double-Button>    break
  126.     bind $itk_component(date) <Triple-Button>    break
  127.     bind $itk_component(date) <2>        break
  128.  
  129.     #
  130.     # Initialize the widget based on the command line options.
  131.     #
  132.     eval itk_initialize $args
  133.  
  134.     #
  135.     # Initialize the date to the current date.
  136.     #
  137.     $itk_component(date) delete 0 end
  138.     $itk_component(date) insert end \
  139.     [clock format [clock seconds] -format "%m/%d/%Y"]
  140. }
  141.  
  142. # ------------------------------------------------------------------
  143. #                             OPTIONS
  144. # ------------------------------------------------------------------
  145.  
  146. # ------------------------------------------------------------------
  147. # OPTION: -childsitepos
  148. #
  149. # Specifies the position of the child site in the widget.  Valid
  150. # locations are n, s, e, and w.
  151. # ------------------------------------------------------------------
  152. configbody iwidgets::Datefield::childsitepos {
  153.     set parent [winfo parent $itk_component(date)]
  154.  
  155.     switch $itk_option(-childsitepos) {
  156.     n {
  157.         grid $itk_component(dfchildsite) -row 0 -column 0 -sticky ew
  158.         grid $itk_component(date) -row 1 -column 0 -sticky nsew
  159.  
  160.         grid rowconfigure $parent 0 -weight 0
  161.         grid rowconfigure $parent 1 -weight 1
  162.         grid columnconfigure $parent 0 -weight 1
  163.         grid columnconfigure $parent 1 -weight 0
  164.     }
  165.     
  166.     e {
  167.         grid $itk_component(dfchildsite) -row 0 -column 1 -sticky ns
  168.         grid $itk_component(date) -row 0 -column 0 -sticky nsew
  169.  
  170.         grid rowconfigure $parent 0 -weight 1
  171.         grid rowconfigure $parent 1 -weight 0
  172.         grid columnconfigure $parent 0 -weight 1
  173.         grid columnconfigure $parent 1 -weight 0
  174.     }
  175.     
  176.     s {
  177.         grid $itk_component(dfchildsite) -row 1 -column 0 -sticky ew
  178.         grid $itk_component(date) -row 0 -column 0 -sticky nsew
  179.  
  180.         grid rowconfigure $parent 0 -weight 1
  181.         grid rowconfigure $parent 1 -weight 0
  182.         grid columnconfigure $parent 0 -weight 1
  183.         grid columnconfigure $parent 1 -weight 0
  184.     }
  185.     
  186.     w {
  187.         grid $itk_component(dfchildsite) -row 0 -column 0 -sticky ns
  188.         grid $itk_component(date) -row 0 -column 1 -sticky nsew
  189.  
  190.         grid rowconfigure $parent 0 -weight 1
  191.         grid rowconfigure $parent 1 -weight 0
  192.         grid columnconfigure $parent 0 -weight 0
  193.         grid columnconfigure $parent 1 -weight 1
  194.     }
  195.     
  196.     default {
  197.         error "bad childsite option\
  198.             \"$itk_option(-childsitepos)\":\
  199.             should be n, e, s, or w"
  200.     }
  201.     }
  202. }
  203.  
  204. # ------------------------------------------------------------------
  205. # OPTION: -command
  206. #
  207. # Command invoked upon detection of return key press event.
  208. # ------------------------------------------------------------------
  209. configbody iwidgets::Datefield::command {}
  210.  
  211. # ------------------------------------------------------------------
  212. # OPTION: -iq
  213. #
  214. # Specifies the level of intelligence to be shown in the actions
  215. # taken by the date field during the processing of keypress events.
  216. # Valid settings include high, average, and low.  With a high iq,
  217. # the date prevents the user from typing in an invalid date.  For 
  218. # example, if the current date is 05/31/1997 and the user changes
  219. # the month to 04, then the day will be instantly modified for them 
  220. # to be 30.  In addition, leap years are fully taken into account.
  221. # With average iq, the month is limited to the values of 01-12, but
  222. # it is possible to type in an invalid day.  A setting of low iq
  223. # instructs the widget to do no validity checking at all during
  224. # date entry.  With both average and low iq levels, it is assumed
  225. # that the validity will be determined at a later time using the
  226. # date's isvalid command.
  227. # ------------------------------------------------------------------
  228. configbody iwidgets::Datefield::iq {
  229.     switch $itk_option(-iq) {
  230.     high - average - low {
  231.     }
  232.     default {
  233.         error "bad iq option \"$itk_option(-iq)\":\
  234.                    should be high, average or low"
  235.     }
  236.     }
  237. }
  238.  
  239. # ------------------------------------------------------------------
  240. #                            METHODS
  241. # ------------------------------------------------------------------
  242.  
  243. # ------------------------------------------------------------------
  244. # PUBLIC METHOD: get ?format?
  245. #
  246. # Return the current contents of the datefield in one of two formats
  247. # string or as an integer clock value using the -string and -clicks
  248. # options respectively.  The default is by string.  Reference the 
  249. # clock command for more information on obtaining dates and their 
  250. # formats.
  251. # ------------------------------------------------------------------
  252. body iwidgets::Datefield::get {{format "-string"}} {
  253.     set datestr [$itk_component(date) get]
  254.  
  255.     switch -- $format {
  256.     "-string" {
  257.         return $datestr
  258.     }
  259.     "-clicks" {
  260.         return [clock scan $datestr]
  261.     }
  262.     default {
  263.         error "bad format option \"$format\":\
  264.                    should be -string or -clicks"
  265.     }
  266.     }
  267. }
  268.  
  269. # ------------------------------------------------------------------
  270. # PUBLIC METHOD: show date
  271. #
  272. # Changes the currently displayed date to be that of the date 
  273. # argument.  The date may be specified either as a string or an
  274. # integer clock value.  Reference the clock command for more 
  275. # information on obtaining dates and their formats.
  276. # ------------------------------------------------------------------
  277. body iwidgets::Datefield::show {{date "now"}} {
  278.     if {$date == "now"} {
  279.     set seconds [clock seconds]
  280.     } else {
  281.     if {[catch {clock format $date}] == 0} {
  282.         set seconds $date
  283.     } elseif {[catch {set seconds [clock scan $date]}] != 0} {
  284.         error "bad date: \"$date\", must be a valid date\
  285.                string, clock clicks value or the keyword now"
  286.     }
  287.     }
  288.  
  289.     $itk_component(date) delete 0 end
  290.     $itk_component(date) insert end [clock format $seconds -format "%m/%d/%Y"]
  291.  
  292.     _setField month
  293.  
  294.     return
  295. }
  296.  
  297. # ------------------------------------------------------------------
  298. # PUBLIC METHOD: isvalid
  299. #
  300. # Returns a boolean indication of the validity of the currently
  301. # displayed date value.  For example, 3/3/1960 is valid whereas
  302. # 02/29/1997 is invalid.
  303. # ------------------------------------------------------------------
  304. body iwidgets::Datefield::isvalid {} {
  305.     if {[catch {clock scan [$itk_component(date) get]}] != 0} {
  306.     return 0
  307.     } else {
  308.     return 1
  309.     }
  310. }
  311.  
  312. # ------------------------------------------------------------------
  313. # PROTECTED METHOD: _focusIn
  314. #
  315. # This method is bound to the <FocusIn> event.  It resets the 
  316. # insert cursor and field settings to be back to their last known
  317. # positions.
  318. # ------------------------------------------------------------------
  319. body iwidgets::Datefield::_focusIn {} {
  320.     _setField $_cfield
  321. }
  322.  
  323. # ------------------------------------------------------------------
  324. # PROTECTED METHOD: _keyPress 
  325. #
  326. # This method is the workhorse of the class.  It is bound to the
  327. # <KeyPress> event and controls the processing of all key strokes.
  328. # ------------------------------------------------------------------
  329. body iwidgets::Datefield::_keyPress {char sym state} {
  330.     #
  331.     #  Determine which field we are in currently.  This is needed
  332.     # since the user may have moved to this position via a mouse
  333.     # selection and so it would not be in the position we last 
  334.     # knew it to be.
  335.     #
  336.     _whichField 
  337.  
  338.     #
  339.     # Set up a few basic variables we'll be needing throughout the
  340.     # rest of the method such as the position of the insert cursor
  341.     # and the currently displayed day, month, and year.
  342.     #
  343.     set icursor [$itk_component(date) index insert]
  344.     set splist [split [$itk_component(date) get] "/"]
  345.     set month [lindex $splist 0]
  346.     set day [lindex $splist 1]
  347.     set year [lindex $splist 2]
  348.  
  349.     #
  350.     # Process numeric keystrokes.  This involes a fair amount of 
  351.     # processing with step one being to check and make sure we
  352.     # aren't attempting to insert more that 10 characters.  If
  353.     # so ring the bell and break.
  354.     #
  355.     if {[regexp {[0-9]} $char]} {
  356.     if {[$itk_component(date) index insert] == 10} {
  357.         bell
  358.         return -code break
  359.     }
  360.  
  361.     #
  362.     # If we are currently in the month field then we process the
  363.         # number entered based on the cursor position.  If we are at
  364.     # at the first position and our iq is low, then accept any 
  365.         # input.  
  366.     #
  367.     if {$_cfield == "month"} {
  368.         if {[$itk_component(date) index insert] == 0} {
  369.         if {$itk_option(-iq) == "low"} {
  370.             $itk_component(date) delete 0
  371.             $itk_component(date) insert 0 $char
  372.  
  373.         } else {            
  374.             
  375.             #
  376.             # Otherwise, we're slightly smarter.  If the number
  377.             # is less than two insert it at position zero.  If 
  378.             # this makes the month greater than twelve, set the 
  379.             # number at position one to zero which makes in 
  380.             # effect puts the month back in range.  
  381.             #
  382.             regsub {([0-9])([0-9])} $month "$char\\2" month2b
  383.  
  384.             if {$char < 2} {
  385.             $itk_component(date) delete 0
  386.             $itk_component(date) insert 0 $char
  387.  
  388.             if {$month2b > 12} {
  389.                 $itk_component(date) delete 1
  390.                 $itk_component(date) insert 1 0
  391.                 $itk_component(date) icursor 1
  392.             } elseif {$month2b == "00"} {
  393.                 $itk_component(date) delete 1
  394.                 $itk_component(date) insert 1 1
  395.                 $itk_component(date) icursor 1
  396.             }                
  397.  
  398.             #
  399.             # Finally, if the number is greater than one we'll 
  400.             # assume that they really mean to be entering a zero
  401.                   # followed by their number, do so for them, and 
  402.             # proceed to skip to the next field which is the 
  403.             # day field.
  404.             #
  405.             } else {
  406.             $itk_component(date) delete 0 2
  407.             $itk_component(date) insert 0 0$char
  408.             _setField day
  409.             }
  410.         }
  411.             
  412.         #
  413.         # Else, we're at cursor position one.  Again, if we aren't
  414.         # too smart, let them enter anything.  Otherwise, if the 
  415.         # number makes the month exceed twelve, set the month to
  416.         # zero followed by their number to get it back into range.
  417.             #
  418.         } else {
  419.         regsub {([0-9])([0-9])} $month "\\1$char" month2b
  420.         
  421.         if {$itk_option(-iq) == "low"} {
  422.             $itk_component(date) delete 1
  423.             $itk_component(date) insert 1 $char
  424.         } else {
  425.             if {$month2b > 12} {
  426.             $itk_component(date) delete 0 2
  427.             $itk_component(date) insert 0 0$char
  428.             } elseif {$month2b == "00"} {
  429.             bell
  430.             return -code break
  431.             } else {
  432.             $itk_component(date) delete 1
  433.             $itk_component(date) insert 1 $char
  434.             }            
  435.         }
  436.         
  437.         _setField day
  438.         }
  439.  
  440.             # 
  441.             # Now, the month processing is complete and if we're of a
  442.         # high level of intelligence, then we'll make sure that the
  443.             # current value for the day is valid for this month.  If
  444.         # it is beyond the last day for this month, change it to
  445.             # be the last day of the new month.
  446.             #
  447.         if {$itk_option(-iq) == "high"} {
  448.         set splist [split [$itk_component(date) get] "/"]
  449.         set month [lindex $splist 0]
  450.  
  451.         if {$day > [set endday [_lastDay $month $year]]} {
  452.             set icursor [$itk_component(date) index insert]
  453.             $itk_component(date) delete 3 5
  454.             $itk_component(date) insert 3 $endday
  455.             $itk_component(date) icursor $icursor
  456.         }
  457.         }
  458.         
  459.         #
  460.         # Finally, return with a code of break to stop any normal
  461.         # processing in that we've done all that is necessary.
  462.         #
  463.         return -code break
  464.     }
  465.  
  466.     #
  467.     # This next block of code is for processing of the day field
  468.     # which is quite similar is strategy to that of the month.
  469.     #
  470.     if {$_cfield == "day"} {
  471.         if {$itk_option(-iq) == "high"} {
  472.         set endofMonth [_lastDay $month $year]
  473.         } else {
  474.         set endofMonth 31
  475.         }
  476.  
  477.         #
  478.         # If we are at the third cursor position we are porcessing 
  479.         # the first character of the day field.  If we have an iq 
  480.         # of low accept any input.
  481.         #
  482.         if {[$itk_component(date) index insert] == 3} {
  483.         if {$itk_option(-iq) == "low"} {
  484.             $itk_component(date) delete 3
  485.             $itk_component(date) insert 3 $char
  486.  
  487.         } else {
  488.  
  489.             #
  490.             # If the day to be is double zero, then make the
  491.             # day be the first.
  492.             #
  493.             regsub {([0-9])([0-9])} $day "$char\\2" day2b
  494.  
  495.             if {$day2b == "00"} {
  496.             $itk_component(date) delete 3 5
  497.             $itk_component(date) insert 3 01
  498.             $itk_component(date) icursor 4
  499.  
  500.             #
  501.             # Otherwise, if the character is less than four 
  502.             # and the month is not Feburary, insert the number 
  503.             # and if this makes the day be beyond the valid 
  504.             # range for this month, than set to be back in 
  505.             # range.  
  506.             #
  507.             } elseif {($char < 4) && ($month != "02")} {
  508.             $itk_component(date) delete 3
  509.             $itk_component(date) insert 3 $char
  510.             
  511.             if {$day2b > $endofMonth} {
  512.                 $itk_component(date) delete 4
  513.                 $itk_component(date) insert 4 0
  514.                 $itk_component(date) icursor 4
  515.             } 
  516.             
  517.             #
  518.             # For Feburary with a number to be entered of 
  519.             # less than three, make sure the number doesn't 
  520.             # make the day be greater than the correct range
  521.             # and if so adjust the input. 
  522.                     #
  523.             } elseif {$char < 3} {
  524.             $itk_component(date) delete 3
  525.             $itk_component(date) insert 3 $char
  526.             
  527.             if {$day2b > $endofMonth} {
  528.                 $itk_component(date) delete 3 5
  529.                 $itk_component(date) insert 3 $endofMonth
  530.                 $itk_component(date) icursor 4
  531.             } 
  532.  
  533.             #
  534.             # Finally, if the number is greater than three,
  535.                     # set the day to be zero followed by the number 
  536.             # entered and proceed to the year field.
  537.                     #
  538.             } else {
  539.             $itk_component(date) delete 3 5
  540.             $itk_component(date) insert 3 0$char
  541.             _setField year
  542.             }
  543.         }
  544.             
  545.         #
  546.         # Else, we're dealing with the second number in the day
  547.         # field.  If we're not too bright accept anything, otherwise
  548.         # if the day is beyond the range for this month or equal to
  549.         # zero then ring the bell.
  550.         #
  551.         } else {
  552.         regsub {([0-9])([0-9])} $day "\\1$char" day2b
  553.  
  554.         if {($itk_option(-iq) != "low") && \
  555.             (($day2b > $endofMonth) || ($day2b == "00"))} {
  556.             bell
  557.         } else {
  558.             $itk_component(date) delete 4
  559.             $itk_component(date) insert 4 $char
  560.             _setField year
  561.         }
  562.         }
  563.  
  564.         #
  565.         # Return with a code of break to prevent normal processing. 
  566.         #
  567.         return -code break
  568.     }
  569.  
  570.     #
  571.     # This month and day we're tough, the code for the year is 
  572.     # comparitively simple.  Accept any input and if we are really
  573.     # sharp, then make sure the day is correct for the month
  574.     # given the year.  In short, handle leap years.
  575.     #
  576.     if {$_cfield == "year"} {
  577.         if {$itk_option(-iq) == "low"} {
  578.         $itk_component(date) delete $icursor
  579.         $itk_component(date) insert $icursor $char
  580.         } else {
  581.  
  582.         set prevdate [get]
  583.  
  584.         if {[$itk_component(date) index insert] == 6} {
  585.             set yrdgt [lindex [split [lindex \
  586.                      [split $prevdate "/"] 2] ""] 0]
  587.             if {$char != $yrdgt} {
  588.             if {$char == 1} {
  589.                 $itk_component(date) delete $icursor end
  590.                 $itk_component(date) insert $icursor 1999
  591.             } elseif {$char == 2} {
  592.                 $itk_component(date) delete $icursor end
  593.                 $itk_component(date) insert $icursor 2000
  594.             } else {
  595.                 bell
  596.                 return -code break
  597.             }
  598.             }
  599.  
  600.             $itk_component(date) icursor 7
  601.             return -code break
  602.         }
  603.  
  604.         $itk_component(date) delete $icursor
  605.         $itk_component(date) insert $icursor $char
  606.  
  607.         if {[catch {clock scan [get]}] != 0} {
  608.             $itk_component(date) delete 6 end
  609.             $itk_component(date) insert end \
  610.             [lindex [split $prevdate "/"] 2]
  611.             $itk_component(date) icursor $icursor
  612.  
  613.             bell
  614.             return -code break
  615.         }
  616.  
  617.         if {$itk_option(-iq) == "high"} {
  618.             set splist [split [$itk_component(date) get] "/"]
  619.             set year [lindex $splist 2]
  620.  
  621.             if {$day > [set endday [_lastDay $month $year]]} {
  622.             set icursor [$itk_component(date) index insert]
  623.             $itk_component(date) delete 3 5
  624.             $itk_component(date) insert 3 $endday
  625.             $itk_component(date) icursor $icursor
  626.             }
  627.         }
  628.         }
  629.         
  630.         return -code break
  631.     }
  632.  
  633.     #
  634.     # Process the plus and the up arrow keys.  They both yeild the same
  635.     # effect, they increment the day by one.
  636.     #
  637.     } elseif {($sym == "plus") || ($sym == "Up")} {
  638.     if {[catch {show [clock scan "1 day" -base [get -clicks]]}] != 0} {
  639.         bell
  640.     }
  641.     return -code break
  642.     
  643.     #
  644.     # Process the minus and the down arrow keys which decrement the day.
  645.     #
  646.     } elseif {($sym == "minus") || ($sym == "Down")} {
  647.     if {[catch {show [clock scan "-1 day" -base [get -clicks]]}] != 0} {
  648.         bell
  649.     }
  650.     return -code break
  651.  
  652.     #
  653.     # A tab key moves the day/month/year field forward by one unless
  654.     # the current field is the year.  In that case we'll let tab
  655.     # do what is supposed to and pass the focus onto the next widget.
  656.     #
  657.     } elseif {($sym == "Tab") && ($state == 0)} {
  658.     if {$_cfield != "year"} {
  659.         _moveField forward
  660.         return -code break
  661.     } else {
  662.         _setField "month"
  663.         return -code continue
  664.     }
  665.  
  666.     #
  667.     # A ctrl-tab key moves the day/month/year field backwards by one 
  668.     # unless the current field is the month.  In that case we'll let 
  669.     # tab take the focus to a previous widget.
  670.     #
  671.     } elseif {($sym == "Tab") && ($state == 4)} {
  672.     if {$_cfield != "month"} {
  673.         _moveField backward
  674.         return -code break
  675.     } else {
  676.         set _cfield "month"
  677.         return -code continue
  678.     }
  679.  
  680.     #
  681.     # A right arrow key moves the insert cursor to the right one.
  682.     #
  683.     } elseif {$sym == "Right"} {
  684.     _forward
  685.     return -code break
  686.  
  687.     #
  688.     # A left arrow, backspace, or delete key moves the insert cursor 
  689.     # to the left one.  This is what you expect for the left arrow
  690.     # and since the whole widget always operates in overstrike mode,
  691.     # it makes the most sense for backspace and delete to do the same.
  692.     #
  693.     } elseif {$sym == "Left" || $sym == "BackSpace" || $sym == "Delete"} {
  694.     _backward
  695.     return -code break
  696.  
  697.     } elseif {($sym == "Control_L") || ($sym == "Shift_L") || \
  698.           ($sym == "Control_R") || ($sym == "Shift_R")} {
  699.     return -code break
  700.  
  701.     #
  702.     # A Return key invokes the optionally specified command option.
  703.     #
  704.     } elseif {$sym == "Return"} {
  705.     uplevel #0 $itk_option(-command)
  706.     return -code break 
  707.  
  708.     } else {
  709.     bell
  710.     return -code break
  711.     }
  712. }
  713.  
  714. # ------------------------------------------------------------------
  715. # PROTECTED METHOD: _setField field
  716. #
  717. # Internal method which adjusts the field to be that of the 
  718. # argument, setting the insert cursor appropriately.
  719. # ------------------------------------------------------------------
  720. body iwidgets::Datefield::_setField {field} {
  721.     set _cfield $field
  722.  
  723.     switch $field {
  724.     "month" {
  725.         $itk_component(date) icursor 0
  726.     }
  727.     "day" {
  728.         $itk_component(date) icursor 3
  729.     }
  730.     "year" {
  731.         $itk_component(date) icursor 8
  732.     }
  733.     default {
  734.         error "bad field: \"$field\", must be month, day or year"
  735.     }
  736.     }
  737. }
  738.  
  739. # ------------------------------------------------------------------
  740. # PROTECTED METHOD: _moveField
  741. #
  742. # Internal method for moving the field forward or backward by one.
  743. # ------------------------------------------------------------------
  744. body iwidgets::Datefield::_moveField {direction} {
  745.     set index [lsearch $_fields $_cfield]
  746.  
  747.     if {$direction == "forward"} {
  748.     set newIndex [expr $index + 1]
  749.     } else {
  750.     set newIndex [expr $index - 1]
  751.     }
  752.  
  753.     if {$newIndex == [llength $_fields]} {
  754.     set newIndex 0
  755.     }
  756.     if {$newIndex < 0} {
  757.     set newIndex [expr [llength $_fields] - 1]
  758.     }
  759.  
  760.     _setField [lindex $_fields $newIndex]
  761.  
  762.     return
  763. }
  764.  
  765. # ------------------------------------------------------------------
  766. # PROTECTED METHOD: _whichField
  767. #
  768. # Internal method which returns the current field that the cursor
  769. # is currently within.
  770. # ------------------------------------------------------------------
  771. body iwidgets::Datefield::_whichField {} {
  772.     set icursor [$itk_component(date) index insert]
  773.  
  774.     switch $icursor {
  775.     0 - 1 {
  776.         set _cfield "month"
  777.     }
  778.     3 - 4 {
  779.         set _cfield "day"
  780.     }
  781.     6 - 7 - 8 - 9 {
  782.         set _cfield "year"
  783.     }
  784.     }
  785. }
  786.  
  787. # ------------------------------------------------------------------
  788. # PROTECTED METHOD: _forward
  789. #
  790. # Internal method which moves the cursor forward by one character
  791. # jumping over the slashes and wrapping.
  792. # ------------------------------------------------------------------
  793. body iwidgets::Datefield::_forward {} {
  794.     set icursor [$itk_component(date) index insert]
  795.  
  796.     switch $icursor {
  797.     1 {
  798.         _setField day
  799.     }
  800.     4 {
  801.         _setField year
  802.     }
  803.     9 - 10 {
  804.         _setField month
  805.     }
  806.     default {
  807.         $itk_component(date) icursor [expr $icursor + 1]
  808.     }
  809.     }
  810. }
  811.  
  812. # ------------------------------------------------------------------
  813. # PROTECTED METHOD: _backward
  814. #
  815. # Internal method which moves the cursor backward by one character
  816. # jumping over the slashes and wrapping.
  817. # ------------------------------------------------------------------
  818. body iwidgets::Datefield::_backward {} {
  819.     set icursor [$itk_component(date) index insert]
  820.  
  821.     switch $icursor {
  822.     6 {
  823.         _setField day
  824.     }
  825.     3 {
  826.         _setField month
  827.     }
  828.     0 {
  829.         _setField year
  830.     }
  831.     default {
  832.         $itk_component(date) icursor [expr $icursor -1]
  833.     }
  834.     }
  835. }
  836.  
  837. # ------------------------------------------------------------------
  838. # PROTECTED METHOD: _lastDay month year
  839. #
  840. # Internal method which determines the last day of the month for
  841. # the given month and year.  We start at 28 and go forward till
  842. # we fail.  Crude but effective.
  843. # ------------------------------------------------------------------
  844. body iwidgets::Datefield::_lastDay {month year} {
  845.     set lastone 28
  846.  
  847.     for {set lastone 28} {$lastone < 32} {incr lastone} {
  848.     if {[catch {clock scan $month/[expr $lastone + 1]/$year}] != 0} {
  849.         return $lastone
  850.     }
  851.     }
  852. }
  853.