/* * 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 */ using GLib; using Gee; namespace zavai { public interface Resource : Object { /** * 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. */ public abstract void shutdown(); } public abstract class Service : Object, Resource { public string name { get; construct; } bool _started; public bool started { get { return _started; } set { _started = value; } default = false; } public signal void toggled(bool new_state); protected HashMap requests; construct { requests = new HashMap(str_hash, str_equal); } public void shutdown() { stop(); } /// Activate the service protected virtual void start() { if (!started) { stderr.printf("SERVICE %s started\n", name); started = true; toggled(started); } } /// Deactivate the service protected virtual void stop() { if (started) { stderr.printf("SERVICE %s stopped\n", name); started = false; toggled(started); } } /** Request a resource using the given ID. * * If it is the first time the resource is requested, start it and * return true. Else, take note of the request and return false. * * If a resource is requested multiple times with the same ID, it will * need to be released multiple times with that ID. */ public bool request(string id) { bool res = (requests.size == 0); if (id in requests) requests[id] = requests[id] + 1; else requests.set(id, 1); if (res) start(); return res; } /** * Release a resource using the given ID. * * If after the call nothing is requesting the resource, stop it and * return true. Else, take note of the release and return false. * * If a resource is requested multiple times with the same ID, it will * need to be released multiple times with that ID. */ public bool release(string id) { if (id in requests) { if (requests[id] > 1) requests[id] = requests[id] - 1; else requests.remove(id); } else { return false; } if (requests.size > 0) return false; stop(); return true; } } // class Service(Resource): // "Service that is activated only when someone is listening" // def __init__(self, types = []): // """ // Initialise a service that can emit signals for the given event types // """ // super(Service, self).__init__() // self.callbacks = dict() // for t in types: // self.callbacks[t] = set() // self.started = False // // 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() // self.started = True // // def disconnect(self, type, callback): // "Disconnect a callback to this resource, activating it if needed" // if not self.has_callbacks(): return // self.callbacks[type].discard(callback) // if not self.has_callbacks(): // self.stop() // self.started = False // 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() public class Registry : Object, Resource { HashMap memb_resources; HashMap memb_services; HashMap memb_applets; HashMap memb_menus; protected ArrayList registration_order; public DBus.Connection sbus; public Registry() { memb_resources = new HashMap(str_hash, str_equal); memb_services = new HashMap(str_hash, str_equal); memb_applets = new HashMap(str_hash, str_equal); memb_menus = new HashMap(str_hash, str_equal); registration_order = new ArrayList(); sbus = DBus.Bus.get(DBus.BusType.SYSTEM); } public void shutdown() { // Shutdown in reverse registration order for (int i = registration_order.size - 1; i >= 0; --i) registration_order[i].shutdown(); } public void register_resource(string name, Resource obj) { memb_resources[name] = obj; registration_order.add(obj); } public void register_service(Service obj) { memb_services[obj.name] = obj; registration_order.add(obj); } public void register_applet(string name, Applet obj) { memb_applets[name] = obj; registration_order.add(obj); } public void register_menu(string name, Menu obj) { memb_applets[name] = obj; memb_menus[name] = obj; registration_order.add(obj); } public Resource? getr(string name) { if (name in memb_resources) return memb_resources[name]; else { log.error("getr: no resource found: " + name); return null; } } public Service? gets(string name) { if (name in memb_services) return memb_services[name]; else { log.error("gets: no service found: " + name); return null; } } public Applet? geta(string name) { if (name in memb_applets) return memb_applets[name]; else { log.error("geta: no applet found: " + name); return null; } } public Menu? getmenu(string name) { if (name in memb_menus) return memb_menus[name]; else { log.error("getmenu: no menu found: " + name); return null; } } } // 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, 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 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(res, name) // 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() // zavai.Registry registry; }