blob: e28733d37b80715e2fe53a51f573422f46d261fb [file] [log] [blame]
# Variable tree implementation for Insight.
# Copyright (C) 2002 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License (GPL) as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
itcl::body VarTree::constructor {args} {
debug $args
if {!$initialized} {
_init_data
}
eval itk_initialize $args
itk_component add canvas {
iwidgets::scrolledcanvas $itk_interior.c -autoresize 1 -hscrollmode dynamic -vscrollmode dynamic \
-background $::Colors(textbg) -borderwidth 0 -highlightthickness 0
}
set c [$itk_component(canvas) childsite]
pack $itk_component(canvas) -side top -fill both -expand 1
bind $c <1> "[code $this clicked %W %x %y 0]"
# Add popup menu - we populate it in _but3
itk_component add popup {
menu $itk_interior.pop -tearoff 0
} {}
set pop $itk_component(popup)
$pop configure -disabledforeground $::Colors(fg)
bind $c <3> [code $this _but3 %x %y %X %Y]
set selection {}
set selidx {}
after idle [code $this build]
}
itcl::body VarTree::destructor {} {
debug
}
itcl::body VarTree::build {} {
debug
$c delete all
catch {unset var_to_items}
catch {unset item_to_var}
set _y 30
buildlayer $rootlist 10
$c config -scrollregion [$c bbox all] -background $::Colors(textbg) -borderwidth 0 -highlightthickness 0
update 1
drawselection
}
itcl::body VarTree::buildlayer {tlist in} {
set start [expr $_y - 10]
foreach var $tlist {
set y $_y
incr _y 17
if {$in > 10} {
$c create line $in $y [expr $in+10] $y -fill $colors(line)
}
set x [expr $in + 12]
set j1 [$c create text $x $y -text "[$var name] = " -fill $colors(name) -anchor w -font global/fixed]
set x [expr [lindex [$c bbox $j1] 2] + 5]
set j2 [$c create text $x $y -text "([$var type])" -fill $colors(type) -anchor w -font global/fixed]
set x [expr [lindex [$c bbox $j2] 2] + 5]
if {[catch {$var value} val]} {
# error accessing memory, etc.
set j3 [$c create text $x $y -text $val -fill $colors(error) -anchor w -font global/fixed]
} else {
set j3 [$c create text $x $y -text $val -fill $colors(value) -anchor w -font global/fixed]
}
set var_to_items($var) [list $j1 $j2 $j3]
set item_to_var($j1) $var
set item_to_var($j2) $var
set item_to_var($j3) $var
$c bind $j1 <Double-1> "[code $this clicked %W %x %y 1]"
$c bind $j2 <Double-1> "[code $this clicked %W %x %y 1]"
$c bind $j3 <Double-1> "[code $this edit $j3];break"
if {[$var numChildren]} {
if {[closed $var]} {
set j [$c create image $in $y -image closedbm]
$c bind $j <1> "[code $this open $var]"
} else {
set j [$c create image $in $y -image openbm]
$c bind $j <1> "[code $this close $var]"
buildlayer [$var children] [expr $in+18]
}
}
}
if {$in > 10} {
$c lower [$c create line $in $start $in [expr $y+1] -fill $colors(line) ]
}
}
# add: add a list of varobj to the tree
itcl::body VarTree::add {var} {
debug $var
if {$var == ""} {return}
set rootlist [concat $rootlist $var]
after idle [code $this build]
}
# remove: remove a varobj from the tree
# if the name is "all" then remove all
itcl::body VarTree::remove {name} {
debug $name
if {$name == ""} {return}
if {$name == "all"} {
set rootlist {}
} else {
set rootlist [lremove $rootlist $name]
}
after idle [code $this build]
}
# update a var
itcl::body VarTree::update_var {var enabled check} {
if {$enabled && $check} {return}
lassign $var_to_items($var) nam typ val
if {$enabled} {
$c itemconfigure $nam -fill $colors(name)
$c itemconfigure $typ -fill $colors(type)
if {[catch {$var value} value]} {
set color $colors(error)
} elseif {[$c itemcget $val -text] != $value} {
set color $colors(change)
} else {
set color $colors(value)
}
$c itemconfigure $val -text $value -fill $color
} else {
$c itemconfigure $nam -fill $colors(disabled)
$c itemconfigure $typ -fill $colors(disabled)
$c itemconfigure $val -fill $colors(disabled)
}
if {![closed $var] && [$var numChildren]} {
foreach child [$var children] {
update_var $child $enabled $check
}
}
}
# update: update the values of the vars in the tree.
# The "check" argument is a hack we have to do because
# [$varobj value] does not return an error; only [$varobj update]
# does. So after changing the tree layout in build, we must then
# do an update. The "check" argument just optimizes things a bit over
# a normal update by not fetching values, just calling update.
itcl::body VarTree::update {{check 0}} {
debug
# delete selection box if it is visible
if {$selidx != ""} {
$c delete $selidx
}
# update all the root variables
foreach var $rootlist {
if {[$var update] == "-1"} {
set enabled 0
} else {
set enabled 1
}
update_var $var $enabled $check
}
}
# Draw the selection highlight
itcl::body VarTree::drawselection {} {
#debug "selidx=$selidx selection=$selection"
if {$selidx != ""} {
$c delete $selidx
}
if {$selection == ""} return
if {![info exists var_to_items($selection)]} return
set bbox [eval "$c bbox $var_to_items($selection)"]
if {[llength $bbox] == 4} {
set selidx [eval $c create rectangle $bbox -fill $::Colors(sbg) -outline {{}}]
$c lower $selidx
} else {
set selidx {}
}
}
# button 1 callback
itcl::body VarTree::clicked {w x y open} {
#debug "clicked $w $x $y $open"
set x [$w canvasx $x]
set y [$w canvasy $y]
foreach m [$w find overlapping $x $y $x $y] {
if {[info exists item_to_var($m)]} {
if {$open} {
set var $item_to_var($m)
if {[closed $var]} {
set closed($var) 0
} else {
set closed($var) 1
}
after idle [code $this build]
} else {
setselection $item_to_var($m)
}
return
}
}
if {!$open} {
setselection ""
}
}
#
# Change the selection to the indicated item
#
itcl::body VarTree::setselection {var} {
#debug "setselection $var"
set selection $var
drawselection
}
# Check if a node is closed.
# If it is a new node, set it to closed
itcl::body VarTree::closed {name} {
if {![info exists closed($name)]} {
set closed($name) 1
}
return $closed($name)
}
# mark a node open
itcl::body VarTree::open {name} {
set closed($name) 0
after idle [code $this build]
}
# mark a node closed
itcl::body VarTree::close {name} {
set closed($name) 1
after idle [code $this build]
}
# edit a varobj.
# creates an entry widget in place of the current value
itcl::body VarTree::edit {j} {
#debug "$j"
# if another edit is in progress, cancel it
if {$entry != ""} { unedit $j }
set entryobj $item_to_var($j)
set entry [entry $c.entry -bg $::Colors(bg) -fg $::Colors(fg) -font global/fixed]
set entrywin [$c create window [$c coords $j] -window $entry -anchor w]
focus $entry
bind $entry <Return> [code $this changeValue $j]
bind $entry <Escape> [code $this unedit $j]
}
# cancel or clean up after an edit
itcl::body VarTree::unedit {j} {
#debug
# cancel the edit
$c delete $entrywin
destroy $entry
set entry ""
$c raise $j
}
# change the value of a varobj.
itcl::body VarTree::changeValue {j} {
#debug "value = [$entry get]"
set new [string trim [$entry get] \ \r\n]
if {$new == ""} {
unedit $j
return
}
if {[catch {$entryobj value $new} errTxt]} {
# gdbtk-varobj doesn't actually return meaningful error messages
# so use a generic one.
set errTxt "GDB could not evaluate that expression"
tk_messageBox -icon error -type ok -message $errTxt \
-title "Error in Expression" -parent [winfo toplevel $itk_interior]
focus $entry
$entry selection to end
} else {
unedit $j
# We may have changed a register or something else that is
# being displayed in another window
gdbtk_update
}
}
# change the format for a var
itcl::body VarTree::_change_format {var} {
#debug "$var $popup_temp"
catch {$var format $popup_temp}
after idle [code $this update]
}
# button 3 callback. Pops up a menu.
itcl::body VarTree::_but3 {x y X Y} {
set x [$c canvasx $x]
set y [$c canvasy $y]
catch {destroy $pop.format}
set var ""
foreach item [$c find overlapping $x $y $x $y] {
if {![catch {set var $item_to_var($item)}]} {
break
}
}
setselection $var
if {$var == ""} {
_do_default_menu $X $Y
return
}
set popup_temp [$var format]
set j3 [lindex $var_to_items($var) 2]
#debug "var=$var [$var name] format=$popup_temp this=$this"
$pop delete 0 end
$pop add command -label [$var name] -state disabled
$pop add separator
$pop add cascade -menu $pop.format -label "Format" -underline 0
set f [menu $pop.format -tearoff 0]
$f add radio -label "Natural" -variable [scope popup_temp] -value "natural" -command [code $this _change_format $var]
$f add radio -label "Decimal" -variable [scope popup_temp] -value "decimal" -command [code $this _change_format $var]
$f add radio -label "Hex" -variable [scope popup_temp] -value "hexadecimal" -command [code $this _change_format $var]
$f add radio -label "Octal" -variable [scope popup_temp] -value "octal" -command [code $this _change_format $var]
$f add radio -label "Binary" -variable [scope popup_temp] -value "binary" -command [code $this _change_format $var]
$pop add command -label "Edit" -command [code $this edit $j3]
$pop add command -label "Delete" -command [code $this remove $var]
if {![catch {$var value} value]} {
$pop add separator
$pop add command -label "Dump Memory at [$var name]" -command [list ManagedWin::open MemWin -force -addr_exp [$var name]]
}
$pop add separator
if {$type == "local"} {
$pop add command -label "Help" -command "open_help watch.html"
} else {
$pop add command -label "Help" -command "open_help locals.html"
}
$pop add separator
$pop add command -label "Close" -command "destroy [winfo toplevel $itk_interior]"
tk_popup $pop $X $Y
}
# popup menu over empty space
itcl::body VarTree::_do_default_menu {X Y} {
#debug
$pop delete 0 end
if {$type == "local"} {
$pop add command -label "Local Variables" -state disabled
} else {
$pop add command -label "Watch Window" -state disabled
}
$pop add separator
$pop add command -label "Sort" -command [code $this _sort]
if {$type == "local"} {
$pop add command -label "Help" -command "open_help watch.html"
} else {
$pop add command -label "Help" -command "open_help locals.html"
}
$pop add separator
$pop add command -label "Close" -command "destroy [winfo toplevel $itk_interior]"
tk_popup $pop $X $Y
}
# alphabetize the variable names in the list
itcl::body VarTree::_sort {} {
#debug $rootlist
set rootlist [lsort -command [code $this _compare] $rootlist]
after idle [code $this build]
}
# comparison function for lsort.
itcl::body VarTree::_compare {a b} {
return [string compare [$a name] [$b name]]
}
# ititialize common data
itcl::body VarTree::_init_data {} {
set colors(name) "\#0000C0"
set colors(type) "red"
set colors(error) "red"
set colors(value) "black"
set colors(change) $::Colors(change)
set colors(disabled) "gray50"
set colors(line) "gray50"
set maskdata "#define solid_width 9\n#define solid_height 9"
append maskdata {
static unsigned char solid_bits[] = {
0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01,
0xff, 0x01, 0xff, 0x01, 0xff, 0x01
};
}
set data "#define open_width 9\n#define open_height 9"
append data {
static unsigned char open_bits[] = {
0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7d, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0xff, 0x01
};
}
image create bitmap openbm -data $data -maskdata $maskdata \
-foreground black -background white
set data "#define closed_width 9\n#define closed_height 9"
append data {
static unsigned char closed_bits[] = {
0xff, 0x01, 0x01, 0x01, 0x11, 0x01, 0x11, 0x01, 0x7d, 0x01, 0x11, 0x01,
0x11, 0x01, 0x01, 0x01, 0xff, 0x01
};
}
image create bitmap closedbm -data $data -maskdata $maskdata \
-foreground black -background white
set initialized 1
}