array set left_tooltips {
	select	"selection tool"
	start	"start the session"
	link	"link tool"
	routers "network-layer virtual nodes"
	hubs	"link-layer nodes"
	bgobjs	"background annotation tools"
	routers0	"router"
	routers1	"host"
	routers2	"pc"
	hubs0	"ethernet hub"
	hubs1	"ethernet switch"
	hubs2	"wireless LAN"
	hubs3	"rj45 physical interface tool"
	hubs4	"tunnel tool"
	hubs5	"ktunnel tool"
	bgobjs0	"marker"
	bgobjs1	"oval"
	bgobjs2	"rectangle"
	bgobjs3	"text"
	stop	"stop the session"
	marker	"marker"
	mobility "add and remove static links"
	twonode	"run command from one node to another"
}

proc leftToolTip { w parent } {
    global left_tooltips g_prefs
    if { ! $g_prefs(gui_show_tooltips) } { return } ;# user has turned off ttips
    if { ![info exists left_tooltips($w)] } { return }
    balloon $parent.$w $left_tooltips($w)
}

#
# Show a sub-menu tooltip. This is called from a <<MenuSelect>> event.
proc leftToolTipSubMenu { w } {
    global left_tooltips g_prefs
    if { ! $g_prefs(gui_show_tooltips) } { return } ;# user has turned off ttips

    set index [$w index active]
    if { $index == "none" } { return }

    # fix window path for submenus
    if { [lindex [split $w .] end] == "menu" } {
	set newlen [expr {[string length $w] - 6}]
	set w [string range $w 0 $newlen]
    }
    # set ypos [$w yposition $index]
    set wmx [expr [winfo rootx $w] + ($index * 64)]
    set wmy [expr [winfo rooty $w]+int(1.5*[winfo height $w])]
    # puts "submenu: $index = $ypos"
    set submenu [lindex [split $w .] end]
    if { [info exists left_tooltips($submenu$index)] } {
	set caption $left_tooltips($submenu$index)
    } elseif { [info exists left_tooltips($w)] } {
	set caption $left_tooltips($w)
    } else {
	return
    }
    ::balloon::show2 $w $wmx $wmy $caption
}

proc floatingInfo3 { c node } {
    global zoom
#    if { $oper_mode != "exec" } {
#	return
#    }
    if { $node != "" } {
	# this floating info already exists
        if { [$c find withtag "floatinfo && $node"] != "" } {
	    return
        }
    } else {
	# popdown floating info
        $c delete -withtag floatinfo
	return
    }
    set coords [getNodeCoords $node]
    set x [expr {int([lindex $coords 0] * $zoom)}]
    set y [expr {int([lindex $coords 1] * $zoom)}]
    set w ""
    set wmx [expr [winfo rootx $c] + $x + 40]
    set wmy [expr [winfo rooty $c] + $y + 40]
# int(1.5*[winfo height $c])
    foreach n [$c find withtag "node && $node"] {
	::balloon::show2 $w $wmx $wmy "$node stuff"
    }
}

