777 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
		
		
			
		
	
	
			777 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
|  | # Memory display window class definition for Insight. | ||
|  | # Copyright (C) 1998, 1999, 2001, 2002, 2005, 2008 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. | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  constructor - build the dialog | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::constructor {args} { | ||
|  |   global _mem | ||
|  |   debug $args | ||
|  |   eval itk_initialize $args | ||
|  | 
 | ||
|  |   set top [winfo toplevel $itk_interior] | ||
|  |   gdbtk_busy | ||
|  | 
 | ||
|  |   set _mem($this,enabled) 1 | ||
|  | 
 | ||
|  |   if {![info exists type(1)]} { | ||
|  |     set type(1) char | ||
|  |     set type(2) short | ||
|  |     set type(4) int | ||
|  |     set type(8) "long long" | ||
|  |   } | ||
|  | 
 | ||
|  |   if {[pref getd gdb/mem/menu] != ""} { | ||
|  |     set mbar 0 | ||
|  |   } | ||
|  | 
 | ||
|  |   # Load defaults from preferences. | ||
|  |   set size [pref getd gdb/mem/size] | ||
|  |   set numbytes [pref getd gdb/mem/numbytes] | ||
|  |   set format [pref getd gdb/mem/format] | ||
|  |   set ascii [pref getd gdb/mem/ascii] | ||
|  |   set ascii_char [pref getd gdb/mem/ascii_char] | ||
|  |   set bytes_per_row [pref getd gdb/mem/bytes_per_row] | ||
|  |   set color [pref getd gdb/mem/color] | ||
|  | 
 | ||
