#!/usr/bin/env python

# yes, this requires python 2.x and an XML parser module (eg. PyExpat)

import sys
import string
import xml.dom.minidom


# --- Code to represent a widget in memory, and dump it to stdout ---

class WidgetDef:
    def __init__(self):
        self.wclass = None
        self.name = None
        self.property_names = [ ]
        self.properties = { }
        self.signals = []
        self.accels = []
        self.children = []

    def __getitem__(self, name):
        if name == 'name':
            return self.name
        if name == 'class':
            return self.wclass
        return self.properties[name]
    def __setitem__(self, name, value):
        if name == 'name':
            self.name = value
            return
        if name == 'class':
            self.wclass = value
            return
        if self.properties.has_key(name):
            self.property_names.remove(name)
            del self.properties[name]
        if value == 'True': value = 'yes'
        if value == 'False': value = 'no'
        self.property_names.append(name)
        self.properties[name] = value
    def __delitem__(self, name):
        if self.properties.has_key(name):
            self.property_names.remove(name)
            del self.properties[name]
        else:
            raise KeyError, "unknown property `%s'" % name
    def has_prop(self, name):
        return self.properties.has_key(name)
    def rename_prop(self, old_name, new_name):
        if self.has_prop(old_name):
            self[new_name] = self[old_name]
            del self[old_name]
    def remove_prop(self, name):
        if self.properties.has_key(name):
            self.property_names.remove(name)
            del self.properties[name]

    def add_signal(self, name, handler, object=None, after=0):
        self.signals.append((name, handler, object, after))
    def add_accel(self, key, modifiers, signal):
        self.accels.append((key, modifiers, signal))

    class ChildDef:
        def __init__(self, widget, internal_child=None):
            self.internal_child = None
            self.property_names = []
            self.properties = {}
            self.widget = widget
        def __getitem__(self, name):
            return self.properties[name]
        def __setitem__(self, name, value):
            if self.properties.has_key(name):
                self.property_names.remove(name)
                del self.properties[name]
            if value == 'True': value = 'yes'
            if value == 'False': value = 'no'
            self.property_names.append(name)
            self.properties[name] = value
        def __delitem__(self, name):
            if self.properties.has_key(name):
                self.property_names.remove(name)
                del self.properties[name]
            else:
                raise KeyError, "unknown property `%s'" % name
        def has_prop(self, name):
            return self.properties.has_key(name)
        def rename_prop(self, old_name, new_name):
            if self.has_prop(old_name):
                self[new_name] = self[old_name]
                del self[old_name]
        def remove_prop(self, name):
            if self.properties.has_key(name):
                self.property_names.remove(name)
                del self.properties[name]
        def dump(self, indent):
            if self.internal_child:
                print '%s<child internal-child="%s">' % \
                      (indent, self.internal_child)
            else:
                print '%s<child>' % indent
            self.widget.dump(indent + '  ')
            if self.properties:
                print '%s  <packing>' % indent
                for name in self.property_names:
                    print '%s    <property name="%s">%s</property>' % \
                          (indent, name, self.properties[name])
                print '%s  </packing>' % indent
            print '%s</child>' % indent

    def add_child(self, widget, internal_child=None):
        child = self.ChildDef(widget, internal_child)
        self.children.append(child)
        return child

    def dump(self, indent):
        print '%s<widget class="%s" id="%s">' %(indent, self.wclass, self.name)
        want_newline = 0
        for name in self.property_names:
            print '%s  <property name="%s">%s</property>' % \
                  (indent, name, self.properties[name])
            want_newline = 1
        if want_newline and (self.signals or self.accels or self.children):
            print

        want_newline = 0
        for name, handler, object, after in self.signals:
            print '%s  <signal name="%s" handler="%s"' % (indent, name,
                                                          handler),
            if object: print 'object="%s"' % object,
            if after: print 'after="yes"',
            print '/>'
            want_newline = 1
        if want_newline and (self.accels or self.children): print

        want_newline = 0
        for key, modifiers, signal in self.accels:
            print '%s  <accelerator key="%s" modifiers="%s" signal="%s" />' % \
                  (indent, key, modifiers, signal)
            want_newline = 1
        if want_newline and self.children: print

        want_newline = 0
        for child in self.children:
            if want_newline: print
            child.dump(indent + '  ')
            want_newline = 1
        print '%s</widget>' % indent


# --- Code to parse the glade1 XML files into WidgetDef instances ---

def totext(nodelist):
    return string.join(map(lambda node: node.toxml(), nodelist), '')

def handle_signal(widget, signalnode):
    name = None
    handler = None
    object = None
    after = 0
    for node in signalnode.childNodes:
	if node.nodeType != node.ELEMENT_NODE:
	    continue
	if node.nodeName == 'name':
	    name = totext(node.childNodes)
	elif node.nodeName == 'handler':
	    handler = totext(node.childNodes)
	elif node.nodeName == 'object':
	    object = totext(node.childNodes)
	elif node.nodeName == 'after':
	    after = (totext(node.childNodes) == 'True')
    widget.add_signal(name, handler, object, after)