# Draw a square gray box on the canvas at the given node's coordinates
# containing the given caption. This is used by the Observer Widgets.
proc floatingInfo { c node caption } {
    global zoom oper_mode

    if { $oper_mode != "exec" } {
	return
    }
    if { $node != "" } {
	# this floating info already exists
        if { [$c find withtag "floatinfo && $node"] != "" } {
	    return
        }
    } else {
	# popdown floating info
        $c delete -withtag floatinfo
	return
    }
    # this controls the rectangle padding
    set offset 5 

    set coords [getNodeCoords $node]
    set x [expr {15 + [lindex $coords 0] * $zoom}]
    set y [expr {10 + [lindex $coords 1] * $zoom}]

    # only one floatinfo visible at a time
    $c delete -withtag floatinfo

    # text
    set float [$c create text $x $y \
			-text $caption -font "fixed 8" \
			-tag "floatinfo $node" -justify left -anchor nw]
    $c bind $float <Leave> "anyLeave $c"
    # text size is variable, base rectangle size on bounding box
    set bbox [$c bbox $float]
    set x1 [expr { [lindex $bbox 0] - $offset}]
    set y1 [expr { [lindex $bbox 1] - $offset}]
    set x2 [expr { [lindex $bbox 2] + $offset}]
    set y2 [expr { [lindex $bbox 3] + $offset}]

    # shadow
    roundRect $c $x1 $y1 [expr $x2 + 1] [expr $y2 + 1] 10.0 -fill black \
	-tag "floatinfo $node"
    # rounded rectangle
    roundRect $c $x1 $y1 $x2 $y2 10.0 -fill gray -tag "floatinfo $node"

    # raise floatinfo above everything else
    $c raise $float

    # check if the rectangle is drawn off the canvas, slide it into view
    set r [.c cget -scrollregion]
    set minx [expr { [lindex $r 0] * [lindex [.c xview] 0] }]
    set maxx [expr { $minx + ([lindex $r 2] * [lindex [.c xview] 1]) }]
    set miny [expr { [lindex $r 1] * [lindex [.c yview] 0] }]
    set maxy [expr { $miny + ([lindex $r 3] * [lindex [.c yview] 1]) }]

    set dx 0; set dy 0
    if { $x2 > $maxx } {	;# slide left
	set dx [expr {$maxx - $x2}]
    } elseif { $x1 < $minx } {	;# slide right
	set dx [expr {$minx - $x1}]
    }
    if { $y2 > $maxy } {	;# slide up
	set dy [expr {$maxy - $y2}]
    } elseif { $y1 < $miny } { 	;# slide down
	set dy [expr {$miny - $y1}]
    }
    if { $dx != 0 || $dy != 0 } {
	after 550 "floatingInfoSlide $c $node $float $dx $dy 200"
    }
}

# slide the floating info box into view
# recursively calls self with decaying function
proc floatingInfoSlide { c node float dx dy delay } {
    #puts "floatingInfoSlide $c $node $dx $dy"
    if { [$c type $float] == "" } {
	return; # this particular floatinfo text no longer exists
    }

    # calculate a new x,y amount to slide the info box, decaying somewhat
    set movex [expr {$dx/3}]; set movey [expr {$dy/3}]

    $c move "floatinfo && $node" $movex $movey

    # calculate amount left to move
    set newdx [expr {$dx - $movex}]; set newdy [expr {$dy - $movey}]
    # slightly increasing delay slows down the sliding effect
    set delay [expr {int($delay * 1.1)}]

    # recursively call self with new parameters, if any movement is left
    if {$newdx != 0 || $newdy != 0} {
	after $delay "floatingInfoSlide $c $node $float $newdx $newdy $delay"
    }
}

# return the width and height (in characters) of the given caption
proc textCharSize { txt } {
    set width 0
    set height 0
    foreach line [split $txt "\n"] {
	set len [string length $line]
	if { $len > $width } { set width $len }
	incr height
    }
    return [list $width $height]
}
# end Boeing


# This is contributed code from http://wiki.tcl.tk/3060
namespace eval balloon {set last 0 ; namespace export balloon}

