Add URI display, remove attrib print.
[dom-editor.git] / Dome / Change.py
blob519d311805ae77ee498abb89a3823ec3333646c8
1 # All changes go through here so we can undo...
3 from Beep import Beep
4 from xml.dom import Node
6 # Which undo list operations will affect.
7 # Normal ops add to the undo list.
8 # Undo ops add to the redo list.
9 # Redo ops add to the undo list, but don't clear the redo list.
10 undo_state = '__dom_undo'
11 undo_clear = 1
13 # This value is stored with each undo record.
14 # The caller may use this to indicate whether several operations
15 # should be considered as one.
16 user_op = 1
18 # These actually modifiy the DOM
19 def set_name(node, namespaceURI, qname):
20 # XXX: Hack!
21 tmp = node.ownerDocument.createElementNS(namespaceURI, qname)
22 add_undo(node, lambda node = node, ns = node.namespaceURI, name = node.nodeName:
23 set_name(node, ns, name))
24 node.__dict__['__nodeName'] = tmp.__dict__['__nodeName']
25 node.__dict__['__namespaceURI'] = tmp.__dict__['__namespaceURI']
26 node.__dict__['__prefix'] = tmp.__dict__['__prefix']
27 node.__dict__['__localName'] = tmp.__dict__['__localName']
29 def insert_before(node, new, parent):
30 "Insert 'new' before 'node'. If 'node' is None then insert at the end"
31 "of parent's children."
32 if new.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
33 raise Exception("insert_before() can't take a fragment!")
34 parent.insertBefore(new, node)
35 add_undo(parent, lambda new = new: delete(new))
37 def delete(node):
38 next = node.nextSibling
39 parent = node.parentNode
40 parent.removeChild(node)
41 add_undo(parent,
42 lambda parent = parent, next = next, node = node:
43 insert_before(next, node, parent = parent))
45 def replace_node(old, new):
46 old.parentNode.replaceChild(new, old)
47 add_undo(new.parentNode,
48 lambda old = old, new = new:
49 replace_node(new, old))
51 def set_data(node, new):
52 old = node.data
53 node.data = new
54 add_undo(node,
55 lambda node = node, old = old:
56 set_data(node, old))
58 def set_attrib(node, namespaceURI, localName, value = None):
59 #print "set_attrib", `namespaceURI`, `localName`, `value`
60 if node.hasAttributeNS(namespaceURI, localName):
61 old = node.getAttributeNS(namespaceURI, localName)
62 else:
63 old = None
64 if value != None:
65 if localName == None:
66 localName = 'xmlns'
67 node.setAttributeNS(namespaceURI, localName, value)
68 else:
69 node.removeAttributeNS(namespaceURI, localName)
71 add_undo(node, lambda node = node, namespaceURI = namespaceURI, localName = localName, old = old: \
72 set_attrib(node, namespaceURI, localName, old))
74 # Support
76 op = 0
78 def newest_change(node, history):
79 "Return the most recent (node,time) change to the 'history' list."
81 try:
82 best_node, best_time = node, getattr(node, history)[-1][0]
83 except:
84 best_node, best_time = None, 0
86 for k in node.childNodes:
87 n, t = newest_change(k, history)
88 if n and (best_node == None or t > best_time):
89 best_node, best_time = n, t
90 return best_node, best_time
92 def can_undo(node):
93 n, t = newest_change(node, '__dom_undo')
94 return n != None
96 def can_redo(node):
97 n, t = newest_change(node, '__dom_redo')
98 return n != None
100 # Undo and redo stuff
101 # XXX: Undo/redo need to check locking
102 def add_undo(node, fn):
103 "Attempting to undo changes to 'node' will call this fn."
104 #return #Disabled
105 global op
106 op += 1
108 if not hasattr(node, undo_state):
109 setattr(node, undo_state, [])
110 getattr(node, undo_state).append((op, fn, user_op))
111 if undo_clear and hasattr(node, '__dom_redo'):
112 del node.__dom_redo
114 def do_undo(node, user_op = None):
115 "Undo changes to this node (including descendants)."
116 "Returns the node containing the undone node, and the user_op."
117 "If 'user_op' is given, only undo if it matches, else return None."
118 node, time = newest_change(node, '__dom_undo')
119 if not node:
120 return
121 op, fn, uop = node.__dom_undo[-1]
122 if user_op and user_op != uop:
123 return
124 parent = node.parentNode
126 del node.__dom_undo[-1]
128 global undo_state, undo_clear
129 undo_state = '__dom_redo'
130 undo_clear = 0
131 fn()
132 undo_clear = 1
133 undo_state = '__dom_undo'
135 return (parent, uop)
137 def do_redo(node, user_op = None):
138 "Redo undos on this node (including descendants)."
139 "Returns the node containing the redone node, and the user_op."
140 "If 'user_op' is given, only redo if it matches, else return None."
141 node, time = newest_change(node, '__dom_redo')
142 if not node:
143 return
144 op, fn, uop = node.__dom_redo[-1]
145 if user_op and user_op != uop:
146 return
147 parent = node.parentNode
149 del node.__dom_redo[-1]
151 global undo_state, undo_clear
152 undo_clear = 0
153 fn()
154 undo_clear = 1
156 return (parent, uop)