def handle_accel(widget, accelnode):
    key = None
    modifiers = None
    signal = None
    for node in accelnode.childNodes:
	if node.nodeType != node.ELEMENT_NODE:
	    continue
	if node.nodeName == 'key':
	    key = totext(node.childNodes)
	    if key[:4] == 'GDK_': key = key[4:]
	elif node.nodeName == 'modifiers':
	    modifiers = totext(node.childNodes)
	elif node.nodeName == 'signal':
	    signal = totext(node.childNodes)
    widget.add_accel(key, modifiers, signal)

def get_child_props(childdef, widgetnode):
    for node in widgetnode.childNodes:
	if node.nodeType != node.ELEMENT_NODE:
	    continue
	if node.nodeName == 'child':
	    child = node
	    break
    else:
	return []
    for node in child.childNodes:
	if node.nodeType != node.ELEMENT_NODE:
	    continue
        childdef[node.nodeName] = totext(node.childNodes)

def handle_widget(widgetnode):
    widget = WidgetDef()
    properties = []
    signals = []
    accels = []
    children = []
    for node in widgetnode.childNodes:
	if node.nodeType != node.ELEMENT_NODE:
	    continue
	if node.nodeName == 'widget':
            child_widget = handle_widget(node)
            childdef = widget.add_child(child_widget)
            get_child_props(childdef, node)
	elif node.nodeName in ('signal', 'Signal'):
	    handle_signal(widget, node)
	elif node.nodeName in ('accelerator', 'Accelerator'):
	    handle_accel(widget, node)
	elif node.nodeName == 'child':
	    pass # handled by the parent widget
	else:
            widget[node.nodeName] = totext(node.childNodes)
    return widget


# --- Code to do widget type specific cleanups ---

name_counter = 0
def make_name():
    global name_counter
    name_counter += 1
    return 'convertwidget' + str(name_counter)

# properties to change on all widgets
global_obsolete_props = [ ]
global_renamed_props = [
    ('width', 'width-request'),
    ('height', 'height-request'),
]

# properties to change on specific widgets
obsolete_props = {
    'GtkWindow': [ 'auto_shrink' ],
    'GtkDialog': [ 'auto_shrink' ],
    'GtkMenuItem': [ 'right_justify' ],
    'GtkGammaCurve': [ 'curve_type', 'min_x', 'max_x', 'min_y', 'max_y' ],
    'GtkHPaned': [ 'handle_size', 'gutter_size' ],
    'GtkVPaned': [ 'handle_size', 'gutter_size' ],
    'GtkHScale': [ 'policy' ],
    'GtkVScale': [ 'policy' ],
    'GtkHScrollbar': [ 'policy' ],
    'GtkVScrollbar': [ 'policy' ],
    'GtkHRuler': [ 'metric' ],
    'GtkVRuler': [ 'metric' ],
}
renamed_props = {
    'GtkWindow': [ ('position', 'window-position') ],
    'GtkDialog': [ ('position', 'window-position') ],
    'GtkEntry': [ ('text_max_length', 'max-length'),
                  ('text_visible', 'visibility') ],
    'GtkFrame': [ ('shadow_type', 'shadow') ],
    'GtkHandleBox': [ ('shadow_type', 'shadow') ],
    'GtkNotebook': [ ('popup_enable', 'enable-popup') ],
    'GtkRange': [ ('policy', 'update-policy') ],
    'GtkTable': [ ('rows', 'n-rows'), ('columns', 'n-columns') ],
}

# child properties to change on specific widgets
obsolete_child_props = {
}
renamed_child_props = {
    'GtkTable' : [ ('xpad', 'x_padding'), ('ypad', 'y_padding') ],
}

def collect_adjustment(widgetdef, prop_prefix, new_name):
    value, lower, upper, step, page, page_size = (0, 0, 100, 1, 10, 10)
    adj_set = 0
    if widgetdef.has_prop(prop_prefix + 'value'):
        value = widgetdef[prop_prefix + 'value']
        del widgetdef[prop_prefix + 'value']
        adj_set = 1
    if widgetdef.has_prop(prop_prefix + 'lower'):
        lower = widgetdef[prop_prefix + 'lower']
        del widgetdef[prop_prefix + 'lower']
        adj_set = 1
    if widgetdef.has_prop(prop_prefix + 'upper'):
        upper = widgetdef[prop_prefix + 'upper']
        del widgetdef[prop_prefix + 'upper']
        adj_set = 1
    if widgetdef.has_prop(prop_prefix + 'step'):
        step = widgetdef[prop_prefix + 'step']
        del widgetdef[prop_prefix + 'step']
        adj_set = 1
    if widgetdef.has_prop(prop_prefix + 'page'):
        page = widgetdef[prop_prefix + 'page']
        del widgetdef[prop_prefix + 'page']
        adj_set = 1
    if widgetdef.has_prop(prop_prefix + 'page_size'):
        page_size = widgetdef[prop_prefix + 'page_size']
        del widgetdef[prop_prefix + 'page_size']
        adj_set = 1
    if adj_set:
        widgetdef[new_name] = '%s %s %s %s %s %s' % (value, lower, upper,
                                                     step, page, page_size)

