1a3d0016e590db81298a5b8721e50f43b276c768
[gregoa/zavai.git] / vala / registry.vala
1 /* 
2  * registry - zavai resource registry
3  *
4  * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 using GLib;
22 using Gee;
23
24 namespace zavai {
25
26 public interface Resource : Object {
27         /**
28          * Shut down this resource.
29          *
30          * Normally one does nothing here, but it is important to give resources a
31          * chance to do cleanup when the program quits.
32          * 
33          * This can be used for tasks like closing the tags on a GPX track,
34          * releasing a FSO resource, restoring mixer settings and so on.
35          */
36         public abstract void shutdown();
37 }
38
39 public abstract class Service : Object, Resource {
40         public string name { get; construct; }
41
42         bool _started;
43         public bool started {
44                 get { return _started; }
45                 set { _started = value; }
46                 default = false;
47         }
48
49         public signal void toggled(bool new_state);
50
51         protected HashMap<string, int> requests;
52
53         construct {
54                 requests = new HashMap<string, int>(str_hash, str_equal);
55         }
56
57         public void shutdown()
58         {
59                 stop();
60         }
61
62         /// Activate the service
63         protected virtual void start()
64         {
65                 if (!started)
66                 {
67 stderr.printf("SERVICE %s started\n", name);
68                         started = true;
69                         toggled(started);
70                 }
71         }
72
73         /// Deactivate the service
74         protected virtual void stop()
75         {
76                 if (started)
77                 {
78 stderr.printf("SERVICE %s stopped\n", name);
79                         started = false;
80                         toggled(started);
81                 }
82         }
83
84         /**
85           Request a resource using the given ID.
86          *
87          * If it is the first time the resource is requested, start it and
88          * return true. Else, take note of the request and return false.
89          *
90          * If a resource is requested multiple times with the same ID, it will
91          * need to be released multiple times with that ID.
92          */
93         public bool request(string id)
94         {
95                 bool res = (requests.size == 0);
96                 if (id in requests)
97                         requests[id] = requests[id] + 1;
98                 else
99                         requests.set(id, 1);
100                 if (res) start();
101                 return res;
102         }
103
104         /**
105          * Release a resource using the given ID.
106          *
107          * If after the call nothing is requesting the resource, stop it and
108          * return true. Else, take note of the release and return false.
109          *
110          * If a resource is requested multiple times with the same ID, it will
111          * need to be released multiple times with that ID.
112          */
113         public bool release(string id)
114         {
115                 if (id in requests)
116                 {
117                         if (requests[id] > 1)
118                                 requests[id] = requests[id] - 1;
119                         else
120                                 requests.remove(id);
121                 } else {
122                         return false;
123                 }
124                 if (requests.size > 0)
125                         return false;
126                 stop();
127                 return true;
128         }
129 }
130
131 // class Service(Resource):
132 //     "Service that is activated only when someone is listening"
133 //     def __init__(self, types = []):
134 //         """
135 //         Initialise a service that can emit signals for the given event types
136 //         """
137 //         super(Service, self).__init__()
138 //         self.callbacks = dict()
139 //         for t in types:
140 //             self.callbacks[t] = set()
141 //         self.started = False
142 // 
143 //     def notify(self, type, *args, **kw):
144 //         "Call all callbacks with the given parameters"
145 //         for cb in self.callbacks[type]:
146 //             cb(*args, **kw)
147 // 
148 //     def has_callbacks(self):
149 //         for i in self.callbacks.values():
150 //             if i: return True
151 //         return False
152 // 
153 //     def connect(self, type, callback):
154 //         "Connect a callback to this resource, activating it if needed"
155 //         do_start = not self.has_callbacks()
156 //         self.callbacks[type].add(callback)
157 //         if do_start:
158 //             self.start()
159 //             self.started = True
160 // 
161 //     def disconnect(self, type, callback):
162 //         "Disconnect a callback to this resource, activating it if needed"
163 //         if not self.has_callbacks(): return
164 //         self.callbacks[type].discard(callback)
165 //         if not self.has_callbacks():
166 //             self.stop()
167 //             self.started = False
168
169
170 // import zavai
171 // import gtk
172 // 
173 // def get_parent(s):
174 //     "Get the parent name for s"
175 //     pos = s.rfind(".")
176 //     if pos == -1: return None
177 //     res = s[:pos]
178 //     if res == "menu": return None
179 //     return res
180 // 
181 // def default_label(s):
182 //     "Compute a default label given the last element of a path"
183 //     pos = s.rfind(".")
184 //     if pos == -1: return s.capitalize()
185 //     return s[pos+1:].capitalize()
186
187 public class Registry : Object, Resource
188 {
189         HashMap<string, Resource> memb_resources;
190         HashMap<string, Service> memb_services;
191         HashMap<string, Applet> memb_applets;
192         HashMap<string, Menu> memb_menus;
193         protected ArrayList<Resource> registration_order;
194         public DBus.Connection sbus;
195
196         public Registry()
197         {
198                 memb_resources = new HashMap<string, Resource>(str_hash, str_equal);
199                 memb_services = new HashMap<string, Service>(str_hash, str_equal);
200                 memb_applets = new HashMap<string, Applet>(str_hash, str_equal);
201                 memb_menus = new HashMap<string, Menu>(str_hash, str_equal);
202                 registration_order = new ArrayList<Resource>();
203                 sbus = DBus.Bus.get(DBus.BusType.SYSTEM);
204         }
205
206         public void shutdown()
207         {
208                 // Shutdown in reverse registration order
209                 for (int i = registration_order.size - 1; i >= 0; --i) 
210                         registration_order[i].shutdown();
211         }
212
213         public void register_resource(string name, Resource obj)
214         {
215                 memb_resources[name] = obj;
216                 registration_order.add(obj);
217         }
218
219         public void register_service(Service obj)
220         {
221                 memb_services[obj.name] = obj;
222                 registration_order.add(obj);
223         }
224
225         public void register_applet(string name, Applet obj)
226         {
227                 memb_applets[name] = obj;
228                 registration_order.add(obj);
229         }
230
231         public void register_menu(string name, Menu obj)
232         {
233                 memb_applets[name] = obj;
234                 memb_menus[name] = obj;
235                 registration_order.add(obj);
236         }
237
238         public Resource? getr(string name)
239         {
240                 if (name in memb_resources)
241                         return memb_resources[name];
242                 else
243                 {
244                         log.error("getr: no resource found: " + name);
245                         return null;
246                 }
247         }
248
249         public Service? gets(string name)
250         {
251                 if (name in memb_services)
252                         return memb_services[name];
253                 else
254                 {
255                         log.error("gets: no service found: " + name);
256                         return null;
257                 }
258         }
259
260         public Applet? geta(string name)
261         {
262                 if (name in memb_applets)
263                         return memb_applets[name];
264                 else
265                 {
266                         log.error("geta: no applet found: " + name);
267                         return null;
268                 }
269         }
270
271         public Menu? getmenu(string name)
272         {
273                 if (name in memb_menus)
274                         return memb_menus[name];
275                 else
276                 {
277                         log.error("getmenu: no menu found: " + name);
278                         return null;
279                 }
280         }
281 }
282
283 // class Registry(object):
284 //     """Collection of resources.
285 // 
286 //     Various factories can be registered by name on the registry. Then when an
287 //     object is requested for the first time, it is created using the factory.
288 //     When it is requested again, the existing object is reused.
289 //     """
290 // 
291 //     def __init__(self):
292 //         self.factories = dict()
293 //         self.objects = dict()
294 //         self.labels = dict()
295 // 
296 //     def register(self, obj, name=None):
297 //         """Register an object at the given path.
298 // 
299 //         Name the path to this object, like "menu.gps.monitor".
300 //         """
301 //         if name is None:
302 //             name = obj.props.name
303 // 
304 //         if name in self.objects:
305 //             return KeyError("%s is already registered", name)
306 //         zavai.info("Registering", name)
307 //         self.objects[name] = obj
308 // 
309 //         if name.startswith("menu."):
310 //             self.add_to_menu(name)
311 // 
312 //     def register_factory(self, fac, name, label = None):
313 //         """Register an object factory at the given path.
314 // 
315 //         Name the path to this object, like "menu.gps.monitor".
316 //         """
317 //         if name in self.factories:
318 //             return KeyError("Factory %s is already registered", name)
319 //         zavai.info("Registering factory", name)
320 //         self.factories[name] = fac
321 //         if label is not None: self.labels[name] = label
322 // 
323 //     def add_to_menu(self, name):
324 //         "Add the applet with the given name to the menu structure"
325 //         parent = get_parent(name)
326 //         if parent is not None:
327 //             zavai.info("Add to menu", name, parent)
328 //             menu = self.menu(parent)
329 // 
330 //             obj = self.resource(name)
331 //             if isinstance(obj, gtk.ToggleAction):
332 //                 menu.add_child(zavai.ToggleButton(self, name, action=obj))
333 //             elif isinstance(obj, gtk.Action):
334 //                 menu.add_child(zavai.LinkButton(self, name, action=obj))
335 //             else:
336 //                 menu.add_child(zavai.LinkButton(self, name, self.label(name)))
337 // 
338 //     def label(self, name):
339 //         "Return the label for the object with the given name"
340 //         res = self.labels.get(name)
341 //         if res is not None:
342 //             return res
343 //         try:
344 //             obj = self.resource(name)
345 //             return obj.props.label
346 //         except:
347 //             return default_label(name)
348 // 
349 //     def resource(self, name):
350 //         """Get a resource from the registry.
351 // 
352 //         If no resource exists at `name` but there is a factory, instantiate the
353 //         object using the factory.
354 // 
355 //         If not even a factory exists at `name`, returns None.
356 //         """
357 //         res = self.objects.get(name, None)
358 //         if res is None:
359 //             fac = self.factories.get(name, None)
360 //             if fac is not None:
361 //                 res = self.objects[name] = fac(self, name)
362 //         return res
363 // 
364 //     def menu(self, name):
365 //         """Get a menu resource, automatically creating it if it is missing.
366 // 
367 //         Menus are created automatically linked to a parent menu, according to
368 //         the hierarchy in `name`.
369 //         """
370 //         res = self.resource(name)
371 //         if res is None:
372 //             # Check if it is a toplevel menu
373 //             if name.startswith("menu."):
374 //                 parent = get_parent(name[5:])
375 //                 if parent is not None:
376 //                     parent = "menu." + parent
377 //             else:
378 //                 parent = get_parent(name)
379 // 
380 //             res = zavai.Menu(self, name, parent)
381 //             self.register(res, name)
382 //         return res
383 // 
384 //     def shutdown(self):
385 //         """Shut down all objects in this Registry.
386 // 
387 //         After shutting down, all objects cannot be used anymore"""
388 //         for o in self.objects.itervalues():
389 //             if isinstance(o, Resource):
390 //                 o.shutdown()
391 //         self.objects.clear()
392 // 
393
394 zavai.Registry registry;
395
396 }
397