From a0c063906bb833e8151ff24f6e50d21f6396e83c Mon Sep 17 00:00:00 2001 From: Mark Doffman Date: Tue, 9 Jun 2009 11:30:13 +0100 Subject: [PATCH] Refactor the application cache to send signals when the cache is updated. --- pyatspi/applicationcache.py | 158 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) diff --git a/pyatspi/applicationcache.py b/pyatspi/applicationcache.py index 2eeb27b..a631e84 100644 --- a/pyatspi/applicationcache.py +++ b/pyatspi/applicationcache.py @@ -111,6 +111,156 @@ class TestApplicationCache(object): #------------------------------------------------------------------------------ +def _list_items_added_removed (l1, l2): + """ + Returns a tuple (boolean, boolean). + The first value indicates if, when + moving from l1 to l2, any items have been added. + The second value indicates whether any items have + been removed. + """ + l1notl2 = [item for item in l1 if item not in l2] + l2notl1 = [item for item in l2 if item not in l1] + return ((len(l1notl2) > 0), (len(l2notl1) > 0)) + +#------------------------------------------------------------------------------ + +class AccessibleCache(object): + """ + There is one accessible cache per application. + For each application the accessible cache stores + data on every accessible object within the app. + + It also acts as the factory for creating client + side proxies for these accessible objects. + + connection - DBus connection. + busName - Name of DBus connection where cache interface resides. + """ + + _PATH = '/org/freedesktop/atspi/tree' + _INTERFACE = 'org.freedesktop.atspi.Tree' + _GET_METHOD = 'getTree' + _UPDATE_SIGNAL = 'updateAccessible' + _REMOVE_SIGNAL = 'removeAccessible' + + def __init__(self, registry, connection, bus_name): + """ + Creates a cache. + + connection - DBus connection. + busName - Name of DBus connection where cache interface resides. + """ + self._registry = registry + self._connection = connection + self._bus_name = bus_name + + obj = connection.get_object(bus_name, self._PATH, introspect=False) + self._tree_itf = _dbus.Interface(obj, self._INTERFACE) + + self._objects = {} + + get_method = self._tree_itf.get_dbus_method(self._GET_METHOD) + self._update_objects(get_method()) + + self._updateMatch = self._tree_itf.connect_to_signal(self._UPDATE_SIGNAL, self._update_single) + self._removeMatch = self._tree_itf.connect_to_signal(self._REMOVE_SIGNAL, self._remove_object) + + self._root = self._tree_itf.getRoot() + + def __getitem__(self, key): + return self._objects[key] + + def __contains__(self, key): + return key in self._objects + + def _dispatch_event(self, olddata, newdata): + if olddata.name != newdata.name: + event = _Event(self._registry.cache, + newdata.path, + self._bus_name, + "org.freedesktop.atspi.Event.Object", + "property-change", + ("accessible-name", 0, 0, newdata.name)) + self._registry._notifyNameChange(event) + + if olddata.description != newdata.description: + event = _Event(self._registry.cache, + newdata.path, + self._bus_name, + "org.freedesktop.atspi.Event.Object", + "property-change", + ("accessible-description", 0, 0, newdata.description)) + self._registry._notifyDescriptionChange(event) + + if olddata.parent != newdata.parent: + event = _Event(self._registry.cache, + newdata.path, + self._bus_name, + "org.freedesktop.atspi.Event.Object", + "property-change", + ("accessible-parent", 0, 0, "")) + self._registry._notifyParentChange(event) + + removed, added = _list_items_added_removed (olddata.children, newdata.children) + + if added: + event = _Event(self._registry.cache, + newdata.path, + self._bus_name, + "org.freedesktop.atspi.Event.Object", + "children-changed", + ("add", 0, 0, "")) + self._registry._notifyChildrenChange(event) + + if removed: + event = _Event(self._registry.cache, + newdata.path, + self._bus_name, + "org.freedesktop.atspi.Event.Object", + "children-changed", + ("remove", 0, 0, "")) + self._registry._notifyChildrenChange(event) + + # TODO This should be the other way around. Single is more common than many. + def _update_single(self, object): + self._update_objects ([object]) + + def _update_objects(self, objects): + cache_update_objects = [] + for data in objects: + #First element is the object path. + path = data[0] + if path in self._objects: + olddata = self._objects[path] + newdata = _CacheData(data) + cache_update_objects.append((olddata, newdata)) + self._objects[path] = newdata + else: + self._objects[path] = _CacheData(data) + for old, new in cache_update_objects: + self._dispatch_event(old, new) + + def _remove_object(self, path): + # TODO I'm squashing a possible error here + # I've seen things appear to be deleted twice + # which needs investigation + try: + del(self._objects[path]) + except KeyError: + pass + + def _get_root(self): + return self._root + + def _refresh(self): + get_method = self._tree_itf.get_dbus_method(self._GET_METHOD) + self._update_objects(get_method()) + + root = property(fget=_get_root) + +#END--------------------------------------------------------------------------- + class ApplicationCache(object): """ Test application store, accesses a single application. @@ -234,7 +384,11 @@ class ApplicationCache(object): return self._connection def _refresh(self): - self.application_list = [] - self.application_list.extend(self._app_register.getApplications()) + app_addresses = self._app_register.getApplications() + added, removed = _list_items_added_removed (self.application_list, app_addresses) + for item in added: + self.update_handler (ApplicationsCache._APPLICATIONS_ADD, item): + for item in removed: + self.update_handler (ApplicationsCache._APPLICATIONS_REMOVE, item): #END---------------------------------------------------------------------------- -- 2.7.4