"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()