Update namespace with leave/enter.
[dom-editor.git] / Dome / View.py
blob6d493e5378fbf6c834ce76e28a031fdfc410b8b3
1 from __future__ import nested_scopes
3 try:
4 from rox import alert
5 except:
6 def alert(message):
7 print "***", message
9 import support
10 from support import *
11 from xml.dom import Node, XMLNS_NAMESPACE
12 from Ft.Xml import XPath
13 from Ft.Xml.XPath import FT_EXT_NAMESPACE, Context
14 from Ft.Xml.cDomlette import implementation
15 from Ft.Xml.Domlette import PrettyPrint
17 import re, string, sys
18 import urlparse
19 from StringIO import StringIO
21 from Program import Op, Block
22 from Beep import Beep
24 import time
25 import urllib, urllib2
26 import traceback
28 from constants import *
30 import re
32 #http://www.w3.org/2001/12/soap-envelope'
33 SOAPENV_NS = 'http://schemas.xmlsoap.org/soap/envelope/'
35 def elements(node):
36 out = []
37 for x in node.childNodes:
38 if x.nodeType == Node.ELEMENT_NODE:
39 out.append(x)
40 return out
42 normal_chars = string.letters + string.digits + "-"
44 fast_global = re.compile('//([-A-Za-z][-A-Za-z0-9]*:)?[-A-Za-z][-A-Za-z0-9]*$')
46 # An view contains:
47 # - A ref to a DOM document
48 # - A set of current nodes
49 # - A root node
50 # - A chroot stack
51 # It does not have any display code. It does contain code to perform actions
52 # (actions affect the document AND the view state).
54 # These actions can be repeated using '.'
55 record_again = [
56 "do_global",
57 "select_children",
58 "subst",
59 "python",
60 "xpath",
61 "ask",
62 "yank",
63 "shallow_yank",
64 "delete_node",
65 "delete_node_no_clipboard",
66 "delete_shallow",
67 "play",
68 "map",
69 "change_node",
70 "add_node",
71 "suck",
72 "http_post",
73 "put_before",
74 "put_after",
75 "put_replace",
76 "put_as_child",
77 "put_as_child_end",
78 "yank_value",
79 "yank_attribs",
80 "paste_attribs",
81 "compare",
82 "fail",
83 "do_pass",
84 "attribute",
85 "set_attrib",
86 "rename_attrib",
87 "add_attrib",
88 "soap_send",
89 "show_canvas",
90 "show_html",
91 "select_dups",
92 "select_region",
95 def same(a, b):
96 "Recursivly compare two nodes."
97 if a.nodeType != b.nodeType or a.nodeName != b.nodeName:
98 return FALSE
99 if a.nodeValue != b.nodeValue:
100 return FALSE
101 aks = a.childNodes
102 bks = b.childNodes
103 if len(aks) != len(bks):
104 return FALSE
105 for (ak, bk) in map(None, aks, bks):
106 if not same(ak, bk):
107 return FALSE
108 return TRUE
110 class InProgress(Exception):
111 "Throw this if the operation will complete later..."
112 class Done(Exception):
113 "Thrown when the chain is completed successfully"
115 class View:
116 def __init__(self, model, callback_handlers = None):
117 """callback_handlers is an (idle_add, idle_remove) tuple"""
118 self.root = None
119 self.displays = []
120 self.lists = []
121 self.single_step = 1 # 0 = Play 1 = Step-into 2 = Step-over
122 self.model = None
123 self.chroots = [] # (model, node, marked)
124 self.foreach_stack = [] # (block, [nodes], restore-nodes, restore-marks)
125 self.current_nodes = []
126 self.clipboard = None
127 self.current_attrib = None
128 self.marked = {}
130 if not callback_handlers:
131 from rox import g
132 self.idle_add, self.idle_remove = g.idle_add, g.idle_remove
133 else:
134 self.idle_add, self.idle_remove = callback_handlers
136 self.exec_point = None # None, or (Op, Exit)
137 self.rec_point = None # None, or (Op, Exit)
138 self.op_in_progress = None
139 self.idle_cb = 0
140 self.callback_on_return = None # Called when there are no more Ops...
141 self.in_callback = 0 # (not the above callback - this is the playback one)
142 self.innermost_failure = None
143 self.call_on_done = None # Called when there is nowhere to return to
144 self.exec_stack = [] # Ops we are inside (display use only)
146 self.breakpoints = {} # (op, exit) keys, values don't matter
147 self.current_nodes = []
148 self.set_model(model)
150 def get_current(self):
151 if len(self.current_nodes) == 1:
152 return self.current_nodes[0]
153 raise Exception('This operation required exactly one selected node!')
155 def set_model(self, model):
156 assert not self.marked
157 if self.root:
158 self.move_to([])
159 self.model.unlock(self.root)
160 self.root = None
161 if self.model:
162 self.model.remove_view(self)
163 self.model.root_program.watchers.remove(self)
164 self.model = model
165 self.model.root_program.watchers.append(self)
166 model.add_view(self)
167 self.set_display_root(self.model.get_root())
168 self.move_to(self.root)
170 def running(self):
171 return self.idle_cb != 0 or self.in_callback
173 def run_new(self, callback = None):
174 "Reset the playback system (stack, step-mode and point)."
175 "Call callback(exit) when execution finishes."
176 if self.idle_cb:
177 self.idle_remove(self.idle_cb)
178 self.idle_cb = 0
179 self.single_step = 0
180 self.innermost_failure = None
181 self.call_on_done = callback
182 self.callback_on_return = None
183 while self.exec_stack:
184 self.pop_stack()
185 self.reset_foreach_stack()
186 self.status_changed()
187 self.update_stack()
189 def reset_foreach_stack(self):
190 for block, nodes, restore, mark in self.foreach_stack:
191 if mark:
192 print "reset_foreach_stack: unlocking %d nodes" % len(mark)
193 [self.model.unlock(x) for x in mark]
194 self.foreach_stack = []
196 def push_stack(self, op):
197 if not isinstance(op, Op):
198 raise Exception('push_stack: not an Op', op)
199 self.exec_stack.append(op)
200 self.update_stack(op)
202 def pop_stack(self):
203 op = self.exec_stack.pop()
204 self.update_stack(op)
206 def update_stack(self, op = None):
207 "Called when exec_stack or foreach_stack changes."
208 for l in self.lists:
209 l.update_stack(op)
211 def set_exec(self, pos):
212 if self.op_in_progress:
213 raise Exception("Operation in progress...")
214 if pos is not None:
215 assert isinstance(pos[0], Op)
216 assert pos[1] in ['next', 'fail']
217 self.exec_point = pos
218 #if pos:
219 #print "set_exec: %s:%s" % pos
220 for l in self.lists:
221 l.update_points()
223 def set_rec(self, pos):
224 self.rec_point = pos
225 for l in self.lists:
226 l.update_points()
227 self.status_changed()
229 def record_at_point(self):
230 if not self.exec_point:
231 alert("No current point!")
232 return
233 self.set_rec(self.exec_point)
234 self.set_exec(None)
236 def stop_recording(self):
237 if self.rec_point:
238 self.set_exec(self.rec_point)
239 self.set_rec(None)
240 else:
241 alert("Not recording!")
243 def may_record(self, action):
244 "Perform and, possibly, record this action"
245 rec = self.rec_point
247 if rec:
248 print "RECORD:", rec, action
249 (op, old_exit) = rec
250 if action == ['enter']:
251 new_op = Block(op.parent)
252 new_op.toggle_enter()
253 if len(self.current_nodes) > 1:
254 new_op.toggle_foreach()
255 else:
256 new_op = Op(action)
257 op.link_to(new_op, old_exit)
258 self.set_exec(rec)
259 try:
260 self.do_one_step()
261 except InProgress:
262 if isinstance(new_op, Block):
263 self.set_rec((new_op.start, 'next'))
264 else:
265 self.set_rec((new_op, 'next'))
266 return
267 play_op, exit = self.exec_point
268 # (do_one_step may have stopped recording)
269 if self.rec_point:
270 self.set_rec((new_op, exit))
271 self.set_exec(None)
272 return
274 exit = 'next'
275 try:
276 self.do_action(action)
277 except InProgress:
278 pass
279 except Beep:
280 from rox import g
281 g.gdk.beep()
282 (type, val, tb) = sys.exc_info()
283 #if not val.may_record:
284 # return 0
285 exit = 'fail'
286 except Done:
287 raise
288 except:
289 rox.report_exception()
291 def add_display(self, display):
292 "Calls move_from(old_node) when we move and update_all() on updates."
293 self.displays.append(display)
294 #print "Added:", self.displays
296 def remove_display(self, display):
297 self.displays.remove(display)
298 #print "Removed, now:", self.displays
299 if not self.displays:
300 self.delete()
302 def update_replace(self, old, new):
303 if old == self.root:
304 self.root = new
305 if old in self.current_nodes:
306 self.model.lock(new)
307 self.model.unlock(old)
308 self.current_nodes.remove(old)
309 self.current_nodes.append(new)
310 self.update_all(new.parentNode)
311 else:
312 self.update_all(new.parentNode)
314 def has_ancestor(self, node, ancestor):
315 while node != ancestor:
316 node = node.parentNode
317 if not node:
318 return FALSE
319 return TRUE
321 def update_all(self, node):
322 for display in self.displays:
323 display.update_all(node)
325 def delete(self):
326 #print "View deleted"
327 self.model.root_program.watchers.remove(self)
328 self.move_to([])
329 for l in self.lists:
330 l.destroy()
331 self.model.unlock(self.root)
332 self.root = None
333 self.model.remove_view(self)
334 self.model = None
336 # 'nodes' may be either a node or a list of nodes.
337 # (duplicates will be removed)
338 # If it's a single node, then an 'attrib' node may also be specified
339 def move_to(self, nodes, attrib = None):
340 if self.current_nodes == nodes:
341 return
343 if attrib and attrib.nodeType != Node.ATTRIBUTE_NODE:
344 raise Exception('attrib not of type ATTRIBUTE_NODE!')
346 if type(nodes) != list:
347 assert nodes
348 nodes = [nodes]
350 if len(nodes) > 1:
351 # Remove duplicates
352 map = {}
353 old = nodes
354 nodes = []
355 for n in old:
356 if n not in map:
357 map[n] = None
358 nodes.append(n)
359 #if len(old) != len(nodes):
360 # print "(move_to: attempt to set duplicate nodes)"
362 old_nodes = self.current_nodes
363 self.current_nodes = nodes
365 for node in self.current_nodes:
366 self.model.lock(node)
367 for node in old_nodes:
368 self.model.unlock(node)
370 self.current_attrib = attrib
372 for display in self.displays:
373 display.move_from(old_nodes)
375 def move_prev_sib(self):
376 try:
377 new = [n.previousSibling or 1/0 for n in self.current_nodes]
378 except:
379 raise Beep
380 self.move_to(new)
382 def move_next_sib(self):
383 try:
384 new = [n.nextSibling or 1/0 for n in self.current_nodes]
385 except:
386 raise Beep
387 self.move_to(new)
389 def move_left(self):
390 new = []
391 for n in self.current_nodes:
392 if n == self.root:
393 raise Beep
394 p = n.parentNode
395 if p not in new:
396 new.append(p)
397 self.move_to(new)
399 def move_right(self):
400 new = []
401 for n in self.current_nodes:
402 kids = n.childNodes
403 if kids:
404 new.append(kids[0])
405 else:
406 raise Beep
407 self.move_to(new)
409 def move_home(self):
410 self.move_to(self.root)
412 def move_end(self):
413 if not self.get_current().childNodes:
414 raise Beep
415 node = self.get_current().childNodes[0]
416 while node.nextSibling:
417 node = node.nextSibling
418 self.move_to(node)
420 def set_display_root(self, root):
421 self.model.lock(root)
422 if self.root:
423 self.model.unlock(self.root)
424 self.root = root
425 self.update_all(root)
427 def enter(self):
428 """Change the display root to a COPY of the selected node.
429 Call Leave to check changes back in."""
430 node = self.get_current()
431 if node is self.root:
432 raise Beep # Locking problems if this happens...
433 if self.model.doc is not node.ownerDocument:
434 raise Exception('Current node not in view!')
435 self.move_to([])
436 self.set_marked([])
438 new_model = self.model.lock_and_copy(node)
439 self.chroots.append((self.model, node, self.marked))
440 self.set_model(new_model)
441 self.update_stack()
443 def leave(self):
444 """Undo the effect of the last chroot()."""
445 if not self.chroots:
446 raise Beep
448 self.set_marked([])
449 self.move_to([])
450 model = self.model
452 (old_model, old_node, old_marked) = self.chroots.pop()
453 self.update_stack()
455 copy = old_model.import_with_ns(self.model.get_root())
456 old_model.unlock(old_node)
457 old_model.replace_node(old_node, copy)
458 self.set_model(old_model)
459 self.move_to([copy])
460 self.set_marked(old_marked.keys())
462 if not model.views:
463 model.undo_stack = None
464 model.__dict__ = {}
465 del model
466 import gc
467 gc.collect()
469 def do_action(self, action):
470 "'action' is a tuple (function, arg1, arg2, ...)"
471 "Performs the action. Returns if action completes, or raises "
472 "InProgress if not (will call resume() later)."
473 if action[0] in record_again:
474 self.last_action = action
475 elif action[0] == 'again':
476 action = self.last_action
477 fn = getattr(self, action[0])
478 exit = 'next'
479 #print "DO:", action[0]
480 self.model.mark()
481 try:
482 new = apply(fn, action[1:])
483 except InProgress:
484 raise
485 except Beep:
486 if not self.op_in_progress:
487 raise
488 exit = 'fail'
489 new = None
490 except:
491 if not self.op_in_progress:
492 raise
493 traceback.print_exc()
494 exit = 'fail'
495 new = None
497 if self.op_in_progress:
498 op = self.op_in_progress
499 self.set_oip(None)
500 self.set_exec((op, exit))
501 if new:
502 self.move_to(new)
504 def breakpoint(self):
505 if self.breakpoints.has_key(self.exec_point):
506 return 1
507 op = self.exec_point[0]
508 if op.parent.start == op and op.next == None:
509 return 1 # Empty program
510 return 0
512 def do_one_step(self):
513 "Execute the next op after exec_point, then:"
514 "- position the point on one of the exits return."
515 "- if there is no op to perform, call callback_on_return() or raise Done."
516 "- if the operation is started but not complete, raise InProgress and "
517 " arrange to resume() later."
518 if self.op_in_progress:
519 alert("Already executing something.")
520 raise Done()
521 if not self.exec_point:
522 alert("No current playback point.")
523 raise Done()
524 (op, exit) = self.exec_point
526 if self.single_step == 0 and self.breakpoint():
527 print "Hit a breakpoint! At " + time.ctime(time.time())
528 if self.rec_point:
529 self.set_rec(None)
530 self.single_step = 1
531 for l in self.lists:
532 l.show_prog(op.get_program())
533 return
535 next = getattr(op, exit)
536 try:
537 if next:
538 self.set_oip(next)
539 self.do_action(next.action) # May raise InProgress
540 return
542 if exit == 'fail' and not self.innermost_failure:
543 #print "Setting innermost_failure on", op
544 self.innermost_failure = op
546 # If we're in a block, try exiting from it...
547 if isinstance(op.parent, Block):
548 if self.start_block_iteration(op.parent, continuing = exit):
549 return # Looping...
550 if not op.parent.is_toplevel():
551 self.set_exec((op.parent, exit))
552 return
553 except Done:
554 print "(skipped a whole program!)"
555 if self.callback_on_return:
556 cb = self.callback_on_return
557 self.callback_on_return = None
558 cb()
559 else:
560 raise Done()
562 def set_oip(self, op):
563 #print "set_oip:", self.exec_point
564 if op:
565 self.set_exec(None)
566 self.op_in_progress = op
567 for l in self.lists:
568 l.update_points()
570 def fast_global(self, name):
571 "Search for nodes with this name anywhere under the root (//name)"
572 #print "Fast global", name
573 if ':' in name:
574 (prefix, localName) = string.split(name, ':', 1)
575 else:
576 (prefix, localName) = (None, name)
577 if self.current_nodes:
578 src = self.current_nodes[-1]
579 else:
580 src = self.root
581 namespaceURI = self.model.prefix_to_namespace(src, prefix)
582 select = []
583 def add(node):
584 if node.nodeType != Node.ELEMENT_NODE:
585 return
586 if node.localName == localName and node.namespaceURI == namespaceURI:
587 select.append(node)
588 map(add, node.childNodes)
589 add(self.root)
590 self.move_to(select)
592 # Actions...
594 def do_global(self, pattern):
595 if len(self.current_nodes) != 1:
596 self.move_to(self.root)
597 if pattern[:2] == '//':
598 if fast_global.match(pattern):
599 self.fast_global(pattern[2:])
600 return
602 assert not self.op_in_progress or (self.op_in_progress.action[1] == pattern)
603 try:
604 code = self.op_in_progress.cached_code
605 except:
606 from Ft.Xml.XPath import XPathParser
607 code = XPathParser.new().parse(self.macro_pattern(pattern))
608 if self.op_in_progress and pattern.find('@CURRENT@') == -1:
609 self.op_in_progress.cached_code = code
611 ns = self.model.namespaces.uri
612 c = Context.Context(self.get_current(), processorNss = ns)
613 #print code
614 nodes = code.evaluate(c)
615 assert type(nodes) == list
617 #don't select the document itself!
618 #Also, don't select attributes (needed for XSLT stuff)
619 nodes = [n for n in nodes if n.parentNode]
621 #nodes = XPath.Evaluate(self.macro_pattern(pattern), contextNode = self.get_current())
622 #print "Found", nodes
623 self.move_to(nodes)
625 def select_children(self):
626 new = []
627 for n in self.current_nodes:
628 new.extend(n.childNodes)
629 self.move_to(new)
631 def select_region(self, path, ns = None):
632 if len(self.current_nodes) == 0:
633 raise Beep
634 src = self.current_nodes[-1]
636 ns = self.model.namespaces.uri
637 c = Context.Context(src, [src], processorNss = ns)
638 rt = XPath.Evaluate(path, context = c)
639 node = None
640 for x in rt:
641 if not self.has_ancestor(x, self.root):
642 print "[ skipping search result above root ]"
643 continue
644 if not node:
645 node = x
646 if not node:
647 print "*** Search for '%s' in select_region failed" % path
648 print " (namespaces were '%s')" % ns
649 raise Beep
650 if node.parentNode != src.parentNode:
651 print "Nodes must have same parent!"
652 raise Beep
653 on = 0
654 selected = []
655 for n in src.parentNode.childNodes:
656 was_on = on
657 if n is src or n is node:
658 on = not was_on
659 if on or was_on:
660 selected.append(n)
661 self.move_to(selected)
663 def macro_pattern(self, pattern):
664 """Do the @CURRENT@ substitution for an XPath"""
665 if len(self.current_nodes) != 1:
666 return pattern
667 node = self.get_current()
668 if node.nodeType == Node.TEXT_NODE:
669 current = node.data
670 else:
671 if self.current_attrib:
672 current = self.current_attrib.value
673 else:
674 current = node.nodeName
675 pattern = pattern.replace('@CURRENT@', current)
676 #print "Searching for", pattern
677 return pattern
679 def do_search(self, pattern, ns = None, toggle = FALSE):
680 if len(self.current_nodes) == 0:
681 src = self.root
682 else:
683 src = self.current_nodes[-1]
685 # May be from a text_search...
686 #assert not self.op_in_progress or (self.op_in_progress.action[1] == pattern)
687 try:
688 code = self.op_in_progress.cached_code
689 except:
690 from Ft.Xml.XPath import XPathParser
691 code = XPathParser.new().parse(self.macro_pattern(pattern))
692 if self.op_in_progress and pattern.find('@CURRENT@') == -1:
693 self.op_in_progress.cached_code = code
695 ns = self.model.namespaces.uri
696 c = Context.Context(src, [src], processorNss = ns)
698 rt = code.evaluate(c)
699 node = None
700 for x in rt:
701 if not self.has_ancestor(x, self.root):
702 print "[ skipping search result above root ]"
703 continue
704 if not node:
705 node = x
706 #if self.node_to_line[x] > self.current_line:
707 #node = x
708 #break
709 if not node:
710 #print "*** Search for '%s' failed" % pattern
711 #print " (namespaces were '%s')" % ns
712 raise Beep
713 if toggle:
714 new = self.current_nodes[:]
715 if node in new:
716 new.remove(node)
717 else:
718 new.append(node)
719 self.move_to(new)
720 else:
721 self.move_to(node)
723 def do_text_search(self, pattern):
724 pattern = self.macro_pattern(pattern)
725 return self.do_search("//text()[ext:match('%s')]" % pattern)
727 def subst(self, replace, with):
728 "re search and replace on the current node"
729 nodes = self.current_nodes[:]
730 check = len(nodes) == 1
731 a = self.current_attrib
732 if a:
733 new, num = re.subn(replace, with, a.value)
734 if not num:
735 raise Beep
736 a = self.model.set_attrib(nodes[0], a.name, new)
737 self.move_to(nodes[0], a)
738 else:
739 self.move_to([])
740 final = []
741 for n in nodes:
742 if n.nodeType == Node.TEXT_NODE:
743 old = n.data.replace('\n', ' ')
744 new, num = re.subn(replace, with, old)
745 if check and not num:
746 self.move_to(n)
747 raise Beep
748 self.model.set_data(n, new)
749 final.append(n)
750 elif n.nodeType == Node.ELEMENT_NODE:
751 old = str(n.nodeName)
752 new, num = re.subn(replace, with, old)
753 if check and not num:
754 self.move_to(n)
755 raise Beep
756 new_ns, x = self.model.split_qname(n, new)
757 final.append(self.model.set_name(n, new_ns, new))
758 else:
759 self.move_to(n)
760 raise Beep
761 self.move_to(final)
763 def xpath(self, expr):
764 "Put the result of 'expr' on the clipboard."
765 expr = 'string(%s)' % expr
766 if len(self.current_nodes) == 0:
767 src = self.root
768 else:
769 src = self.current_nodes[-1]
771 try:
772 code = self.op_in_progress.cached_code
773 except:
774 from Ft.Xml.XPath import XPathParser
775 code = XPathParser.new().parse(self.macro_pattern(expr))
776 if self.op_in_progress and expr.find('@CURRENT@') == -1:
777 self.op_in_progress.cached_code = code
779 ns = self.model.namespaces.uri
780 c = Context.Context(src, [src], processorNss = ns)
782 rt = code.evaluate(c)
784 self.clipboard = self.model.doc.createTextNode(rt)
785 print "Result is", self.clipboard
787 def python(self, expr):
788 "Replace node with result of expr(old_value)"
789 if self.get_current().nodeType == Node.TEXT_NODE:
790 vars = {'x': self.get_current().data, 're': re, 'sub': re.sub, 'string': string}
791 result = eval(expr, vars)
792 new = self.python_to_node(result)
793 node = self.get_current()
794 self.move_to([])
795 self.model.replace_node(node, new)
796 self.move_to(new)
797 else:
798 raise Beep
800 def resume(self, exit = 'next'):
801 "After raising InProgress, call this to start moving again."
802 if self.op_in_progress:
803 op = self.op_in_progress
804 self.set_oip(None)
805 self.set_exec((op, exit))
806 if not self.single_step:
807 self.sched()
808 self.status_changed()
809 else:
810 print "(nothing to resume)"
812 def ask(self, q):
813 def ask_cb(result, self = self):
814 if result is None:
815 exit = 'fail'
816 else:
817 self.clipboard = self.model.doc.createTextNode(result)
818 exit = 'next'
819 self.resume(exit)
820 from GetArg import GetArg
821 box = GetArg('Input:', ask_cb, [q], destroy_return = 1)
822 raise InProgress
824 def python_to_node(self, data):
825 "Convert a python data structure into a tree and return the root."
826 if type(data) == list:
827 nlist = self.model.doc.createElementNS(DOME_NS, 'dome:list')
828 nlist.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:dome', DOME_NS)
829 for x in data:
830 nlist.appendChild(self.python_to_node(x))
831 return nlist
832 return self.model.doc.createTextNode(str(data))
834 def yank(self, deep = 1):
835 if self.current_attrib:
836 a = self.current_attrib
838 self.clipboard = self.model.doc.createElementNS(a.namespaceURI, a.nodeName)
839 self.clipboard.appendChild(self.model.doc.createTextNode(a.value))
840 else:
841 self.clipboard = self.model.doc.createDocumentFragment()
842 for n in self.current_nodes:
843 c = n.cloneNode(deep)
844 #print n, "->", c
845 self.clipboard.appendChild(c)
847 #print "Clip now", self.clipboard
849 def shallow_yank(self):
850 self.yank(0)
852 def delete_shallow(self):
853 nodes = self.current_nodes[:]
854 if not nodes:
855 return
856 if self.root in nodes:
857 raise Beep
858 self.shallow_yank()
859 self.move_to([])
860 for n in nodes:
861 self.model.delete_shallow(n)
862 self.move_home()
864 def delete_node_no_clipboard(self):
865 self.delete_node(yank = 0)
867 def delete_node(self, yank = 1):
868 nodes = self.current_nodes[:]
869 if not nodes:
870 return
871 if yank:
872 self.yank()
873 if self.current_attrib:
874 ca = self.current_attrib
875 self.current_attrib = None
876 self.model.set_attrib(self.get_current(), ca.name, None)
877 return
878 if self.root in nodes:
879 raise Beep
880 self.move_to([])
881 new = [x.parentNode for x in nodes]
882 self.move_to(new)
883 self.model.delete_nodes(nodes)
885 def undo(self):
886 nodes = self.current_nodes[:]
887 self.move_to([])
888 self.model.unlock(self.root)
889 try:
890 self.model.undo()
891 finally:
892 self.model.lock(self.root)
893 self.move_to(filter(lambda x: self.has_ancestor(x, self.root), nodes))
895 def redo(self):
896 nodes = self.current_nodes[:]
897 self.move_to([])
898 self.model.unlock(self.root)
899 try:
900 self.model.redo()
901 finally:
902 self.model.lock(self.root)
903 self.move_to(filter(lambda x: self.has_ancestor(x, self.root), nodes))
905 def default_done(self, exit):
906 "Called when execution of a program returns. op_in_progress has been "
907 "restored - move to the exit."
908 #print "default_done(%s)" % exit
909 if self.op_in_progress:
910 op = self.op_in_progress
911 self.set_oip(None)
912 self.set_exec((op, exit))
913 else:
914 print "No operation to return to!"
915 c = self.call_on_done
916 if c:
917 self.call_on_done = None
918 c(exit)
919 elif exit == 'fail':
920 self.jump_to_innermost_failure()
921 raise Done()
923 def jump_to_innermost_failure(self):
924 assert self.innermost_failure != None
926 print "Returning to innermost failure:", self.innermost_failure
927 self.set_exec((self.innermost_failure, 'fail'))
928 for l in self.lists:
929 if hasattr(l, 'set_innermost_failure'):
930 l.set_innermost_failure(self.innermost_failure)
932 def play(self, name, done = None):
933 "Play this macro. When it returns, restore the current op_in_progress (if any)"
934 "and call done(exit). Default for done() moves exec_point."
935 "done() is called from do_one_step() - usual rules apply."
937 prog = self.name_to_prog(name)
938 self.innermost_failure = None
940 if not done:
941 done = self.default_done
943 def cbor(self = self, op = self.op_in_progress, done = done,
944 name = name,
945 old_cbor = self.callback_on_return,
946 old_ss = self.single_step):
947 "We're in do_one_step..."
949 #print "Return from '%s'..." % name
951 if old_ss == 2 and self.single_step == 0:
952 self.single_step = old_ss
953 self.callback_on_return = old_cbor
955 o, exit = self.exec_point
956 if op:
957 #print "Resume op '%s' (%s)" % (op.program.name, op)
958 self.pop_stack()
959 self.set_oip(op)
960 return done(exit)
962 self.callback_on_return = cbor
964 if self.single_step == 2:
965 self.single_step = 0
967 if self.op_in_progress:
968 self.push_stack(self.op_in_progress)
969 self.set_oip(None)
970 self.play_block(prog.code)
971 self.sched()
972 self.status_changed()
973 raise InProgress
975 def start_block_iteration(self, block, continuing = None):
976 "True if we are going to run the block, False to exit the loop"
977 "Continuing is 'next' or 'fail' if we reached the end of the block."
978 #print "Start interation"
979 if not self.foreach_stack:
980 raise Done
981 stack_block, nodes_list, restore, old_mark = self.foreach_stack[-1]
982 if stack_block != block:
983 self.reset_foreach_stack()
984 self.update_stack()
985 raise Exception("Reached the end of a block we never entered")
987 if continuing:
988 if block.enter:
989 self.leave()
990 if block.foreach:
991 restore.extend(self.current_nodes)
992 if continuing == 'fail':
993 print "Error in block; exiting early in program", block.get_program()
994 if old_mark:
995 [self.model.unlock(x) for x in old_mark]
996 self.foreach_stack.pop()
997 self.update_stack()
998 return 0
999 while nodes_list and nodes_list[0].parentNode == None:
1000 print "Skipping deleted node", nodes_list[0]
1001 del nodes_list[0]
1003 if not nodes_list:
1004 self.foreach_stack.pop()
1005 self.update_stack()
1006 if block.foreach:
1007 nodes = filter(lambda x: self.has_ancestor(x, self.root), restore)
1008 self.move_to(nodes)
1009 if old_mark is not None:
1010 self.set_marked(old_mark)
1011 [self.model.unlock(x) for x in old_mark]
1012 return 0 # Nothing left to do
1013 nodes = nodes_list[0]
1014 del nodes_list[0]
1015 self.move_to(nodes)
1017 if nodes_list:
1018 print "[ %d after this ]" % len(nodes_list),
1019 sys.stdout.flush()
1021 if block.enter:
1022 self.enter()
1023 self.set_exec((block.start, 'next'))
1024 return 1
1026 def play_block(self, block):
1027 assert isinstance(block, Block)
1028 #print "Enter Block!"
1029 if block.foreach:
1030 list = self.current_nodes[:]
1031 else:
1032 list = [self.current_nodes[:]] # List of one item, containing everything
1034 if block.restore:
1035 marks = self.marked.copy()
1036 [self.model.lock(x) for x in marks]
1037 else:
1038 marks = None
1039 self.foreach_stack.append((block, list, [], marks))
1041 self.update_stack()
1042 if not self.start_block_iteration(block):
1043 # No nodes selected...
1044 if not block.is_toplevel():
1045 self.set_exec((block, 'next'))
1046 else:
1047 self.set_oip(None)
1048 self.set_exec((block.start, 'next'))
1049 raise Done
1051 def Block(self):
1052 assert self.op_in_progress
1053 oip = self.op_in_progress
1054 self.set_oip(None)
1055 self.play_block(oip)
1056 if not self.single_step:
1057 self.sched()
1058 raise InProgress
1060 def sched(self):
1061 if self.op_in_progress:
1062 raise Exception("Operation in progress")
1063 if self.idle_cb:
1064 raise Exception("Already playing!")
1065 self.idle_cb = self.idle_add(self.play_callback)
1067 def play_callback(self):
1068 self.idle_remove(self.idle_cb)
1069 self.idle_cb = 0
1070 try:
1071 self.in_callback = 1
1072 try:
1073 self.do_one_step()
1074 finally:
1075 self.in_callback = 0
1076 except Done:
1077 (op, exit) = self.exec_point
1078 if exit == 'fail' and self.innermost_failure:
1079 self.jump_to_innermost_failure()
1080 print "Done, at " + time.ctime(time.time())
1081 self.run_new()
1082 return 0
1083 except InProgress:
1084 #print "InProgress"
1085 return 0
1086 except:
1087 type, val, tb = sys.exc_info()
1088 list = traceback.extract_tb(tb)
1089 stack = traceback.format_list(list[-2:])
1090 ex = traceback.format_exception_only(type, val) + ['\n\n'] + stack
1091 traceback.print_exception(type, val, tb)
1092 print "Error in do_one_step(): stopping playback"
1093 node = self.op_in_progress
1094 self.set_oip(None)
1095 if node:
1096 self.set_exec((node, 'fail'))
1097 self.status_changed()
1098 return 0
1099 if self.op_in_progress or self.single_step:
1100 self.status_changed()
1101 return 0
1102 self.sched()
1103 return 0
1105 def status_changed(self):
1106 for display in self.displays:
1107 if hasattr(display, 'update_state'):
1108 display.update_state()
1110 def map(self, name):
1111 print "Map", name
1113 nodes = self.current_nodes[:]
1114 if not nodes:
1115 print "map of nothing: skipping..."
1116 return
1117 inp = [nodes, None] # Nodes, next
1118 def next(exit = exit, self = self, name = name, inp = inp):
1119 "This is called while in do_one_step() - normal rules apply."
1120 nodes, next = inp
1121 print "[ %d to go ]" % len(nodes),
1122 sys.stdout.flush()
1123 if exit == 'fail':
1124 print "Map: nodes remaining, but an error occurred..."
1125 return self.default_done(exit)
1126 while nodes and nodes[0].parentNode == None:
1127 print "Skipping deleted node", nodes[0]
1128 del nodes[0]
1129 if not nodes:
1130 return self.default_done(exit)
1131 self.move_to(nodes[0])
1132 del nodes[0]
1133 if not nodes:
1134 next = None
1135 #print "Map: calling play (%d after this)" % len(nodes)
1136 self.play(name, done = next) # Should raise InProgress
1137 if nodes is self.current_nodes:
1138 raise Exception("Slice failed!")
1139 inp[1] = next
1140 next('next')
1142 def name_to_prog(self, name):
1143 comps = string.split(name, '/')
1144 prog = self.model.root_program
1145 if prog.name != comps[0]:
1146 raise Exception("No such program as '%s'!" % name)
1147 del comps[0]
1148 while comps:
1149 prog = prog.subprograms[comps[0]]
1150 del comps[0]
1151 return prog
1153 def change_node(self, new_data):
1154 nodes = self.current_nodes
1155 if not nodes:
1156 return
1157 self.move_to([])
1158 if nodes[0].nodeType == Node.ELEMENT_NODE:
1159 # Slow, so do this here, even if vaguely incorrect...
1160 assert ' ' not in new_data
1161 if ':' in new_data:
1162 (prefix, localName) = string.split(new_data, ':', 1)
1163 else:
1164 (prefix, localName) = (None, new_data)
1165 namespaceURI = self.model.prefix_to_namespace(nodes[0], prefix)
1166 out = []
1167 for node in nodes:
1168 if node is self.root:
1169 self.model.unlock(self.root)
1170 new = self.model.set_name(node, namespaceURI, new_data)
1171 self.model.lock(new)
1172 self.root = new
1173 else:
1174 new = self.model.set_name(node, namespaceURI, new_data)
1175 out.append(new)
1176 self.move_to(out)
1177 else:
1178 for node in nodes:
1179 self.model.set_data(node, new_data)
1180 self.move_to(nodes)
1182 def add_node(self, where, data):
1183 cur = self.get_current()
1184 if where[1] == 'e':
1185 if ':' in data:
1186 (prefix, localName) = string.split(data, ':', 1)
1187 else:
1188 (prefix, localName) = (None, data)
1189 namespaceURI = self.model.prefix_to_namespace(self.get_current(), prefix)
1190 new = self.model.doc.createElementNS(namespaceURI, data)
1191 elif where[1] == 'a':
1192 self.add_attrib(None, data)
1193 return
1194 else:
1195 new = self.model.doc.createTextNode(data)
1197 try:
1198 if where[0] == 'i':
1199 self.model.insert_before(cur, new)
1200 elif where[0] == 'a':
1201 self.model.insert_after(cur, new)
1202 elif where[0] == 'e':
1203 self.model.insert_before(None, new, parent = cur)
1204 else:
1205 self.model.insert(cur, new)
1206 except:
1207 raise Beep
1209 self.move_to(new)
1211 def request_from_node(self, node, attrib):
1212 """Return a urllib2.Request object. If attrib is set then the URI is
1213 taken from that, otherwise search for a good attribute."""
1214 uri = None
1215 if node.nodeType == Node.TEXT_NODE:
1216 uri = node.nodeValue
1217 else:
1218 if attrib:
1219 uri = attrib.value
1220 elif node.hasAttributeNS(None, 'uri'):
1221 uri = node.getAttributeNS(None, 'uri')
1222 else:
1223 for attr in node.attributes.keys():
1224 a_node = node.attributes[attr]
1225 if a_node.namespaceURI == XMLNS_NAMESPACE:
1226 continue
1227 uri = a_node.value
1228 if uri.find('//') != -1 or uri.find('.htm') != -1:
1229 break
1230 if not uri:
1231 print "Can't suck", node, "(no uri attribute found)"
1232 raise Beep
1233 if uri.find('//') == -1:
1234 base = self.model.get_base_uri(node)
1235 if not ':' in base:
1236 base = os.path.dirname(base)
1237 print "Relative URI..."
1238 if base:
1239 print "Base URI is:", base, "add", uri
1240 if uri.startswith('/'):
1241 uri = urlparse.urljoin(base, uri)
1242 else:
1243 uri = base + '/' + uri
1244 print "Final URI is:", uri
1245 else:
1246 pass
1247 #print "Warning: Can't find 'uri' attribute!"
1248 if not ':' in uri:
1249 uri = 'file://' + uri
1250 request = urllib2.Request(uri)
1252 return request
1254 def http_post(self):
1255 node = self.get_current()
1256 attrs = node.attributes
1257 post = []
1258 request = self.request_from_node(node, self.current_attrib)
1259 for (ns,name) in attrs.keys():
1260 if ns is not None: continue
1261 value = str(attrs[(ns, name)].value)
1262 if name.startswith('header-'):
1263 request.add_header(str(name)[7:], value)
1264 else:
1265 post.append((str(name), value))
1267 request.add_data(urllib.urlencode(post))
1268 node = self.suck_node(node, request)
1269 if node:
1270 self.move_to(node)
1272 def suck(self, md5_only = 0):
1273 nodes = self.current_nodes[:]
1274 attrib = self.current_attrib
1275 self.move_to([])
1276 final = []
1277 for x in nodes:
1278 request = self.request_from_node(x, attrib)
1279 try:
1280 new = self.suck_node(x, request, md5_only = md5_only)
1281 finally:
1282 self.move_to(x)
1283 final.append(new)
1284 self.move_to(final)
1286 def suck_md5(self):
1287 self.suck(md5_only = 1)
1289 def suck_node(self, node, request, md5_only = 0):
1290 """Load the resource specified by request and replace 'node' with the
1291 sucked data."""
1292 uri = request.get_full_url()
1293 if uri.startswith('file:///'):
1294 print "Loading", uri
1296 assert not request.has_data()
1297 stream = open(uri[7:])
1298 # (could read the mod time here...)
1299 last_mod = None
1300 else:
1301 print "Sucking", uri
1303 if request.has_data():
1304 print "POSTING", request.get_data()
1305 stream = urllib2.urlopen(request)
1306 headers = stream.info().headers
1307 last_mod = None
1308 for x in headers:
1309 if x.lower().startswith('last-modified:'):
1310 last_mod = x[14:].strip()
1311 break
1313 current_last_mod = node.getAttributeNS(None, 'last-modified')
1314 if current_last_mod and last_mod:
1315 if current_last_mod == last_mod:
1316 self.model.set_attrib(node, 'modified', None)
1317 print "not modified => not sucking!\n"
1318 return
1320 print "Fetching page contents..."
1321 data = stream.read()
1322 print "got data... tidying..."
1324 if data.startswith('<?xml'):
1325 pass
1326 else:
1327 data = support.to_html_doc(data)
1328 #print "Converted to", data
1330 old_md5 = node.getAttributeNS(None, 'md5_sum')
1332 import md5
1333 new_md5 = md5.new(data).hexdigest()
1335 if old_md5 and new_md5 == old_md5:
1336 self.model.set_attrib(node, 'modified', None)
1337 print "MD5 sums match => not parsing!"
1338 return node
1340 if md5_only:
1341 # This is a nasty hack left in for backwards compat.
1342 self.model.set_attrib(node, 'md5_sum', new_md5)
1343 return node
1345 print "parsing...",
1347 try:
1348 root = support.parse_data(data, uri)
1349 except:
1350 raise Beep
1352 new = self.model.import_with_ns(root.documentElement)
1353 new.setAttributeNS(None, 'uri', uri)
1355 if last_mod:
1356 new.setAttributeNS(None, 'last-modified', last_mod)
1357 new.setAttributeNS(None, 'modified', 'yes')
1358 new.setAttributeNS(None, 'md5_sum', new_md5)
1360 self.move_to([])
1361 if node == self.root:
1362 self.model.unlock(self.root)
1363 self.model.replace_node(self.root, new)
1364 self.model.strip_space(new)
1365 self.model.lock(new)
1366 self.root = new
1367 else:
1368 self.model.replace_node(node, new)
1369 self.model.strip_space(new)
1371 print "Loaded."
1372 return new
1374 def put_before(self):
1375 node = self.get_current()
1376 if self.clipboard == None:
1377 raise Beep
1378 new = self.clipboard.cloneNode(1)
1379 try:
1380 self.model.insert_before(node, new)
1381 except:
1382 raise Beep
1384 def put_after(self):
1385 node = self.get_current()
1386 if self.clipboard == None:
1387 raise Beep
1388 new = self.clipboard.cloneNode(1)
1389 self.model.insert_after(node, new)
1391 def put_replace(self):
1392 node = self.get_current()
1393 if self.clipboard == None:
1394 print "No clipboard!"
1395 raise Beep
1396 if self.current_attrib:
1397 if self.clipboard.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1398 value = self.clipboard.childNodes[0].data
1399 else:
1400 value = self.clipboard.data
1401 a = self.current_attrib
1402 value = value.replace('\n', ' ')
1403 a = self.model.set_attrib(node, a.name, value)
1404 self.move_to(node, a)
1405 return
1406 if self.clipboard.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1407 if len(self.clipboard.childNodes) != 1:
1408 print "Multiple nodes in clipboard!"
1409 raise Beep
1410 new = self.clipboard.childNodes[0].cloneNode(1)
1411 else:
1412 new = self.clipboard.cloneNode(1)
1413 if new.nodeType != Node.ELEMENT_NODE:
1414 raise Beep
1415 self.move_to([])
1416 try:
1417 if node == self.root:
1418 self.model.unlock(self.root)
1419 try:
1420 self.model.replace_node(self.root, new)
1421 self.root = new
1422 finally:
1423 self.model.lock(self.root)
1424 else:
1425 self.model.replace_node(node, new)
1426 self.move_to(new)
1427 except:
1428 type, val, tb = sys.exc_info()
1429 traceback.print_exception(type, val, tb)
1430 print "Replace failed!"
1431 raise Beep
1433 def put_as_child_end(self):
1434 self.put_as_child(end = 1)
1436 def put_as_child(self, end = 0):
1437 node = self.get_current()
1438 if self.clipboard == None:
1439 raise Beep
1440 new = self.clipboard.cloneNode(1)
1441 if new.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1442 to = []
1443 for n in new.childNodes:
1444 to.append(n)
1445 else:
1446 to = new
1447 try:
1448 if end:
1449 self.model.insert_before(None, new, parent = node)
1450 else:
1451 self.model.insert(node, new, index = 0)
1452 except:
1453 raise Beep
1455 self.move_to(to)
1457 def yank_value(self):
1458 if not self.current_attrib:
1459 raise Beep
1460 value = self.current_attrib.value
1461 self.clipboard = self.model.doc.createTextNode(value)
1462 #print "Clip now", self.clipboard
1464 def yank_attribs(self, name = None):
1465 if name:
1466 print "yank_attribs: DEPRECATED -- use Yank instead!"
1467 self.clipboard = self.model.doc.createDocumentFragment()
1468 if name:
1469 if not self.get_current().hasAttributeNS(None, name):
1470 raise Beep
1471 attribs = [self.get_current().getAttributeNodeNS(None, name)]
1472 else:
1473 attribs = []
1474 dict = self.get_current().attributes
1475 for a in dict.keys():
1476 attribs.append(dict[a])
1478 # Make sure the attributes always come out in the same order
1479 # (helps with macros).
1480 def by_name(a, b):
1481 diff = cmp(a.name, b.name)
1482 if diff == 0:
1483 diff = cmp(a.namespaceURI, b.namespaceURI)
1484 return diff
1486 attribs.sort(by_name)
1487 for a in attribs:
1488 n = self.model.doc.createElementNS(a.namespaceURI, a.nodeName)
1489 n.appendChild(self.model.doc.createTextNode(a.value))
1490 self.clipboard.appendChild(n)
1491 #print "Clip now", self.clipboard
1493 def paste_attribs(self):
1494 if self.clipboard.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1495 attribs = self.clipboard.childNodes
1496 else:
1497 attribs = [self.clipboard]
1498 new = []
1499 for a in attribs:
1500 try:
1501 new.append((a.nodeName, a.childNodes[0].data))
1502 except:
1503 raise Beep
1504 for node in self.current_nodes:
1505 # XXX: Set NS attribs first...
1506 for (name, value) in new:
1507 self.model.set_attrib(node, name, value)
1509 def compare(self):
1510 "Ensure that all selected nodes have the same value."
1511 if len(self.current_nodes) < 2:
1512 raise Beep # Not enough nodes!
1513 base = self.current_nodes[0]
1514 for n in self.current_nodes[1:]:
1515 if not same(base, n):
1516 raise Beep(may_record = 1)
1518 def fail(self):
1519 raise Beep(may_record = 1)
1521 def do_pass(self):
1522 pass
1524 def fail_if(self, xpath):
1525 """Evaluate xpath as a boolean, and fail if true."""
1526 src = self.get_current()
1527 ns = self.model.namespaces.uri
1528 c = Context.Context(src.parentNode, [src.parentNode], processorNss = ns)
1530 rt = XPath.Evaluate(xpath, context = c)
1531 #print "Got", rt
1532 if src in rt:
1533 raise Beep(may_record = 1)
1535 def attribute(self, namespace = None, attrib = ''):
1536 node = self.get_current()
1538 if attrib == '':
1539 self.move_to(node)
1540 return
1542 if attrib == 'xmlns':
1543 attrib = None
1544 #print "(ns, attrib)", `namespace`, attrib
1546 a = node.attributes.get((namespace, attrib), None)
1548 if a:
1549 self.move_to(node, a)
1550 else:
1551 print "No such attribute"
1552 print "Looking for %s in %s" % ((namespace, attrib), node.attributes)
1553 raise Beep()
1555 def set_attrib(self, value):
1556 a = self.current_attrib
1557 if not a:
1558 raise Beep()
1559 node = self.get_current()
1560 a = self.model.set_attrib(node, a.name, value)
1561 self.move_to(node, a)
1563 def rename_attrib(self, new):
1564 a = self.current_attrib
1565 if not a:
1566 raise Beep()
1567 node = self.get_current()
1568 new_attr = self.model.set_attrib(node, new, a.value)
1569 self.model.set_attrib(node, a.name, None)
1570 self.move_to(node, new_attr)
1572 def add_attrib(self, UNUSED, name, value = ''):
1573 node = self.get_current()
1574 a = self.model.set_attrib(node, name, value)
1575 self.move_to(node, a)
1577 def set_root_from_doc(self, doc):
1578 new = self.model.import_with_ns(doc.documentElement)
1580 if self.root:
1581 self.model.unlock(self.root)
1582 self.move_to([])
1583 self.model.replace_node(self.root, new)
1584 self.model.lock(new)
1585 self.root = new
1586 self.move_to(self.root)
1588 def load_html(self, path):
1589 "Replace root with contents of this HTML file."
1590 print "Reading HTML..."
1591 doc = self.model.load_html(path)
1592 self.set_root_from_doc(doc)
1594 def load_xml(self, path):
1595 "Replace root with contents of this XML (or Dome) file."
1596 print "Reading XML..."
1597 data = file(path).read()
1598 doc = support.parse_data(data, path)
1599 self.set_root_from_doc(doc)
1601 def select_dups(self):
1602 node = self.get_current()
1603 select = []
1604 for n in node.parentNode.childNodes:
1605 if n is node:
1606 continue
1607 if same(node, n):
1608 select.append(n)
1609 self.move_to(select)
1611 def select_marked_region(self, attr = "unused"):
1612 select = []
1613 if len(self.marked) != 1:
1614 print "Must be exactly one marked node!"
1615 raise Beep()
1616 if len(self.current_nodes) != 1:
1617 print "Must be exactly one selected node!"
1618 raise Beep()
1619 import Path
1620 a = Path.path_to(self.get_current())
1621 b = Path.path_to(self.marked.keys()[0])
1623 while a and b and a[0] == b[0]:
1624 del a[0]
1625 del b[0]
1627 if a and b:
1628 select = []
1629 s = 0
1630 a = a[0]
1631 b = b[0]
1632 for x in a.parentNode.childNodes:
1633 if x == a:
1634 s = not s
1635 elif x == b:
1636 s = not s
1637 if s:
1638 select.append(x)
1639 self.move_to(select)
1640 else:
1641 print "One node is a parent of the other!"
1642 raise Beep()
1644 def show_html(self):
1645 from HTML import HTML
1646 HTML(self.model, self.get_current()).show()
1648 def show_canvas(self):
1649 from Canvas import Canvas
1650 Canvas(self, self.get_current()).show()
1652 def toggle_hidden(self):
1653 nodes = self.current_nodes[:]
1654 self.move_to([])
1655 for node in nodes:
1656 if node.nodeType != Node.ELEMENT_NODE:
1657 raise Beep
1658 if node.hasAttributeNS(None, 'hidden'):
1659 new = None
1660 else:
1661 new = 'yes'
1662 self.model.set_attrib(node, 'hidden', new, with_update = 0)
1663 self.model.update_all(self.root)
1664 self.move_to(nodes)
1666 def soap_send(self):
1667 copy = node_to_xml(self.get_current())
1668 env = copy.documentElement
1670 if env.namespaceURI != SOAPENV_NS:
1671 alert("Not a SOAP-ENV:Envelope (bad namespace)")
1672 raise Done()
1673 if env.localName != 'Envelope':
1674 alert("Not a SOAP-ENV:Envelope (bad local name)")
1675 raise Done()
1677 if len(env.childNodes) != 2:
1678 alert("SOAP-ENV:Envelope must have one header and one body")
1679 raise Done()
1681 kids = elements(env)
1682 head = kids[0]
1683 body = kids[1]
1685 if head.namespaceURI != SOAPENV_NS or \
1686 head.localName != 'Head':
1687 alert("First child must be a SOAP-ENV:Head element")
1688 raise Done()
1690 if body.namespaceURI != SOAPENV_NS or \
1691 body.localName != 'Body':
1692 alert("Second child must be a SOAP-ENV:Body element")
1693 raise Done()
1695 sft = None
1696 for header in elements(head):
1697 if header.namespaceURI == DOME_NS and header.localName == 'soap-forward-to':
1698 sft = header
1699 break
1700 print header.namespaceURI
1701 print header.localName
1703 if not sft:
1704 alert("Head must contain a dome:soap-forward-to element")
1705 raise Done()
1707 dest = sft.childNodes[0].data
1708 parent = sft.parentNode
1709 if len(elements(parent)) == 1:
1710 sft = parent
1711 parent = sft.parentNode # Delete the whole header
1712 parent.removeChild(sft)
1714 import httplib, urlparse
1716 (scheme, addr, path, p, q, f) = urlparse.urlparse(dest, allow_fragments = 0)
1717 if scheme != 'http':
1718 alert("SOAP is only supported for 'http:' -- sorry!")
1719 raise Done()
1721 stream = StrGrab()
1722 PrettyPrint(copy, stream = stream)
1723 message = stream.data
1725 conn = httplib.HTTP(addr)
1726 conn.putrequest("POST", path)
1727 conn.putheader('Content-Type', 'text/xml; charset="utf-8"')
1728 conn.putheader('Content-Length', str(len(message)))
1729 conn.putheader('SOAPAction', '')
1730 conn.endheaders()
1731 conn.send(message)
1732 (code, r_mess, r_headers) = conn.getreply()
1734 reply = conn.getfile().read()
1735 print "Got:\n", reply
1737 new_doc = support.parse_data(reply, None)
1739 new = self.model.doc.importNode(new_doc.documentElement, 1)
1741 self.model.strip_space(new)
1743 old = self.get_current()
1744 self.move_to([])
1745 self.model.replace_node(old, new)
1746 self.move_to(new)
1748 def program_changed(self, changed_op):
1749 print "Check points..."
1750 if self.rec_point:
1751 (op, exit) = self.rec_point
1752 if not op.parent:
1753 print "Lost rec_point"
1754 self.rec_point = None
1755 if self.exec_point:
1756 (op, exit) = self.exec_point
1757 if not op.parent:
1758 print "Lost exec_point"
1759 self.exec_point = None
1760 for l in self.lists:
1761 l.update_points()
1762 self.status_changed()
1764 def prog_tree_changed(self):
1765 pass
1767 def export_all(self):
1768 doc = implementation.createDocument(DOME_NS, 'dome:dome', None)
1769 node = self.model.root_program.to_xml(doc)
1770 doc.documentElement.appendChild(node)
1771 node = doc.createElementNS(DOME_NS, 'dome:dome-data')
1772 doc.documentElement.appendChild(node)
1774 if self.chroots:
1775 print "*** WARNING: Saving from a chroot!"
1776 model = self.model
1777 data = doc.importNode(model.doc.documentElement, 1)
1778 node.appendChild(data)
1780 return doc
1782 def blank_all(self):
1783 doc = implementation.createDocument(None, 'root', None)
1784 self.move_home()
1785 self.clipboard = self.model.doc.createElementNS(None, 'root')
1786 self.put_replace()
1788 def mark_switch(self):
1789 new = self.marked.keys()
1790 self.set_marked(self.current_nodes)
1791 self.move_to(new)
1793 def set_marked(self, new):
1794 update = self.marked
1795 for x in self.marked.keys():
1796 self.model.unlock(x)
1797 self.marked = {}
1798 for x in new:
1799 self.model.lock(x)
1800 self.marked[x] = None
1801 update[x] = None
1802 update = update.keys()
1803 for display in self.displays:
1804 display.marked_changed(update)
1806 def mark_selection(self):
1807 self.set_marked(self.current_nodes)
1809 def clear_mark(self):
1810 self.set_marked([])
1812 def normalise(self):
1813 self.model.normalise(self.get_current())
1815 def remove_ns(self):
1816 print "remove_ns: Disabled"
1817 return
1818 nodes = self.current_nodes[:]
1819 self.move_to([])
1820 nodes = map(self.model.remove_ns, nodes)
1821 self.move_to(nodes)
1823 def convert_to(self, fn):
1824 nodes = self.current_nodes[:]
1825 self.move_to([])
1826 nodes = map(fn, nodes)
1827 self.move_to(nodes)
1828 def convert_to_element(self): self.convert_to(self.model.convert_to_element)
1829 def convert_to_text(self): self.convert_to(self.model.convert_to_text)
1830 def convert_to_comment(self): self.convert_to(self.model.convert_to_comment)
1832 class StrGrab:
1833 data = ''
1835 def write(self, str):
1836 self.data += str