# 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.
def __init__(self):
self.factories = dict()
self.objects = dict()
+ self.labels = dict()
+
+ def register(self, obj, name=None):
+ """Register an object at the given path.
+
+ Name the path to this object, like "menu.gps.monitor".
+ """
+ if name is None:
+ name = obj.props.name
+
+ 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, fac, name, 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, instantiating it if it has not been done yet.
+ """Get a resource from the registry.
- First it tries to use the factory registered with `name` itself. If it
- fails, it tries to use the factory registered one level above (using
- the dot '.' as a separator).
+ If no resource exists at `name` but there is a factory, instantiate the
+ object using the factory.
- If no suitable factory has been found, returns None.
+ 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)
- root = name
- if fac is None:
- while True:
- pos = root.rfind(".")
- if pos == -1: break
- root = root[:pos]
- fac = self.factories.get(root, None)
- if fac is None:
- return None
- zavai.info("Instantiating", name, "with factory", root)
- res = fac(self, name)
- if res is not None:
- self.objects[name] = res
+ if fac is not None:
+ res = self.objects[name] = fac(self, name)
return res
def menu(self, name):
- """Get a menu resource, instantiating it if it has not been done yet.
-
- All menu resources share the same factory, which builds zavai.Menu
- objects.
- """
- return self.resource("menu." + name)
+ """Get a menu resource, automatically creating it if it is missing.
- def menu_link(self, name, label):
- """Return a MenuLink to the menu with the given name
+ Menus are created automatically linked to a parent menu, according to
+ the hierarchy in `name`.
"""
- return zavai.MenuLink(self, name, label)
-
- def get_existing(self, name):
- """Get a menu resource, but only if it has been already instantiated.
-
- If the resource has not been instantiated yet, returns None.
- """
- return self.objects.get(name, None)
-
- def register(self, name, factory):
- """Register a factory for this registry.
-
- Name is a name for this factory, like "menu.gps.monitor".
-
- Factory is a function that creates a Resource object. The function will
- be passed the Registry itself as the only parameter"""
- if name in self.factories:
- return KeyError("% is already registered as a factory", name)
- self.factories[name] = factory
+ 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(res, name)
+ return res
def shutdown(self):
"""Shut down all objects in this Registry.
self.objects.clear()
class Resource(object):
- def __init__(self, registry, name, *args, **kw):
- """The default constructor does nothing, but is there to eat the
- parameters passed by Registry in case subclassers do not need a
- constructor"""
- pass
+ def __init__(self):
+ super(Resource, self).__init__()
def shutdown(self):
"""Shut down this resource.
releasing a FSO resource, restoring mixer settings and so on.
"""
pass
+
+class Service(Resource):
+ "Service that is activated only when someone is listening"
+ def __init__(self):
+ """
+ Initialise a service that can emit signals for the given event types
+ """
+ super(Service, self).__init__(types = [])
+ self.callbacks = dict()
+ for t in types:
+ self.callbacks[t] = set()
+
+ def shutdown(self):
+ self.stop()
+
+ def start(self):
+ "Activate the service"
+ pass
+
+ def stop(self):
+ "Deactivate the service"
+ pass
+
+ def notify(self, type, *args, **kw):
+ "Call all callbacks with the given parameters"
+ for cb in self.callbacks[type]:
+ cb(*args, **kw)
+
+ def has_callbacks(self):
+ for i in self.callbacks.values():
+ if i: return True
+ return False
+
+ def connect(self, type, callback):
+ "Connect a callback to this resource, activating it if needed"
+ do_start = not self.has_callbacks()
+ self.callbacks[type].add(callback)
+ if do_start:
+ self.start()
+
+ def disconnect(self, type, callback):
+ "Disconnect a callback to this resource, activating it if needed"
+ if not self.has_callbacks(): return
+ self.callbacks.discard(callback)
+ if not self.has_callbacks():
+ self.stop()