2008-08-29 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / registry.py
1 #Copyright (C) 2008 Codethink Ltd
2 #copyright: Copyright (c) 2005, 2007 IBM Corporation
3
4 #This library is free software; you can redistribute it and/or
5 #modify it under the terms of the GNU Lesser General Public
6 #License version 2 as published by the Free Software Foundation.
7
8 #This program is distributed in the hope that it will be useful,
9 #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #GNU General Public License for more details.
12 #You should have received a copy of the GNU Lesser General Public License
13 #along with this program; if not, write to the Free Software
14 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
16 #Portions of this code originally licensed and copyright (c) 2005, 2007
17 #IBM Corporation under the BSD license, available at
18 #U{http://www.opensource.org/licenses/bsd-license.php}
19
20 #authors: Peter Parente, Mark Doffman
21
22 import dbus
23 import gobject
24
25 from dbus.mainloop.glib import DBusGMainLoop
26 DBusGMainLoop(set_as_default=True)
27
28 from test import TestApplicationCache
29 from desktop import Desktop
30 from event import EventType, event_type_to_signal_reciever
31
32 class Registry(object):
33         """
34         Wraps the Accessibility.Registry to provide more Pythonic registration for
35         events. 
36         
37         This object should be treated as a singleton, but such treatment is not
38         enforced. You can construct another instance of this object and give it a
39         reference to the Accessibility.Registry singleton. Doing so is harmless and
40         has no point.
41         
42         @ivar async: Should event dispatch to local listeners be decoupled from event
43                 receiving from the registry?
44         @type async: boolean
45         @ivar reg: Reference to the real, wrapped registry object
46         @type reg: Accessibility.Registry
47         @ivar dev: Reference to the device controller
48         @type dev: Accessibility.DeviceEventController
49         @ivar queue: Queue of events awaiting local dispatch
50         @type queue: Queue.Queue
51         @ivar clients: Map of event names to client listeners
52         @type clients: dictionary
53         @ivar observers: Map of event names to AT-SPI L{_Observer} objects
54         @type observers: dictionary
55         """
56
57         _REGISTRY_NAME = 'org.freedesktop.atspi.Registry'
58
59         def __init__(self, app_name=None):
60                 """
61                 Stores a reference to the AT-SPI registry. Gets and stores a reference
62                 to the DeviceEventController.
63                 
64                 @param reg: Reference to the AT-SPI registry daemon
65                 @type reg: Accessibility.Registry
66                 """
67                 self._bus = dbus.SessionBus()
68                 if app_name:
69                         self._app_name = app_name
70                         self._cache = TestApplicationCache(self._bus, app_name)
71
72                 self._event_listeners = {}
73                 
74         def __call__(self):
75                 """
76                 @return: This instance of the registry
77                 @rtype: L{Registry}
78                 """
79                 return self
80         
81         def start(self, async=False, gil=True):
82                 """
83                 Enter the main loop to start receiving and dispatching events.
84                 
85                 @param async: Should event dispatch be asynchronous (decoupled) from 
86                         event receiving from the AT-SPI registry?
87                 @type async: boolean
88                 @param gil: Add an idle callback which releases the Python GIL for a few
89                         milliseconds to allow other threads to run? Necessary if other threads
90                         will be used in this process.
91                         Note - No Longer used.
92                 @type gil: boolean
93                 """
94                 self._loop = gobject.MainLoop()
95                 self._loop.run()
96                 try:
97                         loop.run()
98                 except KeyboardInterrupt:
99                         pass
100
101         def stop(self, *args):
102                 """Quits the main loop."""
103                 self._loop.quit()
104                 self.flushEvents()
105                 
106         def getDesktopCount(self):
107                 """
108                 Gets the number of available desktops.
109                 
110                 @return: Number of desktops
111                 @rtype: integer
112                 """
113                 return 1
114                 
115         def getDesktop(self, i):
116                 """
117                 Gets a reference to the i-th desktop.
118                 
119                 @param i: Which desktop to get
120                 @type i: integer
121                 @return: Desktop reference
122                 @rtype: Accessibility.Desktop
123                 """
124                 return Desktop(self._cache)
125
126         def registerEventListener(self, client, *names):
127                 """
128                 Registers a new client callback for the given event names. Supports 
129                 registration for all subevents if only partial event name is specified.
130                 Do not include a trailing colon.
131                 
132                 For example, 'object' will register for all object events, 
133                 'object:property-change' will register for all property change events,
134                 and 'object:property-change:accessible-parent' will register only for the
135                 parent property change event.
136                 
137                 Registered clients will not be automatically removed when the client dies.
138                 To ensure the client is properly garbage collected, call 
139                 L{deregisterEventListener}.
140
141                 @param client: Callable to be invoked when the event occurs
142                 @type client: callable
143                 @param names: List of full or partial event names
144                 @type names: list of string
145                 """
146                 try:
147                         registered = self._event_listeners[client]
148                 except KeyError:
149                         registered = []
150                         self._event_listeners[client] = registered
151
152                 for name in names:
153                         new_type = EventType(name)
154                         registered.append((new_type.name,
155                                            event_type_to_signal_reciever(self._bus, self._cache, client, new_type)))
156
157         def deregisterEventListener(self, client, *names):
158                 """
159                 Unregisters an existing client callback for the given event names. Supports 
160                 unregistration for all subevents if only partial event name is specified.
161                 Do not include a trailing colon.
162                 
163                 This method must be called to ensure a client registered by
164                 L{registerEventListener} is properly garbage collected.
165
166                 @param client: Client callback to remove
167                 @type client: callable
168                 @param names: List of full or partial event names
169                 @type names: list of string
170                 @return: Were event names specified for which the given client was not
171                         registered?
172                 @rtype: boolean
173                 """
174                 try:
175                         registered = self._event_listeners[client]
176                 except KeyError:
177                         # Presumably if were trying to deregister a client with
178                         # no names then the return type is always true.
179                         return True
180                 
181                 for name in names:
182                         remove_type = EventType(name)
183
184                         for i in range(0, len(registered)):
185                                 (type_name, signal_match) = registered[i]
186                                 registered_type = EventType(type_name)
187                                 if remove_type.is_subtype(registered_type):
188                                         signal_match.remove()
189                                         del(registered[i])
190
191                 if registered == []:
192                         del(self._event_listeners[client])