2 from rox
import g
, TRUE
, FALSE
, alert
3 from gnome2
import canvas
7 from StringIO
import StringIO
10 from rox
.Menu
import Menu
15 prog_menu
= Menu('programs', [
16 ('/Play', 'menu_play', '', ''),
17 ('/Map', 'menu_map', '', ''),
18 ('/View', 'menu_new_view', '', ''),
19 ('/', '', '', '<separator>'),
20 ('/New program', 'menu_new_prog', '', ''),
21 ('/Rename', 'menu_rename', '', ''),
22 ('/Delete', 'menu_delete', '', ''),
25 line_menu
= Menu('line', [
26 ('/Set\/clear breakpoint', 'line_toggle_breakpoint', '', ''),
27 ('/Yank chain', 'line_yank_chain', '', ''),
28 ('/Remove link', 'line_del_chain', '', ''),
29 ('/Paste chain', 'line_paste_chain', '', ''),
30 ('/Add block', 'line_add_block', '', '')
33 block_menu
= Menu('op', [
34 ('/Toggle Enter\/Leave', 'block_toggle_enter', '', ''),
35 ('/Toggle Foreach','block_toggle_foreach', '', ''),
36 ('/Toggle Restore Mark','block_toggle_restore', '', ''),
37 ('/Edit comment', 'block_edit_comment', '', ''),
38 ('/Swap next\/fail', 'op_swap_nf', '', ''),
39 ('/Remove node', 'op_del_node', '', '')
42 op_menu
= Menu('op', [
43 ('/Edit node', 'op_edit', '', ''),
44 ('/Swap next\/fail', 'op_swap_nf', '', ''),
45 ('/Remove node', 'op_del_node', '', '')
48 from GetArg
import GetArg
49 from Program
import Program
, load
, Block
51 no_cursor
= g
.gdk
.Cursor(g
.gdk
.TCROSS
)
56 return text
[:16] + '...'
58 def connect(x1
, y1
, x2
, y2
):
59 """Chop 5 pixels off both ends of this line"""
63 l
= math
.hypot(dx
, dy
)
67 return (x1
+ dx
, y1
+ dy
, x2
- dx
, y2
- dy
)
69 DEFAULT_NEXT
= (0, 25)
70 DEFAULT_FAIL
= (20, 20)
72 expand_history
= {} # Prog name -> expanded flag
74 def action_to_text(action
):
78 text
= string
.capitalize(string
.replace(text
, '_', ' '))
81 if action
[0] == 'do_search':
83 pat
= string
.replace(pat
, 'following-sibling::', '>>')
84 pat
= string
.replace(pat
, 'preceding-sibling::', '<<')
85 pat
= string
.replace(pat
, 'child::', '')
86 pat
= string
.replace(pat
, '[1]', '')
87 pat
= string
.replace(pat
, 'text()[ext:match', '[')
90 i
= string
.rfind(pat
[:20], '/')
93 details
= details
+ pat
[:i
+ 1] + '\n'
95 details
= details
+ pat
96 elif action
[0] == 'attribute':
97 details
= trunc(str(action
[2]))
98 elif action
[0] == 'set_attrib':
99 details
= trunc(str(action
[1]))
100 elif action
[0] == 'add_attrib':
101 details
= trunc(str(action
[2]))
102 elif action
[0] == 'add_node':
103 details
= trunc(action
[2])
104 elif action
[0] == 'subst':
105 details
= action
[1] + ' -> ' + action
[2]
106 elif action
[0] == 'play' or action
[0] == 'map':
107 if len(action
[1]) > 20:
108 details
= '...' + str(action
[1][-19:])
110 details
= str(action
[1])
113 details
= `action
[1:]`
115 details
= str(action
[1])
116 if len(details
) > 20:
117 details
= `details`
[:19] + '...'
118 text
= text
+ '\n' + details
122 def __init__(self
, view
):
123 g
.VBox
.__init
__(self
)
126 self
.sub_windows
= []
128 self
.stack_frames
= g
.Label('')
129 self
.pack_start(self
.stack_frames
, FALSE
, TRUE
, 0)
130 self
.stack_frames
.show()
131 self
.update_stack(None)
134 self
.pack_start(pane
, expand
= 1, fill
= 1)
136 swin
= g
.ScrolledWindow()
137 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
140 self
.prog_model
= g
.TreeStore(str, str)
141 tree
= g
.TreeView(self
.prog_model
)
142 tree
.connect('button-press-event', self
.button_press
)
143 tree
.unset_flags(g
.CAN_FOCUS
)
144 tree
.set_headers_visible(FALSE
)
147 cell
= g
.CellRendererText()
148 column
= g
.TreeViewColumn('Program', cell
, text
= 0)
149 tree
.append_column(column
)
151 sel
= tree
.get_selection()
152 def change_prog(tree
, sel
= sel
, self
= self
): # Huh?
153 selected
= sel
.get_selected()
156 model
, iter = selected
158 path
= model
.get_value(iter, 1)
159 self
.chains
.switch_to(self
.view
.name_to_prog(path
))
161 self
.chains
.switch_to(None)
163 sel
.connect('changed', change_prog
)
165 self
.chains
= ChainDisplay(view
)
166 self
.prog_tree_changed()
170 v
.set_shadow_type(g
.SHADOW_NONE
)
173 swin
= g
.ScrolledWindow()
174 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
176 swin
.add(self
.chains
)
179 pane
.set_position(200)
181 sel
.set_mode(g
.SELECTION_BROWSE
)
182 root_iter
= self
.prog_model
.get_iter_first()
183 sel
.select_iter(root_iter
)
184 tree
.expand_row(self
.prog_model
.get_path(root_iter
), FALSE
)
186 self
.view
.lists
.append(self
)
187 self
.view
.model
.root_program
.watchers
.append(self
)
189 def set_innermost_failure(self
, op
):
190 self
.show_prog(op
.get_program())
193 self
.view
.lists
.remove(self
)
194 self
.view
.model
.root_program
.watchers
.remove(self
)
196 def update_points(self
):
197 self
.chains
.update_points()
198 for x
in self
.sub_windows
:
201 def program_changed(self
, op
):
204 def prog_tree_changed(self
):
205 self
.prog_to_path
= {}
206 self
.prog_model
.clear()
207 self
.build_tree(self
.view
.model
.root_program
)
209 # Check for now deleted programs still being displayed
210 root
= self
.view
.model
.root_program
211 if self
.chains
and self
.chains
.prog
and not self
.chains
.prog
.parent
:
212 self
.chains
.switch_to(None)
213 for x
in self
.sub_windows
:
214 if x
.disp
.prog
is not root
and not x
.disp
.prog
.parent
:
217 def build_tree(self
, prog
, iter = None):
218 child_iter
= self
.prog_model
.append(iter)
219 self
.prog_model
.set(child_iter
, 0, prog
.name
,
222 self
.prog_to_path
[prog
] = self
.prog_model
.get_path(child_iter
)
223 for p
in prog
.subprograms
.values():
224 self
.build_tree(p
, child_iter
)
226 def run_return(self
, exit
):
228 print "run_return: failure!"
229 self
.view
.jump_to_innermost_failure()
230 def cb(choice
, self
= self
):
232 self
.view
.record_at_point()
233 if rox
.confirm("Program failed - record a failure case?", g
.STOCK_NO
, 'Record'):
234 self
.view
.record_at_point()
235 print "List: execution done!"
237 def button_press(self
, tree
, event
):
238 if event
.button
== 2 or event
.button
== 3:
239 ret
= tree
.get_path_at_pos(event
.x
, event
.y
)
241 return 1 # Click on blank area
242 path
, col
, cx
, cy
= ret
243 print "Event on", path
244 iter = self
.prog_model
.get_iter(path
)
245 path
= self
.prog_model
.get_value(iter, 1)
246 if event
.button
== 3:
247 prog
= self
.view
.name_to_prog(path
)
248 self
.show_menu(event
, prog
)
250 self
.view
.run_new(self
.run_return
)
251 if event
.state
& g
.gdk
.SHIFT_MASK
:
252 self
.view
.may_record(['map', path
])
254 self
.view
.may_record(['play', path
])
257 def menu_delete(self
):
258 prog
= self
.prog_menu_prog
260 rox
.alert("Can't delete the root program!")
262 prog
.parent
.remove_sub(prog
)
264 def menu_rename(self
):
265 prog
= self
.prog_menu_prog
266 def rename(name
, prog
= prog
):
268 GetArg('Rename program', rename
, ['Program name:'])
270 def menu_new_prog(self
):
271 prog
= self
.prog_menu_prog
275 GetArg('New program', create
, ['Program name:'])
277 def menu_new_view(self
):
278 prog
= self
.prog_menu_prog
279 cw
= ChainWindow(self
.view
, prog
)
281 self
.sub_windows
.append(cw
)
283 self
.sub_windows
.remove(cw
)
285 cw
.connect('destroy', lost_cw
)
288 prog
= self
.prog_menu_prog
289 self
.view
.run_new(self
.run_return
)
290 self
.view
.may_record(['map', prog
.get_path()])
293 prog
= self
.prog_menu_prog
294 self
.view
.run_new(self
.run_return
)
295 self
.view
.may_record(['play', prog
.get_path()])
297 def show_menu(self
, event
, prog
):
298 self
.prog_menu_prog
= prog
299 prog_menu
.popup(self
, event
)
301 def update_stack(self
, op
):
302 "The stack has changed - redraw 'op'"
303 if op
and op
.get_program() == self
.chains
.prog
:
304 self
.chains
.update_all()
305 l
= len(self
.view
.exec_stack
) + len(self
.view
.foreach_stack
)
311 text
= '%d frames' % l
312 if self
.view
.chroots
:
313 text
+= ' (%d enters)' % len(self
.view
.chroots
)
314 self
.stack_frames
.set_text(text
)
316 def show_prog(self
, prog
):
317 path
= self
.prog_to_path
[prog
]
318 iter = self
.prog_model
.get_iter(path
)
319 self
.tree
.get_selection().select_iter(iter)
321 class ChainDisplay(canvas
.Canvas
):
322 "A graphical display of a chain of nodes."
323 def __init__(self
, view
, prog
= None):
324 canvas
.Canvas
.__init
__(self
)
325 self
.connect('destroy', self
.destroyed
)
327 self
.unset_flags(g
.CAN_FOCUS
)
329 self
.drag_last_pos
= None
331 self
.exec_point
= None # CanvasItem, or None
332 self
.rec_point
= None
334 s
= self
.get_style().copy()
336 s
.bg
[g
.STATE_NORMAL
] = g
.gdk
.color_parse('white')
338 s
.bg
[g
.STATE_NORMAL
] = g
.gdk
.color_parse('light green')
343 self
.set_size_request(100, 100)
347 self
.view
.model
.root_program
.watchers
.append(self
)
351 def update_points(self
):
352 self
.put_point('rec_point')
353 self
.put_point('exec_point')
356 self
.scroll_to_show(self
.rec_point
)
358 def scroll_to_show(self
, item
):
359 self
.update_now() # Canvas bug
361 (lx
, ly
, hx
, hy
) = item
.get_bounds()
362 x
, y
= item
.i2w(0, 0)
363 x
, y
= self
.w2c(x
, y
)
370 sx
, sy
= self
.get_scroll_offsets()
376 (x
, y
, w
, h
) = self
.get_allocation()
385 self
.scroll_to(sx
, sy
)
387 def put_point(self
, point
):
388 item
= getattr(self
, point
)
391 setattr(self
, point
, None)
396 opexit
= getattr(self
.view
, point
)
397 if point
== 'exec_point' and self
.view
.op_in_progress
:
398 opexit
= (self
.view
.op_in_progress
, None)
402 if op
.get_program() != self
.prog
:
405 g
= self
.op_to_group
[op
]
408 if point
== 'rec_point':
414 item
= self
.root().add(canvas
.CanvasRect
,
415 x1
= -s
, x2
= s
, y1
= -s
, y2
= s
,
417 outline_color
= 'black', width_pixels
= 1)
418 setattr(self
, point
, item
)
419 item
.connect('event', self
.line_event
, op
, exit
)
422 # TODO: cope with exit == None
423 x1
, y1
, x2
, y2
= self
.get_arrow_ends(op
, exit
)
424 x1
, y1
= g
.i2w(x1
, y1
)
425 x2
, y2
= g
.i2w(x2
, y2
)
426 item
.move((x1
+ x2
) / 2, (y1
+ y2
) / 2)
428 def destroyed(self
, widget
):
432 self
.view
.model
.root_program
.watchers
.remove(self
)
433 print "(ChainDisplay destroyed)"
435 def switch_to(self
, prog
):
436 if prog
is self
.prog
:
441 def prog_tree_changed(self
):
444 def program_changed(self
, op
):
445 if (not op
) or op
.get_program() == self
.prog
:
448 def update_all(self
):
452 self
.op_to_group
= {}
453 self
.nodes
= self
.root().add(canvas
.CanvasGroup
, x
= 0, y
= 0)
455 self
.create_node(self
.prog
.code
, self
.nodes
)
463 def op_colour(self
, op
):
464 if op
in self
.view
.exec_stack
:
468 def update_links(self
, op
= None):
469 """Walk through all nodes in the tree-version of the op graph,
470 making all the links (which already exist as stubs) point to
477 if op
.next
.prev
[0] == op
:
478 self
.update_links(op
.next
)
480 self
.join_nodes(op
, 'next')
482 if op
.fail
.prev
[0] == op
:
483 self
.update_links(op
.fail
)
485 self
.join_nodes(op
, 'fail')
486 if isinstance(op
, Block
):
487 self
.update_links(op
.start
)
489 def create_node(self
, op
, group
):
490 self
.op_to_group
[op
] = group
492 if isinstance(op
, Block
):
493 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
494 self
.create_node(op
.start
, gr
)
495 #self.update_now() # GnomeCanvas bug?
496 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
500 border
= gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 4, y2
= hy
+ 4,
501 outline_color
= 'black', width_pixels
= 1)
502 border
.lower_to_bottom()
504 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= minx
+ 8, y1
= ly
+ 4, y2
= hy
+ 4,
505 fill_color
= 'blue').lower_to_bottom()
508 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 5, y2
= ly
+ 13,
509 fill_color
= colour
).lower_to_bottom()
510 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= hy
- 3, y2
= hy
+ 3,
511 fill_color
= colour
).lower_to_bottom()
514 margin
= op
.enter
* 8
515 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 5 + margin
, y2
= ly
+ 13 + margin
,
516 fill_color
= colour
).lower_to_bottom()
517 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= hy
- 3 - margin
, y2
= hy
+ 3 - margin
,
518 fill_color
= colour
).lower_to_bottom()
520 group
.width
, group
.height
= hx
, hy
524 if op
.action
[0] == 'Start':
525 text
= str(op
.parent
.comment
.replace('\\n', '\n'))
527 #text_font = '-misc-fixed-bold-r-normal-*-*-120-*-*-c-*-iso8859-1'
528 text_col
= 'dark blue'
530 text
= str(action_to_text(op
.action
))
532 #text_font = '-misc-fixed-medium-r-normal-*-*-120-*-*-c-*-iso8859-1'
535 group
.ellipse
= group
.add(canvas
.CanvasEllipse
,
536 fill_color
= self
.op_colour(op
),
537 outline_color
= 'black',
541 group
.ellipse
.connect('event', self
.op_event
, op
)
543 label
= group
.add(canvas
.CanvasText
,
546 anchor
= g
.ANCHOR_NE
,
547 justification
= 'right',
548 fill_color
= text_col
,
551 #self.update_now() # GnomeCanvas bug?
552 (lx
, ly
, hx
, hy
) = label
.get_bounds()
556 group
.width
, group
.height
= 0, 0
558 if op
.next
and op
.next
.prev
[0] == op
:
559 sx
, sy
= self
.get_arrow_start(op
, 'next')
560 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
561 self
.create_node(op
.next
, gr
)
562 #self.update_now() # GnomeCanvas bug?
563 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
564 drop
= max(20, next_off_y
+ 10)
567 while isinstance(next
, Block
):
571 gr
.move(sx
+ x
, sy
+ y
)
573 group
.next_line
= group
.add(canvas
.CanvasLine
,
574 fill_color
= 'black',
575 points
= connect(0, 0, 1, 1),
581 group
.next_line
.connect('event', self
.line_event
, op
, 'next')
583 (x
, y
) = DEFAULT_FAIL
584 if op
.fail
and op
.fail
.prev
[0] == op
:
585 sx
, sy
= self
.get_arrow_start(op
, 'fail')
587 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
588 self
.create_node(op
.fail
, gr
)
589 #self.update_now() # GnomeCanvas bug?
590 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
593 while isinstance(fail
, Block
):
597 gr
.move(sx
+ x
, sy
+ y
)
598 group
.fail_line
= group
.add(canvas
.CanvasLine
,
599 fill_color
= '#ff6666',
600 points
= connect(0, 0, 1, 1),
606 group
.fail_line
.lower_to_bottom()
607 group
.fail_line
.connect('event', self
.line_event
, op
, 'fail')
608 if op
.action
[0] == 'Start':
609 group
.fail_line
.hide()
611 self
.join_nodes(op
, 'next')
612 self
.join_nodes(op
, 'fail')
614 if self
.view
.breakpoints
.has_key((op
, 'next')):
615 group
.next_line
.set(line_style
= g
.gdk
.LINE_ON_OFF_DASH
)
616 if self
.view
.breakpoints
.has_key((op
, 'fail')):
617 group
.fail_line
.set(line_style
= g
.gdk
.LINE_ON_OFF_DASH
)
619 def edit_op(self
, op
):
621 if op
.action
[0] == 'do_search' or op
.action
[0] == 'do_global':
622 t
= editables
[0].get_text()
624 from Ft
.Xml
.XPath
import XPathParser
625 if t
.find('@CURRENT@') == -1:
627 XPathParser
.new().parse(t
)
629 alert('Invalid search pattern!')
635 op
.action
[i
] = e
.get_text()
637 print "Done editing!"
641 win
.vbox
.pack_start(g
.Label(op
.action
[0]), TRUE
, FALSE
, 0)
642 editables
= [] # [ Entry | None ]
644 for x
in op
.action
[1:]:
646 entry
.set_text(str(x
))
647 win
.vbox
.pack_start(entry
, TRUE
, FALSE
, 0)
648 if type(x
) == str or type(x
) == unicode:
649 editables
.append(entry
)
650 entry
.connect('activate', lambda e
: modify())
655 entry
.set_editable(FALSE
)
656 editables
.append(None)
658 win
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
659 win
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
661 def response(box
, resp
):
663 if resp
== g
.RESPONSE_OK
:
665 win
.connect('response', response
)
668 win
.set_response_sensitive(g
.RESPONSE_OK
, FALSE
)
672 def join_nodes(self
, op
, exit
):
674 x1
, y1
, x2
, y2
= self
.get_arrow_ends(op
, exit
)
676 prev_group
= self
.op_to_group
[op
]
677 line
= getattr(prev_group
, exit
+ '_line')
678 line
.set(points
= connect(x1
, y1
, x2
, y2
))
680 print "*** ERROR setting arc from %s:%s" % (op
, exit
)
682 def op_event(self
, item
, event
, op
):
683 if event
.type == g
.gdk
.BUTTON_PRESS
:
684 print "Prev %s = %s" % (op
, map(str, op
.prev
))
685 if event
.button
== 1:
686 if op
.parent
.start
!= op
or not op
.parent
.is_toplevel():
687 self
.drag_last_pos
= (event
.x
, event
.y
)
689 self
.drag_last_pos
= None
691 self
.show_op_menu(event
, op
)
692 elif event
.type == g
.gdk
.BUTTON_RELEASE
:
693 if event
.button
== 1:
694 self
.drag_last_pos
= None
695 self
.program_changed(None)
696 elif event
.type == g
.gdk
.ENTER_NOTIFY
:
697 item
.set(fill_color
= 'white')
698 elif event
.type == g
.gdk
.LEAVE_NOTIFY
:
699 item
.set(fill_color
= self
.op_colour(op
))
700 elif event
.type == g
.gdk
.MOTION_NOTIFY
and self
.drag_last_pos
:
701 if not event
.state
& g
.gdk
.BUTTON1_MASK
:
703 self
.drag_last_pos
= None
704 self
.program_changed(None)
706 x
, y
= (event
.x
, event
.y
)
707 dx
, dy
= x
- self
.drag_last_pos
[0], y
- self
.drag_last_pos
[1]
708 if abs(op
.dx
+ dx
) < 4:
710 x
= dx
+ self
.drag_last_pos
[0]
711 if abs(op
.dy
+ dy
) < 4:
713 y
= dy
+ self
.drag_last_pos
[1]
716 self
.drag_last_pos
= (x
, y
)
718 self
.op_to_group
[op
].move(dx
, dy
)
721 self
.join_nodes(p
, 'next')
723 self
.join_nodes(p
, 'fail')
725 #self.create_node(self.prog.start, self.nodes)
727 elif event
.type == g
.gdk
._2BUTTON
_PRESS
:
728 if op
.action
[0] == 'Start':
729 self
.edit_comment(op
.parent
)
732 print "(edit; stop drag!)"
733 self
.drag_last_pos
= None
734 self
.program_changed(None)
737 def edit_comment(self
, block
):
738 assert isinstance(block
, Block
)
741 block
.set_comment(comment
)
742 GetArg('Comment', set, ['Comment:'],
743 message
= '\\n for a newline', init
= [block
.comment
])
745 def block_toggle_enter(self
):
746 self
.op_menu_op
.toggle_enter()
748 def block_toggle_foreach(self
):
749 self
.op_menu_op
.toggle_foreach()
751 def block_toggle_restore(self
):
752 self
.op_menu_op
.toggle_restore()
754 def block_edit_comment(self
):
755 self
.edit_comment(self
.op_menu_op
)
758 self
.edit_op(self
.op_menu_op
)
760 def op_swap_nf(self
):
761 self
.op_menu_op
.swap_nf()
763 def op_del_node(self
):
765 if op
.next
and op
.fail
:
766 rox
.alert("Can't delete a node with both exits in use")
768 self
.clipboard
= op
.del_node()
770 def show_op_menu(self
, event
, op
):
771 if op
.action
[0] == 'Start':
772 self
.op_menu_op
= op
.parent
773 block_menu
.popup(self
, event
)
776 op_menu
.popup(self
, event
)
778 def paste_chain(self
, op
, exit
):
779 print "Paste", self
.clipboard
781 new
= load(doc
.documentElement
, op
.parent
)
782 start
= new
.start
.next
783 new
.start
.unlink('next', may_delete
= 0)
784 start
.set_parent(None)
785 op
.link_to(start
, exit
)
787 def end_link_drag(self
, item
, event
, src_op
, exit
):
788 # Scan all the nodes looking for one nearby...
789 x
, y
= event
.x
, event
.y
791 def closest_node(op
):
792 "Return the closest (node, dist) in this chain to (x, y)"
793 nx
, ny
= self
.op_to_group
[op
].i2w(0, 0)
796 elif isinstance(op
, Block
):
799 best
= (op
, math
.hypot(nx
- x
, ny
- y
))
800 if op
.next
and op
.next
.prev
[0] == op
:
801 next
= closest_node(op
.next
)
802 if next
and (best
is None or next
[1] < best
[1]):
804 if op
.fail
and op
.fail
.prev
[0] == op
:
805 fail
= closest_node(op
.fail
)
806 if fail
and (best
is None or fail
[1] < best
[1]):
808 if isinstance(op
, Block
):
809 sub
= closest_node(op
.start
)
810 if sub
and (best
is None or sub
[1] < best
[1]):
814 result
= closest_node(self
.prog
.code
)
820 # Too far... put the line back to the disconnected state...
821 self
.join_nodes(src_op
, exit
)
824 while node
.action
[0] == 'Start':
826 src_op
.link_to(node
, exit
)
830 def line_paste_chain(self
):
831 op
, exit
= self
.line_menu_line
832 self
.paste_chain(op
, exit
)
834 def line_add_block(self
):
835 op
, exit
= self
.line_menu_line
836 op
.link_to(Block(op
.parent
), exit
)
838 def line_toggle_breakpoint(self
):
839 op
, exit
= self
.line_menu_line
840 bp
= self
.view
.breakpoints
841 if bp
.has_key((op
, exit
)):
847 def line_yank_chain(self
):
848 op
, exit
= self
.line_menu_line
849 next
= getattr(op
, exit
)
851 rox
.alert('Nothing to yank!')
853 self
.clipboard
= op
.to_doc()
856 def line_del_chain(self
):
857 op
, exit
= self
.line_menu_line
858 next
= getattr(op
, exit
)
860 rox
.alert('Nothing to delete!')
862 self
.clipboard
= next
.to_doc()
865 def line_event(self
, item
, event
, op
, exit
):
866 # Item may be rec_point or exec_point...
867 item
= getattr(self
.op_to_group
[op
], exit
+ '_line')
869 if event
.type == g
.gdk
.BUTTON_PRESS
:
870 if event
.button
== 1:
871 if not getattr(op
, exit
):
872 self
.drag_last_pos
= (event
.x
, event
.y
)
873 #item.grab(BUTTON_RELEASE | MOTION_NOTIFY, no_cursor, event.time)
874 elif event
.button
== 2:
875 self
.paste_chain(op
, exit
)
876 elif event
.button
== 3:
877 self
.line_menu_line
= (op
, exit
)
878 line_menu
.popup(self
, event
)
879 elif event
.type == g
.gdk
.BUTTON_RELEASE
:
880 if event
.button
== 1:
881 print "Clicked exit %s of %s" % (exit
, op
)
882 #item.ungrab(event.time)
883 self
.view
.set_exec((op
, exit
))
884 self
.drag_last_pos
= None
885 if not getattr(op
, exit
):
886 self
.end_link_drag(item
, event
, op
, exit
)
887 elif event
.type == g
.gdk
.MOTION_NOTIFY
and self
.drag_last_pos
:
888 if not event
.state
& g
.gdk
.BUTTON1_MASK
:
890 self
.drag_last_pos
= None
891 if not getattr(op
, exit
):
892 self
.end_link_drag(item
, event
, op
, exit
)
894 x
, y
= (event
.x
, event
.y
)
895 dx
, dy
= x
- self
.drag_last_pos
[0], y
- self
.drag_last_pos
[1]
897 if abs(dx
) > 4 or abs(dy
) > 4:
898 sx
, sy
= self
.get_arrow_start(op
, exit
)
899 x
, y
= item
.w2i(event
.x
, event
.y
)
900 gr
= self
.op_to_group
[op
]
905 item
.set(points
= connect(sx
, sy
, x
, y
))
906 elif event
.type == g
.gdk
.ENTER_NOTIFY
:
907 item
.set(fill_color
= 'white')
908 elif event
.type == g
.gdk
.LEAVE_NOTIFY
:
910 item
.set(fill_color
= 'black')
912 item
.set(fill_color
= '#ff6666')
915 def get_arrow_start(self
, op
, exit
):
916 gr
= self
.op_to_group
[op
]
917 return ((exit
== 'fail' and gr
.width
) or 0, gr
.height
)
919 def get_arrow_ends(self
, op
, exit
):
920 """Return coords of arrow, relative to op's group."""
921 op2
= getattr(op
, exit
)
923 prev_group
= self
.op_to_group
[op
]
925 x1
, y1
= self
.get_arrow_start(op
, exit
)
929 group
= self
.op_to_group
[op2
]
934 x2
, y2
= group
.i2w(0, 0)
935 x2
, y2
= prev_group
.w2i(x2
, y2
)
937 x2
, y2
= DEFAULT_NEXT
941 x2
, y2
= DEFAULT_FAIL
944 return (x1
, y1
, x2
, y2
)
946 def set_bounds(self
):
947 #self.update_now() # GnomeCanvas bug?
948 min_x
, min_y
, max_x
, max_y
= self
.root().get_bounds()
953 self
.set_scroll_region(min_x
, min_y
, max_x
, max_y
)
954 self
.root().move(0, 0) # Magic!
955 #self.set_usize(max_x - min_x, -1)
957 def canvas_to_world(self
, (x
, y
)):
958 "Canvas routine seems to be broken..."
959 mx
, my
, maxx
, maxy
= self
.get_scroll_region()
960 sx
= self
.get_hadjustment().value
961 sy
= self
.get_hadjustment().value
962 return (x
+ mx
+ sx
, y
+ my
+ sy
)
964 class ChainWindow(rox
.Window
):
965 def __init__(self
, view
, prog
):
966 rox
.Window
.__init
__(self
)
967 swin
= g
.ScrolledWindow()
969 disp
= ChainDisplay(view
, prog
)
974 self
.set_default_size(-1, 200)
975 self
.set_title(prog
.name
)
977 def update_points(self
):
978 self
.disp
.update_points()