1 #from __future__ import nested_scopes
4 from rox
import g
, TRUE
, FALSE
, alert
5 from gnome2
import canvas
9 from StringIO
import StringIO
12 from rox
.Menu
import Menu
14 prog_menu
= Menu('programs', [
15 ('/Play', 'menu_play', '', ''),
16 ('/Map', 'menu_map', '', ''),
17 ('/View', 'menu_new_view', '', ''),
18 ('/', '', '', '<separator>'),
19 ('/New program', 'menu_new_prog', '', ''),
20 ('/Rename', 'menu_rename', '', ''),
21 ('/Delete', 'menu_delete', '', ''),
24 line_menu
= Menu('line', [
25 ('/Set\/clear breakpoint', 'line_toggle_breakpoint', '', ''),
26 ('/Yank chain', 'line_yank_chain', '', ''),
27 ('/Remove link', 'line_del_chain', '', ''),
28 ('/Paste chain', 'line_paste_chain', '', ''),
29 ('/Add block', 'line_add_block', '', '')
32 block_menu
= Menu('op', [
33 ('/Toggle Enter\/Leave', 'block_toggle_enter', '', ''),
34 ('/Toggle Foreach','block_toggle_foreach', '', ''),
35 ('/Toggle Restore Mark','block_toggle_restore', '', ''),
36 ('/Edit comment', 'block_edit_comment', '', ''),
37 ('/Swap next\/fail', 'op_swap_nf', '', ''),
38 ('/Remove node', 'op_del_node', '', '')
41 op_menu
= Menu('op', [
42 ('/Edit node', 'op_edit', '', ''),
43 ('/Swap next\/fail', 'op_swap_nf', '', ''),
44 ('/Remove node', 'op_del_node', '', '')
47 from GetArg
import GetArg
48 from Program
import Program
, load
, Block
50 no_cursor
= g
.gdk
.Cursor(g
.gdk
.TCROSS
)
55 return text
[:16] + '...'
57 def connect(x1
, y1
, x2
, y2
):
58 """Chop 5 pixels off both ends of this line"""
62 l
= math
.hypot(dx
, dy
)
66 return (x1
+ dx
, y1
+ dy
, x2
- dx
, y2
- dy
)
68 DEFAULT_NEXT
= (0, 25)
69 DEFAULT_FAIL
= (20, 20)
71 expand_history
= {} # Prog name -> expanded flag
73 def action_to_text(action
):
77 text
= string
.capitalize(string
.replace(text
, '_', ' '))
80 if action
[0] == 'do_search':
82 pat
= string
.replace(pat
, 'following-sibling::', '>>')
83 pat
= string
.replace(pat
, 'preceding-sibling::', '<<')
84 pat
= string
.replace(pat
, 'child::', '')
85 pat
= string
.replace(pat
, '[1]', '')
86 pat
= string
.replace(pat
, 'text()[ext:match', '[')
89 i
= string
.rfind(pat
[:20], '/')
92 details
= details
+ pat
[:i
+ 1] + '\n'
94 details
= details
+ pat
95 elif action
[0] == 'attribute':
96 details
= trunc(str(action
[2]))
97 elif action
[0] == 'set_attrib':
98 details
= trunc(str(action
[1]))
99 elif action
[0] == 'add_attrib':
100 details
= trunc(str(action
[2]))
101 elif action
[0] == 'add_node':
102 details
= trunc(action
[2])
103 elif action
[0] == 'subst':
104 details
= action
[1] + ' -> ' + action
[2]
105 elif action
[0] == 'play' or action
[0] == 'map':
106 if len(action
[1]) > 20:
107 details
= '...' + str(action
[1][-19:])
109 details
= str(action
[1])
112 details
= `action
[1:]`
114 details
= str(action
[1])
115 if len(details
) > 20:
116 details
= `details`
[:19] + '...'
117 text
= text
+ '\n' + details
121 def __init__(self
, view
):
122 g
.VBox
.__init
__(self
)
125 self
.sub_windows
= []
127 self
.stack_frames
= g
.Label('')
128 self
.pack_start(self
.stack_frames
, FALSE
, TRUE
, 0)
129 self
.stack_frames
.show()
130 self
.update_stack(None)
133 self
.pack_start(pane
, expand
= 1, fill
= 1)
135 swin
= g
.ScrolledWindow()
136 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
139 self
.prog_model
= g
.TreeStore(str, str)
140 tree
= g
.TreeView(self
.prog_model
)
141 tree
.connect('button-press-event', self
.button_press
)
142 tree
.unset_flags(g
.CAN_FOCUS
)
143 tree
.set_headers_visible(FALSE
)
146 cell
= g
.CellRendererText()
147 column
= g
.TreeViewColumn('Program', cell
, text
= 0)
148 tree
.append_column(column
)
150 sel
= tree
.get_selection()
151 def change_prog(tree
, sel
= sel
, self
= self
): # Huh?
152 selected
= sel
.get_selected()
155 model
, iter = selected
157 path
= model
.get_value(iter, 1)
158 self
.chains
.switch_to(self
.view
.name_to_prog(path
))
160 self
.chains
.switch_to(None)
162 sel
.connect('changed', change_prog
)
164 self
.chains
= ChainDisplay(view
)
165 self
.prog_tree_changed()
169 v
.set_shadow_type(g
.SHADOW_NONE
)
172 swin
= g
.ScrolledWindow()
173 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
175 swin
.add(self
.chains
)
178 pane
.set_position(200)
180 sel
.set_mode(g
.SELECTION_BROWSE
)
181 root_iter
= self
.prog_model
.get_iter_first()
182 sel
.select_iter(root_iter
)
183 tree
.expand_row(self
.prog_model
.get_path(root_iter
), FALSE
)
185 self
.view
.lists
.append(self
)
186 self
.view
.model
.root_program
.watchers
.append(self
)
188 def set_innermost_failure(self
, op
):
189 self
.show_prog(op
.get_program())
192 self
.view
.lists
.remove(self
)
193 self
.view
.model
.root_program
.watchers
.remove(self
)
195 def update_points(self
):
196 self
.chains
.update_points()
197 for x
in self
.sub_windows
:
200 def program_changed(self
, op
):
203 def prog_tree_changed(self
):
204 self
.prog_to_path
= {}
205 self
.prog_model
.clear()
206 self
.build_tree(self
.view
.model
.root_program
)
208 # Check for now deleted programs still being displayed
209 root
= self
.view
.model
.root_program
210 if self
.chains
and self
.chains
.prog
and not self
.chains
.prog
.parent
:
211 self
.chains
.switch_to(None)
212 for x
in self
.sub_windows
:
213 if x
.disp
.prog
is not root
and not x
.disp
.prog
.parent
:
216 def build_tree(self
, prog
, iter = None):
217 child_iter
= self
.prog_model
.append(iter)
218 self
.prog_model
.set(child_iter
, 0, prog
.name
,
221 self
.prog_to_path
[prog
] = self
.prog_model
.get_path(child_iter
)
222 for p
in prog
.subprograms
.values():
223 self
.build_tree(p
, child_iter
)
225 def run_return(self
, exit
):
227 print "run_return: failure!"
228 self
.view
.jump_to_innermost_failure()
229 def cb(choice
, self
= self
):
231 self
.view
.record_at_point()
232 box
= MultipleChoice("Program failed - record a failure case?",
233 [('Record', self
.view
.record_at_point
), 'Cancel'])
234 box
.set_title('Dome')
236 print "List: execution done!"
238 def button_press(self
, tree
, event
):
239 if event
.button
== 2 or event
.button
== 3:
240 path
, col
, cx
, cy
= tree
.get_path_at_pos(event
.x
, event
.y
)
241 print "Event on", path
242 iter = self
.prog_model
.get_iter(path
)
243 path
= self
.prog_model
.get_value(iter, 1)
244 if event
.button
== 3:
245 prog
= self
.view
.name_to_prog(path
)
246 self
.show_menu(event
, prog
)
248 self
.view
.run_new(self
.run_return
)
249 if event
.state
& g
.gdk
.SHIFT_MASK
:
250 self
.view
.may_record(['map', path
])
252 self
.view
.may_record(['play', path
])
255 def menu_delete(self
):
256 prog
= self
.prog_menu_prog
258 rox
.alert("Can't delete the root program!")
260 prog
.parent
.remove_sub(prog
)
262 def menu_rename(self
):
263 prog
= self
.prog_menu_prog
264 def rename(name
, prog
= prog
):
266 GetArg('Rename program', rename
, ['Program name:'])
268 def menu_new_prog(self
):
269 prog
= self
.prog_menu_prog
273 GetArg('New program', create
, ['Program name:'])
275 def menu_new_view(self
):
276 prog
= self
.prog_menu_prog
277 cw
= ChainWindow(self
.view
, prog
)
279 self
.sub_windows
.append(cw
)
281 self
.sub_windows
.remove(cw
)
283 cw
.connect('destroy', lost_cw
)
286 prog
= self
.prog_menu_prog
287 self
.view
.run_new(self
.run_return
)
288 self
.view
.may_record(['map', prog
.get_path()])
291 prog
= self
.prog_menu_prog
292 self
.view
.run_new(self
.run_return
)
293 self
.view
.may_record(['play', prog
.get_path()])
295 def show_menu(self
, event
, prog
):
296 self
.prog_menu_prog
= prog
297 prog_menu
.popup(self
, event
)
299 def update_stack(self
, op
):
300 "The stack has changed - redraw 'op'"
301 if op
and op
.get_program() == self
.chains
.prog
:
302 self
.chains
.update_all()
303 l
= len(self
.view
.exec_stack
) + len(self
.view
.foreach_stack
)
309 text
= '%d frames' % l
310 if self
.view
.chroots
:
311 text
+= ' (%d enters)' % len(self
.view
.chroots
)
312 self
.stack_frames
.set_text(text
)
314 def show_prog(self
, prog
):
315 path
= self
.prog_to_path
[prog
]
316 iter = self
.prog_model
.get_iter(path
)
317 self
.tree
.get_selection().select_iter(iter)
319 class ChainDisplay(canvas
.Canvas
):
320 "A graphical display of a chain of nodes."
321 def __init__(self
, view
, prog
= None):
322 canvas
.Canvas
.__init
__(self
)
323 self
.connect('destroy', self
.destroyed
)
325 self
.unset_flags(g
.CAN_FOCUS
)
327 self
.drag_last_pos
= None
329 self
.exec_point
= None # CanvasItem, or None
330 self
.rec_point
= None
332 s
= self
.get_style().copy()
333 s
.bg
[g
.STATE_NORMAL
] = g
.gdk
.color_parse('light green')
338 self
.set_size_request(100, 100)
342 self
.view
.model
.root_program
.watchers
.append(self
)
346 def update_points(self
):
347 self
.put_point('rec_point')
348 self
.put_point('exec_point')
351 self
.scroll_to_show(self
.rec_point
)
353 def scroll_to_show(self
, item
):
354 (lx
, ly
, hx
, hy
) = item
.get_bounds()
355 x
, y
= item
.i2w(0, 0)
356 x
, y
= self
.w2c(x
, y
)
363 sx
, sy
= self
.get_scroll_offsets()
369 (x
, y
, w
, h
) = self
.get_allocation()
378 self
.scroll_to(sx
, sy
)
380 def put_point(self
, point
):
381 item
= getattr(self
, point
)
384 setattr(self
, point
, None)
389 opexit
= getattr(self
.view
, point
)
390 if point
== 'exec_point' and self
.view
.op_in_progress
:
391 opexit
= (self
.view
.op_in_progress
, None)
395 if op
.get_program() != self
.prog
:
398 g
= self
.op_to_group
[op
]
401 if point
== 'rec_point':
407 item
= self
.root().add(canvas
.CanvasRect
,
408 x1
= -s
, x2
= s
, y1
= -s
, y2
= s
,
410 outline_color
= 'black', width_pixels
= 1)
411 setattr(self
, point
, item
)
412 item
.connect('event', self
.line_event
, op
, exit
)
415 # TODO: cope with exit == None
416 x1
, y1
, x2
, y2
= self
.get_arrow_ends(op
, exit
)
417 x1
, y1
= g
.i2w(x1
, y1
)
418 x2
, y2
= g
.i2w(x2
, y2
)
419 item
.move((x1
+ x2
) / 2, (y1
+ y2
) / 2)
421 def destroyed(self
, widget
):
425 self
.view
.model
.root_program
.watchers
.remove(self
)
426 print "(ChainDisplay destroyed)"
428 def switch_to(self
, prog
):
429 if prog
is self
.prog
:
434 def prog_tree_changed(self
):
437 def program_changed(self
, op
):
438 if (not op
) or op
.get_program() == self
.prog
:
441 def update_all(self
):
445 self
.op_to_group
= {}
446 self
.nodes
= self
.root().add(canvas
.CanvasGroup
, x
= 0, y
= 0)
448 self
.create_node(self
.prog
.code
, self
.nodes
)
456 def op_colour(self
, op
):
457 if op
in self
.view
.exec_stack
:
461 def update_links(self
, op
= None):
462 """Walk through all nodes in the tree-version of the op graph,
463 making all the links (which already exist as stubs) point to
470 if op
.next
.prev
[0] == op
:
471 self
.update_links(op
.next
)
473 self
.join_nodes(op
, 'next')
475 if op
.fail
.prev
[0] == op
:
476 self
.update_links(op
.fail
)
478 self
.join_nodes(op
, 'fail')
479 if isinstance(op
, Block
):
480 self
.update_links(op
.start
)
482 def create_node(self
, op
, group
):
483 self
.op_to_group
[op
] = group
485 if isinstance(op
, Block
):
486 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
487 self
.create_node(op
.start
, gr
)
488 #self.update_now() # GnomeCanvas bug?
489 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
493 border
= gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 4, y2
= hy
+ 4,
494 outline_color
= 'black', width_pixels
= 1)
495 border
.lower_to_bottom()
497 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= minx
+ 8, y1
= ly
+ 4, y2
= hy
+ 4,
498 fill_color
= 'blue').lower_to_bottom()
501 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 5, y2
= ly
+ 13,
502 fill_color
= colour
).lower_to_bottom()
503 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= hy
- 3, y2
= hy
+ 3,
504 fill_color
= colour
).lower_to_bottom()
507 margin
= op
.enter
* 8
508 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= ly
+ 5 + margin
, y2
= ly
+ 13 + margin
,
509 fill_color
= colour
).lower_to_bottom()
510 gr
.add(canvas
.CanvasRect
, x1
= minx
, x2
= hx
+ 4, y1
= hy
- 3 - margin
, y2
= hy
+ 3 - margin
,
511 fill_color
= colour
).lower_to_bottom()
513 group
.width
, group
.height
= hx
, hy
517 if op
.action
[0] == 'Start':
518 text
= str(op
.parent
.comment
.replace('\\n', '\n'))
520 #text_font = '-misc-fixed-bold-r-normal-*-*-120-*-*-c-*-iso8859-1'
521 text_col
= 'dark blue'
523 text
= str(action_to_text(op
.action
))
525 #text_font = '-misc-fixed-medium-r-normal-*-*-120-*-*-c-*-iso8859-1'
528 group
.ellipse
= group
.add(canvas
.CanvasEllipse
,
529 fill_color
= self
.op_colour(op
),
530 outline_color
= 'black',
534 group
.ellipse
.connect('event', self
.op_event
, op
)
536 label
= group
.add(canvas
.CanvasText
,
539 anchor
= g
.ANCHOR_NE
,
540 justification
= 'right',
541 fill_color
= text_col
,
544 #self.update_now() # GnomeCanvas bug?
545 (lx
, ly
, hx
, hy
) = label
.get_bounds()
549 group
.width
, group
.height
= 0, 0
551 if op
.next
and op
.next
.prev
[0] == op
:
552 sx
, sy
= self
.get_arrow_start(op
, 'next')
553 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
554 self
.create_node(op
.next
, gr
)
555 #self.update_now() # GnomeCanvas bug?
556 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
557 drop
= max(20, next_off_y
+ 10)
560 while isinstance(next
, Block
):
564 gr
.move(sx
+ x
, sy
+ y
)
566 group
.next_line
= group
.add(canvas
.CanvasLine
,
567 fill_color
= 'black',
568 points
= connect(0, 0, 1, 1),
574 group
.next_line
.connect('event', self
.line_event
, op
, 'next')
576 (x
, y
) = DEFAULT_FAIL
577 if op
.fail
and op
.fail
.prev
[0] == op
:
578 sx
, sy
= self
.get_arrow_start(op
, 'fail')
580 gr
= group
.add(canvas
.CanvasGroup
, x
= 0, y
= 0)
581 self
.create_node(op
.fail
, gr
)
582 #self.update_now() # GnomeCanvas bug?
583 (lx
, ly
, hx
, hy
) = gr
.get_bounds()
586 while isinstance(fail
, Block
):
590 gr
.move(sx
+ x
, sy
+ y
)
591 group
.fail_line
= group
.add(canvas
.CanvasLine
,
592 fill_color
= '#ff6666',
593 points
= connect(0, 0, 1, 1),
599 group
.fail_line
.lower_to_bottom()
600 group
.fail_line
.connect('event', self
.line_event
, op
, 'fail')
601 if op
.action
[0] == 'Start':
602 group
.fail_line
.hide()
604 self
.join_nodes(op
, 'next')
605 self
.join_nodes(op
, 'fail')
607 if self
.view
.breakpoints
.has_key((op
, 'next')):
608 group
.next_line
.set(line_style
= g
.gdk
.LINE_ON_OFF_DASH
)
609 if self
.view
.breakpoints
.has_key((op
, 'fail')):
610 group
.fail_line
.set(line_style
= g
.gdk
.LINE_ON_OFF_DASH
)
612 def edit_op(self
, op
):
614 if op
.action
[0] == 'do_search' or op
.action
[0] == 'do_global':
615 t
= editables
[0].get_text()
617 from Ft
.Xml
.XPath
import XPathParser
618 if t
.find('@CURRENT@') == -1:
620 XPathParser
.new().parse(t
)
622 alert('Invalid search pattern!')
628 op
.action
[i
] = e
.get_text()
630 print "Done editing!"
634 win
.vbox
.pack_start(g
.Label(op
.action
[0]), TRUE
, FALSE
, 0)
635 editables
= [] # [ Entry | None ]
637 for x
in op
.action
[1:]:
639 entry
.set_text(str(x
))
640 win
.vbox
.pack_start(entry
, TRUE
, FALSE
, 0)
641 if type(x
) == str or type(x
) == unicode:
642 editables
.append(entry
)
643 entry
.connect('activate', lambda e
: modify())
648 entry
.set_editable(FALSE
)
649 editables
.append(None)
651 win
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
652 win
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
654 def response(box
, resp
):
656 if resp
== g
.RESPONSE_OK
:
658 win
.connect('response', response
)
661 win
.set_response_sensitive(g
.RESPONSE_OK
, FALSE
)
665 def join_nodes(self
, op
, exit
):
667 x1
, y1
, x2
, y2
= self
.get_arrow_ends(op
, exit
)
669 prev_group
= self
.op_to_group
[op
]
670 line
= getattr(prev_group
, exit
+ '_line')
671 line
.set(points
= connect(x1
, y1
, x2
, y2
))
673 print "*** ERROR setting arc from %s:%s" % (op
, exit
)
675 def op_event(self
, item
, event
, op
):
676 if event
.type == g
.gdk
.BUTTON_PRESS
:
677 print "Prev %s = %s" % (op
, map(str, op
.prev
))
678 if event
.button
== 1:
679 if op
.parent
.start
!= op
or not op
.parent
.is_toplevel():
680 self
.drag_last_pos
= (event
.x
, event
.y
)
682 self
.drag_last_pos
= None
684 self
.show_op_menu(event
, op
)
685 elif event
.type == g
.gdk
.BUTTON_RELEASE
:
686 if event
.button
== 1:
687 self
.drag_last_pos
= None
688 self
.program_changed(None)
689 elif event
.type == g
.gdk
.ENTER_NOTIFY
:
690 item
.set(fill_color
= 'white')
691 elif event
.type == g
.gdk
.LEAVE_NOTIFY
:
692 item
.set(fill_color
= self
.op_colour(op
))
693 elif event
.type == g
.gdk
.MOTION_NOTIFY
and self
.drag_last_pos
:
694 if not event
.state
& g
.gdk
.BUTTON1_MASK
:
696 self
.drag_last_pos
= None
697 self
.program_changed(None)
699 x
, y
= (event
.x
, event
.y
)
700 dx
, dy
= x
- self
.drag_last_pos
[0], y
- self
.drag_last_pos
[1]
701 if abs(op
.dx
+ dx
) < 4:
703 x
= dx
+ self
.drag_last_pos
[0]
704 if abs(op
.dy
+ dy
) < 4:
706 y
= dy
+ self
.drag_last_pos
[1]
709 self
.drag_last_pos
= (x
, y
)
711 self
.op_to_group
[op
].move(dx
, dy
)
714 self
.join_nodes(p
, 'next')
716 self
.join_nodes(p
, 'fail')
718 #self.create_node(self.prog.start, self.nodes)
720 elif event
.type == g
.gdk
._2BUTTON
_PRESS
:
721 if op
.action
[0] == 'Start':
722 self
.edit_comment(op
.parent
)
725 print "(edit; stop drag!)"
726 self
.drag_last_pos
= None
727 self
.program_changed(None)
730 def edit_comment(self
, block
):
731 assert isinstance(block
, Block
)
734 block
.set_comment(comment
)
735 GetArg('Comment', set, ['Comment:'],
736 message
= '\\n for a newline', init
= [block
.comment
])
738 def block_toggle_enter(self
):
739 self
.op_menu_op
.toggle_enter()
741 def block_toggle_foreach(self
):
742 self
.op_menu_op
.toggle_foreach()
744 def block_toggle_restore(self
):
745 self
.op_menu_op
.toggle_restore()
747 def block_edit_comment(self
):
748 self
.edit_comment(self
.op_menu_op
)
751 self
.edit_op(self
.op_menu_op
)
753 def op_swap_nf(self
):
754 self
.op_menu_op
.swap_nf()
756 def op_del_node(self
):
758 if op
.next
and op
.fail
:
759 rox
.alert("Can't delete a node with both exits in use")
761 self
.clipboard
= op
.del_node()
763 def show_op_menu(self
, event
, op
):
764 if op
.action
[0] == 'Start':
765 self
.op_menu_op
= op
.parent
766 block_menu
.popup(self
, event
)
769 op_menu
.popup(self
, event
)
771 def paste_chain(self
, op
, exit
):
772 print "Paste", self
.clipboard
774 new
= load(doc
.documentElement
, op
.parent
)
775 start
= new
.start
.next
776 new
.start
.unlink('next', may_delete
= 0)
777 start
.set_parent(None)
778 op
.link_to(start
, exit
)
780 def end_link_drag(self
, item
, event
, src_op
, exit
):
781 # Scan all the nodes looking for one nearby...
782 x
, y
= event
.x
, event
.y
784 def closest_node(op
):
785 "Return the closest (node, dist) in this chain to (x, y)"
786 nx
, ny
= self
.op_to_group
[op
].i2w(0, 0)
789 elif isinstance(op
, Block
):
792 best
= (op
, math
.hypot(nx
- x
, ny
- y
))
793 if op
.next
and op
.next
.prev
[0] == op
:
794 next
= closest_node(op
.next
)
795 if next
and (best
is None or next
[1] < best
[1]):
797 if op
.fail
and op
.fail
.prev
[0] == op
:
798 fail
= closest_node(op
.fail
)
799 if fail
and (best
is None or fail
[1] < best
[1]):
801 if isinstance(op
, Block
):
802 sub
= closest_node(op
.start
)
803 if sub
and (best
is None or sub
[1] < best
[1]):
807 result
= closest_node(self
.prog
.code
)
813 # Too far... put the line back to the disconnected state...
814 self
.join_nodes(src_op
, exit
)
817 while node
.action
[0] == 'Start':
819 src_op
.link_to(node
, exit
)
823 def line_paste_chain(self
):
824 op
, exit
= self
.line_menu_line
825 self
.paste_chain(op
, exit
)
827 def line_add_block(self
):
828 op
, exit
= self
.line_menu_line
829 op
.link_to(Block(op
.parent
), exit
)
831 def line_toggle_breakpoint(self
):
832 op
, exit
= self
.line_menu_line
833 bp
= self
.view
.breakpoints
834 if bp
.has_key((op
, exit
)):
840 def line_yank_chain(self
):
841 op
, exit
= self
.line_menu_line
842 next
= getattr(op
, exit
)
844 rox
.alert('Nothing to yank!')
846 self
.clipboard
= op
.to_doc()
849 def line_del_chain(self
):
850 op
, exit
= self
.line_menu_line
851 next
= getattr(op
, exit
)
853 rox
.alert('Nothing to delete!')
855 self
.clipboard
= next
.to_doc()
858 def line_event(self
, item
, event
, op
, exit
):
859 # Item may be rec_point or exec_point...
860 item
= getattr(self
.op_to_group
[op
], exit
+ '_line')
862 if event
.type == g
.gdk
.BUTTON_PRESS
:
863 if event
.button
== 1:
864 if not getattr(op
, exit
):
865 self
.drag_last_pos
= (event
.x
, event
.y
)
866 #item.grab(BUTTON_RELEASE | MOTION_NOTIFY, no_cursor, event.time)
867 elif event
.button
== 2:
868 self
.paste_chain(op
, exit
)
869 elif event
.button
== 3:
870 self
.line_menu_line
= (op
, exit
)
871 line_menu
.popup(self
, event
)
872 elif event
.type == g
.gdk
.BUTTON_RELEASE
:
873 if event
.button
== 1:
874 print "Clicked exit %s of %s" % (exit
, op
)
875 #item.ungrab(event.time)
876 self
.view
.set_exec((op
, exit
))
877 self
.drag_last_pos
= None
878 if not getattr(op
, exit
):
879 self
.end_link_drag(item
, event
, op
, exit
)
880 elif event
.type == g
.gdk
.MOTION_NOTIFY
and self
.drag_last_pos
:
881 if not event
.state
& g
.gdk
.BUTTON1_MASK
:
883 self
.drag_last_pos
= None
884 if not getattr(op
, exit
):
885 self
.end_link_drag(item
, event
, op
, exit
)
887 x
, y
= (event
.x
, event
.y
)
888 dx
, dy
= x
- self
.drag_last_pos
[0], y
- self
.drag_last_pos
[1]
890 if abs(dx
) > 4 or abs(dy
) > 4:
891 sx
, sy
= self
.get_arrow_start(op
, exit
)
892 x
, y
= item
.w2i(event
.x
, event
.y
)
893 gr
= self
.op_to_group
[op
]
898 item
.set(points
= connect(sx
, sy
, x
, y
))
899 elif event
.type == g
.gdk
.ENTER_NOTIFY
:
900 item
.set(fill_color
= 'white')
901 elif event
.type == g
.gdk
.LEAVE_NOTIFY
:
903 item
.set(fill_color
= 'black')
905 item
.set(fill_color
= '#ff6666')
908 def get_arrow_start(self
, op
, exit
):
909 gr
= self
.op_to_group
[op
]
910 return ((exit
== 'fail' and gr
.width
) or 0, gr
.height
)
912 def get_arrow_ends(self
, op
, exit
):
913 """Return coords of arrow, relative to op's group."""
914 op2
= getattr(op
, exit
)
916 prev_group
= self
.op_to_group
[op
]
918 x1
, y1
= self
.get_arrow_start(op
, exit
)
922 group
= self
.op_to_group
[op2
]
927 x2
, y2
= group
.i2w(0, 0)
928 x2
, y2
= prev_group
.w2i(x2
, y2
)
930 x2
, y2
= DEFAULT_NEXT
934 x2
, y2
= DEFAULT_FAIL
937 return (x1
, y1
, x2
, y2
)
939 def set_bounds(self
):
940 #self.update_now() # GnomeCanvas bug?
941 min_x
, min_y
, max_x
, max_y
= self
.root().get_bounds()
946 self
.set_scroll_region(min_x
, min_y
, max_x
, max_y
)
947 self
.root().move(0, 0) # Magic!
948 #self.set_usize(max_x - min_x, -1)
950 def canvas_to_world(self
, (x
, y
)):
951 "Canvas routine seems to be broken..."
952 mx
, my
, maxx
, maxy
= self
.get_scroll_region()
953 sx
= self
.get_hadjustment().value
954 sy
= self
.get_hadjustment().value
955 return (x
+ mx
+ sx
, y
+ my
+ sy
)
957 class ChainWindow(rox
.Window
):
958 def __init__(self
, view
, prog
):
959 rox
.Window
.__init
__(self
)
960 swin
= g
.ScrolledWindow()
962 disp
= ChainDisplay(view
, prog
)
967 self
.set_default_size(-1, 200)
968 self
.set_title(prog
.name
)
970 def update_points(self
):
971 self
.disp
.update_points()