# registry - zavai resource registry # # Copyright (C) 2009 Enrico Zini # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import zavai import gtk def get_parent(s): "Get the parent name for s" pos = s.rfind(".") if pos == -1: return None res = s[:pos] if res == "menu": return None return res def default_label(s): "Compute a default label given the last element of a path" pos = s.rfind(".") if pos == -1: return s.capitalize() return s[pos+1:].capitalize() class Registry(object): """Collection of resources. Various factories can be registered by name on the registry. Then when an object is requested for the first time, it is created using the factory. When it is requested again, the existing object is reused. """ def __init__(self): self.factories = dict() self.objects = dict() self.labels = dict() def register(self, name, obj): """Register an object at the given path. Name the path to this object, like "menu.gps.monitor". """ if name in self.objects: return KeyError("%s is already registered", name) zavai.info("Registering", name) self.objects[name] = obj if name.startswith("menu."): self.add_to_menu(name) def register_factory(self, name, fac, label = None): """Register an object factory at the given path. Name the path to this object, like "menu.gps.monitor". """ if name in self.factories: return KeyError("Factory %s is already registered", name) zavai.info("Registering factory", name) self.factories[name] = fac if label is not None: self.labels[name] = label def add_to_menu(self, name): "Add the applet with the given name to the menu structure" parent = get_parent(name) if parent is not None: zavai.info("Add to menu", name, parent) menu = self.menu(parent) obj = self.resource(name) if isinstance(obj, gtk.ToggleAction): menu.add_child(zavai.ToggleButton(self, name, action=obj)) elif isinstance(obj, gtk.Action): menu.add_child(zavai.LinkButton(self, name, action=obj)) else: menu.add_child(zavai.LinkButton(self, name, self.label(name))) def label(self, name): "Return the label for the object with the given name" res = self.labels.get(name) if res is not None: return res try: obj = self.resource(name) return obj.props.label except: return default_label(name) def resource(self, name): """Get a resource from the registry. If no resource exists at `name` but there is a factory, instantiate the object using the factory. If not even a factory exists at `name`, returns None. """ res = self.objects.get(name, None) if res is None: fac = self.factories.get(name, None) if fac is not None: res = self.objects[name] = fac(self, name) return res def menu(self, name): """Get a menu resource, automatically creating it if it is missing. Menus are created automatically linked to a parent menu, according to the hierarchy in `name`. """ res = self.resource(name) if res is None: # Check if it is a toplevel menu if name.startswith("menu."): parent = get_parent(name[5:]) if parent is not None: parent = "menu." + parent else: parent = get_parent(name) res = zavai.Menu(self, name, parent) self.register(name, res) return res def shutdown(self): """Shut down all objects in this Registry. After shutting down, all objects cannot be used anymore""" for o in self.objects.itervalues(): if isinstance(o, Resource): o.shutdown() self.objects.clear() class Resource(object): def shutdown(self): """Shut down this resource. Normally one does nothing here, but it is important to give resources a chance to do cleanup when the program quits. This can be used for tasks like closing the tags on a GPX track, releasing a FSO resource, restoring mixer settings and so on. """ pass