You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

773 lines
27 KiB

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