Refactor get_url/get_jump methods to return objects instead of values.
Add infrastructure for indicating whether shapes and elements ought to
be highlighted or not. Only EllipseShape implements it now.
Add infrastructure for selecting the graph item to be highlighted.
From: Marius Gedminas <marius@gedmin.as>
... | ... |
@@ -68,6 +68,12 @@ class Pen: |
68 | 68 |
pen.__dict__ = self.__dict__.copy() |
69 | 69 |
return pen |
70 | 70 |
|
71 |
+ def highlighted(self): |
|
72 |
+ pen = self.copy() |
|
73 |
+ pen.color = (1, 0, 0, 1) |
|
74 |
+ pen.fillcolor = (.7, .2, .2, 1) |
|
75 |
+ return pen |
|
76 |
+ |
|
71 | 77 |
|
72 | 78 |
class Shape: |
73 | 79 |
"""Abstract base class for all the drawing shapes.""" |
... | ... |
@@ -75,7 +81,7 @@ class Shape: |
75 | 81 |
def __init__(self): |
76 | 82 |
pass |
77 | 83 |
|
78 |
- def draw(self, cr): |
|
84 |
+ def draw(self, cr, highlight=False): |
|
79 | 85 |
"""Draw this shape with the given cairo context""" |
80 | 86 |
raise NotImplementedError |
81 | 87 |
|
... | ... |
@@ -97,7 +103,7 @@ class TextShape(Shape): |
97 | 103 |
self.w = w |
98 | 104 |
self.t = t |
99 | 105 |
|
100 |
- def draw(self, cr): |
|
106 |
+ def draw(self, cr, highlight=False): |
|
101 | 107 |
|
102 | 108 |
try: |
103 | 109 |
layout = self.layout |
... | ... |
@@ -181,26 +187,31 @@ class EllipseShape(Shape): |
181 | 187 |
def __init__(self, pen, x0, y0, w, h, filled=False): |
182 | 188 |
Shape.__init__(self) |
183 | 189 |
self.pen = pen.copy() |
190 |
+ self.highlight_pen = pen.highlighted() |
|
184 | 191 |
self.x0 = x0 |
185 | 192 |
self.y0 = y0 |
186 | 193 |
self.w = w |
187 | 194 |
self.h = h |
188 | 195 |
self.filled = filled |
189 | 196 |
|
190 |
- def draw(self, cr): |
|
197 |
+ def draw(self, cr, highlight=False): |
|
191 | 198 |
cr.save() |
192 | 199 |
cr.translate(self.x0, self.y0) |
193 | 200 |
cr.scale(self.w, self.h) |
194 | 201 |
cr.move_to(1.0, 0.0) |
195 | 202 |
cr.arc(0.0, 0.0, 1.0, 0, 2.0*math.pi) |
196 | 203 |
cr.restore() |
204 |
+ if highlight: |
|
205 |
+ pen = self.highlight_pen |
|
206 |
+ else: |
|
207 |
+ pen = self.pen |
|
197 | 208 |
if self.filled: |
198 |
- cr.set_source_rgba(*self.pen.fillcolor) |
|
209 |
+ cr.set_source_rgba(*pen.fillcolor) |
|
199 | 210 |
cr.fill() |
200 | 211 |
else: |
201 |
- cr.set_dash(self.pen.dash) |
|
202 |
- cr.set_line_width(self.pen.linewidth) |
|
203 |
- cr.set_source_rgba(*self.pen.color) |
|
212 |
+ cr.set_dash(pen.dash) |
|
213 |
+ cr.set_line_width(pen.linewidth) |
|
214 |
+ cr.set_source_rgba(*pen.color) |
|
204 | 215 |
cr.stroke() |
205 | 216 |
|
206 | 217 |
|
... | ... |
@@ -212,7 +223,7 @@ class PolygonShape(Shape): |
212 | 223 |
self.points = points |
213 | 224 |
self.filled = filled |
214 | 225 |
|
215 |
- def draw(self, cr): |
|
226 |
+ def draw(self, cr, highlight=False): |
|
216 | 227 |
x0, y0 = self.points[-1] |
217 | 228 |
cr.move_to(x0, y0) |
218 | 229 |
for x, y in self.points: |
... | ... |
@@ -236,7 +247,7 @@ class BezierShape(Shape): |
236 | 247 |
self.pen = pen.copy() |
237 | 248 |
self.points = points |
238 | 249 |
|
239 |
- def draw(self, cr): |
|
250 |
+ def draw(self, cr, highlight=False): |
|
240 | 251 |
x0, y0 = self.points[0] |
241 | 252 |
cr.move_to(x0, y0) |
242 | 253 |
for i in xrange(1, len(self.points), 3): |
... | ... |
@@ -256,9 +267,24 @@ class CompoundShape(Shape): |
256 | 267 |
Shape.__init__(self) |
257 | 268 |
self.shapes = shapes |
258 | 269 |
|
259 |
- def draw(self, cr): |
|
270 |
+ def draw(self, cr, highlight=False): |
|
260 | 271 |
for shape in self.shapes: |
261 |
- shape.draw(cr) |
|
272 |
+ shape.draw(cr, highlight=highlight) |
|
273 |
+ |
|
274 |
+ |
|
275 |
+class Url(object): |
|
276 |
+ |
|
277 |
+ def __init__(self, item, url): |
|
278 |
+ self.item = item |
|
279 |
+ self.url = url |
|
280 |
+ |
|
281 |
+ |
|
282 |
+class Jump(object): |
|
283 |
+ |
|
284 |
+ def __init__(self, item, x, y): |
|
285 |
+ self.item = item |
|
286 |
+ self.x = x |
|
287 |
+ self.y = y |
|
262 | 288 |
|
263 | 289 |
|
264 | 290 |
class Element(CompoundShape): |
... | ... |
@@ -297,12 +323,12 @@ class Node(Element): |
297 | 323 |
return None |
298 | 324 |
#print (x, y), (self.x1, self.y1), "-", (self.x2, self.y2) |
299 | 325 |
if self.is_inside(x, y): |
300 |
- return self.url |
|
326 |
+ return Url(self, self.url) |
|
301 | 327 |
return None |
302 | 328 |
|
303 | 329 |
def get_jump(self, x, y): |
304 | 330 |
if self.is_inside(x, y): |
305 |
- return self.x, self.y |
|
331 |
+ return Jump(self, self.x, self.y) |
|
306 | 332 |
return None |
307 | 333 |
|
308 | 334 |
|
... | ... |
@@ -323,9 +349,9 @@ class Edge(Element): |
323 | 349 |
|
324 | 350 |
def get_jump(self, x, y): |
325 | 351 |
if square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS: |
326 |
- return self.points[-1] |
|
352 |
+ return Jump(self, *self.points[-1]) |
|
327 | 353 |
if square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS: |
328 |
- return self.points[0] |
|
354 |
+ return Jump(self, *self.points[0]) |
|
329 | 355 |
return None |
330 | 356 |
|
331 | 357 |
|
... | ... |
@@ -342,16 +368,16 @@ class Graph(Shape): |
342 | 368 |
def get_size(self): |
343 | 369 |
return self.width, self.height |
344 | 370 |
|
345 |
- def draw(self, cr): |
|
371 |
+ def draw(self, cr, highlight_item=None): |
|
346 | 372 |
cr.set_source_rgba(0.0, 0.0, 0.0, 1.0) |
347 | 373 |
|
348 | 374 |
cr.set_line_cap(cairo.LINE_CAP_BUTT) |
349 | 375 |
cr.set_line_join(cairo.LINE_JOIN_MITER) |
350 | 376 |
|
351 | 377 |
for edge in self.edges: |
352 |
- edge.draw(cr) |
|
378 |
+ edge.draw(cr, highlight=(edge is highlight_item)) |
|
353 | 379 |
for node in self.nodes: |
354 |
- node.draw(cr) |
|
380 |
+ node.draw(cr, highlight=(node is highlight_item)) |
|
355 | 381 |
|
356 | 382 |
def get_url(self, x, y): |
357 | 383 |
for node in self.nodes: |
... | ... |
@@ -730,12 +756,15 @@ class NullAction(DragAction): |
730 | 756 |
|
731 | 757 |
def on_motion_notify(self, event): |
732 | 758 |
dot_widget = self.dot_widget |
733 |
- if dot_widget.get_url(event.x, event.y) is not None: |
|
759 |
+ item = dot_widget.get_url(event.x, event.y) |
|
760 |
+ if item is None: |
|
761 |
+ item = dot_widget.get_jump(event.x, event.y) |
|
762 |
+ if item is not None: |
|
734 | 763 |
dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2)) |
735 |
- elif dot_widget.get_jump(event.x, event.y) is not None: |
|
736 |
- dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1)) |
|
764 |
+ dot_widget.set_highlight(item.item) |
|
737 | 765 |
else: |
738 | 766 |
dot_widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW)) |
767 |
+ dot_widget.set_highlight(None) |
|
739 | 768 |
|
740 | 769 |
|
741 | 770 |
class PanAction(DragAction): |
... | ... |
@@ -824,6 +853,7 @@ class DotWidget(gtk.DrawingArea): |
824 | 853 |
self.animation = NoAnimation(self) |
825 | 854 |
self.drag_action = NullAction(self) |
826 | 855 |
self.presstime = None |
856 |
+ self.highlight = None |
|
827 | 857 |
|
828 | 858 |
def set_dotcode(self, dotcode): |
829 | 859 |
p = subprocess.Popen( |
... | ... |
@@ -861,7 +891,7 @@ class DotWidget(gtk.DrawingArea): |
861 | 891 |
cr.scale(self.zoom_ratio, self.zoom_ratio) |
862 | 892 |
cr.translate(-self.x, -self.y) |
863 | 893 |
|
864 |
- self.graph.draw(cr) |
|
894 |
+ self.graph.draw(cr, highlight_item=self.highlight) |
|
865 | 895 |
cr.restore() |
866 | 896 |
|
867 | 897 |
self.drag_action.draw(cr) |
... | ... |
@@ -876,6 +906,11 @@ class DotWidget(gtk.DrawingArea): |
876 | 906 |
self.y = y |
877 | 907 |
self.queue_draw() |
878 | 908 |
|
909 |
+ def set_highlight(self, item): |
|
910 |
+ if self.highlight != item: |
|
911 |
+ self.highlight = item |
|
912 |
+ self.queue_draw() |
|
913 |
+ |
|
879 | 914 |
def zoom_image(self, zoom_ratio, center=False): |
880 | 915 |
if center: |
881 | 916 |
self.x = self.graph.width/2 |
... | ... |
@@ -988,12 +1023,11 @@ class DotWidget(gtk.DrawingArea): |
988 | 1023 |
x, y = int(event.x), int(event.y) |
989 | 1024 |
url = self.get_url(x, y) |
990 | 1025 |
if url is not None: |
991 |
- self.emit('clicked', unicode(url), event) |
|
1026 |
+ self.emit('clicked', unicode(url.url), event) |
|
992 | 1027 |
else: |
993 | 1028 |
jump = self.get_jump(x, y) |
994 | 1029 |
if jump is not None: |
995 |
- jumpx, jumpy = jump |
|
996 |
- self.animate_to(jumpx, jumpy) |
|
1030 |
+ self.animate_to(jump.x, jump.y) |
|
997 | 1031 |
|
998 | 1032 |
return True |
999 | 1033 |
if event.button == 1 or event.button == 2: |