1 #Copyright (C) 2008 Codethink Ltd
3 #his program is free software; you can redistribute it and/or modify
4 #it under the terms of the GNU General Public License as published by
5 #the Free Software Foundation; either version 2 of the License, or
6 #(at your option) any later version.
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 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.
18 from weakref import ref
19 from dbus.proxies import Interface, ProxyObject
21 ATSPI_ACCESSIBLE = 'org.freedesktop.atspi.Accessible'
22 ATSPI_ACCESSIBLE_TREE = 'org.freedesktop.atspi.Tree'
24 class AccessibleObjectDoesNotExist(Exception):
27 class AccessibleInterfaceNotSupported(Exception):
30 class _CacheData(object):
41 def __init__(self, data):
45 def update(self, data):
46 #Don't cache the path here, used as lookup in cache object dict.
53 self.description) = data
55 class AccessibleCache(object):
57 Caches data for a collection of accessible object proxies.
59 The cache only holds a weak reference to any D-Bus proxies,
60 they are created on demand.
63 _UPDATE_SIGNAL = 'updateTree'
65 def __init__(self, connection, busName, objectStorePath):
67 Creates a cache for accessible object data.
69 All accessible object proxies are created and accessed through this cache.
71 connection - DBus connection.
72 busName - DBus bus name where accessible tree resides.
73 objectStorePath - Path where the accessible tree can be accessed.
75 storeObject = connection.get_object(busName, objectStorePath)
77 self._connection = connection
78 self._busName = busName
79 self._accessibleStore = dbus.Interface(storeObject, ATSPI_ACCESSIBLE_TREE)
81 #TODO are we caching the root accessible or not?
82 #Do we need a roundtrip to access this?
83 #Does the root accessible ever change?
84 #Probably not as the root accessible is the 'Application'
85 self._root = self._accessibleStore.getRoot()
87 self._updateObjects(self._accessibleStore.getTree())
89 #Connect to update signal
90 self._signalMatch = self._accessibleStore.connect_to_signal(self._UPDATE_SIGNAL,
93 def getRootAccessible(self):
95 Gets the accessible object at the root of the tree.
97 return self.getAccessible(self._root)
99 def getAccessible(self, path):
101 Gets a D-Bus proxy object for the given object path.
103 path - The D-Bus path of the remote object.
105 if path in self._objects:
106 cachedata = self._objects[path]
108 if cachedata.proxy is not None:
109 proxy = cachedata.proxy()
111 proxy = AccessibleObjectProxy(self,
116 cachedata.proxy = ref(proxy)
119 raise AccessibleObjectDoesNotExist, "D-Bus reference not found in cache"
121 def _updateObjects(self, objects):
123 Updates the object cache from an
124 array of accessible object cache data.
127 #First element is the object path.
129 if path in self._objects:
130 cachedata = self._objects[path]
131 cachedata.update(data)
133 self._objects[path] = _CacheData(data)
135 def _removeObjects(self, paths):
137 Removes the object data from the cache.
140 del(self._objects[path])
142 def _updateHandler(self, updates):
143 objects, paths = updates
144 self._removeObjects(paths)
145 self._updateObjects(objects)
147 class AccessibleObjectProxy(ProxyObject):
149 A D-Bus proxy for a remote object that implements one or more of the AT-SPI
150 Accessibility interfaces.
153 def __init__(self, cache, data, connection, busName, path):
155 Create an accessible object.
157 cache - The accessible cache that this object is owned by.
158 connection - The D-Bus connection
159 busName - The location on the bus where the remote object is located.
160 path - The object path of the remote object.
162 ProxyObject.__init__(self, connection, busName, path, introspect=False)
170 data = self._cache._objects[self._path]
172 raise AccessibleObjectDoesNotExist(
173 'Cache data cannot be found for path %s' % (self._path,))
182 return self._cache.getAccessible(self._data.parent)
185 def numChildren(self):
186 return len(self._data.children)
188 def getChild(self, index):
189 return self._cache.getAccessible(self._data.children[index])
192 def interfaces(self):
193 return self._data.interfaces
195 def getInterface(self, interface):
196 if interface in self._data.interfaces:
197 return Interface(self, interface)
199 raise AccessibleInterfaceNotSupported(
200 "%s not supported by accessible object at path %s"
201 % (interface, self.path))
205 return self._data.name
209 return self._data.role
212 def description(self):
213 return self._data.description