1 #Copyright (C) 2008 Codethink Ltd
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.
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.
17 from event import Event as _Event
19 #------------------------------------------------------------------------------
21 class _CacheData(object):
33 def __init__(self, data):
36 def _update(self, data):
46 #------------------------------------------------------------------------------
48 def _list_items_added_removed (l1, l2):
50 Returns a tuple (boolean, boolean).
51 The first value indicates if, when
52 moving from l1 to l2, any items have been added.
53 The second value indicates whether any items have
56 l1notl2 = [item for item in l1 if item not in l2]
57 l2notl1 = [item for item in l2 if item not in l1]
58 return ((len(l1notl2) > 0), (len(l2notl1) > 0))
60 #------------------------------------------------------------------------------
62 class AccessibleCache(object):
64 There is one accessible cache per application.
65 For each application the accessible cache stores
66 data on every accessible object within the app.
68 It also acts as the factory for creating client
69 side proxies for these accessible objects.
71 connection - DBus connection.
72 busName - Name of DBus connection where cache interface resides.
75 _PATH = '/org/freedesktop/atspi/tree'
76 _INTERFACE = 'org.freedesktop.atspi.Tree'
77 _GET_METHOD = 'getTree'
78 _UPDATE_SIGNAL = 'updateAccessible'
79 _REMOVE_SIGNAL = 'removeAccessible'
81 def __init__(self, registry, connection, bus_name):
85 connection - DBus connection.
86 busName - Name of DBus connection where cache interface resides.
88 self._registry = registry
89 self._connection = connection
90 self._bus_name = bus_name
92 obj = connection.get_object(bus_name, self._PATH, introspect=False)
93 self._tree_itf = _dbus.Interface(obj, self._INTERFACE)
97 get_method = self._tree_itf.get_dbus_method(self._GET_METHOD)
98 self._update_objects(get_method())
100 self._updateMatch = self._tree_itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_single)
101 self._removeMatch = self._tree_itf.connect_to_signal(self._REMOVE_SIGNAL, self._remove_object)
103 self._root = self._tree_itf.getRoot()
105 def __getitem__(self, key):
106 return self._objects[key]
108 def __contains__(self, key):
109 return key in self._objects
111 def _dispatch_event(self, olddata, newdata):
112 if olddata.name != newdata.name:
113 event = _Event(self._registry.cache,
116 "org.freedesktop.atspi.Event.Object",
118 ("accessible-name", 0, 0, newdata.name))
119 self._registry._notifyNameChange(event)
121 if olddata.description != newdata.description:
122 event = _Event(self._registry.cache,
125 "org.freedesktop.atspi.Event.Object",
127 ("accessible-description", 0, 0, newdata.description))
128 self._registry._notifyDescriptionChange(event)
130 if olddata.parent != newdata.parent:
131 event = _Event(self._registry.cache,
134 "org.freedesktop.atspi.Event.Object",
136 ("accessible-parent", 0, 0, ""))
137 self._registry._notifyParentChange(event)
139 removed, added = _list_items_added_removed (olddata.children, newdata.children)
142 event = _Event(self._registry.cache,
145 "org.freedesktop.atspi.Event.Object",
148 self._registry._notifyChildrenChange(event)
151 event = _Event(self._registry.cache,
154 "org.freedesktop.atspi.Event.Object",
156 ("remove", 0, 0, ""))
157 self._registry._notifyChildrenChange(event)
159 # TODO This should be the other way around. Single is more common than many.
160 def _update_single(self, object):
161 self._update_objects ([object])
163 def _update_objects(self, objects):
164 cache_update_objects = []
166 #First element is the object path.
168 if path in self._objects:
169 olddata = self._objects[path]
170 newdata = _CacheData(data)
171 cache_update_objects.append((olddata, newdata))
172 self._objects[path] = newdata
174 self._objects[path] = _CacheData(data)
175 for old, new in cache_update_objects:
176 self._dispatch_event(old, new)
178 def _remove_object(self, path):
179 # TODO I'm squashing a possible error here
180 # I've seen things appear to be deleted twice
181 # which needs investigation
183 del(self._objects[path])
191 get_method = self._tree_itf.get_dbus_method(self._GET_METHOD)
192 self._update_objects(get_method())
194 root = property(fget=_get_root)
196 #END---------------------------------------------------------------------------