1### Copyright (C) 2002-2008 Stephen Kennedy <stevek@gnome.org>
2### Copyright (C) 2010 Kai Willadsen <kai.willadsen@gmail.com>
3
4### This program is free software; you can redistribute it and/or modify
5### it under the terms of the GNU General Public License as published by
6### the Free Software Foundation; either version 2 of the License, or
7### (at your option) any later version.
8
9### This program is distributed in the hope that it will be useful,
10### but WITHOUT ANY WARRANTY; without even the implied warranty of
11### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12### GNU General Public License for more details.
13
14### You should have received a copy of the GNU General Public License
15### along with this program; if not, write to the Free Software
16### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
17### USA.
18
19import logging
20import re
21
22import gtk
23
24
25# FIXME: duplicate defn in bin/meld
26locale_domain = "meld"
27
28
29class Component(object):
30    """Base class for all glade objects.
31
32    This class handles loading the xml glade file and autoconnects
33    all signals in the glade file.
34
35    The handle to the xml file is stored in 'self.xml'. The
36    toplevel widget is stored in 'self.widget'.
37
38    In addition it calls widget.set_data("pyobject", self) - this
39    allows us to get the python object given only the 'raw' gtk+
40    object, which is sadly sometimes necessary.
41    """
42
43    def __init__(self, filename, root, extra=None):
44        """Load the widgets from the node 'root' in file 'filename'.
45        """
46        self.builder = gtk.Builder()
47        self.builder.set_translation_domain(locale_domain)
48        objects = [root] + extra if extra else [root]
49        self.builder.add_objects_from_file(filename, objects)
50        self.builder.connect_signals(self)
51        self.widget = getattr(self, root)
52        self.widget.set_data("pyobject", self)
53
54    def __getattr__(self, key):
55        """Allow glade widgets to be accessed as self.widgetname.
56        """
57        widget = self.builder.get_object(key)
58        if widget: # cache lookups
59            setattr(self, key, widget)
60            return widget
61        raise AttributeError(key)
62
63    def map_widgets_into_lists(self, widgetnames):
64        """Put sequentially numbered widgets into lists.
65
66        e.g. If an object had widgets self.button0, self.button1, ...,
67        then after a call to object._map_widgets_into_lists(["button"])
68        object has an attribute self.button == [self.button0, self.button1, ...]."
69        """
70        for item in widgetnames:
71            setattr(self,item, [])
72            lst = getattr(self,item)
73            i = 0
74            while 1:
75                key = "%s%i"%(item,i)
76                try:
77                    val = getattr(self, key)
78                except AttributeError:
79                    break
80                lst.append(val)
81                i += 1
82
83# Regular expression to match handler method names patterns
84# on_widget__signal and after_widget__signal.  Note that we use two
85# underscores between the Glade widget name and the signal name.
86handler_re = re.compile(r'^(on|after)_(.*)__(.*)$')
87
88def connect_signal_handlers(obj):
89    log = logging.getLogger(__name__)
90    for attr in dir(obj):
91        match = handler_re.match(attr)
92        if match:
93            when, widgetname, signal = match.groups()
94            method = getattr(obj, attr)
95            assert hasattr(method, '__call__')
96            try:
97                widget = getattr(obj, widgetname)
98            except AttributeError:
99                log.warning("Widget '%s' not found in %s", widgetname, obj)
100                continue
101            if not isinstance(widget,list):
102                widget = [widget]
103            for w in widget:
104                try:
105                    if when == 'on':
106                        w.connect(signal, method)
107                    elif when == 'after':
108                        w.connect_after(signal, method)
109                except TypeError as e:
110                    log.warning("%s in %s %s", e, obj, attr)
111        elif attr.startswith('on_') or attr.startswith('after_'):
112            continue # don't warn until all old code updated
113            # Warn about some possible typos like separating
114            # widget and signal name with _ instead of __.
115            log.warning("Warning: attribute %r not connected as a signal "
116                        "handler", attr)
117
118
119from . import gladesupport
120