summaryrefslogtreecommitdiffstats
path: root/unit_trace/viz/viewer.py
diff options
context:
space:
mode:
authorGary Bressler <garybressler@nc.rr.com>2010-04-08 17:11:08 -0400
committerGary Bressler <garybressler@nc.rr.com>2010-04-08 17:11:08 -0400
commitceff6457bfeb5642616f4711f14e0bb652d12164 (patch)
treed05b4ebd1c3ee6e28884c669d65fd31700941086 /unit_trace/viz/viewer.py
parent01abc8352aa2fd192678b4066b26ea749a203801 (diff)
Updated the documentation to describe the visualizer, made unit-trace itself not require gtk/cairo, and a few other minor things.
Diffstat (limited to 'unit_trace/viz/viewer.py')
-rw-r--r--unit_trace/viz/viewer.py409
1 files changed, 249 insertions, 160 deletions
diff --git a/unit_trace/viz/viewer.py b/unit_trace/viz/viewer.py
index 9b8502a..4d1fb7f 100644
--- a/unit_trace/viz/viewer.py
+++ b/unit_trace/viz/viewer.py
@@ -3,8 +3,8 @@
3"""GUI stuff.""" 3"""GUI stuff."""
4 4
5from schedule import * 5from schedule import *
6
7from renderer import * 6from renderer import *
7from windows import *
8 8
9import pygtk 9import pygtk
10import gtk 10import gtk
@@ -13,145 +13,157 @@ import copy
13 13
14class GraphContextMenu(gtk.Menu): 14class GraphContextMenu(gtk.Menu):
15 MAX_STR_LEN = 80 15 MAX_STR_LEN = 80
16 16
17 def __init__(self, selected): 17 def __init__(self, selected, info_win):
18 super(GraphContextMenu, self).__init__() 18 super(GraphContextMenu, self).__init__()
19 19
20 self.info_win = info_win
21
20 if not selected: 22 if not selected:
21 item = gtk.MenuItem("(No events selected)") 23 item = gtk.MenuItem("(No events selected)")
22 item.set_sensitive(False) 24 item.set_sensitive(False)
23 self.append(item) 25 self.append(item)
24 item.show() 26 item.show()
25 else: 27 else:
26 for layer in selected: 28 for layer in selected:
27 for event in selected[layer]: 29 for event in selected[layer]:
28 string = str(event) 30 string = str(event)
29 if len(string) > GraphContextMenu.MAX_STR_LEN - 3: 31 if len(string) > GraphContextMenu.MAX_STR_LEN - 3:
30 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...' 32 string = string[:GraphContextMenu.MAX_STR_LEN - 3] + '...'
31 item = gtk.MenuItem(string) 33 item = gtk.MenuItem(string)
34 item.connect('activate', self.update_info_window, event)
32 self.append(item) 35 self.append(item)
33 item.show() 36 item.show()
34 37
38 def update_info_window(self, widget, data):
39 self.info_win.set_event(data)
40 self.info_win.present()
41
35class GraphArea(gtk.DrawingArea): 42class GraphArea(gtk.DrawingArea):
36 HORIZ_PAGE_SCROLL_FACTOR = 10.8 43 HORIZ_PAGE_SCROLL_FACTOR = 10.8
37 HORIZ_STEP_SCROLL_FACTOR = 0.8 44 HORIZ_STEP_SCROLL_FACTOR = 0.8
38 VERT_PAGE_SCROLL_FACTOR = 3.0 45 VERT_PAGE_SCROLL_FACTOR = 3.0
39 VERT_STEP_SCROLL_FACTOR = 0.5 46 VERT_STEP_SCROLL_FACTOR = 0.5
40 47
41 REFRESH_INFLATION_FACTOR = 4.0 48 REFRESH_INFLATION_FACTOR = 4.0
42 49
50 MIN_ZOOM_OUT = 0.25
51 MAX_ZOOM_IN = 4.0
52 ZOOM_INCR = 0.25
53
43 def __init__(self, renderer): 54 def __init__(self, renderer):
44 super(GraphArea, self).__init__() 55 super(GraphArea, self).__init__()
45 56
46 self.renderer = renderer 57 self.renderer = renderer
47 58
48 self.cur_x = 0 59 self.cur_x = 0
49 self.cur_y = 0 60 self.cur_y = 0
50 self.width = 0 61 self.width = 0
51 self.height = 0 62 self.height = 0
52 self.scale = 1.0 63 self.scale = 1.0
53 64
54 self.set_set_scroll_adjustments_signal('set-scroll-adjustments') 65 self.set_set_scroll_adjustments_signal('set-scroll-adjustments')
55 66
56 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | 67 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK |
57 gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK) 68 gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.EXPOSURE_MASK)
58 69
59 self.band_rect = None 70 self.band_rect = None
60 self.ctrl_clicked = False 71 self.ctrl_clicked = False
61 self.last_selected = {} 72 self.last_selected = {}
62 self.dirtied_regions = [] 73 self.dirtied_regions = []
63 74
64 self.connect('expose-event', self.expose) 75 self.connect('expose-event', self.expose)
65 self.connect('size-allocate', self.size_allocate) 76 self.connect('size-allocate', self.size_allocate)
66 self.connect('set-scroll-adjustments', self.set_scroll_adjustments) 77 self.connect('set-scroll-adjustments', self.set_scroll_adjustments)
67 self.connect('button-press-event', self.button_press) 78 self.connect('button-press-event', self.button_press)
68 self.connect('button-release-event', self.button_release) 79 self.connect('button-release-event', self.button_release)
69 self.connect('motion-notify-event', self.motion_notify) 80 self.connect('motion-notify-event', self.motion_notify)
70 81
71 def expose(self, widget, expose_event, data=None): 82 def expose(self, widget, expose_event, data=None):
72 ctx = widget.window.cairo_create() 83 ctx = widget.window.cairo_create()
73 graph = self.renderer.get_graph() 84 graph = self.renderer.get_graph()
74 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx) 85 graph.update_view(self.cur_x, self.cur_y, self.width, self.height, self.scale, ctx)
75 86
76 # We ourselves didn't update dirtied_regions, so this means that X or the 87 # If X caused the expose event, we need to update the entire area, not just the
77 # window manager must have caused the expose event. So just update the 88 # changes we might have made. An expose event caused by X needs to take priority
78 # expose_event's bounding area. 89 # over any expose events caused by updates to the state of the graph because
79 if not self.dirtied_regions or expose_event.send_event: 90 # the areas we marked as dirty only include the state changes, which is completely
91 # unrelated to the area that X indicates must be updated.
92 if expose_event.type == gtk.gdk.EXPOSE:
80 self.dirtied_regions = [(expose_event.area.x, expose_event.area.y, 93 self.dirtied_regions = [(expose_event.area.x, expose_event.area.y,
81 expose_event.area.width, expose_event.area.height)] 94 expose_event.area.width, expose_event.area.height)]
82 95
83 graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions) 96 graph.render_surface(self.renderer.get_schedule(), self.dirtied_regions)
84 97
85 # render dragging band rectangle, if there is one 98 # render dragging band rectangle, if there is one
86 if self.band_rect is not None: 99 if self.band_rect is not None:
87 x, y, width, height = self.band_rect 100 x, y, width, height = self.band_rect
88 thickness = GraphFormat.BAND_THICKNESS 101 thickness = GraphFormat.BAND_THICKNESS
89 color = GraphFormat.BAND_COLOR 102 color = GraphFormat.BAND_COLOR
90 103
91 ctx.rectangle(x, y, width, height) 104 ctx.rectangle(x, y, width, height)
92 ctx.set_line_width(thickness) 105 ctx.set_line_width(thickness)
93 ctx.set_source_rgb(color[0], color[1], color[2]) 106 ctx.set_source_rgb(color[0], color[1], color[2])
94 ctx.stroke() 107 ctx.stroke()
95 108
96 self.dirtied_regions = [] 109 self.dirtied_regions = []
97 110
98 def get_renderer(self): 111 def get_renderer(self):
99 return self.renderer 112 return self.renderer
100 113
101 def get_graph(self): 114 def get_graph(self):
102 return self.renderer.get_graph() 115 return self.renderer.get_graph()
103 116
104 MIN_ZOOM_OUT = 0.25 117 def get_schedule(self):
105 MAX_ZOOM_IN = 4.0 118 return self.renderer.get_schedule()
106 ZOOM_INCR = 0.25 119
107
108 def zoom_in(self): 120 def zoom_in(self):
109 scale = self.scale + GraphArea.ZOOM_INCR 121 scale = self.scale + GraphArea.ZOOM_INCR
110 if scale > GraphArea.MAX_ZOOM_IN: 122 if scale > GraphArea.MAX_ZOOM_IN:
111 scale = GraphArea.MAX_ZOOM_IN 123 scale = GraphArea.MAX_ZOOM_IN
112 self.set_scale(scale) 124 self.set_scale(scale)
113 125
114 def zoom_out(self): 126 def zoom_out(self):
115 scale = self.scale - GraphArea.ZOOM_INCR 127 scale = self.scale - GraphArea.ZOOM_INCR
116 if scale < GraphArea.MIN_ZOOM_OUT: 128 if scale < GraphArea.MIN_ZOOM_OUT:
117 scale = GraphArea.MIN_ZOOM_OUT 129 scale = GraphArea.MIN_ZOOM_OUT
118 self.set_scale(scale) 130 self.set_scale(scale)
119 131
120 def set_scale(self, scale): 132 def set_scale(self, scale):
121 if scale == self.scale: 133 if scale == self.scale:
122 return 134 return
123 135
124 self.scale = scale 136 self.scale = scale
125 self._dirty(0, 0, self.width, self.height) 137 self._dirty(0, 0, self.width, self.height)
126 138
127 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None): 139 def set_scroll_adjustments(self, widget, horizontal, vertical, data=None):
128 graph = self.renderer.get_graph() 140 graph = self.renderer.get_graph()
129 width = graph.get_width() 141 width = graph.get_width()
130 height = graph.get_height() 142 height = graph.get_height()
131 143
132 self.horizontal = horizontal 144 self.horizontal = horizontal
133 self.vertical = vertical 145 self.vertical = vertical
134 self.config_scrollbars(self.cur_x, self.cur_y) 146 self.config_scrollbars(self.cur_x, self.cur_y)
135 147
136 if self.horizontal is not None: 148 if self.horizontal is not None:
137 self.horizontal.connect('value-changed', self.horizontal_value_changed) 149 self.horizontal.connect('value-changed', self.horizontal_value_changed)
138 if self.vertical is not None: 150 if self.vertical is not None:
139 self.vertical.connect('value-changed', self.vertical_value_changed) 151 self.vertical.connect('value-changed', self.vertical_value_changed)
140 152
141 def horizontal_value_changed(self, adjustment): 153 def horizontal_value_changed(self, adjustment):
142 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width()) 154 self.cur_x = min(adjustment.value, self.renderer.get_graph().get_width())
143 self.cur_x = max(adjustment.value, 0.0) 155 self.cur_x = max(adjustment.value, 0.0)
144 156
145 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) 157 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True)
146 self._dirty(0, 0, self.width, self.height) 158 self._dirty(0, 0, self.width, self.height)
147 159
148 def vertical_value_changed(self, adjustment): 160 def vertical_value_changed(self, adjustment):
149 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height()) 161 self.cur_y = min(adjustment.value, self.renderer.get_graph().get_height())
150 self.cur_y = max(adjustment.value, 0.0) 162 self.cur_y = max(adjustment.value, 0.0)
151 163
152 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True) 164 self.renderer.get_graph().render_surface(self.renderer.get_schedule(), [(0, 0, self.width, self.height)], True)
153 self._dirty(0, 0, self.width, self.height) 165 self._dirty(0, 0, self.width, self.height)
154 166
155 def size_allocate(self, widget, allocation): 167 def size_allocate(self, widget, allocation):
156 self.width = allocation.width 168 self.width = allocation.width
157 self.height = allocation.height 169 self.height = allocation.height
@@ -161,7 +173,7 @@ class GraphArea(gtk.DrawingArea):
161 graph = self.renderer.get_graph() 173 graph = self.renderer.get_graph()
162 width = graph.get_width() 174 width = graph.get_width()
163 height = graph.get_height() 175 height = graph.get_height()
164 176
165 if self.horizontal is not None: 177 if self.horizontal is not None:
166 self.horizontal.set_all(hvalue, 0.0, width + self.width, 178 self.horizontal.set_all(hvalue, 0.0, width + self.width,
167 graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR, 179 graph.get_attrs().maj_sep * GraphArea.HORIZ_STEP_SCROLL_FACTOR,
@@ -169,8 +181,8 @@ class GraphArea(gtk.DrawingArea):
169 if self.vertical is not None: 181 if self.vertical is not None:
170 self.vertical.set_all(vvalue, 0.0, height + self.height, 182 self.vertical.set_all(vvalue, 0.0, height + self.height,
171 graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR, 183 graph.get_attrs().y_item_size * GraphArea.VERT_STEP_SCROLL_FACTOR,
172 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height) 184 graph.get_attrs().y_item_size * GraphArea.VERT_PAGE_SCROLL_FACTOR, self.height)
173 185
174 def refresh_events(self, sender, new, old, replace): 186 def refresh_events(self, sender, new, old, replace):
175 """Even if the selected areas change on one graph, they change 187 """Even if the selected areas change on one graph, they change
176 everywhere, and different events might be in different regions on 188 everywhere, and different events might be in different regions on
@@ -182,9 +194,9 @@ class GraphArea(gtk.DrawingArea):
182 refreshes the drawing of the graph that requested the change.""" 194 refreshes the drawing of the graph that requested the change."""
183 if self is not sender: 195 if self is not sender:
184 self.renderer.get_graph().render_events(new, True) 196 self.renderer.get_graph().render_events(new, True)
185 197
186 self._tag_events(new) 198 self._tag_events(new)
187 199
188 if self is sender: 200 if self is sender:
189 self._copy_tags(old) 201 self._copy_tags(old)
190 self._dirty_events(new) 202 self._dirty_events(new)
@@ -194,14 +206,14 @@ class GraphArea(gtk.DrawingArea):
194 else: 206 else:
195 self.renderer.get_schedule().remove_selected(old) 207 self.renderer.get_schedule().remove_selected(old)
196 self.renderer.get_schedule().add_selected(new) 208 self.renderer.get_schedule().add_selected(new)
197 209
198 def _find_max_layer(self, regions): 210 def _find_max_layer(self, regions):
199 max_layer = Canvas.BOTTOM_LAYER 211 max_layer = Canvas.BOTTOM_LAYER
200 for event in regions: 212 for event in regions:
201 if event.get_layer() > max_layer: 213 if event.get_layer() > max_layer:
202 max_layer = event.get_layer() 214 max_layer = event.get_layer()
203 return max_layer 215 return max_layer
204 216
205 def _dirty_events(self, events): 217 def _dirty_events(self, events):
206 # if an event changed selected status, update the bounding area 218 # if an event changed selected status, update the bounding area
207 for layer in events: 219 for layer in events:
@@ -212,7 +224,7 @@ class GraphArea(gtk.DrawingArea):
212 width * self.scale, 224 width * self.scale,
213 height * self.scale, 225 height * self.scale,
214 GraphFormat.BORDER_THICKNESS * self.scale) 226 GraphFormat.BORDER_THICKNESS * self.scale)
215 227
216 def _tag_events(self, selected): 228 def _tag_events(self, selected):
217 """Some of the events in the collection of selected events might be new. 229 """Some of the events in the collection of selected events might be new.
218 In this case, these events are not yet associated with the region on 230 In this case, these events are not yet associated with the region on
@@ -224,7 +236,7 @@ class GraphArea(gtk.DrawingArea):
224 # note that each graph has its own region associated 236 # note that each graph has its own region associated
225 # with the event 237 # with the event
226 selected[layer][event][self] = graph.get_sel_region(event) 238 selected[layer][event][self] = graph.get_sel_region(event)
227 239
228 def _copy_tags(self, selected): 240 def _copy_tags(self, selected):
229 """When we want to specify a collection of selected events to perform 241 """When we want to specify a collection of selected events to perform
230 an operation on, we usually do not know ahead of time what regions (in 242 an operation on, we usually do not know ahead of time what regions (in
@@ -235,21 +247,21 @@ class GraphArea(gtk.DrawingArea):
235 for layer in selected: 247 for layer in selected:
236 for event in selected[layer]: 248 for event in selected[layer]:
237 selected[layer][event] = cur_selected[layer][event] 249 selected[layer][event] = cur_selected[layer][event]
238 250
239 def _select_event(self, coll, event): 251 def _select_event(self, coll, event):
240 if event.get_layer() not in coll: 252 if event.get_layer() not in coll:
241 coll[event.get_layer()] = {} 253 coll[event.get_layer()] = {}
242 if event not in coll[event.get_layer()]: 254 if event not in coll[event.get_layer()]:
243 coll[event.get_layer()][event] = {} 255 coll[event.get_layer()][event] = {}
244 256
245 def motion_notify(self, widget, motion_event, data=None): 257 def motion_notify(self, widget, motion_event, data=None):
246 msg = None 258 msg = None
247 259
248 graph = self.renderer.get_graph() 260 graph = self.renderer.get_graph()
249 261
250 graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y, 262 graph.render_surface(self.renderer.get_schedule(), [(motion_event.x, motion_event.y,
251 0, 0)], True) 263 0, 0)], True)
252 264
253 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0) 265 just_selected = graph.get_selected_regions(motion_event.x, motion_event.y, 0, 0)
254 was_selected = self.renderer.get_schedule().get_selected() 266 was_selected = self.renderer.get_schedule().get_selected()
255 if not just_selected: 267 if not just_selected:
@@ -257,38 +269,38 @@ class GraphArea(gtk.DrawingArea):
257 the_event = None 269 the_event = None
258 else: 270 else:
259 max_layer = self._find_max_layer(just_selected) 271 max_layer = self._find_max_layer(just_selected)
260 272
261 for event in just_selected: 273 for event in just_selected:
262 if event.get_layer() == max_layer: 274 if event.get_layer() == max_layer:
263 the_event = event 275 the_event = event
264 break 276 break
265 277
266 msg = str(the_event) 278 msg = str(the_event)
267 279
268 self.emit('update-event-description', the_event, msg) 280 self.emit('update-event-description', the_event, msg)
269 281
270 if self.band_rect is not None: 282 if self.band_rect is not None:
271 remove_selected = {} 283 remove_selected = {}
272 add_selected = {} 284 add_selected = {}
273 285
274 # dragging a rectangle 286 # dragging a rectangle
275 x = self.band_rect[0] 287 x = self.band_rect[0]
276 y = self.band_rect[1] 288 y = self.band_rect[1]
277 width = motion_event.x - self.band_rect[0] 289 width = motion_event.x - self.band_rect[0]
278 height = motion_event.y - self.band_rect[1] 290 height = motion_event.y - self.band_rect[1]
279 old_x, old_y, old_width, old_height = self.band_rect 291 old_x, old_y, old_width, old_height = self.band_rect
280 292
281 x_p, y_p, width_p, height_p = self._positivify(x, y, width, height) 293 x_p, y_p, width_p, height_p = self._positivify(x, y, width, height)
282 old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height) 294 old_x_p, old_y_p, old_width_p, old_height_p = self._positivify(old_x, old_y, old_width, old_height)
283 x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p) 295 x_p, y_p, width_p, height_p = int(x_p), int(y_p), int(width_p), int(height_p)
284 old_x_p, old_y_p, old_width_p, old_height_p = int(old_x_p), int(old_y_p), int(old_width_p), int(old_height_p) 296 old_x_p, old_y_p, old_width_p, old_height_p = int(old_x_p), int(old_y_p), int(old_width_p), int(old_height_p)
285 297
286 new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) 298 new_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p))
287 old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) 299 old_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p))
288 300
289 # To find the events that should be deselected and the new events that should be selected, compute 301 # To find the events that should be deselected and the new events that should be selected, compute
290 # the set differences between the old and new selection rectangles 302 # the set differences between the old and new selection rectangles
291 303
292 remove_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p)) 304 remove_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(old_x_p, old_y_p, old_width_p, old_height_p))
293 remove_reg.subtract(new_reg) 305 remove_reg.subtract(new_reg)
294 dirty_list = [] 306 dirty_list = []
@@ -300,7 +312,7 @@ class GraphArea(gtk.DrawingArea):
300 for event in graph.get_selected_regions(rx, ry, rwidth, rheight): 312 for event in graph.get_selected_regions(rx, ry, rwidth, rheight):
301 if event.get_layer() in was_selected and event in was_selected[event.get_layer()]: 313 if event.get_layer() in was_selected and event in was_selected[event.get_layer()]:
302 self._select_event(remove_selected, event) 314 self._select_event(remove_selected, event)
303 315
304 add_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p)) 316 add_reg = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(x_p, y_p, width_p, height_p))
305 add_reg.subtract(old_reg) 317 add_reg.subtract(old_reg)
306 dirty_list = [(x_p, y_p, width_p, 0), (x_p, y_p, 0, height_p), 318 dirty_list = [(x_p, y_p, width_p, 0), (x_p, y_p, 0, height_p),
@@ -312,38 +324,38 @@ class GraphArea(gtk.DrawingArea):
312 rx, ry, rwidth, rheight = rect 324 rx, ry, rwidth, rheight = rect
313 for event in graph.get_selected_regions(rx, ry, rwidth, rheight): 325 for event in graph.get_selected_regions(rx, ry, rwidth, rheight):
314 self._select_event(add_selected, event) 326 self._select_event(add_selected, event)
315 327
316 self.band_rect = x, y, width, height 328 self.band_rect = x, y, width, height
317 self.emit('request-refresh-events', self, add_selected, remove_selected, False) 329 self.emit('request-refresh-events', self, add_selected, remove_selected, False)
318 330
319 self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS) 331 self._dirty_rect_border(old_x, old_y, old_width, old_height, GraphFormat.BAND_THICKNESS)
320 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) 332 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS)
321 333
322 def button_press(self, widget, button_event, data=None): 334 def button_press(self, widget, button_event, data=None):
323 graph = self.renderer.get_graph() 335 graph = self.renderer.get_graph()
324 336
325 self.ctrl_clicked = button_event.state & gtk.gdk.CONTROL_MASK 337 self.ctrl_clicked = button_event.state & gtk.gdk.CONTROL_MASK
326 338
327 if button_event.button == 1: 339 if button_event.button == 1:
328 self.left_button_start_coor = (button_event.x, button_event.y) 340 self.left_button_start_coor = (button_event.x, button_event.y)
329 graph.render_surface(self.renderer.get_schedule(), \ 341 graph.render_surface(self.renderer.get_schedule(), \
330 [(button_event.x, button_event.y, 0, 0)], True) 342 [(button_event.x, button_event.y, 0, 0)], True)
331 343
332 just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0) 344 just_selected = graph.get_selected_regions(button_event.x, button_event.y, 0, 0)
333 345
334 max_layer = self._find_max_layer(just_selected) 346 max_layer = self._find_max_layer(just_selected)
335 347
336 was_selected = self.renderer.get_schedule().get_selected() 348 was_selected = self.renderer.get_schedule().get_selected()
337 if not self.ctrl_clicked: 349 if not self.ctrl_clicked:
338 new_now_selected = {} 350 new_now_selected = {}
339 351
340 more_than_one = 0 352 more_than_one = 0
341 for layer in was_selected: 353 for layer in was_selected:
342 for event in was_selected[layer]: 354 for event in was_selected[layer]:
343 more_than_one += 1 355 more_than_one += 1
344 if more_than_one > 1: 356 if more_than_one > 1:
345 break 357 break
346 358
347 # only select those events which were in the top layer (it's 359 # only select those events which were in the top layer (it's
348 # not intuitive to click something and then have something 360 # not intuitive to click something and then have something
349 # below it get selected). Also, clicking something that 361 # below it get selected). Also, clicking something that
@@ -353,12 +365,12 @@ class GraphArea(gtk.DrawingArea):
353 if not (more_than_one == 1 and event in was_selected): 365 if not (more_than_one == 1 and event in was_selected):
354 self._select_event(new_now_selected, event) 366 self._select_event(new_now_selected, event)
355 break # only pick one event when just clicking 367 break # only pick one event when just clicking
356 368
357 self.emit('request-refresh-events', self, new_now_selected, was_selected, True) 369 self.emit('request-refresh-events', self, new_now_selected, was_selected, True)
358 else: 370 else:
359 remove_selected = {} 371 remove_selected = {}
360 add_selected = {} 372 add_selected = {}
361 373
362 for event in just_selected: 374 for event in just_selected:
363 layer = event.get_layer() 375 layer = event.get_layer()
364 if layer == max_layer: 376 if layer == max_layer:
@@ -367,39 +379,45 @@ class GraphArea(gtk.DrawingArea):
367 else: 379 else:
368 self._select_event(add_selected, event) 380 self._select_event(add_selected, event)
369 break # again, only pick one event because we are just clicking 381 break # again, only pick one event because we are just clicking
370 382
371 self.emit('request-refresh-events', self, add_selected, remove_selected, False) 383 self.emit('request-refresh-events', self, add_selected, remove_selected, False)
372 384
373 if self.band_rect is None: 385 if self.band_rect is None:
374 self.band_rect = (button_event.x, button_event.y, 0, 0) 386 self.band_rect = (button_event.x, button_event.y, 0, 0)
375 387
376 elif button_event.button == 3: 388 elif button_event.button == 3:
377 self._release_band() 389 self._release_band()
378 self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected()) 390 self.emit('request-context-menu', button_event, self.renderer.get_schedule().get_selected())
379 391
380 def button_release(self, widget, button_event, data=None): 392 def button_release(self, widget, button_event, data=None):
381 self.ctrl_clicked = False 393 self.ctrl_clicked = False
382 394
383 if button_event.button == 1: 395 if button_event.button == 1:
384 self._release_band() 396 self._release_band()
385 397
398 def get_width(self):
399 return self.width
400
401 def get_height(self):
402 return self.height
403
386 def _release_band(self): 404 def _release_band(self):
387 if self.band_rect is not None: 405 if self.band_rect is not None:
388 x, y, width, height = self.band_rect 406 x, y, width, height = self.band_rect
389 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS) 407 self._dirty_rect_border(x, y, width, height, GraphFormat.BAND_THICKNESS)
390 self.band_rect = None 408 self.band_rect = None
391 409
392 def _dirty(self, x, y, width, height): 410 def _dirty(self, x, y, width, height):
393 x = max(int(math.floor(x)), 0) 411 x = max(int(math.floor(x)), 0)
394 y = max(int(math.floor(y)), 0) 412 y = max(int(math.floor(y)), 0)
395 width = min(int(math.ceil(width)), self.width) 413 width = min(int(math.ceil(width)), self.width)
396 height = min(int(math.ceil(height)), self.height) 414 height = min(int(math.ceil(height)), self.height)
397 415
398 self.dirtied_regions.append((x, y, width, height)) 416 self.dirtied_regions.append((x, y, width, height))
399 417
400 rect = gtk.gdk.Rectangle(x, y, width, height) 418 rect = gtk.gdk.Rectangle(x, y, width, height)
401 self.window.invalidate_rect(rect, True) 419 self.window.invalidate_rect(rect, True)
402 420
403 def _dirty_inflate(self, x, y, width, height, thickness): 421 def _dirty_inflate(self, x, y, width, height, thickness):
404 t = thickness * GraphArea.REFRESH_INFLATION_FACTOR 422 t = thickness * GraphArea.REFRESH_INFLATION_FACTOR
405 x -= t / 2.0 423 x -= t / 2.0
@@ -407,17 +425,17 @@ class GraphArea(gtk.DrawingArea):
407 width += t 425 width += t
408 height += t 426 height += t
409 self._dirty(x, y, width, height) 427 self._dirty(x, y, width, height)
410 428
411 def _dirty_rect_border(self, x, y, width, height, thickness): 429 def _dirty_rect_border(self, x, y, width, height, thickness):
412 # support rectangles with negative width and height (i.e. -width = width, but going leftwards 430 # support rectangles with negative width and height (i.e. -width = width, but going leftwards
413 # instead of rightwards) 431 # instead of rightwards)
414 x, y, width, height = self._positivify(x, y, width, height) 432 x, y, width, height = self._positivify(x, y, width, height)
415 433
416 self._dirty_inflate(x, y, width, 0, thickness) 434 self._dirty_inflate(x, y, width, 0, thickness)
417 self._dirty_inflate(x, y, 0, height, thickness) 435 self._dirty_inflate(x, y, 0, height, thickness)
418 self._dirty_inflate(x, y + height, width, 0, thickness) 436 self._dirty_inflate(x, y + height, width, 0, thickness)
419 self._dirty_inflate(x + width, y, 0, height, thickness) 437 self._dirty_inflate(x + width, y, 0, height, thickness)
420 438
421 def _positivify(self, x, y, width, height): 439 def _positivify(self, x, y, width, height):
422 if width < 0: 440 if width < 0:
423 x += width 441 x += width
@@ -425,53 +443,65 @@ class GraphArea(gtk.DrawingArea):
425 if height < 0: 443 if height < 0:
426 y += height 444 y += height
427 height = -height 445 height = -height
428 446
429 return x, y, width, height 447 return x, y, width, height
430 448
431class GraphWindow(gtk.ScrolledWindow): 449class GraphWindow(gtk.ScrolledWindow):
432 def __init__(self, renderer): 450 def __init__(self, renderer):
433 super(GraphWindow, self).__init__(None, None) 451 super(GraphWindow, self).__init__(None, None)
434 452
435 self.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.SCROLL_MASK) 453 self.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.SCROLL_MASK)
436 454
437 self.ctr = 0 455 self.ctr = 0
438 self.connect('key-press-event', self.key_press) 456 self.connect('key-press-event', self.key_press)
439 self.connect('scroll-event', self.scroll) 457 self.connect('scroll-event', self.scroll)
440 458
441 self.garea = GraphArea(renderer) 459 self.garea = GraphArea(renderer)
442 self.add(self.garea) 460 self.add(self.garea)
443 self.garea.show() 461 self.garea.show()
444 462
445 def key_press(self, widget, key_event): 463 def key_press(self, widget, key_event):
446 hadj = self.get_hadjustment() 464 hadj = self.get_hadjustment()
447 vadj = self.get_vadjustment() 465 vadj = self.get_vadjustment()
448 if hadj is None or vadj is None: 466 if hadj is None or vadj is None:
449 return 467 return
450 468
451 ctrl_clicked = key_event.state & gtk.gdk.CONTROL_MASK 469 ctrl_clicked = key_event.state & gtk.gdk.CONTROL_MASK
452 470
453 keystr = None 471 keystr = None
454 keymap = {gtk.keysyms.Up : 'up', gtk.keysyms.Down : 'down', 472 keymap = {gtk.keysyms.Up : 'up', gtk.keysyms.Down : 'down',
455 gtk.keysyms.Left : 'left', gtk.keysyms.Right : 'right'} 473 gtk.keysyms.Left : 'left', gtk.keysyms.Right : 'right'}
456 if key_event.keyval in keymap: 474 if key_event.keyval in keymap:
457 keystr = keymap[key_event.keyval] 475 keystr = keymap[key_event.keyval]
458 else: 476 else:
459 return True 477 return True
460 478
461 if ctrl_clicked: 479 if ctrl_clicked:
462 keystr = 'ctrl-' + keystr 480 keystr = 'ctrl-' + keystr
463 481
464 if keystr is not None: 482 if keystr is not None:
465 self._scroll_direction(keystr) 483 self._scroll_direction(keystr)
466 484
467 return True 485 return True
468 486
487 def set_hvalue(self, value):
488 if self.get_hadjustment() is None:
489 return
490
491 self.get_hadjustment().set_value(value)
492
493 def set_vvalue(self, value):
494 if self.get_vadjustment() is None:
495 return
496
497 self.get_vadjustment().set_value(value)
498
469 def _scroll_direction(self, keystr): 499 def _scroll_direction(self, keystr):
470 hadj = self.get_hadjustment() 500 hadj = self.get_hadjustment()
471 vadj = self.get_vadjustment() 501 vadj = self.get_vadjustment()
472 if hadj is None or vadj is None: 502 if hadj is None or vadj is None:
473 return 503 return
474 504
475 hupper = hadj.get_upper() 505 hupper = hadj.get_upper()
476 hlower = hadj.get_lower() 506 hlower = hadj.get_lower()
477 hpincr = hadj.get_page_increment() 507 hpincr = hadj.get_page_increment()
@@ -484,7 +514,7 @@ class GraphWindow(gtk.ScrolledWindow):
484 vpincr = vadj.get_page_increment() 514 vpincr = vadj.get_page_increment()
485 vsincr = vadj.get_step_increment() 515 vsincr = vadj.get_step_increment()
486 vpsize = vadj.get_page_size() 516 vpsize = vadj.get_page_size()
487 517
488 adj_tuple = {'up' : (vadj, -vsincr, 0, vval, max), 518 adj_tuple = {'up' : (vadj, -vsincr, 0, vval, max),
489 'ctrl-up' : (vadj, -vpincr, 0, vval, max), 519 'ctrl-up' : (vadj, -vpincr, 0, vval, max),
490 'down' : (vadj, vsincr, vupper - vpsize, vval, min), 520 'down' : (vadj, vsincr, vupper - vpsize, vval, min),
@@ -493,10 +523,10 @@ class GraphWindow(gtk.ScrolledWindow):
493 'ctrl-left' : (hadj, -hpincr, 0, hval, max), 523 'ctrl-left' : (hadj, -hpincr, 0, hval, max),
494 'right' : (hadj, hsincr, hupper - hpsize, hval, min), 524 'right' : (hadj, hsincr, hupper - hpsize, hval, min),
495 'ctrl-right' : (hadj, hpincr, hupper - hpsize, hval, min)} 525 'ctrl-right' : (hadj, hpincr, hupper - hpsize, hval, min)}
496 526
497 adj, inc, lim, val, extr = adj_tuple[keystr] 527 adj, inc, lim, val, extr = adj_tuple[keystr]
498 adj.set_value(extr(val + inc, lim)) 528 adj.set_value(extr(val + inc, lim))
499 529
500 def scroll(self, widget, scroll_event): 530 def scroll(self, widget, scroll_event):
501 if scroll_event.state & gtk.gdk.CONTROL_MASK: 531 if scroll_event.state & gtk.gdk.CONTROL_MASK:
502 if scroll_event.direction == gtk.gdk.SCROLL_UP: 532 if scroll_event.direction == gtk.gdk.SCROLL_UP:
@@ -508,106 +538,119 @@ class GraphWindow(gtk.ScrolledWindow):
508 self._scroll_direction('up') 538 self._scroll_direction('up')
509 elif scroll_event.direction == gtk.gdk.SCROLL_DOWN: 539 elif scroll_event.direction == gtk.gdk.SCROLL_DOWN:
510 self._scroll_direction('down') 540 self._scroll_direction('down')
511 541
512 return True 542 return True
513 543
514 def get_graph_area(self): 544 def get_graph_area(self):
515 return self.garea 545 return self.garea
516 546
517class MainWindow(gtk.Window): 547class MainWindow(gtk.Window):
518 WINDOW_WIDTH_REQ = 500 548 WINDOW_WIDTH_REQ = 500
519 WINDOW_HEIGHT_REQ = 300 549 WINDOW_HEIGHT_REQ = 300
520 550
521 def __init__(self): 551 def __init__(self):
522 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL) 552 super(MainWindow, self).__init__(gtk.WINDOW_TOPLEVEL)
523 553
524 self.add_events(gtk.gdk.BUTTON_PRESS_MASK) 554 self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
525 555
526 self.connect('delete_event', self.delete_event) 556 self.connect('delete_event', self.delete_event)
527 self.connect('destroy', self.die) 557 self.connect('destroy', self.die)
528 558
529 file_menu = gtk.Menu() 559 file_menu = gtk.Menu()
530 view_menu = gtk.Menu() 560 view_menu = gtk.Menu()
531 561
532 agr = gtk.AccelGroup() 562 agr = gtk.AccelGroup()
533 self.add_accel_group(agr) 563 self.add_accel_group(agr)
534 564
535 quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr) 565 quit_item = gtk.ImageMenuItem(gtk.STOCK_QUIT, agr)
536 key, mod = gtk.accelerator_parse('Q') 566 key, mod = gtk.accelerator_parse('Q')
537 quit_item.add_accelerator('activate', agr, key, mod, 567 quit_item.add_accelerator('activate', agr, key, mod,
538 gtk.ACCEL_VISIBLE) 568 gtk.ACCEL_VISIBLE)
539 quit_item.connect('activate', self.quit_item_activate) 569 quit_item.connect('activate', self.quit_item_activate)
540 quit_item.show() 570 quit_item.show()
541 571
542 file_menu.append(quit_item) 572 file_menu.append(quit_item)
543 573
544 file_item = gtk.MenuItem('_File', True) 574 file_item = gtk.MenuItem('_File', True)
545 file_item.set_submenu(file_menu) 575 file_item.set_submenu(file_menu)
546 file_item.show() 576 file_item.show()
547 577
578 self.move_item = gtk.ImageMenuItem('_Move to Time')
579 key, mod = gtk.accelerator_parse('<Ctrl>M')
580 self.move_item.add_accelerator('activate', agr, key, mod,
581 gtk.ACCEL_VISIBLE)
582 self.move_item.set_sensitive(False)
583
584 self.move_item.connect('activate', self.move_to_time_activate)
585 self.move_item.show()
586
548 zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr) 587 zoom_in_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN, agr)
549 key, mod = gtk.accelerator_parse('<Ctrl>plus') 588 key, mod = gtk.accelerator_parse('<Ctrl>plus')
550 zoom_in_item.add_accelerator('activate', agr, key, mod, 589 zoom_in_item.add_accelerator('activate', agr, key, mod,
551 gtk.ACCEL_VISIBLE) 590 gtk.ACCEL_VISIBLE)
552 key, mod = gtk.accelerator_parse('<Ctrl>equal') 591 key, mod = gtk.accelerator_parse('<Ctrl>equal')
553 zoom_in_item.add_accelerator('activate', agr, key, mod, 0) 592 zoom_in_item.add_accelerator('activate', agr, key, mod, 0)
554 593
555 zoom_in_item.connect('activate', self.zoom_in_item_activate) 594 zoom_in_item.connect('activate', self.zoom_in_item_activate)
556 zoom_in_item.show() 595 zoom_in_item.show()
557 596
558 zoom_out_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_OUT, agr) 597 zoom_out_item = gtk.ImageMenuItem(gtk.STOCK_ZOOM_OUT, agr)
559 key, mod = gtk.accelerator_parse('<Ctrl>minus') 598 key, mod = gtk.accelerator_parse('<Ctrl>minus')
560 zoom_out_item.add_accelerator('activate', agr, key, mod, 599 zoom_out_item.add_accelerator('activate', agr, key, mod,
561 gtk.ACCEL_VISIBLE) 600 gtk.ACCEL_VISIBLE)
562 key, mod = gtk.accelerator_parse('<Ctrl>underscore') 601 key, mod = gtk.accelerator_parse('<Ctrl>underscore')
563 zoom_out_item.add_accelerator('activate', agr, key, mod, 0) 602 zoom_out_item.add_accelerator('activate', agr, key, mod, 0)
564 603
565 zoom_out_item.connect('activate', self.zoom_out_item_activate) 604 zoom_out_item.connect('activate', self.zoom_out_item_activate)
566 zoom_out_item.show() 605 zoom_out_item.show()
567 606
607 view_menu.append(self.move_item)
568 view_menu.append(zoom_in_item) 608 view_menu.append(zoom_in_item)
569 view_menu.append(zoom_out_item) 609 view_menu.append(zoom_out_item)
570 610
571 view_item = gtk.MenuItem('_View', True) 611 view_item = gtk.MenuItem('_View', True)
572 view_item.set_submenu(view_menu) 612 view_item.set_submenu(view_menu)
573 view_item.show() 613 view_item.show()
574 614
575 menu_bar = gtk.MenuBar() 615 menu_bar = gtk.MenuBar()
576 menu_bar.append(file_item) 616 menu_bar.append(file_item)
577 menu_bar.append(view_item) 617 menu_bar.append(view_item)
578 618
579 menu_bar.show() 619 menu_bar.show()
580 self.vbox = gtk.VBox(False, 0) 620 self.vbox = gtk.VBox(False, 0)
581 621
582 self.notebook = gtk.Notebook() 622 self.notebook = gtk.Notebook()
583 623
584 self.notebook.last_page = -1 624 self.notebook.last_page = -1
585 self.notebook.connect('switch-page', self.switch_page) 625 self.notebook.connect('switch-page', self.switch_page)
586 626
587 self.notebook.show() 627 self.notebook.show()
588 628
589 self.desc_label = gtk.Label('') 629 self.desc_label = gtk.Label('')
590 self.desc_label.set_justify(gtk.JUSTIFY_LEFT) 630 self.desc_label.set_alignment(0.0, 0.0)
591 self.desc_label.show() 631 self.desc_label.show()
592 632
593 self.vbox.pack_start(menu_bar, False, False, 0) 633 self.vbox.pack_start(menu_bar, False, False, 0)
594 self.vbox.pack_start(self.notebook, True, True, 0) 634 self.vbox.pack_start(self.notebook, True, True, 0)
595 self.vbox.pack_start(self.desc_label, False, False, 0) 635 self.vbox.pack_start(self.desc_label, False, False, 0)
596 self.vbox.show() 636 self.vbox.show()
597 637
598 self.add(self.vbox) 638 self.add(self.vbox)
599 639
640 self.info_win = InfoWindow()
641
600 self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ) 642 self.set_size_request(MainWindow.WINDOW_WIDTH_REQ, MainWindow.WINDOW_HEIGHT_REQ)
601 643
644 self.set_title('Unit-Trace Visualizer')
602 self.show() 645 self.show()
603 646
604 def connect_widgets(self, gwindow): 647 def connect_widgets(self, gwindow):
605 gwindow.get_graph_area().connect('update-event-description', self.update_event_description) 648 gwindow.get_graph_area().connect('update-event-description', self.update_event_description)
606 gwindow.get_graph_area().connect('request-context-menu', self.request_context_menu) 649 gwindow.get_graph_area().connect('request-context-menu', self.request_context_menu)
607 gwindow.get_graph_area().connect('request-refresh-events', self.request_refresh_events) 650 gwindow.get_graph_area().connect('request-refresh-events', self.request_refresh_events)
608 gwindow.connect('request-zoom-in', self.zoom_in_item_activate) 651 gwindow.connect('request-zoom-in', self.zoom_in_item_activate)
609 gwindow.connect('request-zoom-out', self.zoom_out_item_activate) 652 gwindow.connect('request-zoom-out', self.zoom_out_item_activate)
610 653
611 def set_renderers(self, renderers): 654 def set_renderers(self, renderers):
612 for i in range(0, self.notebook.get_n_pages()): 655 for i in range(0, self.notebook.get_n_pages()):
613 self.notebook.remove_page(0) 656 self.notebook.remove_page(0)
@@ -618,7 +661,13 @@ class MainWindow(gtk.Window):
618 self.notebook.append_page(gwindow, gtk.Label(title)) 661 self.notebook.append_page(gwindow, gtk.Label(title))
619 if self.notebook.get_n_pages() > 0: 662 if self.notebook.get_n_pages() > 0:
620 self.notebook.get_nth_page(0).grab_focus() 663 self.notebook.get_nth_page(0).grab_focus()
621 664
665 if self.notebook.get_n_pages() > 0:
666 self.move_item.set_sensitive(True)
667 else:
668 self.move_item.set_sensitive(False)
669
670
622 def switch_page(self, widget, page, page_num): 671 def switch_page(self, widget, page, page_num):
623 if self.notebook.get_nth_page(self.notebook.last_page) is not None: 672 if self.notebook.get_nth_page(self.notebook.last_page) is not None:
624 old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value() 673 old_value = self.notebook.get_nth_page(self.notebook.last_page).get_hadjustment().get_value()
@@ -626,44 +675,84 @@ class MainWindow(gtk.Window):
626 new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0] 675 new_ofs = self.notebook.get_nth_page(page_num).get_graph_area().get_graph().get_origin()[0]
627 new_value = old_value - old_ofs + new_ofs 676 new_value = old_value - old_ofs + new_ofs
628 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value) 677 self.notebook.get_nth_page(page_num).get_hadjustment().set_value(new_value)
629 678
630 self.notebook.last_page = page_num 679 self.notebook.last_page = page_num
631 680
632 def update_event_description(self, widget, event, msg): 681 def update_event_description(self, widget, event, msg):
633 self.desc_label.set_text(msg) 682 self.desc_label.set_text(msg)
634 683
635 def request_context_menu(self, widget, gdk_event, selected): 684 def request_context_menu(self, widget, gdk_event, selected):
636 button = 0 685 button = 0
637 if hasattr(gdk_event, 'button'): 686 if hasattr(gdk_event, 'button'):
638 button = gdk_event.button 687 button = gdk_event.button
639 time = gdk_event.time 688 time = gdk_event.time
640 689
641 menu = GraphContextMenu(selected) 690 menu = GraphContextMenu(selected, self.info_win)
642 menu.popup(None, None, None, button, time) 691 menu.popup(None, None, None, button, time)
643 692
644 def request_refresh_events(self, widget, sender, old, new, replace): 693 def request_refresh_events(self, widget, sender, old, new, replace):
645 for i in range(0, self.notebook.get_n_pages()): 694 for i in range(0, self.notebook.get_n_pages()):
646 if self.notebook.get_nth_page(i).get_graph_area() is not sender: 695 if self.notebook.get_nth_page(i).get_graph_area() is not sender:
647 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) 696 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace)
648 break 697 break
649 for i in range(0, self.notebook.get_n_pages()): 698 for i in range(0, self.notebook.get_n_pages()):
650 if self.notebook.get_nth_page(i).get_graph_area() is sender: 699 if self.notebook.get_nth_page(i).get_graph_area() is sender:
651 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace) 700 self.notebook.get_nth_page(i).get_graph_area().refresh_events(sender, old, new, replace)
652 701
702 def move_to_time_activate(self, widget):
703 dialog = TextInputDialog('Move to Time', 'What time to move to?', self)
704
705 err = True
706 while err:
707 ret = dialog.run()
708
709 if ret == gtk.RESPONSE_ACCEPT:
710 err, time = None, None
711 try:
712 time = float(dialog.get_input())
713 start, end = self.notebook.get_nth_page(0).get_graph_area().get_schedule().get_time_bounds()
714 if time < start or time > end:
715 err = 'Time out of range!'
716 except ValueError:
717 err = 'Must input a number!'
718
719 if not err:
720 for i in xrange(0, self.notebook.get_n_pages()):
721 garea = self.notebook.get_nth_page(i).get_graph_area()
722 # Center as much as possible
723 pos = garea.get_graph().get_time_xpos(time) - garea.get_width() / 2.0
724 pos = max(0, pos)
725 pos = min(garea.get_graph().get_width(), pos)
726
727 self.notebook.get_nth_page(i).set_hvalue(pos)
728 else:
729 err_dialog = gtk.MessageDialog(self, gtk.DIALOG_DESTROY_WITH_PARENT,
730 gtk.MESSAGE_ERROR,
731 gtk.BUTTONS_CLOSE,
732 err)
733 err_dialog.set_title('Input Error')
734 err_dialog.run()
735 err_dialog.destroy()
736
737 else:
738 break
739
740 dialog.destroy()
741
653 def zoom_in_item_activate(self, widget): 742 def zoom_in_item_activate(self, widget):
654 for i in range(0, self.notebook.get_n_pages()): 743 for i in range(0, self.notebook.get_n_pages()):
655 self.notebook.get_nth_page(i).get_graph_area().zoom_in() 744 self.notebook.get_nth_page(i).get_graph_area().zoom_in()
656 745
657 def zoom_out_item_activate(self, widget): 746 def zoom_out_item_activate(self, widget):
658 for i in range(0, self.notebook.get_n_pages()): 747 for i in range(0, self.notebook.get_n_pages()):
659 self.notebook.get_nth_page(i).get_graph_area().zoom_out() 748 self.notebook.get_nth_page(i).get_graph_area().zoom_out()
660 749
661 def quit_item_activate(self, widget): 750 def quit_item_activate(self, widget):
662 self.destroy() 751 self.destroy()
663 752
664 def delete_event(self, widget, event, data=None): 753 def delete_event(self, widget, event, data=None):
665 return False 754 return False
666 755
667 def die(self, widget, data=None): 756 def die(self, widget, data=None):
668 gtk.main_quit() 757 gtk.main_quit()
669 758