|  |   init_addr_exp | ||
|  |   build_win | ||
|  |   gdbtk_idle | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  destructor - destroy the dialog | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::destructor {} { | ||
|  |   if {[winfo exists $prefs_win]} { | ||
|  |     $prefs_win cancel | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  build_win - build the main memory window | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::build_win {} { | ||
|  |   global gdb_ImageDir _mem ${this}_memval | ||
|  | 
 | ||
|  |   set maxlen 0 | ||
|  |   set maxalen 0 | ||
|  |   set saved_value "" | ||
|  | 
 | ||
|  |   if { $mbar } { | ||
|  |     menu $itk_interior.m -tearoff 0 | ||
|  |     $top configure -menu $itk_interior.m | ||
|  |     $itk_interior.m add cascade -menu $itk_interior.m.addr \ | ||
|  |       -label "Addresses" -underline 0 | ||
|  |     set m [menu $itk_interior.m.addr] | ||
|  |     $m add check -label " Auto Update" -variable _mem($this,enabled) \ | ||
|  |       -underline 1 -command "after idle $this toggle_enabled" | ||
|  |     $m add command -label " Update Now" -underline 1 \ | ||
|  |       -command [code $this _update_address 1] -accelerator {Ctrl+U} | ||
|  |     $m add separator | ||
|  |     $m add command -label " Preferences..." -underline 1 \ | ||
|  |       -command "$this create_prefs" | ||
|  |   } | ||
|  | 
 | ||
|  |   # Numcols = number of columns of data | ||
|  |   # numcols = number of columns in table (data plus headings plus ASCII) | ||
|  |   # if numbytes are 0, then use window size to determine how many to read | ||
|  |   if {$numbytes == 0} { | ||
|  |     set Numrows 8 | ||
|  |   } else { | ||
|  |     set Numrows [expr {$numbytes / $bytes_per_row}] | ||
|  |   } | ||
|  |   set numrows [expr {$Numrows + 1}] | ||
|  | 
 | ||
|  |   set Numcols [expr {$bytes_per_row / $size}] | ||
|  |   if {$ascii} { | ||
|  |     set numcols [expr {$Numcols + 2}] | ||
|  |   } else { | ||
|  |     set numcols [expr {$Numcols + 1}] | ||
|  |   } | ||
|  | 
 | ||
|  |   itk_component add table { | ||
|  |     ::table $itk_interior.t -titlerows 1 -titlecols 1 -variable ${this}_memval \ | ||
|  |       -roworigin -1 -colorigin -1 -bg $::Colors(textbg) -fg $::Colors(textfg) \ | ||
|  |       -browsecmd "$this changed_cell %s %S" -font global/fixed\ | ||
|  |       -colstretch unset -rowstretch unset -selectmode single \ | ||
|  |       -xscrollcommand "$itk_interior.sx set" -resizeborders none \ | ||
|  |       -cols $numcols -rows $numrows -autoclear 1 | ||
|  |   } { | ||
|  |     keep -foreground | ||
|  |     keep -insertbackground | ||
|  |     keep -highlightcolor | ||
|  |     keep -highlightbackground | ||
|  |   } | ||
|  |    | ||
|  |   if {$numbytes} { | ||
|  |     $itk_component(table) configure -yscrollcommand "$itk_interior.sy set" | ||
|  |     scrollbar $itk_interior.sy -command [list $itk_component(table) yview] | ||
|  |   } else { | ||
|  |     $itk_component(table) configure -rowstretchmode none | ||
|  |   } | ||
|  |   scrollbar $itk_interior.sx -command [list $itk_component(table) xview] -orient horizontal | ||
|  |   $itk_component(table) tag config sel -bg [$itk_component(table) cget -bg] -relief sunken | ||
|  |   $itk_component(table) tag config active -relief sunken -wrap 0 \ | ||
|  |     -bg $::Colors(sbg) -fg $::Colors(sfg) | ||
|  |   $itk_component(table) tag config title -bg $::Colors(bg) -fg $::Colors(fg) | ||
|  | 
 | ||
|  |   # rebind all events that use tkTableMoveCell to our local version | ||
|  |   # because we don't want to move into the ASCII column if it exists | ||
|  |   bind $itk_component(table) <Up>		"$this memMoveCell %W -1  0; break" | ||
|  |   bind $itk_component(table) <Down>		"$this memMoveCell %W  1  0; break" | ||
|  |   bind $itk_component(table) <Left>		"$this memMoveCell %W  0 -1; break" | ||
|  |   bind $itk_component(table) <Right>	"$this memMoveCell %W  0  1; break" | ||
|  |   bind $itk_component(table) <Return>	"$this memMoveCell %W 0 1; break" | ||
|  |   bind $itk_component(table) <KP_Enter>	"$this memMoveCell %W 0 1; break" | ||
|  | 
 | ||
|  |   # bind button 3 to popup | ||
|  |   bind $itk_component(table) <3> "$this do_popup %X %Y" | ||
|  | 
 | ||
|  |   # bind Paste and button2 to the paste function | ||
|  |   # this is necessary because we want to not just paste the | ||
|  |   # data into the cell, but we also have to write it | ||
|  |   # out to real memory | ||
|  |   bind $itk_component(table) <ButtonRelease-2> [format {after idle %s paste %s %s} $this %x %y] | ||
|  |   bind $itk_component(table) <<Paste>> [format {after idle %s paste %s %s} $this %x %y] | ||
|  | 
 | ||
|  |   menu $itk_component(table).menu -tearoff 0 | ||
|  |   bind_plain_key $top Control-u [code $this _update_address 1] | ||
|  | 
 | ||
|  |   # bind resize events | ||
|  |   bind $itk_interior <Configure> "$this newsize %h" | ||
|  | 
 | ||
|  |   frame $itk_interior.f | ||
|  |   iwidgets::spinint $itk_interior.f.cntl -labeltext " Address " -width 20 \ | ||
|  |     -command "after idle $this update_address_cb" \ | ||
|  |     -increment "after idle $this incr_addr -1" \ | ||
|  |     -decrement "after idle $this incr_addr 1" -foreground  $::Colors(textfg) \ | ||
|  |     -validate {}  -textbackground $::Colors(textbg)  | ||
|  |   $itk_interior.f.cntl delete 0 end | ||
|  |   $itk_interior.f.cntl insert end $addr_exp | ||
|  | 
 | ||
|  |   label $itk_interior.f.endian -text "Target is [gdbtk_endian] endian" | ||
|  | 
 | ||
|  |   balloon register [$itk_interior.f.cntl childsite].uparrow \ | ||
|  |     "Scroll Up (Decrement Address)" | ||
|  |   balloon register [$itk_interior.f.cntl childsite].downarrow \ | ||
|  |     "Scroll Down (Increment Address)" | ||
|  |   if {!$mbar} { | ||
|  |     button $itk_interior.f.upd -command [code $this _update_address 1] \ | ||
|  |       -image [image create photo -file [::file join $gdb_ImageDir check.gif]] | ||
|  |     balloon register $itk_interior.f.upd "Update Now" | ||
|  |     checkbutton $itk_interior.cb -variable _mem($this,enabled) -command "$this toggle_enabled" | ||
|  |     balloon register $itk_interior.cb "Toggles Automatic Display Updates" | ||
|  |     grid $itk_interior.f.upd $itk_interior.f.cntl $itk_interior.f.endian -sticky ew -padx 5 | ||
|  |   } else { | ||
|  |     grid $itk_interior.f.cntl x $itk_interior.f.endian -sticky e | ||
|  |     grid columnconfigure $itk_interior.f 1 -weight 1 | ||
|  |   } | ||
|  | 
 | ||
|  |   # draw top border | ||
|  |   set col 0 | ||
|  |   for {set i 0} {$i < $bytes_per_row} { incr i $size} { | ||
|  |     set ${this}_memval(-1,$col) [format " %X" $i] | ||
|  |     incr col | ||
|  |   } | ||
|  | 
 | ||
|  |   if {$ascii} { | ||
|  |     set ${this}_memval(-1,$col) ASCII | ||
|  |   } | ||
|  | 
 | ||
|  |   # fill initial display | ||
|  |   if {$nb} { | ||
|  |     _update_address 0 | ||
|  |   } | ||
|  | 
 | ||
|  |   if {!$mbar} { | ||
|  |     grid $itk_interior.f x -row 0 -column 0 -sticky nws | ||
|  |     grid $itk_interior.cb -row 0 -column 1 -sticky news | ||
|  |   } else { | ||
|  |     grid $itk_interior.f -row 0 -column 0 -sticky news | ||
|  |   } | ||
|  |   grid $itk_component(table) -row 1 -column 0 -sticky news | ||
|  |   if {$numbytes} { grid $itk_interior.sy -row 1 -column 1 -sticky ns } | ||
|  |   grid $itk_interior.sx -sticky ew | ||
|  |   grid columnconfig  $itk_interior 0 -weight 1 | ||
|  |   grid rowconfig  $itk_interior 1 -weight 1 | ||
|  |   focus $itk_interior.f.cntl | ||
|  | 
 | ||
|  |   window_name "Memory" | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  paste - paste callback. Update cell contents after paste | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::paste {x y} { | ||
|  |   edit [$itk_component(table) index @$x,$y] | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  validate - because the control widget wants this | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::validate {val} { | ||
|  |   return $val | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  create_prefs - create memory preferences dialog | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::create_prefs {} { | ||
|  |   if {$Running} { return } | ||
|  | 
 | ||
|  |   # make sure row height is set | ||
|  |   if {$rheight == ""} { | ||
|  |     set rheight [lindex [$itk_component(table) bbox 0,0] 3] | ||
|  |   } | ||
|  | 
 | ||
|  |   set prefs_win [ManagedWin::open MemPref -force -over $this\ | ||
|  | 		   -transient -win $this \ | ||
|  | 		   -size $size -format $format -numbytes $numbytes \ | ||
|  | 		   -bpr $bytes_per_row -ascii $ascii \ | ||
|  | 		   -ascii_char $ascii_char -color $color] | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  changed_cell - called when moving from one cell to another | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::changed_cell {from to} { | ||
|  |   #debug "moved from $from to $to" | ||
|  |   #debug "value = [$itk_component(table) get $from]" | ||
|  |   if {$saved_value != ""} { | ||
|  |     if {$saved_value != [$itk_component(table) get $from]} { | ||
|  |       edit $from | ||
|  |     } | ||
|  |   } | ||
|  |   set saved_value [$itk_component(table) get $to] | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  edit - edit a cell | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::edit { cell } { | ||
|  |   global _mem ${this}_memval | ||
|  | 
 | ||
|  |   #debug "edit $cell" | ||
|  | 
 | ||
|  |   if {$Running || $cell == ""} { return } | ||
|  |   set rc [split $cell ,] | ||
|  |   set row [lindex $rc 0] | ||
|  |   set col [lindex $rc 1] | ||
|  |   set val [$itk_component(table) get $cell] | ||
|  | 
 | ||
|  |   if {$col == $Numcols} {  | ||
|  |     # editing the ASCII field | ||
|  |     set addr [gdb_incr_addr $current_addr [expr {$bytes_per_row * $row}]] | ||
|  |     set start_addr $addr | ||
|  | 
 | ||
|  |     # calculate number of rows to modify | ||
|  |     set len [string length $val] | ||
|  |     set rows 0 | ||
|  |     while {$len > 0} {  | ||
|  |       incr rows | ||
|  |       set len [expr {$len - $bytes_per_row}] | ||
|  |     } | ||
|  |     set nb [expr {$rows * $bytes_per_row}] | ||
|  | 
 | ||
|  |     # now process each char, one at a time | ||
|  |     foreach c [split $val ""] { | ||
|  |       if {$c != $ascii_char} { | ||
|  | 	scan $c %c char | ||
|  | 	if {[catch {gdb_set_mem $addr [format %02x $char] 1} res]} { | ||
|  | 	  error_dialog $res | ||
|  | 
 | ||
|  | 	  # reset value | ||
|  | 	  set ${this}_memval($row,$col) $saved_value | ||
|  | 	  return | ||
|  | 	} | ||
|  |       } | ||
|  |       set addr [gdb_incr_addr $addr] | ||
|  |     } | ||
|  |     set addr $start_addr | ||
|  |     set nextval 0 | ||
|  |     # now read back the data and update the widget | ||
|  |     catch {gdb_update_mem ${this}_memval $addr $format $size $nb $bytes_per_row $ascii_char} vals | ||
|  |     return | ||
|  |   } | ||
|  | 
 | ||
|  |   # calculate address based on row and column | ||
|  |   set addr [gdb_incr_addr $current_addr [expr {$bytes_per_row * $row + $size * $col}]] | ||
|  |   #debug "  edit $row,$col         $addr = $val" | ||
|  | 
 | ||
|  |   # Pad the value with zeros, if necessary | ||
|  |   set s [expr {$size * 2}] | ||
|  |   set val [format "0x%0${s}x" $val] | ||
|  | 
 | ||
|  |   # set memory | ||
|  |   #debug "set_mem $addr $val $size" | ||
|  |   if {[catch {gdb_set_mem $addr $val $size} res]} { | ||
|  |     error_dialog $res | ||
|  | 
 | ||
|  |     # reset value | ||
|  |     set ${this}_memval($row,$col) $saved_value | ||
|  |     return | ||
|  |   } | ||
|  | 
 | ||
|  |   # read it back | ||
|  |   # FIXME - HACK ALERT - This call causes trouble with remotes on Windows.  | ||
|  |   # This routine is in fact called from within an idle handler triggered by | ||
|  |   # memMoveCell.  Something evil happens in that handler that causes gdb to | ||
|  |   # start writing this changed value into all the visible cells... | ||
|  |   # I have not figured out the cause of this, so for now I commented this | ||
|  |   # line out.  It will only matter if the write did not succeed, and this was | ||
|  |   # not a very good way to tell the user about that anyway... | ||
|  |   # | ||
|  |   # catch {gdb_update_mem $addr $format $size $size $size ""} val | ||
|  |   # delete whitespace in response | ||
|  |   set val [string trimright $val] | ||
|  |   set val [string trimleft $val] | ||
|  |   set ${this}_memval($row,$col) $val | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  toggle_enabled - called when enable is toggled | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::toggle_enabled {} { | ||
|  |   global _mem | ||
|  | 
 | ||
|  |   if {$Running} { return } | ||
|  |   if {$_mem($this,enabled)} { | ||
|  |     _update_address 1 | ||
|  |     set state normal | ||
|  |     set bg $::Colors(textbg) | ||
|  |   } else { | ||
|  |     set bg $::Colors(bg) | ||
|  |     set state disabled | ||
|  |   } | ||
|  |   $itk_component(table) config -background $bg -state $state | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  update - update widget after every PC change | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::update {event} { | ||
|  |   global _mem | ||
|  |   if {$_mem($this,enabled)} { | ||
|  |     _update_address 0 | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  idle - memory window is idle, so enable menus | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::idle {event} { | ||
|  |   # Fencepost | ||
|  |   set Running 0 | ||
|  | 
 | ||
|  |   # Cursor | ||
|  |   cursor {} | ||
|  | 
 | ||
|  |   if {[winfo exists $itk_interior.m.addr]} { | ||
|  |     # Enable menus | ||
|  |     if {$mbar} { | ||
|  |       for {set i 0} {$i <= [$itk_interior.m.addr index last]} {incr i} { | ||
|  | 	if {[$itk_interior.m.addr type $i] != "separator"} { | ||
|  | 	  $itk_interior.m.addr entryconfigure $i -state normal | ||
|  | 	} | ||
|  |     } | ||
|  |     } | ||
|  |      | ||
|  |     # Enable control | ||
|  |     $itk_interior.f.cntl configure -state normal | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD: busy - BusyEvent handler | ||
|  | #                 Disable menus 'cause we're busy updating things. | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::busy {event} { | ||
|  |   # Fencepost | ||
|  |   set Running 1 | ||
|  | 
 | ||
|  |   # cursor | ||
|  |   cursor watch | ||
|  | 
 | ||
|  |   # go away if window is not finished drawing | ||
|  |   if {![winfo exists $itk_interior.f.cntl]} { return } | ||
|  |    | ||
|  |   # Disable menus | ||
|  |   if {$mbar} { | ||
|  |     for {set i 0} {$i <= [$itk_interior.m.addr index last]} {incr i} { | ||
|  |       if {[$itk_interior.m.addr type $i] != "separator"} { | ||
|  | 	$itk_interior.m.addr entryconfigure $i -state disabled | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   # Disable control | ||
|  |   $itk_interior.f.cntl configure -state disabled | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD: newsize - calculate how many rows to display when the | ||
|  | #  window is resized. | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::newsize {height} { | ||
|  | 
 | ||
|  |   if {$dont_size || $Running} { | ||
|  |     return  | ||
|  |   } | ||
|  |    | ||
|  |   # only add rows if numbytes is zero | ||
|  |   if {$numbytes == 0} { | ||
|  |     ::update idletasks | ||
|  | 
 | ||
|  |     # make sure row height is set | ||
|  |     if {$rheight == ""} { | ||
|  |       set rheight [lindex [$itk_component(table) bbox 0,0] 3] | ||
|  |     } | ||
|  | 
 | ||
|  |     set theight [winfo height $itk_component(table)] | ||
|  |     set Numrows [expr {$theight / $rheight}] | ||
|  |     $itk_component(table) configure -rows $Numrows | ||
|  |     _update_address 1 | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | itcl::body MemWin::_update_address {make_busy} { | ||
|  |   if {$make_busy} { | ||
|  |     gdbtk_busy | ||
|  |   } | ||
|  |   update_address [string trimleft [$itk_interior.f.cntl get]] | ||
|  |   if {$make_busy} { | ||
|  |     gdbtk_idle | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD: update_address_cb - address entry widget callback | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::update_address_cb {} { | ||
|  |   set new_entry 1 | ||
|  |   _update_address 1 | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD: update_address - update address and data displayed | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::update_address {addr} { | ||
|  | 
 | ||
|  |   set bad_expr 0 | ||
|  |   set saved_addr $current_addr | ||
|  |   if {[string match {[a-zA-Z_&0-9\*]*} $addr]} { | ||
|  |     # Looks like an expression | ||
|  |     set retVal [catch {gdb_eval "$addr" x} current_addr] | ||
|  |     #debug "retVal=$retVal current_addr=$current_addr" | ||
|  |     if {$retVal || [string match "No symbol*" $current_addr] || \ | ||
|  | 	  [string match "Invalid *" $current_addr]} { | ||
|  |       BadExpr $current_addr | ||
|  |       return | ||
|  |     } | ||
|  |     if {[string match {\{*} $current_addr]} { | ||
|  |       set current_addr [lindex $current_addr 1] | ||
|  |       if {$current_addr == ""} { | ||
|  | 	return | ||
|  |       } | ||
|  |     } | ||
|  |   } elseif {[regexp {\$[a-zA-Z_]} $addr]} { | ||
|  |     # Looks like a local variable | ||
|  |     set retVal [catch {gdb_eval "$addr" x} current_addr] | ||
|  |     #debug "retVal=$retVal current_addr=$current_addr" | ||
|  |     if {$retVal} { | ||
|  |       BadExpr $current_addr | ||
|  |       return | ||
|  |     } | ||
|  |     if {$current_addr == "void"} { | ||
|  |       BadExpr "No Local Variable Named \"$addr\"" | ||
|  |       return | ||
|  |     } | ||
|  |   } else { | ||
|  |     # something really strange, like "0.1" or "" | ||
|  |     BadExpr "Can't Evaluate \"$addr\"" | ||
|  |     return | ||
|  |   } | ||
|  | 
 | ||
|  |   # Check for spaces - this can happen with gdb_eval and $pc, for example. | ||
|  |   set index [string first \  $current_addr] | ||
|  |   if {$index != -1} { | ||
|  |     incr index -1 | ||
|  |     set current_addr [string range $current_addr 0 $index] | ||
|  |   } | ||
|  |    | ||
|  |   set addr_exp $addr | ||
|  | 
 | ||
|  |   # set table background | ||
|  |   $itk_component(table) config -bg $::Colors(textbg) -state normal | ||
|  |   catch {update_addr} | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  BadExpr - handle a bad expression | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::BadExpr {errTxt} { | ||
|  |   if {$new_entry} { | ||
|  |     tk_messageBox -type ok -icon error -message $errTxt | ||
|  |     set new_entry 0 | ||
|  |   } | ||
|  |   # set table background to gray | ||
|  |   $itk_component(table) config -bg $::Colors(bg) -state disabled | ||
|  |   set current_addr $saved_addr | ||
|  |   set saved_addr "" | ||
|  |   set bad_expr 1 | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  incr_addr - callback from control widget to increment | ||
|  | #  the current address. | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::incr_addr {num} { | ||
|  |   if {$current_addr == ""} { | ||
|  |     return | ||
|  |   } | ||
|  |   set old_addr $current_addr | ||
|  |   set current_addr [gdb_incr_addr $current_addr [expr {$bytes_per_row * $num}]] | ||
|  | 
 | ||
|  |   # A memory address less than zero is probably not a good thing... | ||
|  |   # | ||
|  | 
 | ||
|  |   if {($num < 0 && [gdb_eval "$current_addr > $old_addr"]) \ | ||
|  |       ||($num > 0 && [gdb_eval "$current_addr < $old_addr"]) } { | ||
|  |     bell | ||
|  |     set current_addr $old_addr | ||
|  |     return | ||
|  |   } | ||
|  |   $itk_component(table) config -bg $::Colors(textbg) -state normal | ||
|  |   $itk_interior.f.cntl clear | ||
|  |   $itk_interior.f.cntl insert 0 $current_addr | ||
|  |   _update_address 1 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  update_addr - read in data starting at $current_addr | ||
|  | #  This is just a helper function for update_address.  | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::update_addr {} { | ||
|  |   global _mem ${this}_memval | ||
|  | 
 | ||
|  |     set row 0 | ||
|  | 
 | ||
|  |   if {$numbytes == 0} { | ||
|  |     set nb [expr {$Numrows * $bytes_per_row}] | ||
|  |   } else { | ||
|  |     set nb $numbytes | ||
|  |   } | ||
|  |   if {$ascii} { | ||
|  |     set retVal [catch {gdb_update_mem ${this}_memval $current_addr $format $size $nb $bytes_per_row $ascii_char} vals] | ||
|  | 
 | ||
|  |   } else { | ||
|  |     set retVal [catch {gdb_update_mem ${this}_memval $current_addr $format $size $nb $bytes_per_row} vals] | ||
|  |   } | ||
|  | 
 | ||
|  | 
 | ||
|  |   if {$retVal || [llength $vals] != 3}  { | ||
|  |     BadExpr "Couldn't get memory at address: \"$addr\"" | ||
|  |       debug "gdb_update_mem returned return code: $retVal and value: \"$vals\"" | ||
|  |     return     | ||
|  |   } | ||
|  |   # set default column width to the max in the data columns | ||
|  |   $itk_component(table) configure -colwidth [lindex $vals 1] | ||
|  | 
 | ||
|  |   # set border column width | ||
|  |   $itk_component(table) width -1 [lindex $vals 0] | ||
|  | 
 | ||
|  |   # set ascii column width | ||
|  |   if {$ascii} { | ||
|  |     $itk_component(table) width $Numcols [lindex $vals 2] | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  hidemb - hide the menubar.  NOT CURRENTLY USED | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::hidemb {} { | ||
|  |   set mbar 0 | ||
|  |   reconfig | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  reconfig - used when preferences change | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::reconfig {} { | ||
|  |   debug | ||
|  |   set addr_exp [string trimright [string trimleft $addr_exp]] | ||
|  |   set wh [winfo height $top] | ||
|  | 
 | ||
|  |   if [winfo exists $itk_interior.m] { destroy $itk_interior.m } | ||
|  |   if [winfo exists $itk_interior.cb] { destroy $itk_interior.cb } | ||
|  |   if [winfo exists $itk_interior.f.upd] { destroy $itk_interior.f.upd } | ||
|  |   if [winfo exists $itk_interior.sy] { destroy $itk_interior.sy }   | ||
|  |   destroy $itk_interior.f.cntl $itk_interior.f $itk_component(table) \ | ||
|  |     $itk_interior.sx  | ||
|  | 
 | ||
|  |   set dont_size 1 | ||
|  | 
 | ||
|  |   # If the fonts change, then you will need to recompute the  | ||
|  |   # row height.  Ditto for switch from fixed number of rows to | ||
|  |   # depends on size. | ||
|  | 
 | ||
|  |   set rheight "" | ||
|  | 
 | ||
|  |   # Update preferences to reflect new reality | ||
|  |   pref setd gdb/mem/size $size | ||
|  |   pref setd gdb/mem/numbytes $numbytes | ||
|  |   pref setd gdb/mem/format $format | ||
|  |   pref setd gdb/mem/ascii $ascii | ||
|  |   pref setd gdb/mem/ascii_char $ascii_char | ||
|  |   pref setd gdb/mem/bytes_per_row $bytes_per_row | ||
|  |   pref setd gdb/mem/color $color | ||
|  | 
 | ||
|  |   build_win | ||
|  |   set dont_size 0 | ||
|  |   ::update | ||
|  |    | ||
|  |   if {$numbytes == 0} { | ||
|  |     newsize $wh | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  do_popup - Display popup menu | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::do_popup {X Y} { | ||
|  |   if {$Running} { return } | ||
|  |   $itk_component(table).menu delete 0 end | ||
|  |   $itk_component(table).menu add check -label "Auto Update" -variable _mem($this,enabled) \ | ||
|  |     -underline 0 -command "$this toggle_enabled" | ||
|  |   $itk_component(table).menu add command -label "Update Now" -underline 0 \ | ||
|  |     -command [code $this _update_address 1] | ||
|  |   $itk_component(table).menu add command -label "Go To [$itk_component(table) curvalue]" -underline 0 \ | ||
|  |     -command "$this goto [$itk_component(table) curvalue]" | ||
|  |   $itk_component(table).menu add command -label "Open New Window at [$itk_component(table) curvalue]" -underline 0 \ | ||
|  |     -command [list ManagedWin::open MemWin -force -addr_exp [$itk_component(table) curvalue]] | ||
|  |   $itk_component(table).menu add separator | ||
|  |   $itk_component(table).menu add command -label "Preferences..." -underline 0 \ | ||
|  |     -command "$this create_prefs" | ||
|  |   tk_popup $itk_component(table).menu $X $Y  | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  goto - change the address of the current memory window | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::goto { addr } { | ||
|  |   set current_addr $addr | ||
|  |   $itk_interior.f.cntl delete 0 end | ||
|  |   $itk_interior.f.cntl insert end $addr | ||
|  |   _update_address 1 | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  init_addr_exp - initialize address expression | ||
|  | #  On startup, if the public variable "addr_exp" was not set, | ||
|  | #  then set it to the start of ".data" if found, otherwise "$pc" | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::init_addr_exp {} { | ||
|  |   if {$addr_exp == ""} { | ||
|  |     set err [catch {gdb_cmd "info file"} result] | ||
|  |     if {!$err} { | ||
|  |       foreach line [split [string trim $result] \n] {  | ||
|  | 	if {[scan $line {%x - %x is %s} start stop section] == 3} { | ||
|  | 	  if {$section == ".data"} { | ||
|  | 	    set addr_exp [format "%#08x" $start] | ||
|  | 	    break | ||
|  | 	  } | ||
|  | 	} | ||
|  |       } | ||
|  |     } | ||
|  |     if {$addr_exp == ""} { | ||
|  |       set addr_exp \$pc | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------ | ||
|  | #  METHOD:  cursor - set the cursor | ||
|  | # ------------------------------------------------------------------ | ||
|  | itcl::body MemWin::cursor {glyph} { | ||
|  |   # Set cursor for all labels | ||
|  |   # for {set i 0} {$i < $bytes_per_row} {incr i $size} { | ||
|  |   #   $itk_component(table).h.$i configure -cursor $glyph | ||
|  |   # } | ||
|  |   $top configure -cursor $glyph | ||
|  | } | ||
|  | 
 | ||
|  | # memMoveCell -- | ||
|  | # | ||
|  | # Moves the location cursor (active element) by the specified number | ||
|  | # of cells and changes the selection if we're in browse or extended | ||
|  | # selection mode. | ||
|  | # | ||
|  | # Don't allow movement into the ASCII column. | ||
|  | # | ||
|  | # Arguments: | ||
|  | # w - The table widget. | ||
|  | # x - +1 to move down one cell, -1 to move up one cell. | ||
|  | # y - +1 to move right one cell, -1 to move left one cell. | ||
|  | 
 | ||
|  | itcl::body MemWin::memMoveCell {w x y} { | ||
|  |   if {[catch {$w index active row} r]} return | ||
|  |   set c [$w index active col] | ||
|  |   if {$ascii && ($c == $Numcols)} { | ||
|  |     # we're in the ASCII column so behave differently | ||
|  |     if {$y == 1} {set x 1} | ||
|  |     if {$y == -1} {set x -1} | ||
|  |     incr r $x | ||
|  |   } else { | ||
|  |     incr r $x | ||
|  |     incr c $y | ||
|  |     if { $c < 0 } { | ||
|  |       if {$r == 0} { | ||
|  | 	set c 0 | ||
|  |       } else { | ||
|  | 	set c [expr {$Numcols - 1}] | ||
|  | 	incr r -1 | ||
|  |       } | ||
|  |     } elseif { $c >= $Numcols } { | ||
|  |       if {$r >= [expr {$Numrows - 1}]} { | ||
|  | 	set c [expr {$Numcols - 1}] | ||
|  |       } else { | ||
|  | 	set c 0 | ||
|  | 	incr r | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  |   if { $r < 0 } { set r 0 } | ||
|  |   $w activate $r,$c | ||
|  |   $w see active | ||
|  | } | ||
|  | 
 | ||
|  | # ------------------------------------------------------------ | ||
|  | #  PUBLIC METHOD:  error_dialog - Open and error dialog. | ||
|  | #    Arguments: | ||
|  | #        msg      - The message to display in the dialog | ||
|  | #        modality - The dialog modailty. Default: task | ||
|  | #        type     - The dialog type (tk_messageBox). | ||
|  | #                    Default: ok | ||
|  | # ------------------------------------------------------------ | ||
|  | itcl::body MemWin::error_dialog {msg {modality task} {type ok}} { | ||
|  |   set parent [winfo toplevel [namespace tail $this]] | ||
|  |   tk_messageBox -icon error -title Error -type $type \ | ||
|  |     -message $msg -parent $parent | ||
|  | } | ||
|  | 
 |