proc Ticker:create {w args} { global Ticker eval canvas $w -bg black $args set Ticker($w:add_space) 1 set Ticker($w:data) "" set Ticker($w:item_list) "" set Ticker($w:single_data) "" set Ticker($w:ypos) [expr [$w cget -width] / 2] ;# these defaults are needed; make sure that all are specified Ticker:configure $w -speed 200 -quality 5 -direction 0 \ -defaultfont helvetica -defaultsize 12 \ -background black -foreground white } proc Ticker:run {w} { global Ticker ;# move all current items left if {$Ticker($w:direction) != 0} { set offset [expr -1 * $Ticker($w:scroll_quality)] } else { set offset $Ticker($w:scroll_quality) } set new_list "" foreach item_id $Ticker($w:item_list) { incr Ticker($w:item_${item_id}:x) $offset $w coords $item_id $Ticker($w:item_${item_id}:x) $Ticker($w:ypos) ;# check if item is off the screen; if so, delete it if {[expr $Ticker($w:direction) != 0 && \ [expr $Ticker($w:item_${item_id}:x) + \ $Ticker($w:item_${item_id}:width)] < -30] || \ [expr $Ticker($w:direction) == 0 && \ [expr $Ticker($w:item_${item_id}:x) - 30] > \ [winfo width $w]]} { $w delete $item_id array unset Ticker $w:item_${item_id}:* } else { ;# otherwise adjust its position on the screen lappend new_list $item_id } } set Ticker($w:item_list) $new_list ;# check if need to add a new item to the display if {[Ticker:check_space $w $Ticker($w:direction)]} { ;# figure out which datum to add to the display set datum "" set repeat 0 ;# pop off the next datum from the list if {[llength $Ticker($w:single_data)] > 0} { set datum [lindex $Ticker($w:single_data) 0] set Ticker($w:single_data) [lrange $Ticker($w:single_data) 1 end] } elseif {[llength $Ticker($w:data)] > 0} { set datum [lindex $Ticker($w:data) 0] set Ticker($w:data) [lrange $Ticker($w:data) 1 end] lappend Ticker($w:data) $datum } Ticker:add_item $w $Ticker($w:direction) $datum } after $Ticker($w:scroll_speed) [list Ticker:run $w] } proc Ticker:advance {w amount} { global Ticker foreach item_id $Ticker($w:item_list) { incr Ticker($w:item_${item_id}:x) $amount } ;# if advancing in the opposite direction that the ticker is ;# scrolling, check if need to add a new item # if {[expr $Ticker($w:direction) != 0 && \ # $amount > 0 && [Ticker:check_space $w 0]] || \ # [expr $Ticker($w:direction) == 0 && \ # $amount < 0 && [Ticker:check_space $w 1]]} { # ;# add the last repeating item from the list # set datum "" # if {[llength $Ticker($w:data)] > 0} { # set datum [lindex $Ticker($w:data) end] # set old_data [lrange $Ticker($w:data) 0 end-1] # set Ticker($w:data) [concat [list $datum] $old_data] # } # Ticker:add_item $w [expr ! $Ticker($w:direction)] $datum # } } proc Ticker:pause {w} { after cancel Ticker:run $w } proc Ticker:set_height {w} { global Ticker set Ticker($w:ypos) [expr [winfo height $w] / 2] foreach item_key [array names Ticker $w:item*:id] { set item_id $Ticker($item_key) $w coords $item_id $Ticker($w:item_${item_id}:x) $Ticker($w:ypos) } } proc Ticker:configure {w args} { global Ticker for {set i 0} {$i < [llength $args]} {incr i 2} { set option [lindex $args $i] set value [lindex $args [expr $i + 1]] switch -- $option { -background { set Ticker($w:bg) $value $w configure -background $Ticker($w:bg) } -foreground { set Ticker($w:fg) $value } -defaultfont { set Ticker($w:font) $value } -defaultsize { set Ticker($w:size) $value } -direction { set Ticker($w:direction) $value } -quality { set Ticker($w:scroll_quality) $value } -speed { set Ticker($w:scroll_speed) $value } default { error "Invalid option:" $option } } } if {$i != [llength $args]} { error "Not enough arguments" } Ticker:add_font_tag $w default_font $Ticker($w:font) $Ticker($w:size) \ $Ticker($w:fg) Ticker:add_font_tag $w default_space $Ticker($w:font) $Ticker($w:size) \ $Ticker($w:bg) } proc Ticker:add_single_datum {w text tag} { global Ticker lappend Ticker($w:single_data) [list $text $tag] } proc Ticker:add_repeat_datum {w text tag} { global Ticker lappend Ticker($w:data) [list $text $tag] } proc Ticker:current_datum {w} { global Ticker set id [$w find withtag current] if {![string equal $id ""]} { return [list $Ticker($w:item_${id}:text) $Ticker($w:item_${id}:tag)] } else { return "" } } proc Ticker:clear_display {w} { global Ticker foreach item_id $Ticker($w:item_list) { $w delete $item_id array unset Ticker $w:item_${item_id}:* } set Ticker($w:item_list) "" } proc Ticker:clear_repeat_data {w} { global Ticker set Ticker($w:data) "" } proc Ticker:add_font_tag {w tag_name font size color args} { global Ticker ;# if the tag already exists, delete it first if {[info exists Ticker($w:tag:$tag_name)]} { font delete [lindex $Ticker($w:tag:$tag_name) 0] } set font_id [eval font create -family $font -size $size $args] set Ticker($w:tag:$tag_name) [list $font_id $color] ;# now scan through all current items being displayed; alter their ;# appearance as needed set xpos "" foreach item_id $Ticker($w:item_list) { if {[string equal $Ticker($w:item_${item_id}:tag) $tag_name]} { Ticker:update_text_item $w $item_id set Ticker($w:item_${item_id}:width) \ [font measure $font_id -displayof $w \ $Ticker($w:item_${item_id}:text)] } if {[string equal $xpos ""]} { set xpos $Ticker($w:item_${item_id}:x) } else { set Ticker($w:item_${item_id}:x) $xpos } incr xpos $Ticker($w:item_${item_id}:width) } } ;####################################################################### ;# private functions below proc Ticker:check_space {w side} { global Ticker ;# checks if there's space along the right edge ($side != 0) or ;# left edge ($side == 0); add the new item before actually needed if {[llength $Ticker($w:item_list)] == 0} { return 1 } elseif {$side != 0} { set id [lindex $Ticker($w:item_list) end] if {[expr $Ticker($w:item_${id}:x) + \ $Ticker($w:item_${id}:width) - 30] < [winfo width $w]} { return 1 } } elseif {$side == 0} { set id [lindex $Ticker($w:item_list) 0] if {[expr $Ticker($w:item_${id}:x) > -30]} { return 1 } } return 0 } proc Ticker:add_item {w side datum} { global Ticker if {[llength $datum] == 0} { Ticker:add_text_item $w $side "(nothing to display)" "default_font" } else { ;# for now, only allow text-based data Ticker:add_text_item $w $side [lindex $datum 0] [lindex $datum 1] } if {$Ticker($w:add_space)} { Ticker:add_text_item $w $side " " "default_space" } } proc Ticker:add_text_item {w side text tag} { global Ticker if {![info exists Ticker($w:tag:$tag)]} { set tag "default_font" } set font [lindex $Ticker($w:tag:$tag) 0] set color [lindex $Ticker($w:tag:$tag) 1] set new_id [$w create text 0 0 \ -text $text -anchor w -font $font -fill $color] set Ticker($w:item_${new_id}:tag) $tag set Ticker($w:item_${new_id}:text) $text set Ticker($w:item_${new_id}:width) \ [font measure $font -displayof $w $text] ;# figure out where to place it if {[llength $Ticker($w:item_list)] == 0} { set xpos [expr [winfo width $w] / 2] set Ticker($w:item_list) $new_id } else { if {$side != 0} { set id [lindex $Ticker($w:item_list) end] set xpos [expr $Ticker($w:item_${id}:x) + \ $Ticker($w:item_${id}:width)] lappend Ticker($w:item_list) $new_id } else { set id [lindex $Ticker($w:item_list) 0] set xpos [expr $Ticker($w:item_${id}:x) - \ $Ticker($w:item_${new_id}:width)] set Ticker($w:item_list) [concat $new_id $Ticker($w:item_list)] } } set Ticker($w:item_${new_id}:x) $xpos $w coords $new_id $xpos $Ticker($w:ypos) } proc Ticker:update_text_item {w item_id} { global Ticker set tag $Ticker($w:item_${item_id}:tag) set font [lindex $Ticker($w:tag:$tag) 0] set color [lindex $Ticker($w:tag:$tag) 1] $w itemconfigure $item_id -font $font -fill $color }