def fixup_widget(widget):
    do_children = 1

    # table based property removals/renames
    for name in global_obsolete_props:
        widget.remove_prop(name)
    for old, new in global_renamed_props:
        widget.rename_prop(old, new)
    if obsolete_props.has_key(widget['class']):
        for name in obsolete_props[widget['class']]:
            widget.remove_prop(name)
    if renamed_props.has_key(widget['class']):
        for old, new in renamed_props[widget['class']]:
            widget.rename_prop(old, new)
    if obsolete_child_props.has_key(widget['class']):
        for name in obsolete_child_props[widget['class']]:
            for childdef in widget.children:
                childdef.remove_prop(name)
    if renamed_child_props.has_key(widget['class']):
        for old, new in renamed_child_props[widget['class']]:
            for childdef in widget.children:
                childdef.rename_prop(old, new)

    # fix up child packing properties for tables
    if widget['class'] == 'GtkTable':
        for childdef in widget.children:
            options = []
            if childdef.has_prop('xexpand'):
                if childdef['xexpand'] == 'yes':
                    options.append('expand')
                del childdef['xexpand']
            if childdef.has_prop('xshrink'):
                if childdef['xshrink'] == 'yes':
                    options.append('shrink')
                del childdef['xshrink']
            if childdef.has_prop('xfill'):
                if childdef['xfill'] == 'yes':
                    options.append('fill')
                del childdef['xfill']
            if options:
                childdef['x_options'] = string.join(options,'|')
            
            options = []
            if childdef.has_prop('yexpand'):
                if childdef['yexpand'] == 'yes':
                    options.append('expand')
                del childdef['yexpand']
            if childdef.has_prop('yshrink'):
                if childdef['yshrink'] == 'yes':
                    options.append('shrink')
                del childdef['yshrink']
            if childdef.has_prop('yfill'):
                if childdef['yfill'] == 'yes':
                    options.append('fill')
                del childdef['yfill']
            if options:
                childdef['y_options'] = string.join(options,'|')

    # fix up adjustment properties
    if widget['class'] in ('GtkHScale', 'GtkHScrollbar',
                           'GtkVScale', 'GtkVScrollbar',
                           'GtkSpinButton'):
        collect_adjustment(widget, 'h', 'adjustment') # compat
        collect_adjustment(widget, 'v', 'adjustment') # compat
        collect_adjustment(widget, '', 'adjustment')
    if widget['class'] in ('GtkViewport', 'GtkLayout', 'GtkScrolledWindow'):
        collect_adjustment(widget, 'h', 'hadjustment')
        collect_adjustment(widget, 'v', 'vadjustment')

    # add label children to menu items.
    if widget['class'] in ('GtkMenuItem', 'GtkCheckMenuItem',
                           'GtkRadioMenuItem'):
        if widget.has_prop('label'):
            label = WidgetDef()
            label['class'] = 'GtkAccelLabel'
            label['name'] = make_name()
            label['accel-object'] = widget['name']
            label['xalign'] = '0.0'
            label['label'] = widget['label']
            label['use-underline'] = 'yes'
            widget.add_child(label)
            del widget['label']

    if widget['class'] == 'GtkPixmapMenuItem':
        widget['class'] = 'GtkImageMenuItem'

    if widget['class'] == 'GtkDialog':
        childdef = widget.children[0]
        childdef.internal_child = 'vbox'
        del childdef.widget['child_name']

        childdef = filter(lambda x: x.widget.has_prop('child_name'),
                          childdef.widget.children)[0]
        childdef.widget['class'] = 'GtkHButtonBox'
        childdef.internal_child = 'action_area'
        del childdef.widget['child_name']

    # add the visible property if missing:
    if not widget.has_prop('visible'):
        widget['visible'] = 'yes'

    if do_children:
        for childdef in widget.children:
            fixup_widget(childdef.widget)


# --- parse the file for widget definitions, fixup problems and dump.

def handle_file(filename):
    document = xml.dom.minidom.parse(filename)
    
    widgets = []
    for node in document.documentElement.childNodes:
	if node.nodeType == node.ELEMENT_NODE and \
	   node.nodeName == 'widget':
            widgets.append(handle_widget(node))

    print '<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->'
    print '<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd" >'
    print
    print '<glade-interface>'
    indent = '  '

    for widgetdef in widgets:
        fixup_widget(widgetdef)
        widgetdef.dump(indent)

    print '</glade-interface>'
    document.unlink() # only needed for python interpreters without cyclic gc

if __name__ == '__main__':
    if len(sys.argv) != 2:
	print >> sys.stderr, 'usage: libglade-convert oldfile.glade'
	sys.exit(1)
    handle_file(sys.argv[1])
