| # |
| # Watch |
| # ---------------------------------------------------------------------- |
| # Implements a a clock widget in a canvas. |
| # |
| # ---------------------------------------------------------------------- |
| # AUTHOR: John A. Tucker EMAIL: jatucker@spd.dsccc.com |
| # |
| # ====================================================================== |
| # 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. |
| # ====================================================================== |
| |
| # |
| # Default resources. |
| # |
| option add *Watch.labelFont \ |
| -*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-* widgetDefault |
| |
| # |
| # Usual options. |
| # |
| itk::usual Watch { |
| keep -background -cursor -labelfont -foreground |
| } |
| |
| itcl::class iwidgets::Watch { |
| |
| inherit itk::Widget |
| |
| itk_option define -hourradius hourRadius Radius .50 |
| itk_option define -hourcolor hourColor Color red |
| |
| itk_option define -minuteradius minuteRadius Radius .80 |
| itk_option define -minutecolor minuteColor Color yellow |
| |
| itk_option define -pivotradius pivotRadius Radius .10 |
| itk_option define -pivotcolor pivotColor Color white |
| |
| itk_option define -secondradius secondRadius Radius .90 |
| itk_option define -secondcolor secondColor Color black |
| |
| itk_option define -clockcolor clockColor Color white |
| itk_option define -clockstipple clockStipple ClockStipple {} |
| |
| itk_option define -state state State normal |
| itk_option define -showampm showAmPm ShowAmPm true |
| |
| itk_option define -tickcolor tickColor Color black |
| |
| constructor {args} {} |
| destructor {} |
| |
| # |
| # Public methods |
| # |
| public { |
| method get {{format "-string"}} |
| method show {{time "now"}} |
| method watch {args} |
| } |
| |
| # |
| # Private methods |
| # |
| private { |
| method _handMotionCB {tag x y} |
| method _drawHand {tag} |
| method _handReleaseCB {tag x y} |
| method _displayClock {{when "later"}} |
| |
| variable _interior |
| variable _radius |
| variable _theta |
| variable _extent |
| variable _reposition "" ;# non-null => _displayClock pending |
| variable _timeVar |
| variable _x0 1 |
| variable _y0 1 |
| |
| common _ampmVar |
| common PI [expr {2*asin(1.0)}] |
| } |
| } |
| |
| # |
| # Provide a lowercased access method for the Watch class. |
| # |
| proc ::iwidgets::watch {pathName args} { |
| uplevel ::iwidgets::Watch $pathName $args |
| } |
| |
| # |
| # Use option database to override default resources of base classes. |
| # |
| option add *Watch.width 155 widgetDefault |
| option add *Watch.height 175 widgetDefault |
| |
| # ----------------------------------------------------------------------------- |
| # CONSTRUCTOR |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::constructor { args } { |
| # |
| # Add back to the hull width and height options and make the |
| # borderwidth zero since we don't need it. |
| # |
| set _interior $itk_interior |
| |
| itk_option add hull.width hull.height |
| component hull configure -borderwidth 0 |
| grid propagate $itk_component(hull) no |
| |
| set _ampmVar($this) "AM" |
| set _radius(outer) 1 |
| |
| set _radius(hour) 1 |
| set _radius(minute) 1 |
| set _radius(second) 1 |
| |
| set _theta(hour) 30 |
| set _theta(minute) 6 |
| set _theta(second) 6 |
| |
| set _extent(hour) 14 |
| set _extent(minute) 14 |
| set _extent(second) 2 |
| |
| set _timeVar(hour) 12 |
| set _timeVar(minute) 0 |
| set _timeVar(second) 0 |
| |
| # |
| # Create the frame in which the "AM" and "PM" radiobuttons will be drawn |
| # |
| itk_component add frame { |
| frame $itk_interior.frame |
| } |
| |
| # |
| # Create the canvas in which the clock will be drawn |
| # |
| itk_component add canvas { |
| canvas $itk_interior.canvas |
| } |
| bind $itk_component(canvas) <Map> +[itcl::code $this _displayClock] |
| bind $itk_component(canvas) <Configure> +[itcl::code $this _displayClock] |
| |
| # |
| # Create the "AM" and "PM" radiobuttons to be drawn in the canvas |
| # |
| itk_component add am { |
| radiobutton $itk_component(frame).am \ |
| -text "AM" \ |
| -value "AM" \ |
| -variable [itcl::scope _ampmVar($this)] |
| } { |
| usual |
| rename -font -labelfont labelFont Font |
| } |
| |
| itk_component add pm { |
| radiobutton $itk_component(frame).pm \ |
| -text "PM" \ |
| -value "PM" \ |
| -variable [itcl::scope _ampmVar($this)] |
| } { |
| usual |
| rename -font -labelfont labelFont Font |
| } |
| |
| # |
| # Create the canvas item for displaying the main oval which encapsulates |
| # the entire clock. |
| # |
| watch create oval 0 0 2 2 -width 5 -tags clock |
| |
| # |
| # Create the canvas items for displaying the 60 ticks marks around the |
| # inner perimeter of the watch. |
| # |
| set extent 3 |
| for {set i 0} {$i < 60} {incr i} { |
| set start [expr {$i*6-1}] |
| set tag [expr {[expr {$i%5}] == 0 ? "big" : "little"}] |
| watch create arc 0 0 0 0 \ |
| -style arc \ |
| -extent $extent \ |
| -start $start \ |
| -tags "tick$i tick $tag" |
| } |
| |
| # |
| # Create the canvas items for displaying the hour, minute, and second hands |
| # of the watch. Add bindings to allow the mouse to move and set the |
| # clock hands. |
| # |
| watch create arc 1 1 1 1 -extent 30 -tags minute |
| watch create arc 1 1 1 1 -extent 30 -tags hour |
| watch create arc 1 1 1 1 -tags second |
| |
| # |
| # Create the canvas item for displaying the center of the watch in which |
| # the hour, minute, and second hands will pivot. |
| # |
| watch create oval 0 0 1 1 -width 5 -fill black -tags pivot |
| |
| # |
| # Position the "AM/PM" button frame and watch canvas. |
| # |
| grid $itk_component(frame) -row 0 -column 0 -sticky new |
| grid $itk_component(canvas) -row 1 -column 0 -sticky nsew |
| |
| grid rowconfigure $itk_interior 0 -weight 0 |
| grid rowconfigure $itk_interior 1 -weight 1 |
| grid columnconfigure $itk_interior 0 -weight 1 |
| |
| eval itk_initialize $args |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # DESTURCTOR |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::destructor {} { |
| if {$_reposition != ""} { |
| after cancel $_reposition |
| } |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # METHODS |
| # ----------------------------------------------------------------------------- |
| |
| # ----------------------------------------------------------------------------- |
| # METHOD: _handReleaseCB tag x y |
| # |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::_handReleaseCB {tag x y} { |
| |
| set atanab [expr {atan2(double($y-$_y0),double($x-$_x0))*(180/$PI)}] |
| set degrees [expr {$atanab > 0 ? [expr {360-$atanab}] : abs($atanab)}] |
| set ticks [expr {round($degrees/$_theta($tag))}] |
| set _timeVar($tag) [expr {((450-$ticks*$_theta($tag))%360)/$_theta($tag)}] |
| |
| if {$tag == "hour" && $_timeVar(hour) == 0} { |
| set _timeVar($tag) 12 |
| } |
| |
| _drawHand $tag |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # PROTECTED METHOD: _handMotionCB tag x y |
| # |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::_handMotionCB {tag x y} { |
| if {$x == $_x0 || $y == $_y0} { |
| return |
| } |
| |
| set a [expr {$y-$_y0}] |
| set b [expr {$x-$_x0}] |
| set c [expr {hypot($a,$b)}] |
| |
| set atanab [expr {atan2(double($a),double($b))*(180/$PI)}] |
| set degrees [expr {$atanab > 0 ? [expr 360-$atanab] : abs($atanab)}] |
| |
| set x2 [expr {$_x0+$_radius($tag)*($b/double($c))}] |
| set y2 [expr {$_y0+$_radius($tag)*($a/double($c))}] |
| watch coords $tag \ |
| [expr {$x2-$_radius($tag)}] \ |
| [expr {$y2-$_radius($tag)}] \ |
| [expr {$x2+$_radius($tag)}] \ |
| [expr {$y2+$_radius($tag)}] |
| set start [expr {$degrees-180-($_extent($tag)/2)}] |
| watch itemconfigure $tag -start $start -extent $_extent($tag) |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # PROTECTED METHOD: get ?format? |
| # |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::get {{format "-string"}} { |
| set timestr [format "%02d:%02d:%02d %s" \ |
| $_timeVar(hour) $_timeVar(minute) \ |
| $_timeVar(second) $_ampmVar($this)] |
| |
| switch -- $format { |
| "-string" { |
| return $timestr |
| } |
| "-clicks" { |
| return [clock scan $timestr] |
| } |
| default { |
| error "bad format option \"$format\":\ |
| should be -string or -clicks" |
| } |
| } |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # METHOD: watch ?args? |
| # |
| # Evaluates the specified args against the canvas component. |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::watch {args} { |
| return [eval $itk_component(canvas) $args] |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # METHOD: _drawHand tag |
| # |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::_drawHand {tag} { |
| |
| set degrees [expr {abs(450-($_timeVar($tag)*$_theta($tag)))%360}] |
| set radians [expr {$degrees*($PI/180)}] |
| set x [expr {$_x0+$_radius($tag)*cos($radians)}] |
| set y [expr {$_y0+$_radius($tag)*sin($radians)*(-1)}] |
| watch coords $tag \ |
| [expr {$x-$_radius($tag)}] \ |
| [expr {$y-$_radius($tag)}] \ |
| [expr {$x+$_radius($tag)}] \ |
| [expr {$y+$_radius($tag)}] |
| set start [expr {$degrees-180-($_extent($tag)/2)}] |
| watch itemconfigure $tag -start $start |
| } |
| |
| # ------------------------------------------------------------------ |
| # 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. |
| # ------------------------------------------------------------------ |
| itcl::body iwidgets::Watch::show {{time "now"}} { |
| if {$time == "now"} { |
| set seconds [clock seconds] |
| } elseif {![catch {clock format $time}]} { |
| set seconds $time |
| } elseif {[catch {set seconds [clock scan $time]}]} { |
| error "bad time: \"$time\", must be a valid time\ |
| string, clock clicks value or the keyword now" |
| } |
| |
| set timestring [clock format $seconds -format "%I %M %S %p"] |
| set _timeVar(hour) [expr int(1[lindex $timestring 0] - 100)] |
| set _timeVar(minute) [expr int(1[lindex $timestring 1] - 100)] |
| set _timeVar(second) [expr int(1[lindex $timestring 2] - 100)] |
| set _ampmVar($this) [lindex $timestring 3] |
| |
| _drawHand hour |
| _drawHand minute |
| _drawHand second |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # PROTECTED METHOD: _displayClock ?when? |
| # |
| # Places the hour, minute, and second dials in the canvas. If "when" is "now", |
| # the change is applied immediately. If it is "later" or it is not specified, |
| # then the change is applied later, when the application is idle. |
| # ----------------------------------------------------------------------------- |
| itcl::body iwidgets::Watch::_displayClock {{when "later"}} { |
| |
| if {$when == "later"} { |
| if {$_reposition == ""} { |
| set _reposition [after idle [itcl::code $this _displayClock now]] |
| } |
| return |
| } |
| |
| # |
| # Compute the center coordinates for the clock based on the |
| # with and height of the canvas. |
| # |
| set width [winfo width $itk_component(canvas)] |
| set height [winfo height $itk_component(canvas)] |
| set _x0 [expr {$width/2}] |
| set _y0 [expr {$height/2}] |
| |
| # |
| # Set the radius of the watch, pivot, hour, minute and second items. |
| # |
| set _radius(outer) [expr {$_x0 < $_y0 ? $_x0 : $_y0}] |
| set _radius(pivot) [expr {$itk_option(-pivotradius)*$_radius(outer)}] |
| set _radius(hour) [expr {$itk_option(-hourradius)*$_radius(outer)}] |
| set _radius(minute) [expr {$itk_option(-minuteradius)*$_radius(outer)}] |
| set _radius(second) [expr {$itk_option(-secondradius)*$_radius(outer)}] |
| set outerWidth [watch itemcget clock -width] |
| |
| # |
| # Set the coordinates of the clock item |
| # |
| set x1Outer $outerWidth |
| set y1Outer $outerWidth |
| set x2Outer [expr {$width-$outerWidth}] |
| set y2Outer [expr {$height-$outerWidth}] |
| watch coords clock $x1Outer $y1Outer $x2Outer $y2Outer |
| |
| # |
| # Set the coordinates of the tick items |
| # |
| set offset [expr {$outerWidth*2}] |
| set x1Tick [expr {$x1Outer+$offset}] |
| set y1Tick [expr {$y1Outer+$offset}] |
| set x2Tick [expr {$x2Outer-$offset}] |
| set y2Tick [expr {$y2Outer-$offset}] |
| for {set i 0} {$i < 60} {incr i} { |
| watch coords tick$i $x1Tick $y1Tick $x2Tick $y2Tick |
| } |
| set maxTickWidth [expr {$_radius(outer)-$_radius(second)+1}] |
| set minTickWidth [expr {round($maxTickWidth/2)}] |
| watch itemconfigure big -width $maxTickWidth |
| watch itemconfigure little -width [expr {round($maxTickWidth/2)}] |
| |
| # |
| # Set the coordinates of the pivot item |
| # |
| set x1Center [expr {$_x0-$_radius(pivot)}] |
| set y1Center [expr {$_y0-$_radius(pivot)}] |
| set x2Center [expr {$_x0+$_radius(pivot)}] |
| set y2Center [expr {$_y0+$_radius(pivot)}] |
| watch coords pivot $x1Center $y1Center $x2Center $y2Center |
| |
| # |
| # Set the coordinates of the hour, minute, and second dial items |
| # |
| watch itemconfigure hour -extent $_extent(hour) |
| _drawHand hour |
| |
| watch itemconfigure minute -extent $_extent(minute) |
| _drawHand minute |
| |
| watch itemconfigure second -extent $_extent(second) |
| _drawHand second |
| |
| set _reposition "" |
| } |
| |
| # ----------------------------------------------------------------------------- |
| # OPTIONS |
| # ----------------------------------------------------------------------------- |
| |
| # ------------------------------------------------------------------ |
| # OPTION: state |
| # |
| # Configure the editable state of the widget. Valid values are |
| # normal and disabled. In a disabled state, the hands of the |
| # watch are not selectabled. |
| # ------------------------------------------------------------------ |
| itcl::configbody ::iwidgets::Watch::state { |
| if {$itk_option(-state) == "normal"} { |
| watch bind minute <B1-Motion> \ |
| [itcl::code $this _handMotionCB minute %x %y] |
| watch bind minute <ButtonRelease-1> \ |
| [itcl::code $this _handReleaseCB minute %x %y] |
| |
| watch bind hour <B1-Motion> \ |
| [itcl::code $this _handMotionCB hour %x %y] |
| watch bind hour <ButtonRelease-1> \ |
| [itcl::code $this _handReleaseCB hour %x %y] |
| |
| watch bind second <B1-Motion> \ |
| [itcl::code $this _handMotionCB second %x %y] |
| watch bind second <ButtonRelease-1> \ |
| [itcl::code $this _handReleaseCB second %x %y] |
| |
| $itk_component(am) configure -state normal |
| $itk_component(pm) configure -state normal |
| |
| } elseif {$itk_option(-state) == "disabled"} { |
| watch bind minute <B1-Motion> {} |
| watch bind minute <ButtonRelease-1> {} |
| |
| watch bind hour <B1-Motion> {} |
| watch bind hour <ButtonRelease-1> {} |
| |
| watch bind second <B1-Motion> {} |
| watch bind second <ButtonRelease-1> {} |
| |
| $itk_component(am) configure -state disabled \ |
| -disabledforeground [$itk_component(am) cget -background] |
| $itk_component(pm) configure -state normal \ |
| -disabledforeground [$itk_component(am) cget -background] |
| |
| } else { |
| error "bad state option \"$itk_option(-state)\":\ |
| should be normal or disabled" |
| } |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: showampm |
| # |
| # Configure the display of the AM/PM radio buttons. |
| # ------------------------------------------------------------------ |
| itcl::configbody ::iwidgets::Watch::showampm { |
| switch -- $itk_option(-showampm) { |
| 0 - no - false - off { |
| pack forget $itk_component(am) |
| pack forget $itk_component(pm) |
| } |
| |
| 1 - yes - true - on { |
| pack $itk_component(am) -side left -fill both -expand 1 |
| pack $itk_component(pm) -side right -fill both -expand 1 |
| } |
| |
| default { |
| error "bad showampm option \"$itk_option(-showampm)\":\ |
| should be boolean" |
| } |
| } |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: pivotcolor |
| # |
| # Configure the color of the clock pivot. |
| # |
| itcl::configbody ::iwidgets::Watch::pivotcolor { |
| watch itemconfigure pivot -fill $itk_option(-pivotcolor) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: clockstipple |
| # |
| # Configure the stipple pattern for the clock fill color. |
| # |
| itcl::configbody ::iwidgets::Watch::clockstipple { |
| watch itemconfigure clock -stipple $itk_option(-clockstipple) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: clockcolor |
| # |
| # Configure the color of the clock. |
| # |
| itcl::configbody ::iwidgets::Watch::clockcolor { |
| watch itemconfigure clock -fill $itk_option(-clockcolor) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: hourcolor |
| # |
| # Configure the color of the hour hand. |
| # |
| itcl::configbody ::iwidgets::Watch::hourcolor { |
| watch itemconfigure hour -fill $itk_option(-hourcolor) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: minutecolor |
| # |
| # Configure the color of the minute hand. |
| # |
| itcl::configbody ::iwidgets::Watch::minutecolor { |
| watch itemconfigure minute -fill $itk_option(-minutecolor) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: secondcolor |
| # |
| # Configure the color of the second hand. |
| # |
| itcl::configbody ::iwidgets::Watch::secondcolor { |
| watch itemconfigure second -fill $itk_option(-secondcolor) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: tickcolor |
| # |
| # Configure the color of the ticks. |
| # |
| itcl::configbody ::iwidgets::Watch::tickcolor { |
| watch itemconfigure tick -outline $itk_option(-tickcolor) |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: hourradius |
| # |
| # Configure the radius of the hour hand. |
| # |
| itcl::configbody ::iwidgets::Watch::hourradius { |
| _displayClock |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: minuteradius |
| # |
| # Configure the radius of the minute hand. |
| # |
| itcl::configbody ::iwidgets::Watch::minuteradius { |
| _displayClock |
| } |
| |
| # ------------------------------------------------------------------ |
| # OPTION: secondradius |
| # |
| # Configure the radius of the second hand. |
| # |
| itcl::configbody ::iwidgets::Watch::secondradius { |
| _displayClock |
| } |
| |