Test adding of toggle actions
[gregoa/zavai.git] / zavai / registry.py
1 # registry - zavai resource registry
2 #
3 # Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19 import zavai
20 import gtk
21
22 def get_parent(s):
23     "Get the parent name for s"
24     pos = s.rfind(".")
25     if pos == -1: return None
26     res = s[:pos]
27     if res == "menu": return None
28     return res
29
30 def default_label(s):
31     "Compute a default label given the last element of a path"
32     pos = s.rfind(".")
33     if pos == -1: return s.capitalize()
34     return s[pos+1:].capitalize()
35
36
37 class Registry(object):
38     """Collection of resources.
39
40     Various factories can be registered by name on the registry. Then when an
41     object is requested for the first time, it is created using the factory.
42     When it is requested again, the existing object is reused.
43     """
44
45     def __init__(self):
46         self.factories = dict()
47         self.objects = dict()
48         self.labels = dict()
49
50     def register(self, name, obj, label = None):
51         """Register an object at the given path.
52
53         Name the path to this object, like "menu.gps.monitor".
54         """
55         if name in self.objects:
56             return KeyError("%s is already registered", name)
57         zavai.info("Registering", name)
58         self.objects[name] = obj
59         if label is not None: self.labels[name] = label
60
61         if name.startswith("menu."):
62             self.add_to_menu(name)
63
64     def register_factory(self, name, fac, label = None):
65         """Register an object factory at the given path.
66
67         Name the path to this object, like "menu.gps.monitor".
68         """
69         if name in self.factories:
70             return KeyError("Factory %s is already registered", name)
71         zavai.info("Registering factory", name)
72         self.factories[name] = fac
73         if label is not None: self.labels[name] = label
74
75         if name.startswith("menu."):
76             self.add_to_menu(name)
77
78     def register_action(self, name, obj):
79         """Register an object at the given path.
80
81         Name the path to this object, like "menu.gps.monitor".
82         """
83         if name in self.objects:
84             return KeyError("%s is already registered", name)
85         zavai.info("Registering action", name)
86         self.objects[name] = obj
87
88         if name.startswith("menu."):
89             parent = get_parent(name)
90             if parent is not None:
91                 zavai.info("Add action to menu", name, parent)
92                 menu = self.menu(parent)
93                 if isinstance(obj, gtk.ToggleAction):
94                     menu.add_child(zavai.ToggleButton(self, name, action=obj))
95                 else:
96                     menu.add_child(zavai.LinkButton(self, name, action=obj))
97
98     def add_to_menu(self, name):
99         "Add the applet with the given name to the menu structure"
100         parent = get_parent(name)
101         if parent is not None:
102             zavai.info("Add to menu", name, parent)
103             menu = self.menu(parent)
104             menu.add_child(zavai.LinkButton(self, name, self.label(name)))
105
106     def label(self, name):
107         "Return the label for the object with the given name"
108         res = self.labels.get(name)
109         if res is not None:
110             return res
111         return default_label(name)
112
113     def resource(self, name):
114         """Get a resource from the registry.
115
116         If no resource exists at `name` but there is a factory, instantiate the
117         object using the factory.
118
119         If not even a factory exists at `name`, returns None.
120         """
121         res = self.objects.get(name, None)
122         if res is None:
123             fac = self.factories.get(name, None)
124             if fac is not None:
125                 res = self.objects[name] = fac(self, name)
126         return res
127
128     def menu(self, name):
129         """Get a menu resource, automatically creating it if it is missing.
130
131         Menus are created automatically linked to a parent menu, according to
132         the hierarchy in `name`.
133         """
134         res = self.resource(name)
135         if res is None:
136             # Check if it is a toplevel menu
137             if name.startswith("menu."):
138                 parent = get_parent(name[5:])
139                 if parent is not None:
140                     parent = "menu." + parent
141             else:
142                 parent = get_parent(name)
143
144             res = zavai.Menu(self, name, parent)
145             self.register(name, res)
146         return res
147
148     def shutdown(self):
149         """Shut down all objects in this Registry.
150
151         After shutting down, all objects cannot be used anymore"""
152         for o in self.objects.itervalues():
153             if isinstance(o, Resource):
154                 o.shutdown()
155         self.objects.clear()
156
157 class Resource(object):
158     def shutdown(self):
159         """Shut down this resource.
160
161         Normally one does nothing here, but it is important to give resources a
162         chance to do cleanup when the program quits.
163
164         This can be used for tasks like closing the tags on a GPX track,
165         releasing a FSO resource, restoring mixer settings and so on.
166         """
167         pass