proc ::balloon::balloon {args} {
   variable last
   variable tips

   set numArgs [llength $args]
   if { $numArgs < 1 || $numArgs > 2 } {
        return -code error "wrong # args: should be \"balloon widget ?text?\"";
      }

   set w [lindex $args 0]
   if { ![winfo exists $w] } {
        return -code error "bad window path name \"$w\""
      }

   if { [winfo class $w] == "Toplevel" } {
        return -code error "cannot create tooltip for toplevel windows";
      }

   if { $numArgs == "1" } {
        if { [info exists tips($w)] } {
             return $tips($w);
           } else {
             return "";
           }
      }

   set text [lindex $args 1]

   if { $text == "" } {
        # turn off tooltip
        if { [set x [lsearch [bindtags $w] "Balloon"]] >= 0 } {
             bindtags $w [lreplace [bindtags $w] $x $x]
           }
        unset -nocomplain tips($w)
        trace remove command $w delete ::balloon::autoclear
        return;
      }

   # OK, set up a (new?) tooltip

   if { [lsearch [bindtags $w] "Balloon"] < 0 } {
        bindtags $w [linsert [bindtags $w] 0 "Balloon"]
      }

   if { [lsearch [trace info command $w] {delete ::balloon::autoclear}] < 0 } {
        trace add command $w delete ::balloon::autoclear
      }

   set tips($w) $text

 };# balloon::balloon

 proc ::balloon::show {w} {
    variable tips
    if { ![info exists tips($w)] } {return}
    if {[eval winfo containing [winfo pointerxy .]]!=$w} {return}
     set top "$w.balloon"
     catch {destroy $top}
     toplevel $top -bd 0 -bg black
     wm overrideredirect $top 1
     pack [message $top.txt -aspect 10000 -bg lightyellow -relief raised \
             -text $tips($w)]
     set wmx [winfo rootx $w]
     set wmy [expr [winfo rooty $w]+[winfo height $w]]
     wm geometry $top \
       [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$wmx+$wmy
     raise $top
 };# balloon::show

 proc ::balloon::delay {} {
   variable last

   set then $last
   set last [clock seconds]
   if { [expr {$last - $then}] < 3} {
        return 50
      } else {
        return 1000
      }

 };# balloon::delay

 proc ::balloon::autoclear {old new op} {
   variable tips

   unset -nocomplain tips([namespace tail $old]);

 };# balloon::autoclear

# Boeing
# this is a modified form of ::balloon::show but accepts 
# x,y coordinates and a caption for the tooltip
 proc ::balloon::show2 {w x y caption} {
    variable tips
#    if { ![info exists tips($w)] } {return}
     set top "$w.balloon"
     catch {destroy $top}
     toplevel $top -bd 0 -bg black
     wm overrideredirect $top 1
     pack [message $top.txt -aspect 10000 -bg lightyellow -relief raised \
             -text $caption]
     wm geometry $top \
       [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$x+$y
     raise $top
 };# balloon::show2
# end Boeing

 namespace import ::balloon::balloon
 if {[catch { bind Balloon <Enter> {after [::balloon::delay] \
					  [list ::balloon::show %W]}} err]} {
    # DISPLAY variable probably not set!
    if { ![info exists tk_patchLevel] } { 
	puts ""
	puts -nonewline " Error initializing Tcl/Tk. Make sure that you are "
	puts "running CORE from X.org or" 
	puts " that you have X11 forwarding turned on (via SSH). "
	puts ""
    }
    exit.real
}
 bind Balloon <Leave> {destroy %W.balloon}

# This is contributed code from http://wiki.tcl.tk/1416
 #----------------------------------------------------------------------
 #
 # roundRect --
 #
 #       Draw a rounded rectangle in the canvas.
 #
 # Parameters:
 #       w - Path name of the canvas
 #       x0, y0 - Co-ordinates of the upper left corner, in pixels
 #       x3, y3 - Co-ordinates of the lower right corner, in pixels
 #       radius - Radius of the bend at the corners, in any form
 #                acceptable to Tk_GetPixels
 #       args - Other args suitable to a 'polygon' item on the canvas
 #
 # Results:
 #       Returns the canvas item number of the rounded rectangle.
 #
 # Side effects:
 #       Creates a rounded rectangle as a smooth polygon in the canvas.
 #
 #----------------------------------------------------------------------
 proc roundRect { w x0 y0 x3 y3 radius args } {

    set r [winfo pixels $w $radius]
    set d [expr { 2 * $r }]

    # Make sure that the radius of the curve is less than 3/8
    # size of the box!

    set maxr 0.75

    if { $d > $maxr * ( $x3 - $x0 ) } {
        set d [expr { $maxr * ( $x3 - $x0 ) }]
    }
    if { $d > $maxr * ( $y3 - $y0 ) } {
        set d [expr { $maxr * ( $y3 - $y0 ) }]
    }

    set x1 [expr { $x0 + $d }]
    set x2 [expr { $x3 - $d }]
    set y1 [expr { $y0 + $d }]
    set y2 [expr { $y3 - $d }]

    set cmd [list $w create polygon]
    lappend cmd $x0 $y0
    lappend cmd $x1 $y0
    lappend cmd $x2 $y0
    lappend cmd $x3 $y0
    lappend cmd $x3 $y1
    lappend cmd $x3 $y2
    lappend cmd $x3 $y3
    lappend cmd $x2 $y3
    lappend cmd $x1 $y3
    lappend cmd $x0 $y3
    lappend cmd $x0 $y2
    lappend cmd $x0 $y1
    lappend cmd -smooth 1
    return [eval $cmd $args]
}