1 from __future__
import generators
4 from rox
import g
, TRUE
, FALSE
, alert
8 from StringIO
import StringIO
12 from rox
.Menu
import Menu
17 prog_menu
= Menu('programs', [
18 ('/Play', 'menu_play', '', ''),
19 ('/Map', 'menu_map', '', ''),
20 ('/View', 'menu_new_view', '', ''),
21 ('/', '', '', '<separator>'),
22 ('/New program', 'menu_new_prog', '', ''),
23 ('/Rename', 'menu_rename', '', ''),
24 ('/Delete', 'menu_delete', '', ''),
27 line_menu
= Menu('line', [
28 ('/Set\/clear breakpoint', 'line_toggle_breakpoint', '', ''),
29 ('/Yank chain', 'line_yank_chain', '', ''),
30 ('/Remove link', 'line_del_chain', '', ''),
31 ('/Paste chain', 'line_paste_chain', '', ''),
32 ('/Add block', 'line_add_block', '', '')
35 block_menu
= Menu('op', [
36 ('/Toggle Enter\/Leave', 'block_toggle_enter', '', ''),
37 ('/Toggle Foreach','block_toggle_foreach', '', ''),
38 ('/Toggle Restore Mark','block_toggle_restore', '', ''),
39 ('/Edit comment', 'block_edit_comment', '', ''),
40 ('/Swap next\/fail', 'op_swap_nf', '', ''),
41 ('/Remove node', 'op_del_node', '', '')
44 op_menu
= Menu('op', [
45 ('/Edit node', 'op_edit', '', ''),
46 ('/Swap next\/fail', 'op_swap_nf', '', ''),
47 ('/Remove node', 'op_del_node', '', '')
50 from GetArg
import GetArg
51 from Program
import Program
, load
, Block
58 return text
[:26] + '...'
60 def connect(x1
, y1
, x2
, y2
):
61 """Chop 5 pixels off both ends of this line"""
65 l
= math
.hypot(dx
, dy
)
69 return (x1
+ dx
, y1
+ dy
, x2
- dx
, y2
- dy
)
71 DEFAULT_NEXT
= (0, 25)
72 DEFAULT_FAIL
= (20, 20)
74 expand_history
= {} # Prog name -> expanded flag
76 def action_to_text(action
):
78 if text
== 'Start': return ''
81 text
= string
.capitalize(string
.replace(text
, '_', ' '))
86 if action
[0] in ('do_search', 'xpath', 'move_selection'):
88 pat
= string
.replace(pat
, 'following-sibling::', '>>')
89 pat
= string
.replace(pat
, 'preceding-sibling::', '<<')
90 pat
= string
.replace(pat
, 'child::', '')
91 pat
= string
.replace(pat
, '[1]', '')
92 pat
= string
.replace(pat
, 'text()[ext:match', '[')
95 i
= string
.rfind(pat
[:20], '/')
97 i
= string
.rfind(pat
[:20], ':')
100 details
= details
+ pat
[:i
+ 1] + '\n'
102 details
= details
+ pat
103 elif action
[0] == 'attribute':
104 details
= trunc(str(action
[2]))
105 elif action
[0] == 'set_attrib':
106 details
= trunc(str(action
[1]))
107 elif action
[0] == 'add_attrib':
108 details
= trunc(str(action
[2]))
109 elif action
[0] == 'add_node':
110 details
= trunc(action
[2])
111 elif action
[0] == 'subst':
112 details
= action
[1] + ' -> ' + action
[2]
113 elif action
[0] == 'play' or action
[0] == 'map':
114 if len(action
[1]) > 20:
115 details
= '...' + str(action
[1][-19:])
117 details
= str(action
[1])
120 details
= `action
[1:]`
122 details
= str(action
[1])
123 if len(details
) > 20:
124 details
= trunc(`details`
)
125 text
= text
+ '\n' + details
129 def __init__(self
, view
):
130 g
.VBox
.__init
__(self
)
132 def destroyed(widget
):
133 #print "List destroy!!"
134 sel
.disconnect(self
.sel_changed_signal
)
135 self
.view
.lists
.remove(self
)
136 self
.view
.model
.root_program
.watchers
.remove(self
)
137 self
.connect('destroy', destroyed
)
140 self
.sub_windows
= []
142 self
.stack_frames
= g
.Label('')
143 self
.pack_start(self
.stack_frames
, FALSE
, TRUE
, 0)
144 self
.stack_frames
.show()
145 self
.update_stack(None)
148 self
.pack_start(pane
, expand
= 1, fill
= 1)
150 swin
= g
.ScrolledWindow()
151 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
153 self
.prog_model
= g
.TreeStore(str, str)
154 tree
= g
.TreeView(self
.prog_model
)
155 tree
.connect('button-press-event', self
.button_press
)
156 tree
.unset_flags(g
.CAN_FOCUS
)
157 tree
.set_headers_visible(FALSE
)
160 cell
= g
.CellRendererText()
161 column
= g
.TreeViewColumn('Program', cell
, text
= 0)
162 tree
.append_column(column
)
164 sel
= tree
.get_selection()
165 # Doesn't get destroyed, so record signal number
166 self
.sel_changed_signal
= sel
.connect('changed', self
.change_prog
)
168 self
.chains
= ChainDisplay(view
)
169 self
.prog_tree_changed()
173 v
.set_shadow_type(g
.SHADOW_NONE
)
176 swin
= g
.ScrolledWindow()
177 swin
.set_policy(g
.POLICY_AUTOMATIC
, g
.POLICY_AUTOMATIC
)
179 swin
.add_with_viewport(self
.chains
)
182 pane
.set_position(200)
184 sel
.set_mode(g
.SELECTION_BROWSE
)
185 root_iter
= self
.prog_model
.get_iter_first()
186 sel
.select_iter(root_iter
)
187 tree
.expand_row(self
.prog_model
.get_path(root_iter
), FALSE
)
189 self
.view
.lists
.append(self
)
190 self
.view
.model
.root_program
.watchers
.append(self
)
192 def change_prog(self
, sel
):
193 selected
= sel
.get_selected()
196 model
, iter = selected
198 path
= model
.get_value(iter, 1)
199 self
.chains
.switch_to(self
.view
.name_to_prog(path
))
201 self
.chains
.switch_to(None)
203 def set_innermost_failure(self
, op
):
204 prog
= op
.get_program()
205 #print "list: set_innermost_failure:", prog
208 def update_points(self
):
209 self
.chains
.update_points()
210 for x
in self
.sub_windows
:
213 def program_changed(self
, op
):
216 def prog_tree_changed(self
):
217 self
.prog_to_path
= {}
218 self
.prog_model
.clear()
219 self
.build_tree(self
.view
.model
.root_program
)
221 # Check for now deleted programs still being displayed
222 root
= self
.view
.model
.root_program
223 if self
.chains
and self
.chains
.prog
and not self
.chains
.prog
.parent
:
224 self
.chains
.switch_to(None)
225 for x
in self
.sub_windows
:
226 if x
.disp
.prog
is not root
and not x
.disp
.prog
.parent
:
229 def build_tree(self
, prog
, iter = None):
230 child_iter
= self
.prog_model
.append(iter)
231 self
.prog_model
.set(child_iter
, 0, prog
.name
,
234 self
.prog_to_path
[prog
] = self
.prog_model
.get_path(child_iter
)
235 for p
in prog
.subprograms
.values():
236 self
.build_tree(p
, child_iter
)
238 def run_return(self
, exit
):
239 print "List execution finished:", exit
241 #self.view.jump_to_innermost_failure()
243 if rox
.confirm("Program failed - record a failure case?",
244 g
.STOCK_NO
, 'Record'):
245 self
.view
.record_at_point()
249 def button_press(self
, tree
, event
):
250 if event
.button
== 2 or event
.button
== 3:
251 ret
= tree
.get_path_at_pos(int(event
.x
), int(event
.y
))
253 return 1 # Click on blank area
254 path
, col
, cx
, cy
= ret
255 #print "Event on", path
256 iter = self
.prog_model
.get_iter(path
)
257 path
= self
.prog_model
.get_value(iter, 1)
258 if event
.button
== 3:
259 prog
= self
.view
.name_to_prog(path
)
260 self
.show_menu(event
, prog
)
262 self
.view
.run_new(self
.run_return
)
263 self
.view
.set_status("Running '%s'" % path
)
264 if event
.state
& g
.gdk
.SHIFT_MASK
:
265 self
.view
.may_record(['map', path
])
267 self
.view
.may_record(['play', path
])
270 def menu_delete(self
):
271 prog
= self
.prog_menu_prog
273 rox
.alert("Can't delete the root program!")
275 prog
.parent
.remove_sub(prog
)
277 def menu_rename(self
):
278 prog
= self
.prog_menu_prog
279 def rename(name
, prog
= prog
):
281 GetArg('Rename program', rename
, ['Program name:'])
283 def menu_new_prog(self
):
284 prog
= self
.prog_menu_prog
288 GetArg('New program', create
, ['Program name:'])
290 def menu_new_view(self
):
291 prog
= self
.prog_menu_prog
292 cw
= ChainWindow(self
.view
, prog
)
294 self
.sub_windows
.append(cw
)
296 self
.sub_windows
.remove(cw
)
297 cw
.connect('destroy', lost_cw
)
300 prog
= self
.prog_menu_prog
301 self
.view
.run_new(self
.run_return
)
302 self
.view
.set_status("Running '%s'" % prog
.get_path())
303 self
.view
.may_record(['map', prog
.get_path()])
306 prog
= self
.prog_menu_prog
307 self
.view
.run_new(self
.run_return
)
308 self
.view
.set_status("Running '%s'" % prog
.get_path())
309 self
.view
.may_record(['play', prog
.get_path()])
311 def show_menu(self
, event
, prog
):
312 self
.prog_menu_prog
= prog
313 prog_menu
.popup(self
, event
)
315 def update_stack(self
, op
):
316 "The stack has changed - redraw 'op'"
317 if op
and op
.get_program() == self
.chains
.prog
:
318 self
.chains
.update_all()
319 l
= len(self
.view
.exec_stack
) + len(self
.view
.foreach_stack
)
325 text
= '%d frames' % l
326 if self
.view
.chroots
:
327 text
+= ' (%d enters)' % len(self
.view
.chroots
)
328 self
.stack_frames
.set_text(text
)
330 def show_prog(self
, prog
):
331 path
= self
.prog_to_path
[prog
]
335 self
.tree
.expand_row(tuple(partial
), FALSE
)
336 iter = self
.prog_model
.get_iter(path
)
337 self
.tree
.get_selection().select_iter(iter)
339 def selected_program(self
):
340 return self
.chains
.prog
342 class ChainDummy(g
.TreeView
):
343 def __init__(self
, view
, prog
= None):
344 g
.TreeView
.__init
__(self
)
346 def switch_to(self
, prog
):
348 def update_points(self
):
352 "A visual object in the display."
353 def __init__(self
, da
, x
, y
):
361 w
.draw_rectangle(da
.style
.black_gc
, True, self
.x
, self
.y
, 10, 10)
363 def maybe_clicked(self
, event
):
366 class ChainOp(ChainNode
):
370 def __init__(self
, da
, op
, x
, y
):
372 ChainNode
.__init
__(self
, da
, x
, y
)
376 da
.op_to_object
[op
] = self
378 if op
.next
and op
.next
.prev
[0] == op
:
379 self
.next
= da
.create_op(op
.next
, x
, y
+ self
.height
+ 4)
380 self
.total_width
= max(self
.width
, self
.next
.total_width
)
383 self
.total_width
= self
.width
385 if op
.fail
and op
.fail
.prev
[0] == op
:
387 indent
= self
.total_width
+ 20
390 self
.fail
= da
.create_op(op
.fail
, x
+ indent
, y
+ self
.height
+ 4)
391 self
.total_width
= indent
+ self
.fail
.total_width
395 def build_leaf(self
):
396 text
= str(action_to_text(self
.op
.action
))
397 self
.layout
= self
.da
.create_pango_layout(text
)
399 self
.width
, self
.height
= self
.layout
.get_pixel_size()
401 self
.height
= max(self
.height
, 20)
408 w
.draw_arc(da
.style
.white_gc
, True, self
.x
, self
.y
, 10, 10, 0, 400 * 60)
409 w
.draw_arc(da
.style
.black_gc
, False, self
.x
, self
.y
, 10, 10, 0, 400 * 60)
410 w
.draw_layout(da
.style
.black_gc
, self
.x
+ 12, self
.y
- 2, self
.layout
)
412 self
.draw_link(self
.next
, 5, 10, 'black')
413 self
.draw_link(self
.fail
, 5, 12, 'red')
415 if (op
, 'next') in self
.da
.view
.breakpoints
:
416 w
.draw_arc(da
.style
.black_gc
, True,
417 self
.x
+ 2, self
.y
+ 12, 6, 6, 0, 400 * 60)
418 if (op
, 'fail') in self
.da
.view
.breakpoints
:
419 w
.draw_arc(da
.style
.black_gc
, True,
420 self
.x
+ 14, self
.y
+ 10, 6, 6, 0, 400 * 60)
422 def draw_link(self
, dest
, dx
, dy
, colour
):
427 pen
= da
.style
.white_gc
428 pen
.set_rgb_fg_color(g
.gdk
.color_parse(colour
))
429 da
.backing
.draw_line(pen
, self
.x
+ dx
, self
.y
+ dy
, dest
.x
+ 5, self
.y
+ dy
)
430 da
.backing
.draw_line(pen
, dest
.x
+ 5, self
.y
+ dy
, dest
.x
+ 5, dest
.y
)
431 pen
.set_rgb_fg_color(g
.gdk
.color_parse('white'))
433 def where(self
, x
, y
):
434 "Identify where (x,y) falls on us -> None, 'op', 'next', 'fail'"
436 if x
< 0: return False
438 if y
< 0: return False
440 if x
>= self
.next_box
[0] and y
>= self
.next_box
[1] and \
441 x
<= self
.next_box
[0] + box_size
and y
<= self
.next_box
[1] + box_size
:
444 if x
>= self
.fail_box
[0] and y
>= self
.fail_box
[1] and \
445 x
<= self
.fail_box
[0] + box_size
and y
<= self
.fail_box
[1] + box_size
:
448 if x
< self
.width
and y
< self
.height
: return 'op'
450 def maybe_clicked(self
, event
):
451 pos
= self
.where(event
.x
, event
.y
)
453 if event
.button
== 1:
454 if pos
in ('next', 'fail'):
455 self
.da
.view
.set_exec((self
.op
, pos
))
458 self
.da
.show_menu(event
, self
.op
, pos
)
460 self
.da
.show_menu(event
, self
.op
)
466 for n
in self
.next
.all_nodes(): yield n
468 for n
in self
.fail
.all_nodes(): yield n
471 class ChainBlock(ChainOp
):
472 def __init__(self
, da
, block
, x
, y
):
473 assert isinstance(block
, Block
)
474 ChainOp
.__init
__(self
, da
, block
, x
, y
)
477 while p
and not isinstance(p
, Program
):
480 self
.next_box
= (0, self
.height
)
481 self
.fail_box
= (self
.width
, self
.height
- box_size
)
483 def build_leaf(self
):
488 self
.layout
= self
.da
.create_pango_layout(self
.op
.comment
.replace('\\n', '\n'))
489 self
.width
, height
= self
.layout
.get_pixel_size()
495 self
.margin
= (4 + self
.op
.foreach
* 6, 4 + (self
.op
.enter
+ self
.op
.restore
) * 6)
496 self
.width
+= self
.margin
[0]
498 self
.start
= self
.da
.create_op(self
.op
.start
, x
+ self
.margin
[0], y
+ self
.margin
[1])
502 for node
in self
.start
.all_nodes():
503 self
.width
= max(self
.width
, node
.x
+ node
.width
- self
.x
)
504 self
.height
= max(self
.height
, node
.y
+ node
.height
- self
.y
)
507 self
.height
+= 4 + (self
.op
.enter
+ self
.op
.restore
) * 6
512 w
.draw_rectangle(da
.style
.black_gc
, False, self
.x
, self
.y
, self
.width
, self
.height
)
513 pen
= da
.style
.white_gc
518 d
= 15 - min(self
.depth
, 7)
519 pen
.set_rgb_fg_color(g
.gdk
.color_parse('#%x%x%x' % (d
, d
, d
)))
520 w
.draw_rectangle(pen
, True, self
.x
+ 1, self
.y
+ 1, self
.width
- 1, self
.height
- 1)
524 pen
.set_rgb_fg_color(g
.gdk
.color_parse('blue'))
525 w
.draw_rectangle(pen
, True, x
+ 1, y
+ 1, 6, self
.height
- 1)
530 pen
.set_rgb_fg_color(g
.gdk
.color_parse('yellow'))
531 w
.draw_rectangle(pen
, True, x
+ 1, y
+ 1, width
- 1, 6)
532 w
.draw_rectangle(pen
, True, x
+ 1, y
+ self
.height
- 6, width
- 1, 6)
534 pen
.set_rgb_fg_color(g
.gdk
.color_parse('orange'))
535 margin
= op
.enter
* 6
536 w
.draw_rectangle(pen
, True, x
+ 1, y
+ 1 + margin
, width
- 1, 6)
537 w
.draw_rectangle(pen
, True, x
+ 1, y
+ self
.height
- 6 - margin
, width
- 1, 6)
540 pen
.set_rgb_fg_color(g
.gdk
.color_parse('blue'))
541 w
.draw_layout(pen
, self
.x
+ self
.margin
[0], self
.y
+ self
.margin
[1], self
.layout
)
543 pen
.set_rgb_fg_color(g
.gdk
.color_parse('white'))
545 w
.draw_line(pen
, self
.x
+ 1, self
.y
+ 1, self
.x
+ self
.width
- 2, self
.y
+ 1)
546 w
.draw_line(pen
, self
.x
+ 1, self
.y
+ 1, self
.x
+ 1, self
.y
+ self
.height
- 2)
550 self
.draw_link(self
.next
, 5, self
.height
, 'black')
551 self
.draw_link(self
.fail
, self
.width
, self
.height
, 'red')
553 def where(self
, x
, y
):
554 if self
.depth
== 1: return None
556 pos
= ChainOp
.where(self
, x
, y
)
557 if pos
== 'op': pos
= None
560 def contains(self
, x
, y
):
561 return x
>= self
.x
and y
>= self
.y
and \
562 x
< self
.x
+ self
.width
and y
< self
.y
+ self
.height
564 class ChainDisplay(g
.EventBox
):
565 "A graphical display of a chain of nodes."
566 def __init__(self
, view
, prog
= None):
567 g
.EventBox
.__init
__(self
)
568 self
.connect('destroy', self
.destroyed
)
569 self
.set_app_paintable(True)
570 self
.set_double_buffered(False)
571 self
.connect('size-allocate', lambda w
, a
: self
.size_allocate(a
))
574 self
.unset_flags(g
.CAN_FOCUS
)
576 self
.drag_last_pos
= None
578 self
.exec_point
= None # CanvasItem, or None
579 self
.rec_point
= None
585 self
.set_size_request(100, 100)
589 self
.view
.model
.root_program
.watchers
.append(self
)
591 self
.connect('expose-event', self
.expose
)
594 self
.add_events(g
.gdk
.BUTTON_PRESS_MASK | g
.gdk
.BUTTON_RELEASE_MASK |
595 g
.gdk
.POINTER_MOTION_MASK
)
596 self
.connect('button-press-event', self
.button_press
)
597 self
.connect('button-release-event', self
.button_release
)
599 self
.connect('motion-notify-event', self
.motion
)
603 def set_active(self
, active
):
605 self
.modify_bg(g
.STATE_NORMAL
, g
.gdk
.color_parse('white'))
607 self
.modify_bg(g
.STATE_NORMAL
, g
.gdk
.color_parse('#B3AA73'))
609 self
.modify_bg(g
.STATE_NORMAL
, g
.gdk
.color_parse('#FFC0C0'))
611 def update_points(self
):
615 self
.scroll_to_show(self
.rec_point
)
617 def scroll_to_show(self
, item
):
618 print "TODO: scroll_to_show"
620 def put_point(self
, point
):
624 if op
.get_program() != self
.prog
: return
626 obj
= self
.op_to_object
[op
]
628 print "Can't find %s!\n" % op
633 if point
is self
.view
.rec_point
:
646 pen
= self
.style
.white_gc
647 pen
.set_rgb_fg_color(g
.gdk
.color_parse(colour
))
648 w
.draw_rectangle(self
.style
.black_gc
, False, x
, y
, size
, size
)
649 w
.draw_rectangle(pen
, True, x
+ 1, y
+ 1, size
- 1, size
- 1)
650 pen
.set_rgb_fg_color(g
.gdk
.color_parse('white'))
652 def destroyed(self
, widget
):
653 self
.view
.model
.root_program
.watchers
.remove(self
)
655 def switch_to(self
, prog
):
656 if prog
is self
.prog
:
661 def prog_tree_changed(self
):
664 def program_changed(self
, op
):
665 if (not op
) or op
.get_program() == self
.prog
:
668 def create_op(self
, op
, x
, y
):
669 if isinstance(op
, Block
):
670 return ChainBlock(self
, op
, x
, y
)
672 return ChainOp(self
, op
, x
, y
)
674 def update_all(self
):
675 self
.op_to_object
= {}
677 self
.root_object
= self
.create_op(self
.prog
.code
, 4, 4)
678 self
.set_size_request(self
.root_object
.width
+ 8, self
.root_object
.height
+ 8)
680 self
.root_object
= None
681 self
.set_size_request(-1, -1)
686 def size_allocate(self
, alloc
):
690 def create_backing(self
):
691 self
.backing
= g
.gdk
.Pixmap(self
.window
, self
.allocation
.width
, self
.allocation
.height
, -1)
692 self
.window
.set_back_pixmap(self
.backing
, False)
693 self
.backing
.draw_rectangle(self
.style
.bg_gc
[g
.STATE_NORMAL
], True,
694 0, 0, self
.allocation
.width
, self
.allocation
.height
)
696 self
.root_object
.expose()
700 def expose(self
, da
, event
):
701 if not self
.backing
: self
.create_backing()
703 self
.window
.draw_drawable(self
.style
.white_gc
, self
.backing
, 0, 0, 0, 0, -1, -1)
705 self
.put_point(self
.view
.rec_point
)
706 self
.put_point(self
.view
.exec_point
)
709 pen
= self
.style
.black_gc
710 x
= min(self
.drag_box
[0], self
.drag_box
[2])
711 y
= min(self
.drag_box
[1], self
.drag_box
[3])
712 width
= abs(self
.drag_box
[0] - self
.drag_box
[2])
713 height
= abs(self
.drag_box
[1] - self
.drag_box
[3])
714 self
.window
.draw_rectangle(pen
, False, int(x
), int(y
),
715 int(width
), int(height
))
717 op
, exit
= self
.hover
718 pen
= self
.style
.black_gc
721 w
.draw_rectangle(pen
, False, op
.x
+ op
.fail_box
[0], op
.y
+ op
.fail_box
[1],
724 w
.draw_rectangle(pen
, False, op
.x
+ op
.next_box
[0], op
.y
+ op
.next_box
[1],
727 def motion(self
, box
, event
):
729 if (event
.x
, event
.y
) == self
.drag_box
[2:]:
731 self
.drag_box
= (self
.drag_box
[0], self
.drag_box
[1],
735 for op
in self
.op_to_object
.itervalues():
736 pos
= op
.where(event
.x
, event
.y
)
737 if pos
in ('next', 'fail'):
739 if hover
== self
.hover
:
744 def box(self
, top
, ops
):
746 print "Boxed in", block
753 if op
.next
and op
.next
not in ops
:
754 if next
: rox
.alert("New block can't have two next exits!")
756 if op
.fail
and op
.fail
not in ops
:
757 if fail
: rox
.alert("New block can't have two fail exits!")
761 if not rox
.confirm('Put this node in a new block?', 'Create block'):
763 elif not rox
.confirm('Put these %d nodes in a new block?' % len(ops
),
767 new_exits
= (next
and next
.op
.next
, fail
and fail
.op
.fail
)
769 if next
: next
.op
.unlink('next', may_delete
= False)
770 if fail
: fail
.op
.unlink('fail', may_delete
= False)
774 if prev
.next
== top
: exit
= 'next'
776 prev
.unlink(exit
, may_delete
= 0)
777 prev
.link_to(new
, exit
)
781 new
.start
.link_to(top
, 'next')
782 if new_exits
[0]: new
.link_to(new_exits
[0], 'next')
783 if new_exits
[1]: new
.link_to(new_exits
[1], 'fail')
785 def button_release(self
, da
, event
):
786 if event
.button
!= 1 or not self
.drag_box
:
789 x1
, y1
, x2
, y2
= self
.drag_box
790 if x1
> x2
: x1
,x2
= x2
,x1
791 if y1
> y2
: y1
,y2
= y2
,y1
795 for op
in self
.op_to_object
.itervalues():
796 if isinstance(op
, ChainBlock
): continue
797 if not op
.op
.prev
: continue
798 if op
.x
+ 8 < x1
or op
.x
> x2
: continue
799 if op
.y
+ 8 < y1
or op
.y
> y2
: continue
801 if top
is None or op
.y
< top
.y
:
808 block
= top
.op
.parent
811 if op
.op
.parent
!= block
:
815 self
.box(top
.op
, ops
)
817 def button_press(self
, da
, event
):
818 for op
in self
.op_to_object
.itervalues():
819 if op
.maybe_clicked(event
): return
821 if event
.type != g
.gdk
.BUTTON_PRESS
or event
.button
!= 1:
824 self
.drag_box
= (event
.x
, event
.y
, event
.x
, event
.y
)
826 def op_colour(self
, op
):
827 if op
in self
.view
.exec_stack
:
831 def edit_op(self
, op
):
833 if op
.action
[0] == 'do_search' or op
.action
[0] == 'do_global':
834 t
= editables
[0].get_text()
836 from Ft
.Xml
.XPath
import XPathParser
837 if t
.find('@CURRENT@') == -1:
839 XPathParser
.new().parse(t
)
841 alert('Invalid search pattern!')
847 op
.action
[i
] = e
.get_text()
849 print "Done editing!"
853 win
.vbox
.pack_start(g
.Label(op
.action
[0]), TRUE
, FALSE
, 0)
854 editables
= [] # [ Entry | None ]
856 for x
in op
.action
[1:]:
858 entry
.set_text(str(x
))
859 win
.vbox
.pack_start(entry
, TRUE
, FALSE
, 0)
860 if type(x
) == str or type(x
) == unicode:
861 editables
.append(entry
)
862 entry
.connect('activate', lambda e
: modify())
867 entry
.set_editable(FALSE
)
868 editables
.append(None)
870 win
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
871 win
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
873 def response(box
, resp
):
875 if resp
== g
.RESPONSE_OK
:
877 win
.connect('response', response
)
880 win
.set_response_sensitive(g
.RESPONSE_OK
, FALSE
)
884 def edit_comment(self
, block
):
885 assert isinstance(block
, Block
)
888 block
.set_comment(comment
)
889 GetArg('Comment', set, ['Comment:'],
890 message
= '\\n for a newline', init
= [block
.comment
])
892 def block_toggle_enter(self
):
893 self
.op_menu_op
.toggle_enter()
895 def block_toggle_foreach(self
):
896 self
.op_menu_op
.toggle_foreach()
898 def block_toggle_restore(self
):
899 self
.op_menu_op
.toggle_restore()
901 def block_edit_comment(self
):
902 self
.edit_comment(self
.op_menu_op
)
905 self
.edit_op(self
.op_menu_op
)
907 def op_swap_nf(self
):
908 self
.op_menu_op
.swap_nf()
910 def op_del_node(self
):
912 if op
.next
and op
.fail
:
913 rox
.alert("Can't delete a node with both exits in use")
915 self
.clipboard
= op
.del_node()
917 def show_op_menu(self
, event
, op
):
918 if op
.action
[0] == 'Start':
919 self
.op_menu_op
= op
.parent
920 block_menu
.popup(self
, event
)
923 op_menu
.popup(self
, event
)
925 def paste_chain(self
, op
, exit
):
926 print "Paste", self
.clipboard
928 new
= load(doc
.documentElement
, op
.parent
,
929 self
.view
.model
.namespaces
)
930 start
= new
.start
.next
931 new
.start
.unlink('next', may_delete
= 0)
932 start
.set_parent(None)
933 op
.link_to(start
, exit
)
935 def line_paste_chain(self
):
936 op
, exit
= self
.line_menu_line
937 self
.paste_chain(op
, exit
)
939 def line_add_block(self
):
940 op
, exit
= self
.line_menu_line
942 box
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
943 box
.add_button(g
.STOCK_ADD
, g
.RESPONSE_OK
)
944 box
.set_position(g
.WIN_POS_MOUSE
)
945 box
.set_has_separator(False)
947 foreach
= g
.CheckButton('Foreach block')
948 box
.vbox
.pack_start(foreach
)
949 enter
= g
.CheckButton('Enter-leave block')
950 box
.vbox
.pack_start(enter
)
955 if resp
!= g
.RESPONSE_OK
:
959 if foreach
.get_active():
961 if enter
.get_active():
964 #if self.view.rec_point == (op, exit):
965 self
.view
.single_step
= 1
966 if self
.view
.rec_point
:
967 self
.view
.stop_recording()
968 self
.view
.set_exec((op
, exit
))
970 self
.view
.do_one_step()
972 except View
.InProgress
:
974 print self
.exec_point
975 self
.view
.record_at_point()
977 def line_toggle_breakpoint(self
):
978 op
, exit
= self
.line_menu_line
979 bp
= self
.view
.breakpoints
980 if bp
.has_key((op
, exit
)):
986 def line_yank_chain(self
):
987 op
, exit
= self
.line_menu_line
988 next
= getattr(op
, exit
)
990 rox
.alert('Nothing to yank!')
992 self
.clipboard
= next
.to_doc()
995 def line_del_chain(self
):
996 op
, exit
= self
.line_menu_line
997 next
= getattr(op
, exit
)
999 rox
.alert('Nothing to delete!')
1001 self
.clipboard
= next
.to_doc()
1004 def show_menu(self
, event
, op
, exit
= None):
1006 self
.line_menu_line
= (op
, exit
)
1007 line_menu
.popup(self
, event
)
1009 self
.show_op_menu(event
, op
)
1012 class ChainWindow(rox
.Window
):
1013 def __init__(self
, view
, prog
):
1014 rox
.Window
.__init
__(self
)
1015 swin
= g
.ScrolledWindow()
1017 disp
= ChainDisplay(view
, prog
)
1022 self
.set_default_size(-1, 200)
1023 self
.set_title(prog
.name
)
1025 def update_points(self
):
1026 self
.disp
.update_points()