# 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 "[code $this clicked %W %x %y 1]" $c bind $j2 "[code $this clicked %W %x %y 1]" $c bind $j3 "[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 [code $this changeValue $j] bind $entry [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 }