2 from rox
import g
, TRUE
, FALSE
, alert
3 from gnome
import canvas
7 from StringIO
import StringIO
11 from rox
.Menu
import Menu
16 prog_menu
= Menu('programs', [
17 ('/Play', 'menu_play', '', ''),
18 ('/Map', 'menu_map', '', ''),
19 ('/View', 'menu_new_view', '', ''),
20 ('/', '', '', '<separator>'),
21 ('/New program', 'menu_new_prog', '', ''),
22 ('/Rename', 'menu_rename', '', ''),
23 ('/Delete', 'menu_delete', '', ''),
26 line_menu
= Menu('line', [
27 ('/Set\/clear breakpoint', 'line_toggle_breakpoint', '', ''),
28 ('/Yank chain', 'line_yank_chain', '', ''),
29 ('/Remove link', 'line_del_chain', '', ''),
30 ('/Paste chain', 'line_paste_chain', '', ''),
31 ('/Add block', 'line_add_block', '', '')
34 block_menu
= Menu('op', [
35 ('/Toggle Enter\/Leave', 'block_toggle_enter', '', ''),
36 ('/Toggle Foreach','block_toggle_foreach', '', ''),
37 ('/Toggle Restore Mark','block_toggle_restore', '', ''),
38 ('/Edit comment', 'block_edit_comment', '', ''),
39 ('/Swap next\/fail', 'op_swap_nf', '', ''),
40 ('/Remove node', 'op_del_node', '', '')
43 op_menu
= Menu('op', [
44 ('/Edit node', 'op_edit', '', ''),
45 ('/Swap next\/fail', 'op_swap_nf', '', ''),
46 ('/Remove node', 'op_del_node', '', '')
49 from GetArg
import GetArg
50 from Program
import Program
, load
, Block
52 no_cursor
= g
.gdk
.Cursor(g
.gdk
.TCROSS
)
57 return text
[:26] + '...'
59 def connect(x1
, y1
, x2
, y2
):
60 """Chop 5 pixels off both ends of this line"""
64 l
= math
.hypot(dx
, dy
)
68 return (x1
+ dx
, y1
+ dy
, x2
- dx
, y2
- dy
)
70 DEFAULT_NEXT
= (0, 25)
71 DEFAULT_FAIL
= (20, 20)
73 expand_history
= {} # Prog name -> expanded flag
75 def action_to_text(action
):
79 text
= string
.capitalize(string
.replace(text
, '_', ' '))
84 if action
[0] == 'do_search' or action
[0] == 'xpath':
86 pat
= string
.replace(pat
, 'following-sibling::', '>>')
87 pat
= string
.replace(pat
, 'preceding-sibling::', '<<')
88 pat
= string
.replace(pat
, 'child::', '')
89 pat
= string
.replace(pat
, '[1]', '')
90 pat
= string
.replace(pat
, 'text()[ext:match', '[')
93 i
= string
.rfind(pat
[:20], '/')
95 i
= string
.rfind(pat
[:20], ':')
98 details
= details
+ pat
[:i
+ 1] + '\n'
100 details
= details
+ pat
101 elif action
[0] == 'attribute':
102 details
= trunc(str(action
[2]))
103 elif action
[0] == 'set_attrib':
104 details
= trunc(str(action
[1]))
105 elif action
[0] == 'add_attrib':
106 details
= trunc(str(action
[2]))
107 elif action
[0] == 'add_node':
108 details
= trunc(action
[2])
109 elif action
[0] == 'subst':
110 details
= action
[1] + ' -> ' + action
[2]
111 elif action
[0] == 'play' or action
[0] == 'map':
112 if len(action
[1]) > 20:
113 details
= '...' + str(action
[1][-19:])
115 details
= str(action
[1])
118 details
= `action
[1:]`
120 details
= str(action
[1])
121 if len(details
) > 20:
122 details
= trunc(`details`
)
123 text
= text
+ '\n' + details
127 def __init__(self
, view
):
128 g
.VBox
.__init
__(self
)
131 self
.sub_windows
= []
133 self
.stack_frames
= g
.Label('')
134 self
.pack_start(self
.stack_frames
, FALSE
, TRUE
, 0)
135 self
.stack_frames
.show()
136 self
.update_stack(None)
139 self
.pack_start(pane
, expand
= 1, fill
= 1)
141 swin
= g
.ScrolledWindow()
142 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
145 self
.prog_model
= g
.TreeStore(str, str)
146 tree
= g
.TreeView(self
.prog_model
)
147 tree
.connect('button-press-event', self
.button_press
)
148 tree
.unset_flags(g
.CAN_FOCUS
)
149 tree
.set_headers_visible(FALSE
)
152 cell
= g
.CellRendererText()
153 column
= g
.TreeViewColumn('Program', cell
, text
= 0)
154 tree
.append_column(column
)
156 def change_prog(sel
):
157 selected
= sel
.get_selected()
160 model
, iter = selected
162 path
= model
.get_value(iter, 1)
163 self
.chains
.switch_to(self
.view
.name_to_prog(path
))
165 self
.chains
.switch_to(None)
167 sel
= tree
.get_selection()
168 sel
.connect('changed', change_prog
)
170 self
.chains
= ChainDisplay(view
)
171 self
.prog_tree_changed()
175 v
.set_shadow_type(g
.SHADOW_NONE
)
178 swin
= g
.ScrolledWindow()
179 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
181 swin
.add(self
.chains
)
184 pane
.set_position(200)
186 sel
.set_mode(g
.SELECTION_BROWSE
)
187 root_iter
= self
.prog_model
.get_iter_first()
188 sel
.select_iter(root_iter
)
189 tree
.expand_row(self
.prog_model
.get_path(root_iter
), FALSE
)
191 self
.view
.lists
.append(self
)
192 self
.view
.model
.root_program
.watchers
.append(self
)
195 def set_innermost_failure(self
, op
):
196 prog
= op
.get_program()
197 print "list: set_innermost_failure:", prog
201 self
.view
.lists
.remove(self
)
202 self
.view
.model
.root_program
.watchers
.remove(self
)
204 def update_points(self
):
205 self
.chains
.update_points()
206 for x
in self
.sub_windows
:
209 def program_changed(self
, op
):
212 def prog_tree_changed(self
):
213 self
.prog_to_path
= {}
214 self
.prog_model
.clear()
215 self
.build_tree(self
.view
.model
.root_program
)
217 # Check for now deleted programs still being displayed
218 root
= self
.view
.model
.root_program
219 if self
.chains
and self
.chains
.prog
and not self
.chains
.prog
.parent
:
220 self
.chains
.switch_to(None)
221 for x
in self
.sub_windows
:
222 if x
.disp
.prog
is not root
and not x
.disp
.prog
.parent
:
225 def build_tree(self
, prog
, iter = None):
226 child_iter
= self
.prog_model
.append(iter)
227 self
.prog_model
.set(child_iter
, 0, prog
.name
,
230 self
.prog_to_path
[prog
] = self
.prog_model
.get_path(child_iter
)
231 for p
in prog
.subprograms
.values():
232 self
.build_tree(p
, child_iter
)
234 def run_return(self
, exit
):
236 print "run_return: failure!"
237 self
.view
.jump_to_innermost_failure()
238 def cb(choice
, self
= self
):
240 self
.view
.record_at_point()
241 if rox
.confirm("Program failed - record a failure case?", g
.STOCK_NO
, 'Record'):
242 self
.view
.record_at_point()
243 print "List: execution done!"
245 def button_press(self
, tree
, event
):
246 if event
.button
== 2 or event
.button
== 3:
247 ret
= tree
.get_path_at_pos(event
.x
, event
.y
)
249 return 1 # Click on blank area
250 path
, col
, cx
, cy
= ret
251 print "Event on", path
252 iter = self
.prog_model
.get_iter(path
)
253 path
= self
.prog_model
.get_value(iter, 1)
254 if event
.button
== 3:
255 prog
= self
.view
.name_to_prog(path
)
256 self
.show_menu(event
, prog
)
258 self
.view
.run_new(self
.run_return
)
259 if event
.state
& g
.gdk
.SHIFT_MASK
:
260 self
.view
.may_record(['map', path
])
262 self
.view
.may_record(['play', path
])
265 def menu_delete(self
):
266 prog
= self
.prog_menu_prog
268 rox
.alert("Can't delete the root program!")
270 prog
.parent
.remove_sub(prog
)
272 def menu_rename(self
):
273 prog
= self
.prog_menu_prog
274 def rename(name
, prog
= prog
):
276 GetArg('Rename program', rename
, ['Program name:'])
278 def menu_new_prog(self
):
279 prog
= self
.prog_menu_prog
283 GetArg('New program', create
, ['Program name:'])
285 def menu_new_view(self
):
286 prog
= self
.prog_menu_prog
287 cw
= ChainWindow(self
.view
, prog
)
289 self
.sub_windows
.append(cw
)
291 self
.sub_windows
.remove(cw
)
293 cw
.connect('destroy', lost_cw
)
296 prog
= self
.prog_menu_prog
297 self
.view
.run_new(self
.run_return
)
298 self
.view
.may_record(['map', prog
.get_path()])
301 prog
= self
.prog_menu_prog
302 self
.view
.run_new(self
.run_return
)
303 self
.view
.may_record(['play', prog
.get_path()])
305 def show_menu(self
, event
, prog
):
306 self
.prog_menu_prog
= prog
307 prog_menu
.popup(self
, event
)
309 def update_stack(self
, op
):
310 "The stack has changed - redraw 'op'"
311 if op
and op
.get_program() == self
.chains
.prog
:
312 self
.chains
.update_all()
313 l
= len(self
.view
.exec_stack
) + len(self
.view
.foreach_stack
)
319 text
= '%d frames' % l
320 if self
.view
.chroots
:
321 text
+= ' (%d enters)' % len(self
.view
.chroots
)
322 self
.stack_frames
.set_text(text
)
324 def show_prog(self
, prog
):
325 path
= self
.prog_to_path
[prog
]
329 self
.tree
.expand_row(tuple(partial
), FALSE
)
330 iter = self
.prog_model
.get_iter(path
)
331 self
.tree
.get_selection().select_iter(iter)
333 class ChainDisplay(canvas
.Canvas
):
334 "A graphical display of a chain of nodes."
335 def __init__(self
, view
, prog
= None):
336 canvas
.Canvas
.__init
__(self
)
337 self
.connect('destroy', self
.destroyed
)
339 self
.unset_flags(g
.CAN_FOCUS
)
341 self
.drag_last_pos
= None
343 self
.exec_point
= None # CanvasItem, or None
344 self
.rec_point
= None
350 self
.set_size_request(100, 100)
354 self
.view
.model
.root_program
.watchers
.append(self
)
358 def set_active(self
, active
):
360 self
.modify_bg(g
.STATE_NORMAL
, g
.gdk
.color_parse('white'))
362 self
.modify_bg(g
.STATE_NORMAL
, g
.gdk
.color_parse('#F7F7F7'))
364 self
.modify_bg(g
.STATE_NORMAL
, g
.gdk
.color_parse('#FFC0C0'))
366 def update_points(self
):
367 self
.put_point('rec_point')
368 self
.put_point('exec_point')
371 self
.scroll_to_show(self
.rec_point
)
373 def scroll_to_show(self
, item
):
374 self
.update_now() # Canvas bug
376 (lx
, ly
, hx
, hy
) = item
.get_bounds()
377 x
, y
= item
.i2w(0, 0)
378 x
, y
= self
.w2c(x
, y
)
385 sx
, sy
= self
.get_scroll_offsets()
391 (x
, y
, w
, h
) = self
.get_allocation()
400 self
.scroll_to(sx
, sy
)
402 def put_point(self
, point
):
403 item
= getattr(self
, point
)
406 setattr(self
, point
, None)
411 opexit
= getattr(self
.view
, point
)
412 if point
== 'exec_point' and self
.view
.op_in_progress
:
413 opexit
= (self
.view
.op_in_progress
, None)
417 if op
.get_program() != self
.prog
:
420 g
= self
.op_to_group
[op
]
423 if point
== 'rec_point':
429 item
= self
.root().add(canvas
.CanvasRect
,
430 x1
= -s
, x2
= s
, y1
= -s
, y2
= s
,
432 outline_color
= 'black', width_pixels
= 1)
433 setattr(self
, point
, item
)
434 item
.connect('event', self
.line_event
, op
, exit
)
437 # TODO: cope with exit == None
438 x1
, y1
, x2
, y2
= self
.get_arrow_ends(op
, exit
)
439 x1
, y1
= g
.i2w(x1
, y1
)
440 x2
, y2
= g
.i2w(x2
, y2
)
441 item
.move((x1
+ x2
) / 2, (y1
+ y2
) / 2)
443 def destroyed(self
, widget
):
447 self
.view
.model
.root_program
.watchers
.remove(self
)
448 print "(ChainDisplay destroyed)"
450 def switch_to(self
, prog
):
451 if prog
is self
.prog
:
456 def prog_tree_changed(self
):
459 def program_changed(self
, op
):
460 if (not op
) or op
.get_program() == self
.prog
:
463 def update_all(self
):
467 self
.op_to_group
= {}
468 self
.nodes
= self
.root().add(canvas
.CanvasGroup
, x
= 0, y
= 0)
470 self
.create_node(self
.prog
.code
, self
.nodes
)
478 def op_colour(self
, op
):
479 if op
in self
.view
.exec_stack
:
483 def update_links(self
, op
= None):
484 """Walk through all nodes in the tree-version of the op graph,
485 making all the links (which already exist as stubs) point to
492 if op
.next
.prev
[0] == op
:
493 self
.update_links(op
.next
)
495 self
.join_nodes(op
, 'next')
497 if op
.fail
.prev
[0] == op
:
498 self
.update_links(op
.fail
)
500 self
.join_nodes(op
, 'fail')
501 if isinstance(op
, Block
):
502 self
.update_links(op
.start
)
504 def create_node(self
, op
, group
):
505 self
.op_to_group
[op
] = group
507 if isinstance(op
, Block
):
508 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
509 self
.create_node(op
.start
, gr
)
510 #self.update_now() # GnomeCanvas bug?
511 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
515 border
= gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 4, y2
= hy
+ 4,
516 outline_color
= 'black', width_pixels
= 1)
517 border
.lower_to_bottom()
519 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= minx
+ 8, y1
= ly
+ 4, y2
= hy
+ 4,
520 fill_color
= 'blue').lower_to_bottom()
523 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 5, y2
= ly
+ 13,
524 fill_color
= colour
).lower_to_bottom()
525 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= hy
- 4, y2
= hy
+ 4,
526 fill_color
= colour
).lower_to_bottom()
529 margin
= op
.enter
* 8
530 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 5 + margin
, y2
= ly
+ 13 + margin
,
531 fill_color
= colour
).lower_to_bottom()
532 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= hy
- 4 - margin
, y2
= hy
+ 4 - margin
,
533 fill_color
= colour
).lower_to_bottom()
535 group
.width
, group
.height
= hx
, hy
539 text_font
= 'verdana 10'
541 if op
.action
[0] == 'Start':
542 text
= str(op
.parent
.comment
.replace('\\n', '\n'))
545 text_font
= 'verdana bold 10'
547 text_col
= 'dark blue'
549 text
= str(action_to_text(op
.action
))
552 group
.ellipse
= group
.add(canvas
.CanvasEllipse
,
553 fill_color
= self
.op_colour(op
),
554 outline_color
= 'black',
558 group
.ellipse
.connect('event', self
.op_event
, op
)
560 label
= group
.add(canvas
.CanvasText
,
563 anchor
= g
.ANCHOR_NE
,
564 justification
= 'right',
565 fill_color
= text_col
,
569 #self.update_now() # GnomeCanvas bug?
570 (lx
, ly
, hx
, hy
) = label
.get_bounds()
574 group
.width
, group
.height
= 0, 0
576 if op
.next
and op
.next
.prev
[0] == op
:
577 sx
, sy
= self
.get_arrow_start(op
, 'next')
578 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
579 self
.create_node(op
.next
, gr
)
580 #self.update_now() # GnomeCanvas bug?
581 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
582 drop
= max(20, next_off_y
+ 10)
585 while isinstance(next
, Block
):
589 gr
.move(sx
+ x
, sy
+ y
)
591 group
.next_line
= group
.add(canvas
.CanvasLine
,
592 fill_color
= 'black',
593 points
= connect(0, 0, 1, 1),
599 group
.next_line
.connect('event', self
.line_event
, op
, 'next')
601 (x
, y
) = DEFAULT_FAIL
602 if op
.fail
and op
.fail
.prev
[0] == op
:
603 sx
, sy
= self
.get_arrow_start(op
, 'fail')
605 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
606 self
.create_node(op
.fail
, gr
)
607 #self.update_now() # GnomeCanvas bug?
608 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
611 while isinstance(fail
, Block
):
615 gr
.move(sx
+ x
, sy
+ y
)
616 group
.fail_line
= group
.add(canvas
.CanvasLine
,
617 fill_color
= '#ff6666',
618 points
= connect(0, 0, 1, 1),
624 group
.fail_line
.lower_to_bottom()
625 group
.fail_line
.connect('event', self
.line_event
, op
, 'fail')
626 if op
.action
[0] == 'Start':
627 group
.fail_line
.hide()
629 self
.join_nodes(op
, 'next')
630 self
.join_nodes(op
, 'fail')
632 if self
.view
.breakpoints
.has_key((op
, 'next')):
633 group
.next_line
.set(line_style
= g
.gdk
.LINE_ON_OFF_DASH
)
634 if self
.view
.breakpoints
.has_key((op
, 'fail')):
635 group
.fail_line
.set(line_style
= g
.gdk
.LINE_ON_OFF_DASH
)
637 def edit_op(self
, op
):
639 if op
.action
[0] == 'do_search' or op
.action
[0] == 'do_global':
640 t
= editables
[0].get_text()
642 from Ft
.Xml
.XPath
import XPathParser
643 if t
.find('@CURRENT@') == -1:
645 XPathParser
.new().parse(t
)
647 alert('Invalid search pattern!')
653 op
.action
[i
] = e
.get_text()
655 print "Done editing!"
659 win
.vbox
.pack_start(g
.Label(op
.action
[0]), TRUE
, FALSE
, 0)
660 editables
= [] # [ Entry | None ]
662 for x
in op
.action
[1:]:
664 entry
.set_text(str(x
))
665 win
.vbox
.pack_start(entry
, TRUE
, FALSE
, 0)
666 if type(x
) == str or type(x
) == unicode:
667 editables
.append(entry
)
668 entry
.connect('activate', lambda e
: modify())
673 entry
.set_editable(FALSE
)
674 editables
.append(None)
676 win
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
677 win
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
679 def response(box
, resp
):
681 if resp
== g
.RESPONSE_OK
:
683 win
.connect('response', response
)
686 win
.set_response_sensitive(g
.RESPONSE_OK
, FALSE
)
690 def join_nodes(self
, op
, exit
):
692 x1
, y1
, x2
, y2
= self
.get_arrow_ends(op
, exit
)
694 prev_group
= self
.op_to_group
[op
]
695 line
= getattr(prev_group
, exit
+ '_line')
696 line
.set(points
= connect(x1
, y1
, x2
, y2
))
698 print "*** ERROR setting arc from %s:%s" % (op
, exit
)
700 def op_event(self
, item
, event
, op
):
701 if event
.type == g
.gdk
.BUTTON_PRESS
:
702 print "Prev %s = %s" % (op
, map(str, op
.prev
))
703 if event
.button
== 1:
704 if op
.parent
.start
!= op
or not op
.parent
.is_toplevel():
705 self
.drag_last_pos
= (event
.x
, event
.y
)
707 self
.drag_last_pos
= None
709 self
.show_op_menu(event
, op
)
710 elif event
.type == g
.gdk
.BUTTON_RELEASE
:
711 if event
.button
== 1:
712 self
.drag_last_pos
= None
713 self
.program_changed(None)
714 elif event
.type == g
.gdk
.ENTER_NOTIFY
:
715 item
.set(fill_color
= '#339900')
716 elif event
.type == g
.gdk
.LEAVE_NOTIFY
:
717 item
.set(fill_color
= self
.op_colour(op
))
718 elif event
.type == g
.gdk
.MOTION_NOTIFY
and self
.drag_last_pos
:
719 if not event
.state
& g
.gdk
.BUTTON1_MASK
:
721 self
.drag_last_pos
= None
722 self
.program_changed(None)
724 x
, y
= (event
.x
, event
.y
)
725 dx
, dy
= x
- self
.drag_last_pos
[0], y
- self
.drag_last_pos
[1]
726 if abs(op
.dx
+ dx
) < 4:
728 x
= dx
+ self
.drag_last_pos
[0]
729 if abs(op
.dy
+ dy
) < 4:
731 y
= dy
+ self
.drag_last_pos
[1]
734 self
.drag_last_pos
= (x
, y
)
736 self
.op_to_group
[op
].move(dx
, dy
)
739 self
.join_nodes(p
, 'next')
741 self
.join_nodes(p
, 'fail')
743 #self.create_node(self.prog.start, self.nodes)
745 elif event
.type == g
.gdk
._2BUTTON
_PRESS
:
746 if op
.action
[0] == 'Start':
747 self
.edit_comment(op
.parent
)
750 print "(edit; stop drag!)"
751 self
.drag_last_pos
= None
752 self
.program_changed(None)
755 def edit_comment(self
, block
):
756 assert isinstance(block
, Block
)
759 block
.set_comment(comment
)
760 GetArg('Comment', set, ['Comment:'],
761 message
= '\\n for a newline', init
= [block
.comment
])
763 def block_toggle_enter(self
):
764 self
.op_menu_op
.toggle_enter()
766 def block_toggle_foreach(self
):
767 self
.op_menu_op
.toggle_foreach()
769 def block_toggle_restore(self
):
770 self
.op_menu_op
.toggle_restore()
772 def block_edit_comment(self
):
773 self
.edit_comment(self
.op_menu_op
)
776 self
.edit_op(self
.op_menu_op
)
778 def op_swap_nf(self
):
779 self
.op_menu_op
.swap_nf()
781 def op_del_node(self
):
783 if op
.next
and op
.fail
:
784 rox
.alert("Can't delete a node with both exits in use")
786 self
.clipboard
= op
.del_node()
788 def show_op_menu(self
, event
, op
):
789 if op
.action
[0] == 'Start':
790 self
.op_menu_op
= op
.parent
791 block_menu
.popup(self
, event
)
794 op_menu
.popup(self
, event
)
796 def paste_chain(self
, op
, exit
):
797 print "Paste", self
.clipboard
799 new
= load(doc
.documentElement
, op
.parent
)
800 start
= new
.start
.next
801 new
.start
.unlink('next', may_delete
= 0)
802 start
.set_parent(None)
803 op
.link_to(start
, exit
)
805 def end_link_drag(self
, item
, event
, src_op
, exit
):
806 # Scan all the nodes looking for one nearby...
807 x
, y
= event
.x
, event
.y
809 def closest_node(op
):
810 "Return the closest (node, dist) in this chain to (x, y)"
811 nx
, ny
= self
.op_to_group
[op
].i2w(0, 0)
814 elif isinstance(op
, Block
):
817 best
= (op
, math
.hypot(nx
- x
, ny
- y
))
818 if op
.next
and op
.next
.prev
[0] == op
:
819 next
= closest_node(op
.next
)
820 if next
and (best
is None or next
[1] < best
[1]):
822 if op
.fail
and op
.fail
.prev
[0] == op
:
823 fail
= closest_node(op
.fail
)
824 if fail
and (best
is None or fail
[1] < best
[1]):
826 if isinstance(op
, Block
):
827 sub
= closest_node(op
.start
)
828 if sub
and (best
is None or sub
[1] < best
[1]):
832 result
= closest_node(self
.prog
.code
)
838 # Too far... put the line back to the disconnected state...
839 self
.join_nodes(src_op
, exit
)
842 while node
.action
[0] == 'Start':
844 src_op
.link_to(node
, exit
)
848 def line_paste_chain(self
):
849 op
, exit
= self
.line_menu_line
850 self
.paste_chain(op
, exit
)
852 def line_add_block(self
):
853 op
, exit
= self
.line_menu_line
855 box
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
856 box
.add_button(g
.STOCK_ADD
, g
.RESPONSE_OK
)
858 foreach
= g
.CheckButton('Foreach block')
859 box
.vbox
.pack_start(foreach
)
860 enter
= g
.CheckButton('Enter-leave block')
861 box
.vbox
.pack_start(enter
)
866 if resp
== g
.RESPONSE_OK
:
868 if foreach
.get_active():
870 if enter
.get_active():
873 if self
.view
.rec_point
== (op
, exit
):
874 self
.view
.single_step
= 1
875 self
.view
.stop_recording()
877 self
.view
.do_one_step()
878 except View
.InProgress
:
881 def line_toggle_breakpoint(self
):
882 op
, exit
= self
.line_menu_line
883 bp
= self
.view
.breakpoints
884 if bp
.has_key((op
, exit
)):
890 def line_yank_chain(self
):
891 op
, exit
= self
.line_menu_line
892 next
= getattr(op
, exit
)
894 rox
.alert('Nothing to yank!')
896 self
.clipboard
= next
.to_doc()
899 def line_del_chain(self
):
900 op
, exit
= self
.line_menu_line
901 next
= getattr(op
, exit
)
903 rox
.alert('Nothing to delete!')
905 self
.clipboard
= next
.to_doc()
908 def line_event(self
, item
, event
, op
, exit
):
909 # Item may be rec_point or exec_point...
910 item
= getattr(self
.op_to_group
[op
], exit
+ '_line')
912 if event
.type == g
.gdk
.BUTTON_PRESS
:
913 if event
.button
== 1:
914 if not getattr(op
, exit
):
915 self
.drag_last_pos
= (event
.x
, event
.y
)
916 #item.grab(BUTTON_RELEASE | MOTION_NOTIFY, no_cursor, event.time)
917 elif event
.button
== 2:
918 self
.paste_chain(op
, exit
)
919 elif event
.button
== 3:
920 self
.line_menu_line
= (op
, exit
)
921 line_menu
.popup(self
, event
)
922 elif event
.type == g
.gdk
.BUTTON_RELEASE
:
923 if event
.button
== 1:
924 print "Clicked exit %s of %s" % (exit
, op
)
925 #item.ungrab(event.time)
926 self
.view
.set_exec((op
, exit
))
927 self
.drag_last_pos
= None
928 if not getattr(op
, exit
):
929 self
.end_link_drag(item
, event
, op
, exit
)
930 elif event
.type == g
.gdk
.MOTION_NOTIFY
and self
.drag_last_pos
:
931 if not event
.state
& g
.gdk
.BUTTON1_MASK
:
933 self
.drag_last_pos
= None
934 if not getattr(op
, exit
):
935 self
.end_link_drag(item
, event
, op
, exit
)
937 x
, y
= (event
.x
, event
.y
)
938 dx
, dy
= x
- self
.drag_last_pos
[0], y
- self
.drag_last_pos
[1]
940 if abs(dx
) > 4 or abs(dy
) > 4:
941 sx
, sy
= self
.get_arrow_start(op
, exit
)
942 x
, y
= item
.w2i(event
.x
, event
.y
)
943 gr
= self
.op_to_group
[op
]
948 item
.set(points
= connect(sx
, sy
, x
, y
))
949 elif event
.type == g
.gdk
.ENTER_NOTIFY
:
950 item
.set(fill_color
= '#339900')
951 elif event
.type == g
.gdk
.LEAVE_NOTIFY
:
953 item
.set(fill_color
= 'black')
955 item
.set(fill_color
= '#ff6666')
958 def get_arrow_start(self
, op
, exit
):
959 gr
= self
.op_to_group
[op
]
960 return ((exit
== 'fail' and gr
.width
) or 0, gr
.height
)
962 def get_arrow_ends(self
, op
, exit
):
963 """Return coords of arrow, relative to op's group."""
964 op2
= getattr(op
, exit
)
966 prev_group
= self
.op_to_group
[op
]
968 x1
, y1
= self
.get_arrow_start(op
, exit
)
972 group
= self
.op_to_group
[op2
]
977 x2
, y2
= group
.i2w(0, 0)
978 x2
, y2
= prev_group
.w2i(x2
, y2
)
980 x2
, y2
= DEFAULT_NEXT
984 x2
, y2
= DEFAULT_FAIL
987 return (x1
, y1
, x2
, y2
)
989 def set_bounds(self
):
990 #self.update_now() # GnomeCanvas bug?
991 min_x
, min_y
, max_x
, max_y
= self
.root().get_bounds()
996 self
.set_scroll_region(min_x
, min_y
, max_x
, max_y
)
997 self
.root().move(0, 0) # Magic!
998 #self.set_usize(max_x - min_x, -1)
1000 def canvas_to_world(self
, (x
, y
)):
1001 "Canvas routine seems to be broken..."
1002 mx
, my
, maxx
, maxy
= self
.get_scroll_region()
1003 sx
= self
.get_hadjustment().value
1004 sy
= self
.get_hadjustment().value
1005 return (x
+ mx
+ sx
, y
+ my
+ sy
)
1007 class ChainWindow(rox
.Window
):
1008 def __init__(self
, view
, prog
):
1009 rox
.Window
.__init
__(self
)
1010 swin
= g
.ScrolledWindow()
1012 disp
= ChainDisplay(view
, prog
)
1017 self
.set_default_size(-1, 200)
1018 self
.set_title(prog
.name
)
1020 def update_points(self
):
1021 self
.disp
.update_points()