import gdb
import gdb.types
import gtk
+import cairo
import pango
import pangocairo
import math
class GraphViewerNode():
def __init__(self, x, y, name, gdb_value):
- print "node created: %d, %d, %s, %s"% (x, y, name, gdb_value.address)
self.x = x
self.y = y
self.width = 30
-class GraphViewer(gtk.DrawingArea):
+class GraphViewer(gtk.Layout):
def __init__(self, value_viewer):
- super(gtk.DrawingArea, self).__init__()
+ super(gtk.Layout, self).__init__()
self.nodes = []
- self.selected_node = None
+ self.selected_node = None # node selected by clicking on it
+ self.highlighted_node = None # node highlighted by selecting its address on the treeview
self.value_viewer = value_viewer
self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.connect("expose_event", self.on_expose)
self.connect("button_press_event", self.on_click)
def on_expose(self, widget, event):
- context = pangocairo.CairoContext(widget.window.cairo_create())
+ context = pangocairo.CairoContext(widget.bin_window.cairo_create())
context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
context.clip()
+ context.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
self.draw(context)
return False
def on_click(self, widget, event):
- self.selected_node = None
+ new_selected_node = None
for node in self.nodes:
if event.x >= node.x and event.x < node.x + node.width and event.y >= node.y and event.y < node.y + node.height:
- self.selected_node = node
+ new_selected_node = node
- if self.selected_node != None:
- self.value_viewer.set_value(self.selected_node.gdb_value)
+ if new_selected_node is not self.selected_node:
+ self.selected_node = new_selected_node
+ if self.selected_node is not None:
+ self.value_viewer.set_value(self.selected_node.gdb_value)
+
+ self.queue_draw()
- self.queue_draw()
return False
name_layout = context.create_layout()
name_layout.set_font_description(pango.FontDescription("sans 8"))
display_name = str(node.gdb_value.address) #node.name
+ #display_name = node.name
if node is not self.selected_node:
template_args_start = display_name.find("<")
if template_args_start != -1:
if node is self.selected_node:
context.set_source_rgb(1,0,0)
context.set_line_width(3)
+ elif node is self.highlighted_node:
+ context.set_source_rgb(0.3,0.7,0.4)
+ context.set_line_width(3)
context.rectangle(node.x, node.y, node.width, node.height)
context.stroke()
context.restore()
vy = p2[1]-p1[1]
name_layout = context.create_layout()
name_layout.set_font_description(pango.FontDescription("sans 8"))
- name_layout.set_text(name)
+ name_layout.set_text(u"\u2192"+name)
_, name_height = name_layout.get_pixel_size()
- context.move_to((1-NAME_OFFSET)*p1[0] + NAME_OFFSET*p2[0], (1-NAME_OFFSET)*p1[1] + NAME_OFFSET*p2[1])
- context.rotate(-orientation(vx, vy))
+ rotation = -orientation(vx, vy)
+ y_offset = -name_height - 2 # a couple of extra pixels to avoid text "stepping on the line"
+
+ # cairo seems to ignore context.translate for text ops, but set_matrix works ok :D
+ transform = cairo.Matrix()
+ transform.translate((1-NAME_OFFSET)*p1[0] + NAME_OFFSET*p2[0], (1-NAME_OFFSET)*p1[1] + NAME_OFFSET*p2[1] )
+ transform.rotate(rotation)
+ transform.translate(0, y_offset)
+ context.set_matrix(transform)
+ context.set_source_rgb(1,0,0)
context.show_layout(name_layout)
context.restore()
self.set_search_column(0)
self.field_column.set_sort_column_id(0)
+ self.selection = self.get_selection()
+ self.selection.set_mode(gtk.SELECTION_SINGLE)
+ self.selection.connect("changed", self.on_selection_changed)
+
+ def on_selection_changed(self, selection):
+ (model, iterator) = selection.get_selected()
+ new_highlighted_node = None
+
+ if iterator is not None:
+ value = model.get_value(iterator, 1)
+ for node in self.graph_viewer.nodes:
+ if str(node.gdb_value.address) == value:
+ new_highlighted_node = node
+
+ if self.graph_viewer.highlighted_node is not new_highlighted_node:
+ self.graph_viewer.highlighted_node = new_highlighted_node
+ self.graph_viewer.queue_draw()
+
+ def set_graph_viewer(self, graph_viewer):
+ self.graph_viewer = graph_viewer
+
def visit_value(self, value, store_iter):
if type_has_fields(value.type):
for f in value.type.fields():
def __init__(self):
super(gtk.Window, self).__init__()
self.entry = gtk.Entry()
+ self.entry.connect("activate", self.on_entry_changed)
self.properties = GdbValueViewer()
- self.viewer = GraphViewer(self.properties)
+ self.viewer = GraphViewer(self.properties) # node viewer changes tree values when clicking on nodes
+ self.properties.set_graph_viewer(self.viewer) # tree viewer can highlight nodes
self.vbox = gtk.VBox()
self.paned = gtk.HPaned()
labelhbox = gtk.HBox()
self.set_default_size(1000,600)
self.paned.set_position(750)
+ def on_entry_changed(self, entry):
+ expression = entry.get_text()
+ value = gdb.parse_and_eval(expression)
+ nodes = create_viewer_nodes(value, self.recursion_level)
+ self.viewer.nodes = nodes
+ self.viewer.queue_draw()
+
def visit_values(node_dict, value, x, y, recursion_level):
"""
Recursively traverse children of a gdb Value, storing pairs (address, GraphViewerNode).
def create_viewer_nodes(value, recursion_level):
# create the nodes
- print "creating nodes"
nodes = {}
width, height = visit_values(nodes, value, 50, 50, recursion_level)
for addr in nodes:
value = nodes[addr].gdb_value
if type_is_pointer(value.type) and value != 0:
- print "pointer node"
nodes[addr].connections["*"] = nodes[str(value)]
else:
- print "struct node@%s: %s"%(addr,value)
for f in value.type.fields():
child = value[f.name]
if type_is_pointer(child.type):
if child != 0:
- print "connection %s --> %s"%(addr, child), value, f.name
if str(child) in nodes:
nodes[addr].connections[f.name] = nodes[str(child)]
else:
recursion_level = 10
+
is_gtk_main_running = False
try:
window = ViewerWindow()
window.connect("destroy", gtk.main_quit)
+ window.recursion_level = recursion_level
window.show_all()
nodes = create_viewer_nodes(value, recursion_level)