"Fossies" - the Fresh Open Source Software Archive

Member "pysize-0.2/pysize/ui/gtk/gazpacho_loader/loader.py" (11 Mar 2007, 34816 Bytes) of package /linux/privat/old/pysize-0.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 # Copyright (C) 2005 Johan Dahlin
    2 #
    3 # This program is free software; you can redistribute it and/or
    4 # modify it under the terms of the GNU Lesser General Public License
    5 # as published by the Free Software Foundation; either version 2
    6 # of the License, or (at your option) any later version.
    7 #
    8 # This program is distributed in the hope that it will be useful,
    9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 # GNU General Public License for more details.
   12 #
   13 # You should have received a copy of the GNU Lesser General Public License
   14 # along with this program; if not, write to the Free Software
   15 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   16 
   17 # TODO:
   18 # Parser tags: atk, relation
   19 # Document public API
   20 # Parser subclass
   21 # Improved unittest coverage
   22 # Old style toolbars
   23 # Require importer/resolver (gazpacho itself)
   24 # GBoxed properties
   25 
   26 import os
   27 from gettext import textdomain, dgettext
   28 from xml.parsers import expat
   29 
   30 import gobject
   31 import gtk
   32 from gtk import gdk
   33 
   34 from pysize.ui.gtk.gazpacho_loader.custom import adapter_registry, \
   35      flagsfromstring, str2bool
   36 
   37 __all__ = ['ObjectBuilder', 'ParseError']
   38 
   39 def open_file(filename):
   40     if filename.endswith('.gz'):
   41         import gzip
   42         return gzip.GzipFile(filename, mode='rb')
   43     if filename.endswith('.bz2'):
   44         import bz2
   45         return bz2.BZ2File(filename, mode='rU')
   46     return open(filename, mode='r')
   47 
   48 class ParseError(Exception):
   49     pass
   50 
   51 class Stack(list):
   52     push = list.append
   53     def peek(self):
   54         if self:
   55             return self[-1]
   56 
   57 class BaseInfo:
   58     def __init__(self):
   59         self.data = ''
   60 
   61     def __repr__(self):
   62         return '<%s data=%r>' % (self.__class__.__name__,
   63                                  self.data)
   64 
   65 class WidgetInfo(BaseInfo):
   66     def __init__(self, attrs, parent):
   67         BaseInfo.__init__(self)
   68         self.klass = str(attrs.get('class'))
   69         self.id = str(attrs.get('id'))
   70         self.constructor  = attrs.get('constructor')
   71         self.children = []
   72         self.properties = []
   73         self.attributes = {} # these are used for layout (e.g. cell renderers)
   74         self.signals = []
   75         self.uis = []
   76         self.accelerators = []
   77         self.parent = parent
   78         self.gobj = None
   79         # If it's a placeholder, used by code for unsupported widgets
   80         self.placeholder = False
   81         # these attributes are useful for models:
   82         self.columns = []
   83         self.rows = []
   84 
   85     def is_internal_child(self):
   86         if self.parent and self.parent.internal_child:
   87             return True
   88         return False
   89 
   90     def __repr__(self):
   91         return '<WidgetInfo %s>' % (self.id)
   92 
   93 class ChildInfo(BaseInfo):
   94     def __init__(self, attrs, parent):
   95         BaseInfo.__init__(self)
   96         self.type = attrs.get('type')
   97         self.internal_child = attrs.get('internal-child')
   98         self.properties = []
   99         self.packing_properties = []
  100         self.attributes = {}
  101         self.placeholder = False
  102         self.parent = parent
  103         self.widget = None
  104 
  105     def __repr__(self):
  106         return '<ChildInfo containing a %s>' % (self.widget)
  107 
  108 class PropertyInfo(BaseInfo):
  109     def __init__(self, attrs):
  110         BaseInfo.__init__(self)
  111         self.name = str(attrs.get('name'))
  112         self.translatable = str2bool(attrs.get('translatable', 'no'))
  113         self.context = str2bool(attrs.get('context', 'no'))
  114         self.agent = attrs.get('agent') # libglade
  115         self.comments = attrs.get('comments')
  116 
  117     def __repr__(self):
  118         return '<PropertyInfo of type %s=%r>' % (self.name, self.data)
  119 
  120 class SignalInfo(BaseInfo):
  121     def __init__(self, attrs):
  122         BaseInfo.__init__(self)
  123         self.name = attrs.get('name')
  124         self.handler = attrs.get('handler')
  125         self.after = str2bool(attrs.get('after', 'no'))
  126         self.object = attrs.get('object')
  127         self.last_modification_time = attrs.get('last_modification_time')
  128         self.gobj = None
  129 
  130 class AcceleratorInfo(BaseInfo):
  131     def __init__(self, attrs):
  132         BaseInfo.__init__(self)
  133         self.key = gdk.keyval_from_name(attrs.get('key'))
  134         self.modifiers = flagsfromstring(attrs.get('modifiers'),
  135                                          flags=gdk.ModifierType)
  136         self.signal = str(attrs.get('signal'))
  137 
  138 class UIInfo(BaseInfo):
  139     def __init__(self, attrs):
  140         BaseInfo.__init__(self)
  141         self.id = attrs.get('id')
  142         self.filename = attrs.get('filename')
  143         self.merge = str2bool(attrs.get('merge', 'yes'))
  144 
  145 class SizeGroupInfo(BaseInfo):
  146     def __init__(self, name):
  147         BaseInfo.__init__(self)
  148         self.name = name
  149         self.widgets = []
  150 
  151 class AttributeInfo(BaseInfo):
  152     def __init__(self, attrs):
  153         BaseInfo.__init__(self)
  154         self.name = str(attrs.get('name'))
  155 
  156 class ColumnsInfo(BaseInfo):
  157     def __init__(self, attrs):
  158         BaseInfo.__init__(self)
  159         self.columns = []
  160 
  161 class ColumnInfo(BaseInfo):
  162     def __init__(self, attrs):
  163         BaseInfo.__init__(self)
  164         type_name = str(attrs.get('type'))
  165         try:
  166             self.type = gobject.type_from_name(type_name)
  167         except RuntimeError:
  168             raise ParseError('the type %s is not a valid GType' % type_name)
  169 
  170 class RowInfo(BaseInfo):
  171     def __init__(self, attrs):
  172         BaseInfo.__init__(self)
  173         self.cols = []
  174 
  175 class ColInfo(BaseInfo):
  176     def __init__(self, attrs):
  177         BaseInfo.__init__(self)
  178         try:
  179             self.id = int(attrs.get('id'))
  180         except ValueError:
  181             raise ParseError('id attribute must be integer')
  182 
  183 class ExpatParser(object):
  184     def __init__(self, builder):
  185         self._builder = builder
  186         self.requires = []
  187         self._stack = Stack()
  188         self._state_stack = Stack()
  189         self._ui_state = False
  190         self._widgets_state = False
  191         self._sizegroups = []
  192         self._parser = expat.ParserCreate()
  193         self._parser.buffer_text = True
  194         self._parser.StartElementHandler = self._handle_startelement
  195         self._parser.EndElementHandler = self._handle_endelement
  196         self._parser.CharacterDataHandler = self._handle_characterdata
  197 
  198     # Public API
  199     def parse_file(self, filename):
  200         fp = open_file(filename)
  201         try:
  202             self._parser.ParseFile(fp)
  203         except expat.ExpatError, e:
  204             raise ParseError(e)
  205 
  206     def parse_stream(self, buffer):
  207         try:
  208             self._parser.Parse(buffer)
  209         except expat.ExpatError, e:
  210             raise ParseError(e)
  211 
  212     def _build_ui(self, name, attrs):
  213         extra = ''
  214         for attr, value in attrs.items():
  215             extra += " %s=\"%s\"" % (attr, value)
  216 
  217         ui = self._stack.peek()
  218         ui.data += "<%s%s>" % (name, extra)
  219 
  220     # Expat callbacks
  221     def _handle_startelement(self, name, attrs):
  222         if self._ui_state:
  223             self._build_ui(name, attrs)
  224             return
  225         self._state_stack.push(name)
  226         name = name.replace('-', '_')
  227         func = getattr(self, '_start_%s' % name, None)
  228         if func:
  229             item = func(attrs)
  230             self._stack.push(item)
  231 
  232     def _handle_endelement(self, name):
  233         if self._ui_state:
  234             ui = self._stack.peek()
  235             if name != "ui":
  236                 ui.data += "</%s>" % name
  237                 return
  238             elif not ui.data.startswith("<ui>"):
  239                 ui.data = "<ui>%s</ui>" % ui.data
  240 
  241         self._state_stack.pop()
  242         name = name.replace('-', '_')
  243         func = getattr(self, '_end_%s' % name, None)
  244         if func:
  245             item = self._stack.pop()
  246             func(item)
  247 
  248     def _handle_characterdata(self, data):
  249         info = self._stack.peek()
  250         if info:
  251             info.data += str(data)
  252 
  253     # Tags
  254     def _start_glade_interface(self, attrs):
  255         # libglade extension, add a domain argument to the interface
  256         if 'domain' in attrs:
  257             self._builder._domain = str(attrs['domain'])
  258         self._builder._version = "libglade"
  259 
  260     def _end_glade_interface(self, obj):
  261         pass
  262 
  263     def _start_interface(self, attrs):
  264         # libglade extension, add a domain argument to the interface
  265         if 'domain' in attrs:
  266             self._builder._domain = str(attrs['domain'])
  267         self._builder._version = "gtkbuilder"
  268 
  269     def _end_interface(self, attrs):
  270         pass
  271 
  272     def _start_requires(self, attrs):
  273         self.requires.append(attrs)
  274 
  275     def _end_requires(self, obj):
  276         pass
  277 
  278     def _start_signal(self, attrs):
  279         if not 'name' in attrs:
  280             raise ParseError("<signal> needs a name attribute")
  281         if not 'handler' in attrs:
  282             raise ParseError("<signal> needs a handler attribute")
  283         return SignalInfo(attrs)
  284 
  285     def _end_signal(self, signal):
  286         obj = self._stack.peek()
  287         obj.signals.append(signal)
  288 
  289     def _start__common(self, attrs):
  290         if not 'class' in attrs:
  291             raise ParseError("<widget> needs a class attribute")
  292         if not 'id' in attrs:
  293             raise ParseError("<widget> needs an id attribute")
  294 
  295         return WidgetInfo(attrs, self._stack.peek())
  296 
  297     def _start_widget(self, attrs):
  298         if self._widgets_state:
  299             obj = self._stack.peek()
  300             obj.widgets.append(str(attrs.get('name')))
  301             return
  302         else:
  303             if self._builder._version == 'gtkbuilder':
  304                 raise ParseError("Unknown tag: widget")
  305         return self._start__common(attrs)
  306 
  307     def _start_object(self, attrs):
  308         if self._builder._version == 'libglade':
  309             raise ParseError("Unknown tag: object")
  310         return self._start__common(attrs)
  311 
  312     def _end__common(self, obj):
  313         obj.parent = self._stack.peek()
  314 
  315         if not obj.gobj:
  316             obj.gobj = self._builder._build_phase1(obj)
  317 
  318         self._builder._build_phase2(obj)
  319 
  320         if obj.parent:
  321             obj.parent.widget = obj.gobj
  322 
  323     def _end_widget(self, attrs):
  324         if self._widgets_state:
  325             return
  326         return self._end__common(attrs)
  327 
  328     def _end_object(self, attrs):
  329         return self._end__common(attrs)
  330 
  331     def _start_child(self, attrs):
  332         obj = self._stack.peek()
  333         if not obj.gobj:
  334             obj.gobj = self._builder._build_phase1(obj)
  335 
  336         return ChildInfo(attrs, parent=obj)
  337 
  338     def _end_child(self, child):
  339         obj = self._stack.peek()
  340         obj.children.append(child)
  341 
  342     def _start_property(self, attrs):
  343         if not 'name' in attrs:
  344             raise ParseError("<property> needs a name attribute")
  345         return PropertyInfo(attrs)
  346 
  347     def _end_property(self, prop):
  348         if prop.agent and prop.agent not in ('libglade', 'gazpacho'):
  349             return
  350 
  351         # gettext cannot really handle empty strings, so we need to filter
  352         # them out, otherwise we'll get the po header as the content!
  353         # Note that we should not write properties with empty strings from
  354         # the start, but that is another bug
  355         if prop.translatable and prop.data:
  356             if not self._builder._ignore_domain:
  357                 prop.data = dgettext(self._builder._domain, prop.data)
  358 
  359         obj = self._stack.peek()
  360 
  361         property_type = self._state_stack.peek()
  362         if property_type == 'widget' or property_type == 'object':
  363             obj.properties.append(prop)
  364         elif property_type == 'packing':
  365             obj.packing_properties.append(prop)
  366         else:
  367             raise ParseError("property must be a node of widget or packing")
  368 
  369     def _start_ui(self, attrs):
  370         if self._builder._version != 'gtkbuilder':
  371             if not 'id' in attrs:
  372                 raise ParseError("<ui> needs an id attribute")
  373         self._ui_state = True
  374         return UIInfo(attrs)
  375 
  376     def _end_ui(self, ui):
  377         self._ui_state = False
  378         if not ui.data or ui.filename:
  379             raise ParseError("<ui> needs CDATA or filename")
  380 
  381         obj = self._stack.peek()
  382         obj.uis.append(ui)
  383 
  384     def _start_placeholder(self, attrs):
  385         pass
  386 
  387     def _end_placeholder(self, placeholder):
  388         obj = self._stack.peek()
  389         obj.placeholder = True
  390 
  391     def _start_accelerator(self, attrs):
  392         if not 'key' in attrs:
  393             raise ParseError("<accelerator> needs a key attribute")
  394         if not 'modifiers' in attrs:
  395             raise ParseError("<accelerator> needs a modifiers attribute")
  396         if not 'signal' in attrs:
  397             raise ParseError("<accelerator> needs a signal attribute")
  398         return AcceleratorInfo(attrs)
  399 
  400     def _end_accelerator(self, accelerator):
  401         obj = self._stack.peek()
  402         obj.accelerators.append(accelerator)
  403 
  404     def _start_widgets(self, attrs):
  405         self._widgets_state = True
  406         obj = self._stack.peek()
  407         return SizeGroupInfo(obj.id)
  408 
  409     def _end_widgets(self, obj):
  410         self._builder._add_sizegroup_widgets(obj.name, obj.widgets)
  411 
  412     def _start_attribute(self, attrs):
  413         if not 'name' in attrs:
  414             raise ParseError("<attribute> needs a name attribute")
  415         return AttributeInfo(attrs)
  416 
  417     def _end_attribute(self, attribute):
  418         obj = self._stack.peek()
  419 
  420         try:
  421             data = int(attribute.data)
  422         except ValueError:
  423             raise ParseError("attribute value must be an integer")
  424 
  425         property_type = self._state_stack.peek()
  426         if property_type == 'attributes':
  427             obj.attributes[attribute.name] = data
  428         else:
  429             raise ParseError("attribute must be a node of <attributes>")
  430 
  431     def _start_column(self, attrs):
  432         if not 'type' in attrs:
  433             raise ParseError("<column> needs a type attribute")
  434         return ColumnInfo(attrs)
  435 
  436     def _end_column(self, column):
  437         obj = self._stack.peek()
  438         parent_type = self._state_stack.peek()
  439         if parent_type == 'columns':
  440             obj.columns.append(column)
  441         else:
  442             raise ParseError("column must be a node of <columns>")
  443 
  444     def _start_row(self, attrs):
  445         return RowInfo(attrs)
  446 
  447     def _end_row(self, row):
  448         obj = self._stack.peek()
  449         parent_type = self._state_stack.peek()
  450         if parent_type == 'data':
  451             obj.rows.append(row)
  452         else:
  453             raise ParseError("row must be a node of <data>")
  454 
  455     def _start_col(self, attrs):
  456         if not 'id' in attrs:
  457             raise ParseError("<col> needs an id attribute")
  458         return ColInfo(attrs)
  459 
  460     def _end_col(self, col):
  461         obj = self._stack.peek()
  462         parent_type = self._state_stack.peek()
  463         if parent_type == 'row':
  464             obj.cols.append(col)
  465         else:
  466             raise ParseError("col must be a node of <row>")
  467 
  468 class ObjectBuilder:
  469     def __init__(self, filename='', buffer=None, root=None, placeholder=None,
  470                  custom=None, domain=None, ignore_domain=False):
  471         if ((not filename and not buffer) or
  472             (filename and buffer)):
  473             raise TypeError("need a filename or a buffer")
  474 
  475         self._filename = filename
  476         self._buffer = buffer
  477         self._root = root
  478         self._placeholder = placeholder
  479         self._custom = custom
  480         self._domain = domain
  481         self._ignore_domain = ignore_domain
  482 
  483         # maps the classes of widgets that failed to load to lists of
  484         # widget ids
  485         self._unsupported_widgets = {}
  486 
  487         # name -> GObject
  488         self._widgets = {}
  489         self._signals = []
  490         # GObject -> Constructor
  491         self._constructed_objects = {}
  492         # ui definition name -> UIMerge, see _mergeui
  493         self._uidefs = {}
  494         # ui definition name -> constructor name (backwards compatibility)
  495         self._uistates = {}
  496         self._tooltips = gtk.Tooltips()
  497         self._tooltips.enable()
  498         self._focus_widget = None
  499         self._default_widget = None
  500         self._toplevel = None
  501         self._accel_group = None
  502         self._delayed_properties = {}
  503         self._internal_children = {}
  504         self._sizegroup_widgets = {}
  505 
  506         # Public
  507         self.toplevels = []
  508         self.sizegroups = []
  509 
  510         # If domain is not specified, fetch the default one by
  511         # calling textdomain() without arguments
  512         if not domain:
  513             domain = textdomain()
  514             # messages is the default domain, ignore it
  515             if domain == 'messages':
  516                 domain = None
  517 
  518         self._parser = ExpatParser(self)
  519         if filename:
  520             self._parser.parse_file(filename)
  521         elif buffer:
  522             self._parser.parse_stream(buffer)
  523         self._parse_done()
  524 
  525     def __len__(self):
  526         return len(self._widgets)
  527 
  528     def __nonzero__(self):
  529         return True
  530 
  531     # Public API
  532 
  533     def get_domain(self):
  534         return self._domain
  535 
  536     def get_version(self):
  537         return self._version
  538 
  539     def get_widget(self, widget):
  540         return self._widgets.get(widget)
  541 
  542     def get_widgets(self):
  543         return self._widgets.values()
  544 
  545     def get_unsupported_widgets(self):
  546         """
  547         Get a dict of the widgets that could not be loaded because
  548         they're not supported. The dict maps the class of the widget
  549         to a list of widget ids.
  550 
  551         @return: the unsupported widgets
  552         @rtype: dict
  553         """
  554         return self._unsupported_widgets
  555 
  556     def signal_autoconnect(self, obj):
  557         for gobj, name, handler_name, after, object_name in self.get_signals():
  558             # Firstly, try to map it as a dictionary
  559             try:
  560                 handler = obj.get(handler_name)
  561             except (AttributeError, TypeError):
  562                 # If it fails, try to map it to an attribute
  563                 handler = getattr(obj, handler_name, None)
  564             if not handler:
  565                 continue
  566 
  567             if object_name:
  568                 other = self._widgets.get(object_name)
  569                 if after:
  570                     gobj.connect_object_after(name, handler, other)
  571                 else:
  572                     gobj.connect_object(name, handler, other)
  573             else:
  574                 if after:
  575                     gobj.connect_after(name, handler)
  576                 else:
  577                     gobj.connect(name, handler)
  578 
  579     def show_windows(self):
  580 # Doesn't quite work, disabled for now
  581 #         # First set focus, warn if more than one is focused
  582 #         toplevel_focus_widgets = []
  583 #         for widget in self.get_widgets():
  584 #             if not isinstance(widget, gtk.Widget):
  585 #                 continue
  586 
  587 #             if widget.get_data('gazpacho::is-focus'):
  588 #                 toplevel = widget.get_toplevel()
  589 #                 name = toplevel.get_name()
  590 #                 if name in toplevel_focus_widgets:
  591 #                     print ("Warning: Window %s has more than one "
  592 #                            "focused widget" % name)
  593 #                 toplevel_focus_widgets.append(name)
  594 
  595         # At last, display all of the visible windows
  596         for toplevel in self.toplevels:
  597             if not isinstance(toplevel, gtk.Window):
  598                 continue
  599             value = toplevel.get_data('gazpacho::visible')
  600             toplevel.set_property('visible', value)
  601 
  602     def get_internal_child(self, gobj):
  603         if not gobj in self._internal_children:
  604             return []
  605         return self._internal_children[gobj]
  606 
  607     def get_internal_children(self):
  608         return self._internal_children
  609 
  610     # Adapter API
  611 
  612     def add_signal(self, gobj, name, handler, after=False, sig_object=None):
  613         self._signals.append((gobj, name, handler, after, sig_object))
  614 
  615     def get_signals(self):
  616         return self._signals
  617 
  618     def find_resource(self, filename):
  619         dirname = os.path.dirname(self._filename)
  620         path = os.path.join(dirname, filename)
  621         if os.access(path, os.R_OK):
  622             return path
  623 
  624     def get_ui_definitions(self):
  625         return [(name, info.data, info.merge_id)
  626                     for name, info in self._uidefs.items()]
  627 
  628     def get_constructor(self, gobj):
  629         return self._constructed_objects[gobj]
  630 
  631     def ensure_accel(self):
  632         if not self._accel_group:
  633             self._accel_group = gtk.AccelGroup()
  634             if self._toplevel:
  635                 self._toplevel.add_accel_group(self._accel_group)
  636         return self._accel_group
  637 
  638     def add_delayed_property(self, obj_id, pspec, value):
  639         self._delayed_properties.setdefault(obj_id, []).append((pspec, value))
  640 
  641     # Private
  642 
  643     def _setup_signals(self, gobj, signals):
  644         for signal in signals:
  645             self.add_signal(gobj, signal.name, signal.handler,
  646                            signal.after, signal.object)
  647 
  648     def _setup_accelerators(self, widget, accelerators):
  649         if not accelerators:
  650             return
  651 
  652         accel_group = self.ensure_accel()
  653         widget.set_data('gazpacho::accel-group', accel_group)
  654         for accelerator in accelerators:
  655             widget.add_accelerator(accelerator.signal,
  656                                    accel_group,
  657                                    accelerator.key,
  658                                    accelerator.modifiers,
  659                                    gtk.ACCEL_VISIBLE)
  660 
  661     def _apply_delayed_properties(self):
  662         for obj_id, props in self._delayed_properties.items():
  663             widget = self._widgets.get(obj_id)
  664             if widget is None:
  665                 raise AssertionError
  666 
  667             type_name = gobject.type_name(widget)
  668             adapter = adapter_registry.get_adapter(widget, type_name, self)
  669 
  670             prop_list = []
  671             for pspec, value in props:
  672                 if gobject.type_is_a(pspec.value_type, gobject.GObject):
  673                     other = self._widgets.get(value)
  674                     if other is None:
  675                         raise ParseError(
  676                             "property %s of %s refers to object %s which "
  677                             "does not exist" % (pspec.name, obj_id,value))
  678                     prop_list.append((pspec.name, other))
  679                 else:
  680                     raise NotImplementedError(
  681                         "Only delayed object properties are "
  682                         "currently supported")
  683 
  684             adapter.set_properties(widget, prop_list)
  685 
  686     def _merge_ui(self, uimanager_name, name,
  687                   filename='', data=None, merge=True):
  688         uimanager = self._widgets[uimanager_name]
  689         if merge:
  690             if filename:
  691                 filename = self.find_resource(filename)
  692                 # XXX Catch GError
  693                 merge_id = uimanager.add_ui_from_file(filename)
  694             elif data:
  695                 # XXX Catch GError
  696                 merge_id = uimanager.add_ui_from_string(data)
  697             else:
  698                 raise AssertionError
  699         else:
  700             merge_id = -1
  701 
  702         class UIMerge:
  703             def __init__(self, uimanager, filename, data, merge_id):
  704                 self.uimanager = uimanager,
  705                 self.filename = filename
  706                 self.data = data
  707                 self.merge_id = merge_id
  708 
  709         current = self._uidefs.get(name)
  710         if current:
  711             current.merge_id = merge_id
  712         else:
  713             self._uidefs[name] = UIMerge(uimanager, filename, data,
  714                                          merge_id)
  715 
  716         # Backwards compatibility
  717         self._uistates[name] = uimanager_name
  718 
  719     def _uimanager_construct(self, uimanager_name, obj_id):
  720         uimanager = self._widgets[uimanager_name]
  721         widget = uimanager.get_widget('ui/' + obj_id)
  722         widget.set_data('gazpacho::uimanager-name', uimanager_name)
  723         if widget is None:
  724             # XXX: untested code
  725             uimanager_name = self._uistates.get(obj_id)
  726             if not uimanager_name:
  727                 raise AssertionError
  728             uimanager = self._widgets[uimanager_name]
  729 
  730         return widget
  731 
  732     def _find_internal_child(self, obj):
  733         child = None
  734         childname = str(obj.parent.internal_child)
  735         parent = obj.parent
  736         while parent:
  737             if isinstance(parent, ChildInfo):
  738                 parent = parent.parent
  739                 continue
  740 
  741             gparent = parent.gobj
  742             if not gparent:
  743                 break
  744 
  745             adapter = adapter_registry.get_adapter(gparent, parent.klass, self)
  746             child = adapter.find_internal_child(gparent, childname)
  747             if child is not None:
  748                 break
  749 
  750             parent = parent.parent
  751 
  752         if child is not None:
  753             children = self._internal_children.setdefault(gparent, [])
  754             children.append((childname, child))
  755             # Childname is attribute name, which we use to find the child
  756             # in the parent widget.
  757             # obj.id stores the real name, it needs to be set manually here
  758             # since we're not calling the widget adapters constructor
  759             child.set_name(obj.id)
  760             child.set_data('gazpacho::internal-child-name', childname)
  761         return child
  762 
  763     def _create_custom(self, obj):
  764         kwargs = dict(name=obj.id)
  765         for prop in obj.properties:
  766             prop_name = prop.name
  767             if prop_name in ('string1', 'string2',
  768                              'creation_function',
  769                              'last_modification_time'):
  770                 kwargs[prop_name] = prop.data
  771             elif prop_name in ('int1', 'int2'):
  772                 kwargs[prop_name] = int(prop.data)
  773 
  774         if not self._custom:
  775             return gtk.Label('<Custom: %s>' % obj.id)
  776         elif callable(self._custom):
  777             func = self._custom
  778             return func(**kwargs)
  779         else:
  780             func_name = kwargs['creation_function']
  781             try:
  782                 func = self._custom[func_name]
  783             except (TypeError, KeyError, AttributeError):
  784                 func = getattr(self._custom, func_name, None)
  785 
  786             return func(name=obj.id,
  787                         string1=kwargs.get('string1', None),
  788                         string2=kwargs.get('string2', None),
  789                         int1=kwargs.get('int1', None),
  790                         int2=kwargs.get('int2', None))
  791 
  792     def _create_placeholder(self, obj=None):
  793         if not self._placeholder:
  794             return
  795 
  796         if not obj:
  797             name = 'unknown'
  798         else:
  799             name = obj.id
  800 
  801         return self._placeholder(name)
  802 
  803     def _add_widget(self, object_id, gobj):
  804         gobj.set_data('gazpacho::object-id', object_id)
  805         self._widgets[object_id] = gobj
  806 
  807     def _build_phase1(self, obj):
  808         assert obj.gobj is None, obj.id
  809 
  810         root = self._root
  811         if root and root != obj.id:
  812             return
  813 
  814         if obj.klass == 'Custom':
  815             gobj = self._create_custom(obj)
  816             if gobj:
  817                 self._add_widget(obj.id, gobj)
  818             return gobj
  819 
  820         try:
  821             gtype = gobject.type_from_name(obj.klass)
  822         except RuntimeError:
  823             self._unsupported_widgets.setdefault(obj.klass, []).append(obj.id)
  824             obj.placeholder = True
  825             return self._create_placeholder(obj)
  826 
  827         adapter = adapter_registry.get_adapter(gtype, obj.klass, self)
  828 
  829         construct, normal = adapter.get_properties(gtype,
  830                                                    obj.id,
  831                                                    obj.properties)
  832         if obj.is_internal_child():
  833             gobj = self._find_internal_child(obj)
  834         elif obj.constructor:
  835             if self._widgets.has_key(obj.constructor):
  836                 gobj = self._uimanager_construct(obj.constructor, obj.id)
  837                 constructor = obj.constructor
  838             # Backwards compatibility
  839             elif self._uistates.has_key(obj.constructor):
  840                 constructor = self._uistates[obj.constructor]
  841                 gobj = self._uimanager_construct(constructor, obj.id)
  842             else:
  843                 raise ParseError("constructor %s for object %s could not "
  844                                  "be found" % (obj.constructor, obj.id))
  845             self._constructed_objects[gobj] = self._widgets[constructor]
  846         else:
  847             gobj = adapter.construct(obj.id, gtype, construct)
  848 
  849         if gobj:
  850             self._add_widget(obj.id, gobj)
  851 
  852             if isinstance(gobj, gtk.ListStore):
  853                 adapter.set_column_types(gobj, obj.columns)
  854                 adapter.fill(gobj, obj.rows)
  855 
  856             adapter.set_properties(gobj, normal)
  857 
  858             # This is a little tricky
  859             # We assume the default values for all these are nonzero, eg
  860             # either False or None
  861             # We also need to handle the case when we have two labels, if we
  862             # do we respect the first one. This is due to a bug in the save code
  863             for propinfo in obj.properties:
  864                 key = 'i18n_is_translatable_%s' % propinfo.name
  865                 if not gobj.get_data(key) and propinfo.translatable:
  866                     gobj.set_data(key, propinfo.translatable)
  867 
  868                 key = 'i18n_has_context_%s' % propinfo.name
  869                 if not gobj.get_data(key) and propinfo.context:
  870                     gobj.set_data(key, propinfo.context)
  871 
  872                 # XXX: Rename to i18n_comments
  873                 key = 'i18n_comment_%s' % propinfo.name
  874                 if not gobj.get_data(key) and propinfo.comments:
  875                     gobj.set_data(key, propinfo.comments)
  876 
  877         return gobj
  878 
  879     def _build_phase2(self, obj):
  880         # If we have a root set, we don't want to construct all
  881         # widgets, filter out unwanted here
  882         root = self._root
  883         if root and root != obj.id:
  884             return
  885 
  886         # Skip this step for placeholders, so we don't
  887         # accidentally try to pack something into unsupported widgets
  888         if obj.placeholder:
  889             return
  890 
  891         gobj = obj.gobj
  892         if not gobj:
  893             return
  894 
  895         adapter = adapter_registry.get_adapter(gobj, obj.klass, self)
  896 
  897         for child in obj.children:
  898             self._pack_child(adapter, gobj, child)
  899 
  900         self._setup_signals(gobj, obj.signals)
  901         self._setup_accelerators(gobj, obj.accelerators)
  902 
  903         # Toplevels
  904         if not obj.parent:
  905             if isinstance(gobj, gtk.UIManager):
  906                 for ui in obj.uis:
  907                     self._merge_ui(obj.id,
  908                                    ui.id, ui.filename, ui.data, ui.merge)
  909                     self.accelgroup = gobj.get_accel_group()
  910             elif isinstance(gobj, gtk.Window):
  911                 self._set_toplevel(gobj)
  912             self.toplevels.append(gobj)
  913 
  914     def _pack_child(self, adapter, gobj, child):
  915 
  916         if child.placeholder:
  917             widget = self._create_placeholder()
  918             if not widget:
  919                 return
  920         elif child.widget:
  921             widget = child.widget
  922         else:
  923             return
  924 
  925         if child.internal_child:
  926             gobj = child.parent.gobj
  927             name = child.parent.id
  928             if isinstance(gobj, gtk.Widget):
  929                 gobj.set_name(name)
  930             self._add_widget(name, gobj)
  931             return
  932 
  933         # 5) add child
  934         try:
  935             adapter.add(gobj,
  936                         widget,
  937                         child.packing_properties,
  938                         child.type)
  939         except NotImplementedError:
  940             raise ParseError(
  941                 '%s of type %s has children, but its not supported' % (
  942                 child.parent.id,
  943                 gobject.type_name(gobj)))
  944 
  945         if isinstance(gobj, gtk.CellLayout):
  946             adapter.set_cell_renderer_attributes(gobj, widget, child.attributes)
  947 
  948     def _attach_accel_groups(self):
  949         # This iterates of all objects constructed by a gtk.UIManager
  950         # And attaches an accelgroup to the toplevel window of them
  951         for widget, constructor in self._constructed_objects.items():
  952             if not isinstance(constructor, gtk.UIManager):
  953                 continue
  954             toplevel = widget.get_toplevel()
  955             if not isinstance(toplevel, gtk.Window):
  956                 continue
  957             accel_group = constructor.get_accel_group()
  958             if not accel_group in gtk.accel_groups_from_object(toplevel):
  959                 toplevel.add_accel_group(accel_group)
  960 
  961     def _add_sizegroup_widgets(self, group_name, widget_names):
  962         self._sizegroup_widgets[group_name] = widget_names
  963 
  964     def _setup_sizegroups(self):
  965         # gtkbuilder way
  966         for group_name, widget_names in self._sizegroup_widgets.items():
  967             for widget_name in widget_names:
  968                 widget = self._widgets[widget_name]
  969                 widget.set_data('gazpacho::sizegroup', group_name)
  970 
  971         for widget in self._widgets.values():
  972             # Collect all the sizegroups
  973             if isinstance(widget, gtk.SizeGroup):
  974                 self.sizegroups.append(widget)
  975                 continue
  976 
  977             # And setup the widgets which has a sizegroup
  978             if not isinstance(widget, gtk.Widget):
  979                 continue
  980 
  981             group_name = widget.get_data('gazpacho::sizegroup')
  982             if group_name is None:
  983                 continue
  984             group = self.get_widget(group_name)
  985             if group is None:
  986                 raise ParseError("sizegroup %s does not exist" %
  987                                  group_name)
  988             group.add_widget(widget)
  989 
  990             # Keep a list of widgets inside the sizegroup.
  991             # Perhaps GTK+ should provide an api for this.
  992             sgwidgets = group.get_data('gazpacho::sizegroup-widgets')
  993             if sgwidgets is None:
  994                 sgwidgets = []
  995                 group.set_data('gazpacho::sizegroup-widgets', sgwidgets)
  996             sgwidgets.append(widget)
  997 
  998     def _parse_done(self):
  999         self._apply_delayed_properties()
 1000         self._attach_accel_groups()
 1001         self._setup_sizegroups()
 1002         self.show_windows()
 1003 
 1004     def _set_toplevel(self, window):
 1005         if self._focus_widget:
 1006             self._focus_widget.grab_focus()
 1007             self._focus_widget = None
 1008         if self._default_widget:
 1009             if self._default_widget.flags() & gtk.CAN_DEFAULT:
 1010                 self._default_widget.grab_default()
 1011             self._default_widget = None
 1012         if self._accel_group:
 1013             self._accel_group = None
 1014 
 1015         # the window should hold a reference to the tooltips object
 1016         window.set_data('gazpacho::tooltips', self._tooltips)
 1017         self._toplevel = window
 1018 
 1019 if __name__ == '__main__':
 1020     import sys
 1021     ob = ObjectBuilder(filename=sys.argv[1])
 1022     for toplevel in ob.toplevels:
 1023         if not isinstance(toplevel, gtk.Window):
 1024             continue
 1025         toplevel.connect('delete-event', gtk.main_quit)
 1026         toplevel.show_all()
 1027 
 1028     gtk.main()