Support for an IE-like panning tool.
package provide pan 0.3
namespace eval Pan {
namespace export -clear pan
bind pan_widget <ButtonPress-2> \
[namespace code { pan start %W %X %Y; break }]
bind pan_widget <ButtonRelease-2> \
[namespace code { pan stop %W; break }]
bind pan_widget <B2-Motion> \
[namespace code { pan move %W %X %Y; break }]
variable cursor
array set cursor {
++ bottom_right_corner
+= bottom_side
+- bottom_left_corner
=+ right_side
== fleur
=- left_side
-+ top_right_corner
-= top_side
-- top_left_corner
}
proc graph_xview { w scroll n units } {
foreach slimit {min max} limit [$w xaxis limits] {
set $slimit [$w xaxis transform $limit]
}
if {$max < $min} {
foreach {min max} [list $max $min] break
}
set step [expr {$n*4}]
foreach limit {min max} value [list $min $max] {
set $limit [expr {int($value + $step)}]
}
foreach side { xaxis x2axis } {
foreach axis [$w $side use] {
set omin [$w axis cget $axis -min]
set omax [$w axis cget $axis -max]
if { "$omin" eq "" || "$omax" eq "" } break
set nmin [$w axis invtransform $axis $min]
set nmax [$w axis invtransform $axis $max]
if {$nmax < $nmin} {
foreach {nmin nmax} [list $nmax $nmin] break
}
$w axis configure $axis -min $nmin -max $nmax
}
}
}
proc graph_yview { w scroll n units } {
foreach slimit {min max} limit [$w yaxis limits] {
set $slimit [$w yaxis transform $limit]
}
if {$max < $min} {
foreach {min max} [list $max $min] break
}
set step [expr {$n*4}]
foreach limit {min max} value [list $min $max] {
set $limit [expr {int($value + $step)}]
}
foreach side { yaxis y2axis } {
foreach axis [$w $side use] {
set omin [$w axis cget $axis -min]
set omax [$w axis cget $axis -max]
if { "$omin" eq "" || "$omax" eq "" } break
set nmin [$w axis invtransform $axis $min]
set nmax [$w axis invtransform $axis $max]
if {$nmax < $nmin} {
foreach {nmin nmax} [list $nmax $nmin] break
}
$w axis configure $axis -min $nmin -max $nmax
}
}
}
proc dir { value } {
if { $value > 0 } {
return +
} elseif { $value < 0 } {
return -
} else {
return =
}
}
proc pan { action { w {} } { x {} } { y {} } } {
variable timeout
variable rate
variable accel
variable cursor
variable pan
switch $action {
init { # initialize the pan icon (only called once)
toplevel .pan -class Pan
wm overrideredirect .pan 1
wm withdraw .pan
.pan configure -cursor $cursor(==)
option add *Pan.Label.Background yellow widgetDefault
option add *Pan.Label.Relief raised widgetDefault
pack [label .pan.label -text Pan]
foreach {var val} { rate 200 accel 20 timeout 10000 } {
if {![info exists $var]} {
set tvar [string totitle $var]
option add *Pan.$tvar $val widgetDefault
set $var [option get .pan $var $tvar]
}
}
option add *Axis.ScrollIncrement 1 widgetDefault
}
bind { # bind panning to a widget
bindtags $w [concat pan_widget [bindtags $w]]
}
start { # start panning
if { [info exists pan($w,x)] } { return }
if { [llength $x] == 0 } {
foreach { x y } [winfo pointerxy .] break
}
set pan($w,x) $x
set pan($w,y) $y
set pan($w,v) 0
set pan($w,h) 0
set pan($w,cursor) [$w cget -cursor]
set pan($w,focus) [focus]
$w configure -cursor $cursor(==)
.pan configure -cursor $cursor(==)
set xpos [expr {$x-[winfo width .pan]/2}]
set ypos [expr {$y-[winfo height .pan]/2}]
wm geometry .pan +$xpos+$ypos
wm deiconify .pan
raise .pan
bind .pan <Motion> [namespace code [list pan move $w %X %Y]]
bind .pan <ButtonPress> [list array set [namespace which -variable pan] [list $w,motion 1]]
bind .pan <ButtonRelease> [namespace code [list pan stop $w]]
grab set .pan
after 0 [namespace code [list pan step $w]]
after $timeout [namespace code [list pan cancel $w]]
}
move { # mouse motion
if { ![info exists pan($w,x)] } { return }
set v [expr {$y - $pan($w,y)}]
set h [expr {$x - $pan($w,x)}]
set pan($w,v) [expr {$v/$accel}]
set pan($w,h) [expr {$h/$accel}]
if {$v < 0} {incr pan($w,v)}
if {$h < 0} {incr pan($w,h)}
$w configure -cursor $cursor([dir $pan($w,v)][dir $pan($w,h)])
.pan configure -cursor $cursor([dir $pan($w,v)][dir $pan($w,h)])
set pan($w,motion) 1
after cancel [namespace code [list pan cancel $w]]
after $timeout [namespace code [list pan cancel $w]]
}
step { # do the panning
if { ![info exists pan($w,x)] } { return }
if { [winfo class $w] == "Graph" } {
graph_xview $w scroll $pan($w,h) units
graph_yview $w scroll $pan($w,v) units
} else {
$w xview scroll $pan($w,h) units
$w yview scroll $pan($w,v) units
}
after $rate [namespace code [list pan step $w]]
}
stop { # button release
if { [info exists pan($w,motion)] } { pan cancel $w }
}
cancel { # cancel panning for whatever reason
if { ![info exists pan($w,x)] } { return }
grab release .pan
wm withdraw .pan
$w configure -cursor $pan($w,cursor)
focus $pan($w,focus)
foreach el [array names pan "$w,*"] { unset pan($el) }
after cancel [namespace code [list pan step $w]]
after cancel [namespace code [list pan cancel $w]]
}
}
}
catch { pan init }
}
namespace eval :: {namespace import -force ::Pan::pan}
if {[info exists argv0] && [file tail [info script]]==[file tail $argv0]} {
catch {
package require BLT
blt::graph .g
.g elem create x -xdata { 1 1.2 1.4 1.6 1.8 1.9 2 3 4 5 } \
-ydata { 2 1.8 1.7 1.5 1.3 1.1 1 3 1 2 }
Blt_ZoomStack .g
pan bind .g
grid .g - -sticky news
}
text .t -width 10 -height 5 -wrap no \
-xscrollcommand { .h set } -yscrollcommand { .v set }
scrollbar .h -orient h -command { .t xview }
scrollbar .v -orient v -command { .t yview }
.t insert end "1 This is a bunch of text which I am using to test the panning capabilities\n2 of the text widget.\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22 end of text ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- really!"
pan bind .t
grid .t x -sticky news
grid .h x -sticky ew
grid .v -row 1 -column 1 -sticky ns
grid columnconfigure . 0 -weight 1
}