Spaces:
Running
Running
# -*- mode: TCL; fill-column: 75; tab-width: 8; coding: iso-latin-1-unix -*- | |
# | |
# $Id: Balloon.tcl,v 1.7 2008/02/27 22:17:28 hobbs Exp $ | |
# | |
# Balloon.tcl -- | |
# | |
# The help widget. It provides both "balloon" type of help | |
# message and "status bar" type of help message. You can use | |
# this widget to indicate the function of the widgets inside | |
# your application. | |
# | |
# Copyright (c) 1993-1999 Ioi Kim Lam. | |
# Copyright (c) 2000-2001 Tix Project Group. | |
# Copyright (c) 2004 ActiveState | |
# | |
# See the file "license.terms" for information on usage and redistribution | |
# of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
# | |
tixWidgetClass tixBalloon { | |
-classname TixBalloon | |
-superclass tixShell | |
-method { | |
bind post unbind | |
} | |
-flag { | |
-installcolormap -initwait -state -statusbar -cursor | |
} | |
-configspec { | |
{-installcolormap installColormap InstallColormap false} | |
{-initwait initWait InitWait 1000} | |
{-state state State both} | |
{-statusbar statusBar StatusBar ""} | |
{-cursor cursor Cursor {}} | |
} | |
-default { | |
{*background #ffff60} | |
{*foreground black} | |
{*borderWidth 0} | |
{.borderWidth 1} | |
{.background black} | |
{*Label.anchor w} | |
{*Label.justify left} | |
} | |
} | |
# static seem to be -installcolormap -initwait -statusbar -cursor | |
# Class Record | |
# | |
global tixBalloon | |
set tixBalloon(bals) "" | |
proc tixBalloon:InitWidgetRec {w} { | |
upvar #0 $w data | |
global tixBalloon | |
tixChainMethod $w InitWidgetRec | |
set data(isActive) 0 | |
set data(client) "" | |
lappend tixBalloon(bals) $w | |
} | |
proc tixBalloon:ConstructWidget {w} { | |
upvar #0 $w data | |
tixChainMethod $w ConstructWidget | |
if {[tk windowingsystem] eq "aqua"} { | |
::tk::unsupported::MacWindowStyle style $w help none | |
} else { | |
wm overrideredirect $w 1 | |
} | |
catch {wm attributes $w -topmost 1} | |
wm positionfrom $w program | |
wm withdraw $w | |
# Frame 1 : arrow | |
frame $w.f1 -bd 0 | |
set data(w:label) [label $w.f1.lab -bd 0 -relief flat \ | |
-bitmap [tix getbitmap balarrow]] | |
pack $data(w:label) -side left -padx 1 -pady 1 | |
# Frame 2 : Message | |
frame $w.f2 -bd 0 | |
set data(w:message) [label $w.f2.message -padx 0 -pady 0 -bd 0] | |
pack $data(w:message) -side left -expand yes -fill both -padx 10 -pady 1 | |
# Pack all | |
pack $w.f1 -fill both | |
pack $w.f2 -fill both | |
# This is an event tag used by the clients | |
# | |
bind TixBal$w <Destroy> [list tixBalloon:ClientDestroy $w %W] | |
} | |
proc tixBalloon:Destructor {w} { | |
global tixBalloon | |
set bals "" | |
foreach b $tixBalloon(bals) { | |
if {$w != $b} { | |
lappend bals $b | |
} | |
} | |
set tixBalloon(bals) $bals | |
tixChainMethod $w Destructor | |
} | |
#---------------------------------------------------------------------- | |
# Config: | |
#---------------------------------------------------------------------- | |
proc tixBalloon:config-state {w value} { | |
upvar #0 $w data | |
set re {^(none|balloon|status|both)$} | |
if {![regexp -- $re $value]} { | |
error "invalid value $value, must be none, balloon, status, or both" | |
} | |
} | |
#---------------------------------------------------------------------- | |
# "RAW" event bindings: | |
#---------------------------------------------------------------------- | |
bind all <B1-Motion> "+tixBalloon_XXMotion %X %Y 1" | |
bind all <B2-Motion> "+tixBalloon_XXMotion %X %Y 2" | |
bind all <B3-Motion> "+tixBalloon_XXMotion %X %Y 3" | |
bind all <B4-Motion> "+tixBalloon_XXMotion %X %Y 4" | |
bind all <B5-Motion> "+tixBalloon_XXMotion %X %Y 5" | |
bind all <Any-Motion> "+tixBalloon_XXMotion %X %Y 0" | |
# Should %b be 0? %b is illegal | |
bind all <Leave> "+tixBalloon_XXMotion %X %Y 0" | |
bind all <Button> "+tixBalloon_XXButton %X %Y %b" | |
bind all <ButtonRelease> "+tixBalloon_XXButtonUp %X %Y %b" | |
proc tixBalloon_XXMotion {rootX rootY b} { | |
global tixBalloon | |
foreach w $tixBalloon(bals) { | |
tixBalloon:XXMotion $w $rootX $rootY $b | |
} | |
} | |
proc tixBalloon_XXButton {rootX rootY b} { | |
global tixBalloon | |
foreach w $tixBalloon(bals) { | |
tixBalloon:XXButton $w $rootX $rootY $b | |
} | |
} | |
proc tixBalloon_XXButtonUp {rootX rootY b} { | |
global tixBalloon | |
foreach w $tixBalloon(bals) { | |
tixBalloon:XXButtonUp $w $rootX $rootY $b | |
} | |
} | |
# return true if d is a descendant of w | |
# | |
proc tixIsDescendant {w d} { | |
return [expr {$w eq "." || [string match $w.* $d]}] | |
} | |
# All the button events are fine if the ballooned widget is | |
# a descendant of the grabbing widget | |
# | |
proc tixBalloon:GrabBad {w cw} { | |
global tixBalloon | |
set g [grab current $w] | |
if {$g == ""} { | |
return 0 | |
} | |
if {[info exists tixBalloon(g_ignore,$g)]} { | |
return 1 | |
} | |
if {[info exists tixBalloon(g_ignore,[winfo class $g])]} { | |
return 1 | |
} | |
if {$g == $cw || [tixIsDescendant $g $cw]} { | |
return 0 | |
} | |
return 1 | |
} | |
proc tixBalloon:XXMotion {w rootX rootY b} { | |
upvar #0 $w data | |
if {![info exists data(-state)]} { | |
# puts "tixBalloon:XXMotion called without a state\n$w" | |
set data(state) none | |
return | |
} | |
if {$data(-state) eq "none"} { | |
return | |
} | |
if {$b == 0} { | |
if {[info exists data(b:1)]} {unset data(b:1)} | |
if {[info exists data(b:2)]} {unset data(b:2)} | |
if {[info exists data(b:3)]} {unset data(b:3)} | |
if {[info exists data(b:4)]} {unset data(b:4)} | |
if {[info exists data(b:5)]} {unset data(b:5)} | |
} | |
if {[llength [array names data b:*]]} { | |
# Some buttons are down. Do nothing | |
# | |
return | |
} | |
set cw [winfo containing -displayof $w $rootX $rootY] | |
if {[tixBalloon:GrabBad $w $cw]} { | |
return | |
} | |
# Find the a client window that matches | |
# | |
if {$w eq $cw || [string match $w.* $cw]} { | |
# Cursor moved over the balloon -- Ignore | |
return | |
} | |
while {$cw != ""} { | |
if {[info exists data(m:$cw)]} { | |
set client $cw | |
break | |
} else { | |
set cw [winfo parent $cw] | |
} | |
} | |
if {![info exists client]} { | |
# The cursor is at a position covered by a non-client | |
# Popdown the balloon if it is up | |
if {$data(isActive)} { | |
tixBalloon:Deactivate $w | |
} | |
set data(client) "" | |
if {[info exists data(cancel)]} { | |
unset data(cancel) | |
} | |
return | |
} | |
if {$data(client) ne $client} { | |
if {$data(isActive)} { | |
tixBalloon:Deactivate $w | |
} | |
set data(client) $client | |
after $data(-initwait) tixBalloon:SwitchToClient $w $client | |
} | |
} | |
proc tixBalloon:XXButton {w rootX rootY b} { | |
upvar #0 $w data | |
tixBalloon:XXMotion $w $rootX $rootY $b | |
set data(b:$b) 1 | |
if {$data(isActive)} { | |
tixBalloon:Deactivate $w | |
} else { | |
set data(cancel) 1 | |
} | |
} | |
proc tixBalloon:XXButtonUp {w rootX rootY b} { | |
upvar #0 $w data | |
tixBalloon:XXMotion $w $rootX $rootY $b | |
if {[info exists data(b:$b)]} { | |
unset data(b:$b) | |
} | |
} | |
#---------------------------------------------------------------------- | |
# "COOKED" event bindings: | |
#---------------------------------------------------------------------- | |
# switch the balloon to a new client | |
# | |
proc tixBalloon:SwitchToClient {w client} { | |
upvar #0 $w data | |
if {![winfo exists $w]} { | |
return | |
} | |
if {![winfo exists $client]} { | |
return | |
} | |
if {$client ne $data(client)} { | |
return | |
} | |
if {[info exists data(cancel)]} { | |
unset data(cancel) | |
return | |
} | |
if {[tixBalloon:GrabBad $w $w]} { | |
return | |
} | |
tixBalloon:Activate $w | |
} | |
proc tixBalloon:ClientDestroy {w client} { | |
if {![winfo exists $w]} { | |
return | |
} | |
upvar #0 $w data | |
if {$data(client) eq $client} { | |
tixBalloon:Deactivate $w | |
set data(client) "" | |
} | |
# Maybe thses have already been unset by the Destroy method | |
# | |
if {[info exists data(m:$client)]} {unset data(m:$client)} | |
if {[info exists data(s:$client)]} {unset data(s:$client)} | |
} | |
#---------------------------------------------------------------------- | |
# Popping up balloon: | |
#---------------------------------------------------------------------- | |
proc tixBalloon:Activate {w} { | |
upvar #0 $w data | |
if {[tixBalloon:GrabBad $w $w]} { | |
return | |
} | |
if {[winfo containing -displayof $w \ | |
[winfo pointerx $w] [winfo pointery $w]] == ""} { | |
return | |
} | |
if {![info exists data(-state)]} { | |
# puts "tixBalloon:Activate called without a state\n$w" | |
set data(state) none | |
return | |
} | |
if {$data(-state) eq "none"} { | |
return | |
} | |
switch -exact -- $data(-state) { | |
"both" { | |
tixBalloon:PopUp $w | |
tixBalloon:SetStatus $w | |
} | |
"balloon" { | |
tixBalloon:PopUp $w | |
} | |
"status" { | |
tixBalloon:SetStatus $w | |
} | |
} | |
set data(isActive) 1 | |
after 200 tixBalloon:Verify $w | |
} | |
# %% Perhaps this is no more needed | |
# | |
proc tixBalloon:Verify {w} { | |
upvar #0 $w data | |
if {![winfo exists $w]} { | |
return | |
} | |
if {!$data(isActive)} { | |
return | |
} | |
if {[tixBalloon:GrabBad $w $w]} { | |
tixBalloon:Deactivate $w | |
return | |
} | |
if {[winfo containing -displayof $w \ | |
[winfo pointerx $w] [winfo pointery $w]] == ""} { | |
tixBalloon:Deactivate $w | |
return | |
} | |
after 200 tixBalloon:Verify $w | |
} | |
proc tixBalloon:Deactivate {w} { | |
upvar #0 $w data | |
tixBalloon:PopDown $w | |
tixBalloon:ClearStatus $w | |
set data(isActive) 0 | |
if {[info exists data(cancel)]} { | |
unset data(cancel) | |
} | |
} | |
proc tixBalloon:PopUp {w} { | |
upvar #0 $w data | |
if {[string is true -strict $data(-installcolormap)]} { | |
wm colormapwindows [winfo toplevel $data(client)] $w | |
} | |
# trick: the following lines allow the balloon window to | |
# acquire a stable width and height when it is finally | |
# put on the visible screen | |
# | |
set client $data(client) | |
if {$data(m:$client) == ""} {return ""} | |
$data(w:message) config -text $data(m:$client) | |
wm geometry $w +10000+10000 | |
wm deiconify $w | |
raise $w | |
update | |
# The windows may become destroyed as a result of the "update" command | |
# | |
if {![winfo exists $w]} { | |
return | |
} | |
if {![winfo exists $client]} { | |
return | |
} | |
# Put it on the visible screen | |
# | |
set x [expr {[winfo rootx $client]+[winfo width $client]/2}] | |
set y [expr {int([winfo rooty $client]+[winfo height $client]/1.3)}] | |
set width [winfo reqwidth $w] | |
set height [winfo reqheight $w] | |
set scrwidth [winfo vrootwidth $w] | |
set scrheight [winfo vrootheight $w] | |
# If the balloon is too far right, pull it back to the left | |
# | |
if {($x + $width) > $scrwidth} { | |
set x [expr {$scrwidth - $width}] | |
} | |
# If the balloon is too far left, pull it back to the right | |
# | |
if {$x < 0} { | |
set x 0 | |
} | |
# If the listbox is below bottom of screen, put it upwards | |
# | |
if {($y + $height) > $scrheight} { | |
set y [expr {$scrheight-$height}] | |
} | |
if {$y < 0} { | |
set y 0 | |
} | |
wm geometry $w +$x+$y | |
after idle raise $w | |
} | |
proc tixBalloon:PopDown {w} { | |
upvar #0 $w data | |
# Close the balloon | |
# | |
wm withdraw $w | |
# We don't set the data(client) to be zero, so that the balloon | |
# will re-appear only if you move out then in the client window | |
# set data(client) "" | |
} | |
proc tixBalloon:SetStatus {w} { | |
upvar #0 $w data | |
if {![winfo exists $data(-statusbar)] | |
|| ![info exists data(s:$data(client))]} { | |
return | |
} | |
set vv [$data(-statusbar) cget -textvariable] | |
if {$vv == ""} { | |
$data(-statusbar) config -text $data(s:$data(client)) | |
} else { | |
uplevel #0 set $vv [list $data(s:$data(client))] | |
} | |
} | |
proc tixBalloon:ClearStatus {w} { | |
upvar #0 $w data | |
if {![winfo exists $data(-statusbar)]} { | |
return | |
} | |
# Clear the StatusBar widget | |
# | |
set vv [$data(-statusbar) cget -textvariable] | |
if {$vv == ""} { | |
$data(-statusbar) config -text "" | |
} else { | |
uplevel #0 set $vv [list ""] | |
} | |
} | |
#---------------------------------------------------------------------- | |
# PublicMethods: | |
#---------------------------------------------------------------------- | |
# %% if balloon is already popped-up for this client, change mesage | |
# | |
proc tixBalloon:bind {w client args} { | |
upvar #0 $w data | |
set alreadyBound [info exists data(m:$client)] | |
set opt(-balloonmsg) "" | |
set opt(-statusmsg) "" | |
set opt(-msg) "" | |
tixHandleOptions opt {-balloonmsg -msg -statusmsg} $args | |
if {$opt(-balloonmsg) != ""} { | |
set data(m:$client) $opt(-balloonmsg) | |
} else { | |
set data(m:$client) $opt(-msg) | |
} | |
if {$opt(-statusmsg) != ""} { | |
set data(s:$client) $opt(-statusmsg) | |
} else { | |
set data(s:$client) $opt(-msg) | |
} | |
tixAppendBindTag $client TixBal$w | |
} | |
proc tixBalloon:post {w client} { | |
upvar #0 $w data | |
if {![info exists data(m:$client)] || $data(m:$client) == ""} { | |
return | |
} | |
tixBalloon:Enter $w $client | |
incr data(fakeEnter) | |
} | |
proc tixBalloon:unbind {w client} { | |
upvar #0 $w data | |
if {[info exists data(m:$client)]} { | |
if {[info exists data(m:$client)]} {unset data(m:$client)} | |
if {[info exists data(s:$client)]} {unset data(s:$client)} | |
if {[winfo exists $client]} { | |
catch {tixDeleteBindTag $client TixBal$w} | |
} | |
} | |
} | |
#---------------------------------------------------------------------- | |
# | |
# Utility function | |
# | |
#---------------------------------------------------------------------- | |
# | |
# $w can be a widget name or a classs name | |
proc tixBalIgnoreWhenGrabbed {wc} { | |
global tixBalloon | |
set tixBalloon(g_ignore,$wc) "" | |
} | |
tixBalIgnoreWhenGrabbed TixComboBox | |
tixBalIgnoreWhenGrabbed Menu | |
tixBalIgnoreWhenGrabbed Menubutton | |