2008-07-28 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/upstream/at-spi2-core.git] / pyatspi / cache.py
1 #Copyright (C) 2008 Codethink Ltd
2
3 #This library is free software; you can redistribute it and/or
4 #modify it under the terms of the GNU Lesser General Public
5 #License version 2 as published by the Free Software Foundation.
6
7 #This program is distributed in the hope that it will be useful,
8 #but WITHOUT ANY WARRANTY; without even the implied warranty of
9 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 #GNU General Public License for more details.
11 #You should have received a copy of the GNU Lesser General Public License
12 #along with this program; if not, write to the Free Software
13 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
15 from base import AccessibleObjectNoLongerExists
16 from factory import interfaceFactory
17
18 ATSPI_DEVICE_EVENT_CONTROLLER = 'org.freedesktop.atspi.DeviceEventController'
19 ATSPI_DEVICE_EVENT_LISTENER = 'org.freedesktop.atspi.DeviceEventListener'
20 ATSPI_REGISTRY = 'org.freedesktop.atspi.Registry'
21 ATSPI_TREE = 'org.freedesktop.atspi.Tree'
22
23 #------------------------------------------------------------------------------
24
25 class _CacheData(object):
26         __slots__ =     [
27                         'parent',
28                         'interfaces',
29                         'children',
30                         'role',
31                         'name',
32                         'description',
33                         ]
34
35         def __init__(self, data):
36                 self.update(data)
37
38         def update(self, data):
39                 #Don't cache the path here, used as lookup in cache object dict.
40                 (path,
41                 self.parent,
42                 self.children,
43                 self.interfaces,
44                 self.name,
45                 self.role,
46                 self.description) = data
47
48 #------------------------------------------------------------------------------
49
50 class ApplicationCache(object):
51         """
52         Caches the bus names of all applications.
53
54         Makes calls and recieves updates from the registry
55         daemon to keep the cache up to date.
56         """
57
58         _UPDATE_SIGNAL = 'updateApplications'
59         _REGISTRY_PATH = '/org/freedesktop/atspi/registry'
60
61         def __init__(self, connection, busName):
62                 """
63                 Create a cache for application caches.
64                 BusName -> ApplicationCache
65                 """
66                 self._connection = connection
67                 self._busName = busName
68
69                 registryObject = connection.get_object(busName, self._REGISTRY_PATH)
70                 registryInterface = dbus.Interface(registryObject, ATSPI_REGISTRY)
71
72                 self._applications = {}
73
74                 self._updateApplications(registryInterface.getApplications())
75
76                 #Connect to update signal
77                 self._signalMatch = self.registryInterface.connect_to_signal(self._UPDATE_SIGNAL,
78                                                                              self._updateHandler)
79
80         def _updateApplications(self, names):
81                 """
82                 Updates the application cache from an
83                 array of dbus bus names.
84                 """
85                 for name in names:
86                         if name not in self._applications:
87                                 self._applications[name] = AccessibleCache(self._connection,
88                                                                         self._busName,
89                                                                         self._treePath)
90
91         def _removeApplications(self, names):
92                 """
93                 Removes application caches given by names.
94                 """
95                 for name in names:
96                         del(self._applications[name])
97
98         def _updateHandler(self, updates):
99                 update, remove = updates
100                 self._removeApplications(update)
101                 self._updateApplications(remove)
102
103 #------------------------------------------------------------------------------
104
105 class AccessibleCache(object):
106         """
107         Caches data for a collection of accessible object proxies.
108
109         Makes calls and recieves updates from applications
110         to update the cache of accessible object data.
111         """
112
113         _UPDATE_SIGNAL = 'updateTree'
114         _TREE_PATH = '/org/freedesktop/atspi/tree'
115
116
117         def __init__(self, connection, busName):
118                 """
119                 Creates a cache for accessible object data.
120
121                 All accessible object proxies are created and accessed through this cache.
122
123                 connection - DBus connection.
124                 busName - DBus bus name where accessible tree resides.
125                 """
126                 self._connection = connection
127                 self._busName = busName
128
129                 treeObject = connection.get_object(busName, self._TREE_PATH)
130                 treeInterface = dbus.Interface(treeObject, ATSPI_TREE)
131
132                 self._objects = {}
133
134                 self._root = treeObject.getRoot()
135                 self._updateObjects(treeInterface.getTree())
136
137                 #Connect to update signal
138                 self._signalMatch = treeInterface.connect_to_signal(self._UPDATE_SIGNAL,
139                                                                     self._updateHandler)
140
141         def getRootAccessible(self):
142                 """
143                 Gets the accessible object at the root of the tree.
144                 """
145                 return self.getAccessible(self._root)
146
147         def getAccessible(self, path):
148                 """
149                 Gets a D-Bus proxy object for the given object path.
150                 The object that is returned implements the accessible
151                 interface.
152
153                 path - The D-Bus path of the remote object.
154                 """
155                 if path in self._objects:
156                         proxy = self._connection.get_object(self._busName, path, introspect=False)
157                         return interfaceFactory(proxy, self, self._busName, path, ATSPI_ACCESSIBLE)
158                 else:
159                         raise AccessibleObjectNoLongerExists, "D-Bus reference not found in cache"
160
161         def _updateObjects(self, objects):
162                 """
163                 Updates the object cache from an
164                 array of accessible object cache data.
165                 """
166                 for data in objects:
167                         #First element is the object path.
168                         path = data[0]
169                         if path in self._objects:
170                                 cachedata = self._objects[path]
171                                 cachedata.update(data)
172                         else:
173                                 self._objects[path] = _CacheData(data)
174
175         def _removeObjects(self, paths):
176                 """
177                 Removes the object data from the cache.
178                 """
179                 for path in paths:
180                         del(self._objects[path])
181
182         def _updateHandler(self, updates):
183                 update, remove = updates
184                 self._removeObjects(update)
185                 self._updateObjects(remove)
186
187 #END---------------------------------------------------------------------------