1 from xml
.dom
import Node
2 from rox
.loading
import XDSLoader
5 from rox
import g
, TRUE
, FALSE
9 from Display2
import Display
11 from GetArg
import GetArg
12 from Path
import make_relative_path
14 from rox
.Menu
import Menu
15 from gnome
import canvas
18 ('/File', None, '<Branch>', ''),
19 ('/File/Save', 'menu_save', '', '<Ctrl>S'),
20 ('/File/Blank document', 'do_blank_all', '', '<Ctrl>N'),
21 ('/File/Clear undo buffer', 'menu_clear_undo', '', ''),
23 ('/Edit', None, '<Branch>', ''),
24 ('/Edit/Copy attributes', 'do_yank_attributes', '', ''),
25 ('/Edit/Paste attributes', 'do_paste_attribs', '', ''),
26 ('/Edit/Copy attrib value', 'do_yank_value', '', ''),
27 ('/Edit/Rename attribute', 'menu_rename_attr', '', ''),
28 ('/Edit/', '', '', '<separator>'),
29 ('/Edit/Cut', 'do_delete_node', '', '<Ctrl>X'),
30 ('/Edit/Delete', 'do_delete_node_no_clipboard', '', ''),
31 ('/Edit/Shallow cut', 'do_delete_shallow', '', '<Shift>X'),
32 ('/Edit/', '', '', '<separator>'),
33 ('/Edit/Copy', 'do_yank', '', '<Ctrl>C'),
34 ('/Edit/Shallow copy', 'do_shallow_yank', '', '<Shift>Y'),
35 ('/Edit/', '', '', '<separator>'),
36 ('/Edit/Paste (replace)','do_put_replace', '', '<Ctrl>V'),
37 ('/Edit/Paste (inside)', 'do_put_as_child', '', 'bracketright'),
38 ('/Edit/Paste (before)', 'do_put_before', '', '<Shift>P'),
39 ('/Edit/Paste (after)', 'do_put_after', '', 'p'),
40 ('/Edit/', '', '', '<separator>'),
41 ('/Edit/Edit value', 'toggle_edit', '', 'Return'),
42 ('/Edit/', '', '', '<separator>'),
43 ('/Edit/Undo', 'do_undo', '', '<Ctrl>Z'),
44 ('/Edit/Redo', 'do_redo', '', '<Ctrl>Y'),
46 ('/Move', None, '<Branch>', ''),
47 ('/Move/XPath search', 'menu_show_search', '', 'slash'),
48 ('/Move/Text search', 'menu_show_text_search', '', 'T'),
49 ('/Move/Enter', 'do_enter', '', '<Shift>greater'),
50 ('/Move/Leave', 'do_leave', '', '<Shift>less'),
52 ('/Move/Root node', 'move_home', '', 'Home'),
53 ('/Move/Previous sibling', 'move_prev_sib', '', 'Up'),
54 ('/Move/Next sibling', 'move_next_sib', '', 'Down'),
55 ('/Move/Parent', 'move_left', '', 'Left'),
56 ('/Move/First child', 'move_right', '', 'Right'),
57 ('/Move/Last child', 'move_end', '', 'End'),
59 ('/Move/To attribute', 'menu_select_attrib', '', 'At'),
61 ('/Select', None, '<Branch>', ''),
62 ('/Select/By XPath', 'menu_show_global', '', 'numbersign'),
63 ('/Select/Duplicate Siblings', 'do_select_dups', '', ''),
64 ('/Select/To Mark', 'do_select_marked', '', 'minus'),
65 ('/Select/Child Nodes', 'do_select_children', '', 'asterisk'),
67 ('/Mark', None, '<Branch>', ''),
68 ('/Mark/Mark Selection', 'do_mark_selection', '', 'm'),
69 ('/Mark/Switch with Selection', 'do_mark_switch', '', 'comma'),
70 ('/Mark/Clear Mark', 'do_clear_mark', '', ''),
72 ('/Network', None, '<Branch>', ''),
73 ('/Network/HTTP GET', 'do_suck', '', '<Shift>asciicircum'),
74 ('/Network/HTTP POST', 'do_http_post', '', ''),
75 ('/Network/Send SOAP message', 'do_soap_send', '', ''),
77 ('/Create', None, '<Branch>', ''),
78 ('/Create/Insert node', 'menu_insert_element', '', 'I'),
79 ('/Create/Append node', 'menu_append_element', '', 'A'),
80 ('/Create/Open node inside', 'menu_open_element', '', 'O'),
81 ('/Create/Open node at end', 'menu_open_element_end', '', 'E'),
83 ('/Process', None, '<Branch>', ''),
84 ('/Process/Substitute', 'menu_show_subst', '', 's'),
85 ('/Process/Python expression', 'menu_show_pipe', '', '<Shift>exclam'),
86 ('/Process/XPath expression', 'menu_show_xpath', '', ''),
87 ('/Process/Normalise', 'do_normalise', '', ''),
88 ('/Process/Remove default namespaces', 'do_remove_ns', '', 'r'),
89 ('/Process/Convert to text', 'do_convert_to_text', '', ''),
90 ('/Process/Convert to comment', 'do_convert_to_comment', '', ''),
91 ('/Process/Convert to element', 'do_convert_to_element', '', ''),
93 ('/Program', None, '<Branch>', ''),
94 ('/Program/Input', 'menu_show_ask', '', 'question'),
95 ('/Program/Compare', 'do_compare', '', 'equal'),
96 ('/Program/Fail', 'do_fail', '', ''),
97 ('/Program/Pass', 'do_pass', '', ''),
98 ('/Program/Repeat last', 'do_again', '', 'dot'),
100 ('/View', None, '<Branch>', ''),
101 ('/View/Toggle hidden', 'do_toggle_hidden', '', '<Ctrl>H'),
102 ('/View/Show as HTML', 'do_show_html', '', ''),
103 ('/View/Show as canvas', 'do_show_canvas', '', ''),
104 ('/View/Show namespaces', 'show_namespaces', '', '<Ctrl>;'),
105 ('/View/Close Window', 'menu_close_window', '', '<Ctrl>Q'),
107 #('/Options...', 'menu_options', '', '<Ctrl>O'),
111 return lambda(self
): self
.view
.may_record([action
])
113 class GUIView(Display
, XDSLoader
):
114 def __init__(self
, window
, view
):
115 Display
.__init
__(self
, window
, view
)
116 XDSLoader
.__init
__(self
, ['application/x-dome', 'text/xml',
118 window
.connect('key-press-event', self
.key_press
)
119 self
.cursor_node
= None
120 self
.edit_dialog
= None
123 menu
.attach(window
, self
)
125 def destroyed(self
, widget
):
126 print "GUIView destroyed!"
127 Display
.destroyed(self
, widget
)
130 def update_state(self
):
131 if self
.view
.rec_point
:
132 state
= "(recording)"
133 elif self
.view
.idle_cb
or self
.view
.op_in_progress
:
137 self
.parent_window
.set_state(state
)
140 def xds_load_from_stream(self
, path
, type, stream
):
142 raise Exception('Can only load from files... sorry!')
143 if path
.endswith('.html'):
144 self
.view
.load_html(path
)
146 self
.view
.load_xml(path
)
147 if self
.view
.root
== self
.view
.model
.get_root():
148 self
.parent_window
.uri
= path
149 self
.parent_window
.update_title()
151 def key_press(self
, widget
, kev
):
152 focus
= widget
.focus_widget
153 if focus
and focus
is not widget
and focus
.get_toplevel() is widget
:
155 return TRUE
# Handled
159 if kev
.keyval
== keysyms
.Up
:
160 self
.view
.may_record(['move_prev_sib'])
161 elif kev
.keyval
== keysyms
.Down
:
162 self
.view
.may_record(['move_next_sib'])
163 elif kev
.keyval
== keysyms
.Left
:
164 self
.view
.may_record(['move_left'])
165 elif kev
.keyval
== keysyms
.Right
:
166 self
.view
.may_record(['move_right'])
167 elif kev
.keyval
== keysyms
.KP_Add
:
168 self
.menu_show_add_attrib()
169 elif kev
.keyval
== keysyms
.Tab
:
175 def node_clicked(self
, node
, bev
):
176 print "Clicked", node
.namespaceURI
, node
.localName
178 if bev
.type == g
.gdk
.BUTTON_PRESS
:
179 if len(self
.view
.current_nodes
) == 0:
182 src
= self
.view
.current_nodes
[-1]
183 shift
= bev
.state
& g
.gdk
.SHIFT_MASK
184 add
= bev
.state
& g
.gdk
.CONTROL_MASK
185 select_region
= shift
and node
.nodeType
== Node
.ELEMENT_NODE
186 lit
= shift
and not select_region
189 path
= make_relative_path(src
, node
, lit
, ns
)
190 if path
== '.' and self
.view
.current_nodes
and not self
.view
.current_attrib
:
193 self
.view
.may_record(["select_region", path
, ns
])
195 self
.view
.may_record(["do_search", path
, ns
, add
])
197 self
.view
.may_record(["toggle_hidden"])
199 def attrib_clicked(self
, element
, attrib
, event
):
200 if len(self
.view
.current_nodes
) == 0:
203 src
= self
.view
.current_nodes
[-1]
206 print "attrib_clicked", attrib
, attrib
.namespaceURI
, attrib
.localName
207 path
= make_relative_path(src
, element
, FALSE
, ns
)
209 self
.view
.may_record(["do_search", path
, ns
, FALSE
])
210 self
.view
.may_record(["attribute", attrib
.namespaceURI
, attrib
.localName
])
213 self
.parent_window
.save()
215 def show_menu(self
, bev
):
216 menu
.popup(self
, bev
)
218 def playback(self
, macro
, map):
219 "Called when the user clicks on a macro button."
220 Exec
.exec_state
.clean()
222 self
.view
.may_record(['map', macro
.uri
])
224 self
.view
.may_record(['play', macro
.uri
])
226 def menu_show_ask(self
):
227 def do_ask(q
, self
= self
):
229 self
.view
.may_record(action
)
230 GetArg('Input:', do_ask
, ('Prompt:',))
232 def menu_show_subst(self
):
233 def do_subst(args
, self
= self
):
234 action
= ["subst", args
[0], args
[1]]
235 self
.view
.may_record(action
)
236 GetArg('Substitute:', do_subst
, ('Replace:', 'With:'))
238 def show_namespaces(self
):
240 Namespaces
.GUI(self
.view
.model
).show()
242 def move_from(self
, old
= []):
244 Display
.move_from(self
, old
)
246 def hide_editbox(self
):
248 if self
.cursor_attrib
:
249 self
.cursor_hidden_text
.set(text
= '%s=%s' %
250 (self
.cursor_attrib
.name
, self
.cursor_attrib
.value
))
251 self
.cursor_hidden_text
.show()
252 self
.auto_highlight(self
.cursor_node
)
253 self
.cursor_node
= None
254 self
.edit_box_item
.destroy()
256 def show_editbox(self
):
257 "Edit the current node/attribute"
264 raise Exception("Can't edit while display is hidden!")
266 self
.cursor_node
= self
.view
.current_nodes
[0]
267 group
= self
.node_to_group
[self
.cursor_node
]
268 self
.cursor_attrib
= self
.view
.current_attrib
270 self
.highlight(group
, FALSE
)
272 if self
.cursor_attrib
:
273 group
= group
.attrib_to_group
[self
.cursor_attrib
]
275 self
.cursor_hidden_text
= group
.text
276 if not self
.cursor_attrib
:
277 # Don't hide for attributes, so we can still see the name
280 group
.text
.set(text
= str(self
.cursor_attrib
.name
) + '=')
282 self
.update_now() # GnomeCanvas bug?
283 lx
, ly
, hx
, hy
= group
.text
.get_bounds()
284 x
, y
= group
.i2w(lx
, ly
)
292 self
.edit_box_text
= text
295 #s = eb.get_style().copy()
296 #s.font = load_font('fixed')
298 #if self.cursor_attrib:
299 # name_width = s.font.measure(self.cursor_attrib.name + '=') + 1
304 self
.edit_box_item
= self
.root().add(canvas
.CanvasWidget
, widget
= eb
,
305 x
= x
- m
+ name_width
, y
= y
- m
,
306 anchor
= g
.ANCHOR_NW
)
308 #text.set_editable(TRUE)
309 text
.get_buffer().insert_at_cursor(self
.get_edit_text())
310 text
.set_wrap_mode(g
.WRAP_NONE
)
311 text
.get_buffer().connect('changed', self
.eb_changed
)
312 text
.connect('key-press-event', self
.eb_key
)
317 #eb.select_region(0, -1)
320 def get_edit_text(self
):
321 if node
.nodeType
== Node
.ELEMENT_NODE
:
322 if self
.cursor_attrib
:
323 return str(self
.cursor_attrib
.value
)
326 return node
.nodeValue
328 def eb_key(self
, eb
, kev
):
330 if key
== g
.keysyms
.KP_Enter
:
331 key
= g
.keysyms
.Return
332 if key
== g
.keysyms
.Escape
:
334 elif key
== g
.keysyms
.Return
and kev
.state
& g
.gdk
.CONTROL_MASK
:
335 eb
.insert_defaults('\n')
337 elif key
== g
.keysyms
.Tab
or key
== g
.keysyms
.Return
:
338 buffer = eb
.get_buffer()
339 s
= buffer.get_start_iter()
340 e
= buffer.get_end_iter()
341 text
= buffer.get_text(s
, e
, TRUE
)
343 if text
!= self
.get_edit_text():
344 self
.commit_edit(text
)
349 def commit_edit(self
, new
):
350 if self
.cursor_attrib
:
351 self
.view
.may_record(['set_attrib', new
])
353 self
.view
.may_record(['change_node', new
])
355 def eb_changed(self
, eb
):
360 req
= self
.edit_box_text
.size_request()
362 width
= max(req
[0], 10)
363 height
= max(req
[1], 10)
364 self
.edit_box_item
.set(width
= width
+ 12, height
= height
+ 4)
367 def toggle_edit(self
):
368 node
= self
.view
.current_nodes
[0]
369 attrib
= self
.view
.current_attrib
372 self
.edit_dialog
.destroy()
373 self
.edit_dialog
= rox
.Dialog()
374 eb
= self
.edit_dialog
376 if node
.nodeType
== Node
.ELEMENT_NODE
:
378 text
= unicode(attrib
.value
)
383 entry
.set_activates_default(True)
384 def get_text(): return entry
.get_text()
386 text
= node
.nodeValue
388 buffer = entry
.get_buffer()
389 buffer.insert_at_cursor(text
)
390 entry
.set_size_request(400, 200)
393 start
= buffer.get_start_iter()
394 end
= buffer.get_end_iter()
395 return buffer.get_text(start
, end
, True)
396 eb
.vbox
.pack_start(entry
, TRUE
, FALSE
, 0)
398 eb
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
399 eb
.add_button(g
.STOCK_APPLY
, g
.RESPONSE_OK
)
400 eb
.set_default_response(g
.RESPONSE_OK
)
403 self
.edit_dialog
= None
404 eb
.connect('destroy', destroy
)
405 def response(eb
, resp
):
406 if resp
== g
.RESPONSE_CANCEL
:
408 elif resp
== g
.RESPONSE_OK
:
412 self
.view
.may_record(['set_attrib', new
])
414 self
.view
.may_record(['change_node', new
])
416 eb
.connect('response', response
)
420 #if self.cursor_node:
421 # self.hide_editbox()
423 # self.show_editbox()
425 def menu_select_attrib(self
):
428 (prefix
, localName
) = name
.split(':', 1)
429 namespaceURI
= self
.view
.model
.prefix_to_namespace(self
.view
.get_current(), prefix
)
431 (prefix
, localName
) = (None, name
)
433 action
= ["attribute", namespaceURI
, localName
]
434 self
.view
.may_record(action
)
435 GetArg('Select attribute:', do_attrib
, ['Name:'])
437 def menu_show_add_attrib(self
):
439 action
= ["add_attrib", "UNUSED", name
]
440 self
.view
.may_record(action
)
441 GetArg('Create attribute:', do_it
, ['Name:'])
443 def menu_show_pipe(self
):
445 action
= ["python", expr
]
446 self
.view
.may_record(action
)
447 GetArg('Python expression:', do_pipe
, ['Eval:'], "'x' is the old text...")
449 def menu_show_xpath(self
):
451 action
= ["xpath", expr
]
452 self
.view
.may_record(action
)
453 GetArg('XPath expression:', go
, ['Eval:'], "Result goes on the clipboard")
455 def menu_show_global(self
):
456 def do_global(pattern
):
457 action
= ["do_global", pattern
]
458 self
.view
.may_record(action
)
459 GetArg('Global:', do_global
, ['Pattern:'],
460 '(@CURRENT@ is the current node\'s value)\n' +
461 'Perform next action on all nodes matching')
463 def menu_show_text_search(self
):
464 def do_text_search(pattern
):
465 action
= ["do_text_search", pattern
]
466 self
.view
.may_record(action
)
467 GetArg('Search for:', do_text_search
, ['Text pattern:'],
468 '(@CURRENT@ is the current node\'s value)\n')
470 def menu_show_search(self
):
471 def do_search(pattern
):
472 action
= ["do_search", pattern
]
473 self
.view
.may_record(action
)
474 GetArg('Search for:',
475 do_search
, ['XPath:'],
476 '(@CURRENT@ is the current node\'s value)')
478 def menu_rename_attr(self
):
480 action
= ["rename_attrib", name
]
481 self
.view
.may_record(action
)
482 GetArg('Rename to:', do
, ['New name:'])
485 def do_create(self
, action
, nodeType
, data
):
488 if nodeType
== Node
.ELEMENT_NODE
:
490 elif nodeType
== Node
.TEXT_NODE
:
493 elif nodeType
== Node
.ATTRIBUTE_NODE
:
497 # Check name is valid
498 # XXX: Should be more strict
500 assert '\n' not in data
501 assert ' ' not in data
503 self
.view
.may_record(['add_node', action
, data
])
505 def show_add_box(self
, action
):
508 elif action
[0] == 'a':
510 elif action
[0] == 'o':
512 elif action
[0] == 'e':
519 elif action
[1] == 't':
527 box
.vbox
.pack_start(text
, TRUE
, FALSE
, 0)
528 text
.set_size_request(400, 200)
529 box
.set_has_separator(False)
531 box
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
532 box
.add_button('Add _Attribute', Node
.ATTRIBUTE_NODE
)
533 box
.add_button('Add _Text', Node
.TEXT_NODE
)
534 box
.add_button('Add _Element', Node
.ELEMENT_NODE
)
535 box
.set_default_response(g
.RESPONSE_OK
)
537 def response(box
, resp
):
538 if resp
== g
.RESPONSE_CANCEL
:
541 buffer = text
.get_buffer()
542 start
= buffer.get_start_iter()
543 end
= buffer.get_end_iter()
544 new
= buffer.get_text(start
, end
, True)
546 self
.do_create(action
, resp
, new
)
548 box
.connect('response', response
)
553 cur
= self
.view
.get_current()
554 if cur
.nodeType
== Node
.ELEMENT_NODE
:
556 return cur
.parentNode
.nodeName
558 def menu_insert_element(self
):
560 self
.show_add_box('ie')
562 def menu_append_element(self
):
564 self
.show_add_box('ae')
566 def menu_open_element(self
):
568 self
.show_add_box('oe')
570 def menu_open_element_end(self
):
571 "Open element at end"
572 self
.show_add_box('ee')
574 def menu_insert_text(self
):
576 self
.show_add_box('it')
578 def menu_append_text(self
):
580 self
.show_add_box('at')
582 def menu_open_text(self
):
584 self
.show_add_box('ot')
586 def menu_open_text_end(self
):
588 self
.show_add_box('et')
590 def menu_close_window(self
):
591 self
.parent_window
.destroy()
593 def menu_options(self
):
596 def menu_clear_undo(self
):
597 if rox
.confirm('Really clear the undo buffer?',
599 self
.view
.model
.clear_undo()
601 do_blank_all
= make_do('blank_all')
602 do_enter
= make_do('enter')
603 do_leave
= make_do('leave')
604 do_suck
= make_do('suck')
605 do_http_post
= make_do('http_post')
606 do_soap_send
= make_do('soap_send')
607 do_select_dups
= make_do('select_dups')
608 do_paste_attribs
= make_do('paste_attribs')
609 do_yank_value
= make_do('yank_value')
610 do_yank_attributes
= make_do('yank_attribs')
611 do_delete_node
= make_do('delete_node')
612 do_delete_node_no_clipboard
= make_do('delete_node_no_clipboard')
613 do_delete_shallow
= make_do('delete_shallow')
614 do_yank
= make_do('yank')
615 do_shallow_yank
= make_do('shallow_yank')
616 do_put_replace
= make_do('put_replace')
617 do_put_as_child
= make_do('put_as_child')
618 do_put_before
= make_do('put_before')
619 do_put_after
= make_do('put_after')
620 do_undo
= make_do('undo')
621 do_redo
= make_do('redo')
622 do_fail
= make_do('fail')
623 do_pass
= make_do('do_pass')
624 do_toggle_hidden
= make_do('toggle_hidden')
625 do_show_html
= make_do('show_html')
626 do_show_canvas
= make_do('show_canvas')
627 do_compare
= make_do('compare')
628 do_again
= make_do('again')
629 do_normalise
= make_do('normalise')
630 do_convert_to_text
= make_do('convert_to_text')
631 do_convert_to_comment
= make_do('convert_to_comment')
632 do_convert_to_element
= make_do('convert_to_element')
633 do_convert_to_pi
= make_do('convert_to_pi')
634 do_remove_ns
= make_do('remove_ns')
636 do_clear_mark
= make_do('clear_mark')
637 do_mark_switch
= make_do('mark_switch')
638 do_mark_selection
= make_do('mark_selection')
639 do_select_marked
= make_do('select_marked_region')
640 do_select_children
= make_do('select_children')
642 move_home
= make_do('move_home')
643 move_end
= make_do('move_end')
644 move_left
= make_do('move_left')
645 move_right
= make_do('move_right')
646 move_next_sib
= make_do('move_next_sib')
647 move_prev_sib
= make_do('move_prev_sib')