home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / bin / gtk-builder-convert < prev    next >
Encoding:
Text File  |  2009-01-08  |  26.0 KB  |  747 lines

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2006-2008 Async Open Source
  4. #                         Henrique Romano <henrique@async.com.br>
  5. #                         Johan Dahlin <jdahlin@async.com.br>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU Lesser General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Lesser General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. #
  21. # TODO:
  22. #  Toolbars
  23.  
  24. """Usage: gtk-builder-convert [OPTION] [INPUT] [OUTPUT]
  25. Converts Glade files into XML files which can be loaded with GtkBuilder.
  26. The [INPUT] file is
  27.  
  28.   -w, --skip-windows     Convert everything but GtkWindow subclasses.
  29.   -r, --root             Convert only widget named root and its children
  30.   -h, --help             display this help and exit
  31.  
  32. When OUTPUT is -, write to standard input.
  33.  
  34. Examples:
  35.   gtk-builder-convert preference.glade preferences.ui
  36.  
  37. Report bugs to http://bugzilla.gnome.org/."""
  38.  
  39. import getopt
  40. import os
  41. import sys
  42.  
  43. from xml.dom import minidom, Node
  44.  
  45. WINDOWS = ['GtkWindow',
  46.            'GtkDialog',
  47.            'GtkFileChooserDialog',
  48.            'GtkMessageDialog']
  49.  
  50. # The subprocess is only available in Python 2.4+
  51. try:
  52.     import subprocess
  53.     subprocess # pyflakes
  54. except ImportError:
  55.     subprocess = None
  56.  
  57. def get_child_nodes(node):
  58.     assert node.tagName == 'object'
  59.     nodes = []
  60.     for child in node.childNodes:
  61.         if child.nodeType != Node.ELEMENT_NODE:
  62.             continue
  63.         if child.tagName != 'child':
  64.             continue
  65.         nodes.append(child)
  66.     return nodes
  67.  
  68. def get_properties(node):
  69.     assert node.tagName == 'object'
  70.     properties = {}
  71.     for child in node.childNodes:
  72.         if child.nodeType != Node.ELEMENT_NODE:
  73.             continue
  74.         if child.tagName != 'property':
  75.             continue
  76.         value = child.childNodes[0].data
  77.         properties[child.getAttribute('name')] = value
  78.     return properties
  79.  
  80. def get_property(node, property_name):
  81.     assert node.tagName == 'object'
  82.     properties = get_properties(node)
  83.     return properties.get(property_name)
  84.  
  85. def get_property_node(node, property_name):
  86.     assert node.tagName == 'object'
  87.     properties = {}
  88.     for child in node.childNodes:
  89.         if child.nodeType != Node.ELEMENT_NODE:
  90.             continue
  91.         if child.tagName != 'property':
  92.             continue
  93.         if child.getAttribute('name') == property_name:
  94.             return child
  95.  
  96. def get_signal_nodes(node):
  97.     assert node.tagName == 'object'
  98.     signals = []
  99.     for child in node.childNodes:
  100.         if child.nodeType != Node.ELEMENT_NODE:
  101.             continue
  102.         if child.tagName == 'signal':
  103.             signals.append(child)
  104.     return signals
  105.  
  106. def get_property_nodes(node):
  107.     assert node.tagName == 'object'
  108.     properties = []
  109.     for child in node.childNodes:
  110.         if child.nodeType != Node.ELEMENT_NODE:
  111.             continue
  112.         # FIXME: handle comments
  113.         if child.tagName == 'property':
  114.             properties.append(child)
  115.     return properties
  116.  
  117. def get_accelerator_nodes(node):
  118.     assert node.tagName == 'object'
  119.     accelerators = []
  120.     for child in node.childNodes:
  121.         if child.nodeType != Node.ELEMENT_NODE:
  122.             continue
  123.         if child.tagName == 'accelerator':
  124.             accelerators.append(child)
  125.     return accelerators
  126.  
  127. def get_object_node(child_node):
  128.     assert child_node.tagName == 'child', child_node
  129.     nodes = []
  130.     for node in child_node.childNodes:
  131.         if node.nodeType != Node.ELEMENT_NODE:
  132.             continue
  133.         if node.tagName == 'object':
  134.             nodes.append(node)
  135.     assert len(nodes) == 1, nodes
  136.     return nodes[0]
  137.  
  138. def copy_properties(node, props, prop_dict):
  139.     assert node.tagName == 'object'
  140.     for prop_name in props:
  141.         child = get_property_node(node, prop_name)
  142.         if child is not None:
  143.             prop_dict[prop_name] = child
  144.  
  145.     return node
  146.  
  147. class GtkBuilderConverter(object):
  148.  
  149.     def __init__(self, skip_windows, root):
  150.         self.skip_windows = skip_windows
  151.         self.root = root
  152.         self.root_objects = []
  153.         self.objects = {}
  154.  
  155.     #
  156.     # Public API
  157.     #
  158.  
  159.     def parse_file(self, file):
  160.         self._dom = minidom.parse(file)
  161.         self._parse()
  162.  
  163.     def parse_buffer(self, buffer):
  164.         self._dom = minidom.parseString(buffer)
  165.         self._parse()
  166.  
  167.     def to_xml(self):
  168.         xml = self._dom.toprettyxml("", "")
  169.         return xml.encode('utf-8')
  170.  
  171.     #
  172.     # Private
  173.     #
  174.  
  175.     def _get_object(self, name):
  176.         return self.objects.get(name)
  177.  
  178.     def _get_objects_by_attr(self, attribute, value):
  179.         return [w for w in self._dom.getElementsByTagName("object")
  180.                       if w.getAttribute(attribute) == value]
  181.  
  182.     def _create_object(self, obj_class, obj_id, template=None, properties=None):
  183.         """
  184.         Creates a new <object> tag.
  185.         Optionally a name template can be provided which will be used
  186.         to avoid naming collisions.
  187.         The properties dictionary can either contain string values or Node
  188.         values. If a node is provided the name of the node will be overridden
  189.         by the dictionary key.
  190.  
  191.         @param obj_class: class of the object (class tag)
  192.         @param obj_id: identifier of the object (id tag)
  193.         @param template: name template to use, for example 'button'
  194.         @param properties: dictionary of properties
  195.         @type properties: string or Node.
  196.         @returns: Newly created node of the object
  197.         """
  198.         if template is not None:
  199.             count = 1
  200.             while True:
  201.                 obj_id = template + str(count)
  202.                 widget = self._get_object(obj_id)
  203.                 if widget is None:
  204.                     break
  205.  
  206.                 count += 1
  207.  
  208.         obj = self._dom.createElement('object')
  209.         obj.setAttribute('class', obj_class)
  210.         obj.setAttribute('id', obj_id)
  211.         if properties:
  212.             for name, value in properties.items():
  213.                 if isinstance(value, Node):
  214.                     # Reuse the node, so translatable and context still will be
  215.                     # set when converting nodes. See also #509153
  216.                     prop = value
  217.                 else:
  218.                     prop = self._dom.createElement('property')
  219.                     prop.appendChild(self._dom.createTextNode(value))
  220.  
  221.                 prop.setAttribute('name', str(name))
  222.                 obj.appendChild(prop)
  223.         self.objects[obj_id] = obj
  224.         return obj
  225.  
  226.     def _create_root_object(self, obj_class, template, properties=None):
  227.         obj = self._create_object(obj_class, None, template, properties)
  228.         self.root_objects.append(obj)
  229.         return obj
  230.  
  231.     def _parse(self):
  232.         glade_iface = self._dom.getElementsByTagName("glade-interface")
  233.         assert glade_iface, ("Badly formed XML, there is "
  234.                              "no <glade-interface> tag.")
  235.         # Rename glade-interface to interface
  236.         glade_iface[0].tagName = 'interface'
  237.         self._interface = glade_iface[0]
  238.  
  239.         # Remove glade-interface doc type
  240.         for node in self._dom.childNodes:
  241.             if node.nodeType == Node.DOCUMENT_TYPE_NODE:
  242.                 if node.name == 'glade-interface':
  243.                     self._dom.removeChild(node)
  244.  
  245.         # Strip unsupported tags
  246.         for tag in ['requires', 'requires-version']:
  247.             for child in self._dom.getElementsByTagName(tag):
  248.                 child.parentNode.removeChild(child)
  249.  
  250.         if self.root:
  251.             self._strip_root(self.root)
  252.  
  253.         # Rename widget to object
  254.         objects = self._dom.getElementsByTagName("widget")
  255.         for node in objects:
  256.             node.tagName = "object"
  257.  
  258.         for node in objects:
  259.             self._convert(node.getAttribute("class"), node)
  260.             self.objects[node.getAttribute('id')] = node
  261.  
  262.         # Convert Gazpachos UI tag
  263.         for node in self._dom.getElementsByTagName("ui"):
  264.             self._convert_ui(node)
  265.  
  266.         # Convert accessibility tag
  267.         for node in self._dom.getElementsByTagName("accessibility"):
  268.             self._convert_accessibility(node)
  269.  
  270.         # Output the newly created root objects and sort them
  271.         # by attribute id
  272.         for obj in sorted(self.root_objects,
  273.                           key=lambda n: n.getAttribute('id'),
  274.                           reverse=True):
  275.             self._interface.childNodes.insert(0, obj)
  276.  
  277.     def _convert(self, klass, node):
  278.         if klass == 'GtkNotebook':
  279.             self._packing_prop_to_child_attr(node, "type", "tab")
  280.         elif klass in ['GtkExpander', 'GtkFrame']:
  281.             self._packing_prop_to_child_attr(
  282.                 node, "type", "label_item", "label")
  283.         elif klass == "GtkMenuBar":
  284.             self._convert_menu(node)
  285.         elif klass == "GtkMenu":
  286.             # Only convert toplevel popups
  287.             if node.parentNode == self._interface:
  288.                 self._convert_menu(node, popup=True)
  289.         elif klass in WINDOWS and self.skip_windows:
  290.             self._remove_window(node)
  291.         self._default_widget_converter(node)
  292.  
  293.     def _default_widget_converter(self, node):
  294.         klass = node.getAttribute("class")
  295.         for prop in get_property_nodes(node):
  296.             prop_name = prop.getAttribute("name")
  297.             if prop_name == "sizegroup":
  298.                 self._convert_sizegroup(node, prop)
  299.             elif prop_name == "tooltip" and klass != "GtkAction":
  300.                 prop.setAttribute("name", "tooltip-text")
  301.             elif prop_name in ["response_id", 'response-id']:
  302.                 # It does not make sense to convert responses when
  303.                 # we're not going to output dialogs
  304.                 if self.skip_windows:
  305.                     continue
  306.                 object_id = node.getAttribute('id')
  307.                 response = prop.childNodes[0].data
  308.                 self._convert_dialog_response(node, object_id, response)
  309.                 prop.parentNode.removeChild(prop)
  310.             elif prop_name == "adjustment":
  311.                 self._convert_adjustment(prop)
  312.             elif prop_name == "items" and klass in ['GtkComboBox',
  313.                                                     'GtkComboBoxEntry']:
  314.                 self._convert_combobox_items(node, prop)
  315.             elif prop_name == "text" and klass == 'GtkTextView':
  316.                 self._convert_textview_text(prop)
  317.  
  318.     def _remove_window(self, node):
  319.         object_node = get_object_node(get_child_nodes(node)[0])
  320.         parent = node.parentNode
  321.         parent.removeChild(node)
  322.         parent.appendChild(object_node)
  323.  
  324.     def _convert_menu(self, node, popup=False):
  325.         if node.hasAttribute('constructor'):
  326.             return
  327.  
  328.         uimgr = self._create_root_object('GtkUIManager',
  329.                                          template='uimanager')
  330.  
  331.         if popup:
  332.             name = 'popup'
  333.         else:
  334.             name = 'menubar'
  335.  
  336.         menu = self._dom.createElement(name)
  337.         menu.setAttribute('name', node.getAttribute('id'))
  338.         node.setAttribute('constructor', uimgr.getAttribute('id'))
  339.  
  340.         for child in get_child_nodes(node):
  341.             obj_node = get_object_node(child)
  342.             item = self._convert_menuitem(uimgr, obj_node)
  343.             menu.appendChild(item)
  344.             child.removeChild(obj_node)
  345.             child.parentNode.removeChild(child)
  346.  
  347.         ui = self._dom.createElement('ui')
  348.         uimgr.appendChild(ui)
  349.  
  350.         ui.appendChild(menu)
  351.  
  352.     def _convert_menuitem(self, uimgr, obj_node):
  353.         children = get_child_nodes(obj_node)
  354.         name = 'menuitem'
  355.         if children:
  356.             child_node = children[0]
  357.             menu_node = get_object_node(child_node)
  358.             # Can be GtkImage, which will take care of later.
  359.             if menu_node.getAttribute('class') == 'GtkMenu':
  360.                 name = 'menu'
  361.  
  362.         object_class = obj_node.getAttribute('class')
  363.         if object_class in ['GtkMenuItem',
  364.                             'GtkImageMenuItem',
  365.                             'GtkCheckMenuItem',
  366.                             'GtkRadioMenuItem']:
  367.             menu = self._dom.createElement(name)
  368.         elif object_class == 'GtkSeparatorMenuItem':
  369.             return self._dom.createElement('separator')
  370.         else:
  371.             raise NotImplementedError(object_class)
  372.  
  373.         menu.setAttribute('action', obj_node.getAttribute('id'))
  374.         self._add_action_from_menuitem(uimgr, obj_node)
  375.         if children:
  376.             for child in get_child_nodes(menu_node):
  377.                 obj_node = get_object_node(child)
  378.                 item = self._convert_menuitem(uimgr, obj_node)
  379.                 menu.appendChild(item)
  380.                 child.removeChild(obj_node)
  381.                 child.parentNode.removeChild(child)
  382.         return menu
  383.  
  384.     def _menuitem_to_action(self, node, properties):
  385.         copy_properties(node, ['label', 'tooltip'], properties)
  386.  
  387.     def _togglemenuitem_to_action(self, node, properties):
  388.         self._menuitem_to_action(node, properties)
  389.         copy_properties(node, ['active'], properties)
  390.  
  391.     def _radiomenuitem_to_action(self, node, properties):
  392.         self._togglemenuitem_to_action(node, properties)
  393.         copy_properties(node, ['group'], properties)
  394.  
  395.     def _add_action_from_menuitem(self, uimgr, node):
  396.         properties = {}
  397.         object_class = node.getAttribute('class')
  398.         object_id = node.getAttribute('id')
  399.         if object_class == 'GtkMenuItem':
  400.             name = 'GtkAction'
  401.             self._menuitem_to_action(node, properties)
  402.         elif object_class == 'GtkCheckMenuItem':
  403.             name = 'GtkToggleAction'
  404.             self._togglemenuitem_to_action(node, properties)
  405.         elif object_class == 'GtkRadioMenuItem':
  406.             name = 'GtkRadioAction'
  407.             self._radiomenuitem_to_action(node, properties)
  408.         elif object_class == 'GtkImageMenuItem':
  409.             name = 'GtkAction'
  410.             children = get_child_nodes(node)
  411.             if (children and
  412.                 children[0].getAttribute('internal-child') == 'image'):
  413.                 image = get_object_node(children[0])
  414.                 child = get_property_node(image, 'stock')
  415.                 if child is not None:
  416.                     properties['stock_id'] = child
  417.             self._menuitem_to_action(node, properties)
  418.         elif object_class == 'GtkSeparatorMenuItem':
  419.             return
  420.         else:
  421.             raise NotImplementedError(object_class)
  422.  
  423.         if get_property(node, 'use_stock') == 'True':
  424.             if 'label' in properties:
  425.                 properties['stock_id'] = properties['label']
  426.                 del properties['label']
  427.  
  428.         properties['name'] = object_id
  429.         action = self._create_object(name,
  430.                                      object_id,
  431.                                      properties=properties)
  432.         for signal in get_signal_nodes(node):
  433.             signal_name = signal.getAttribute('name')
  434.             if signal_name in ['activate', 'toggled']:
  435.                 action.appendChild(signal)
  436.             else:
  437.                 print 'Unhandled signal %s::%s' % (node.getAttribute('class'),
  438.                                                    signal_name)
  439.  
  440.         if not uimgr.childNodes:
  441.             child = self._dom.createElement('child')
  442.             uimgr.appendChild(child)
  443.  
  444.             group = self._create_object('GtkActionGroup', None,
  445.                                         template='actiongroup')
  446.             child.appendChild(group)
  447.         else:
  448.             group = uimgr.childNodes[0].childNodes[0]
  449.  
  450.         child = self._dom.createElement('child')
  451.         group.appendChild(child)
  452.         child.appendChild(action)
  453.  
  454.         for accelerator in get_accelerator_nodes(node):
  455.             signal_name = accelerator.getAttribute('signal')
  456.             if signal_name != 'activate':
  457.                 print 'Unhandled accelerator signal for %s::%s' % (
  458.                     node.getAttribute('class'), signal_name)
  459.                 continue
  460.             accelerator.removeAttribute('signal')
  461.             child.appendChild(accelerator)
  462.  
  463.     def _convert_sizegroup(self, node, prop):
  464.         # This is Gazpacho only
  465.         node.removeChild(prop)
  466.         obj = self._get_object(prop.childNodes[0].data)
  467.         if obj is None:
  468.             widgets = self._get_objects_by_attr("class", "GtkSizeGroup")
  469.             if widgets:
  470.                 obj = widgets[-1]
  471.             else:
  472.                 obj = self._create_root_object('GtkSizeGroup',
  473.                                                template='sizegroup')
  474.  
  475.         widgets = obj.getElementsByTagName("widgets")
  476.         if widgets:
  477.             assert len(widgets) == 1
  478.             widgets = widgets[0]
  479.         else:
  480.             widgets = self._dom.createElement("widgets")
  481.             obj.appendChild(widgets)
  482.  
  483.         member = self._dom.createElement("widget")
  484.         member.setAttribute("name", node.getAttribute("id"))
  485.         widgets.appendChild(member)
  486.  
  487.     def _convert_dialog_response(self, node, object_name, response):
  488.         # 1) Get parent dialog node
  489.         while True:
  490.             # If we can't find the parent dialog, give up
  491.             if node == self._dom:
  492.                 return
  493.  
  494.             if (node.tagName == 'object' and
  495.                 node.getAttribute('class') == 'GtkDialog'):
  496.                 dialog = node
  497.                 break
  498.             node = node.parentNode
  499.             assert node
  500.  
  501.         # 2) Get dialogs action-widgets tag, create if not found
  502.         for child in dialog.childNodes:
  503.             if child.nodeType != Node.ELEMENT_NODE:
  504.                 continue
  505.             if child.tagName == 'action-widgets':
  506.                 actions = child
  507.                 break
  508.         else:
  509.             actions = self._dom.createElement("action-widgets")
  510.             dialog.appendChild(actions)
  511.  
  512.         # 3) Add action-widget tag for the response
  513.         action = self._dom.createElement("action-widget")
  514.         action.setAttribute("response", response)
  515.         action.appendChild(self._dom.createTextNode(object_name))
  516.         actions.appendChild(action)
  517.  
  518.     def _convert_adjustment(self, prop):
  519.         properties = {}
  520.         if prop.childNodes:
  521.             data = prop.childNodes[0].data
  522.             value, lower, upper, step, page, page_size = data.split(' ')
  523.             properties.update(value=value,
  524.                               lower=lower,
  525.                               upper=upper,
  526.                               step_increment=step,
  527.                               page_increment=page,
  528.                               page_size=page_size)
  529.         else:
  530.             prop.appendChild(self._dom.createTextNode(""))
  531.  
  532.         adj = self._create_root_object("GtkAdjustment",
  533.                                        template='adjustment',
  534.                                        properties=properties)
  535.         prop.childNodes[0].data = adj.getAttribute('id')
  536.  
  537.     def _convert_combobox_items(self, node, prop):
  538.         parent = prop.parentNode
  539.         if not prop.childNodes:
  540.             parent.removeChild(prop)
  541.             return
  542.         value = prop.childNodes[0].data
  543.         model = self._create_root_object("GtkListStore",
  544.                                          template="model")
  545.  
  546.         columns = self._dom.createElement('columns')
  547.         model.appendChild(columns)
  548.  
  549.         column = self._dom.createElement('column')
  550.         column.setAttribute('type', 'gchararray')
  551.         columns.appendChild(column)
  552.  
  553.         data = self._dom.createElement('data')
  554.         model.appendChild(data)
  555.  
  556.         for item in value.split('\n'):
  557.             row = self._dom.createElement('row')
  558.             data.appendChild(row)
  559.  
  560.             col = self._dom.createElement('col')
  561.             col.setAttribute('id', '0')
  562.             col.appendChild(self._dom.createTextNode(item))
  563.             row.appendChild(col)
  564.  
  565.         model_prop = self._dom.createElement('property')
  566.         model_prop.setAttribute('name', 'model')
  567.         model_prop.appendChild(
  568.             self._dom.createTextNode(model.getAttribute('id')))
  569.         parent.appendChild(model_prop)
  570.  
  571.         parent.removeChild(prop)
  572.  
  573.         child = self._dom.createElement('child')
  574.         node.appendChild(child)
  575.         cell_renderer = self._create_object('GtkCellRendererText', None,
  576.                                             template='renderer')
  577.         child.appendChild(cell_renderer)
  578.  
  579.         attributes = self._dom.createElement('attributes')
  580.         child.appendChild(attributes)
  581.  
  582.         attribute = self._dom.createElement('attribute')
  583.         attributes.appendChild(attribute)
  584.         attribute.setAttribute('name', 'text')
  585.         attribute.appendChild(self._dom.createTextNode('0'))
  586.  
  587.     def _convert_textview_text(self, prop):
  588.         if not prop.childNodes:
  589.             prop.parentNode.removeChild(prop)
  590.             return
  591.  
  592.         data = prop.childNodes[0].data
  593.         if prop.hasAttribute('translatable'):
  594.             prop.removeAttribute('translatable')
  595.         tbuffer = self._create_root_object("GtkTextBuffer",
  596.                                            template='textbuffer',
  597.                                            properties=dict(text=data))
  598.         prop.childNodes[0].data = tbuffer.getAttribute('id')
  599.         prop.setAttribute('name', 'buffer')
  600.  
  601.     def _packing_prop_to_child_attr(self, node, prop_name, prop_val,
  602.                                    attr_val=None):
  603.         for child in get_child_nodes(node):
  604.             packing_props = [p for p in child.childNodes if p.nodeName == "packing"]
  605.             if not packing_props:
  606.                 continue
  607.             assert len(packing_props) == 1
  608.             packing_prop = packing_props[0]
  609.             properties = packing_prop.getElementsByTagName("property")
  610.             for prop in properties:
  611.                 if (prop.getAttribute("name") != prop_name or
  612.                     prop.childNodes[0].data != prop_val):
  613.                     continue
  614.                 packing_prop.removeChild(prop)
  615.                 child.setAttribute(prop_name, attr_val or prop_val)
  616.             if len(properties) == 1:
  617.                 child.removeChild(packing_prop)
  618.  
  619.     def _convert_ui(self, node):
  620.         cdata = node.childNodes[0]
  621.         data = cdata.toxml().strip()
  622.         if not data.startswith("<![CDATA[") or not data.endswith("]]>"):
  623.             return
  624.         data = data[9:-3]
  625.         child = minidom.parseString(data).childNodes[0]
  626.         nodes = child.childNodes[:]
  627.         for child_node in nodes:
  628.             node.appendChild(child_node)
  629.         node.removeChild(cdata)
  630.         if not node.hasAttribute("id"):
  631.             return
  632.  
  633.         # Updating references made by widgets
  634.         parent_id = node.parentNode.getAttribute("id")
  635.         for widget in self._get_objects_by_attr("constructor",
  636.                                                 node.getAttribute("id")):
  637.             widget.getAttributeNode("constructor").value = parent_id
  638.         node.removeAttribute("id")
  639.  
  640.     def _convert_accessibility(self, node):
  641.         objectNode = node.parentNode
  642.         parent_id = objectNode.getAttribute("id")
  643.  
  644.         properties = {}
  645.         for node in node.childNodes:
  646.             if node.nodeName == 'atkproperty':
  647.                 node.tagName = 'property'
  648.                 properties[node.getAttribute('name')] = node
  649.                 node.parentNode.removeChild(node)
  650.             elif node.nodeName == 'atkrelation':
  651.                 node.tagName = 'relation'
  652.                 relation_type = node.getAttribute('type')
  653.                 relation_type = relation_type.replace('_', '-')
  654.                 node.setAttribute('type', relation_type)
  655.             elif node.nodeName == 'atkaction':
  656.                 node.tagName = 'action'
  657.  
  658.         if properties:
  659.             child = self._dom.createElement('child')
  660.             child.setAttribute("internal-child", "accessible")
  661.  
  662.             atkobject = self._create_object(
  663.                 "AtkObject", None,
  664.                 template='a11y-%s' % (parent_id,),
  665.                 properties=properties)
  666.             child.appendChild(atkobject)
  667.             objectNode.appendChild(child)
  668.  
  669.     def _strip_root(self, root_name):
  670.         for widget in self._dom.getElementsByTagName("widget"):
  671.             if widget.getAttribute('id') == root_name:
  672.                 break
  673.         else:
  674.             raise SystemExit("Could not find an object called `%s'" % (
  675.                 root_name))
  676.  
  677.         for child in self._interface.childNodes[:]:
  678.             if child.nodeType != Node.ELEMENT_NODE:
  679.                 continue
  680.             child.parentNode.removeChild(child)
  681.  
  682.         self._interface.appendChild(widget)
  683.  
  684.  
  685. def _indent(output):
  686.     if not subprocess:
  687.         return output
  688.  
  689.     for directory in os.environ['PATH'].split(os.pathsep):
  690.         filename = os.path.join(directory, 'xmllint')
  691.         if os.path.exists(filename):
  692.             break
  693.     else:
  694.         return output
  695.  
  696.     s = subprocess.Popen([filename, '--format', '-'],
  697.                          stdin=subprocess.PIPE,
  698.                          stdout=subprocess.PIPE)
  699.     s.stdin.write(output)
  700.     s.stdin.close()
  701.     return s.stdout.read()
  702.  
  703. def usage():
  704.     print __doc__
  705.  
  706. def main(args):
  707.     try:
  708.         opts, args = getopt.getopt(args[1:], "hwr:",
  709.                                    ["help", "skip-windows", "root="])
  710.     except getopt.GetoptError:
  711.         usage()
  712.         return 2
  713.  
  714.     if len(args) != 2:
  715.         usage()
  716.         return 2
  717.  
  718.     input_filename, output_filename = args
  719.  
  720.     skip_windows = False
  721.     split = False
  722.     root = None
  723.     for o, a in opts:
  724.         if o in ("-h", "--help"):
  725.             usage()
  726.             sys.exit()
  727.         elif o in ("-r", "--root"):
  728.             root = a
  729.         elif o in ("-w", "--skip-windows"):
  730.             skip_windows = True
  731.  
  732.     conv = GtkBuilderConverter(skip_windows=skip_windows,
  733.                                root=root)
  734.     conv.parse_file(input_filename)
  735.  
  736.     xml = _indent(conv.to_xml())
  737.     if output_filename == "-":
  738.         print xml
  739.     else:
  740.         open(output_filename, 'w').write(xml)
  741.         print "Wrote", output_filename
  742.  
  743.     return 0
  744.  
  745. if __name__ == "__main__":
  746.     sys.exit(main(sys.argv))
  747.