diff options
author | Gary Bressler <garybressler@nc.rr.com> | 2010-04-08 17:11:08 -0400 |
---|---|---|
committer | Gary Bressler <garybressler@nc.rr.com> | 2010-04-08 17:11:08 -0400 |
commit | ceff6457bfeb5642616f4711f14e0bb652d12164 (patch) | |
tree | d05b4ebd1c3ee6e28884c669d65fd31700941086 /unit_trace/viz/viewer.py | |
parent | 01abc8352aa2fd192678b4066b26ea749a203801 (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.py | 409 |
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 | ||
5 | from schedule import * | 5 | from schedule import * |
6 | |||
7 | from renderer import * | 6 | from renderer import * |
7 | from windows import * | ||
8 | 8 | ||
9 | import pygtk | 9 | import pygtk |
10 | import gtk | 10 | import gtk |
@@ -13,145 +13,157 @@ import copy | |||
13 | 13 | ||
14 | class GraphContextMenu(gtk.Menu): | 14 | class 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 | |||
35 | class GraphArea(gtk.DrawingArea): | 42 | class 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 | ||
431 | class GraphWindow(gtk.ScrolledWindow): | 449 | class 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 | ||
517 | class MainWindow(gtk.Window): | 547 | class 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 | ||