+#------------------------------------------------------------------------------
+
+class ApplicationCache(object):
+ """
+ Test application store, accesses a single application.
+
+ The store object acts as a central class for creating accessible objects.
+ It interfaces with the ATSPI registry to keep account of all accessible
+ applications. It contains the accessible cache objects from each application.
+
+ @registry: Each accessible cache object must have a reference to the registry
+ object to send update events.
+
+ @connection: D-Bus connection used to access applications.
+
+ @bus_name: The test store only accesses one accessible application, this is its
+ D-Bus path.
+ """
+
+ # An accessible path of '/' implies the desktop object, whatever the application name.
+ _DESKTOP_PATH = '/'
+
+ _APPLICATIONS_ADD = 1
+ _APPLICATIONS_REMOVE = 0
+
+ def __init__(self, registry, connection):
+ self._connection = connection
+ self._registry = registry
+
+ self.application_list = []
+ self.application_cache = {}
+
+ self._regsig = connection.add_signal_receiver(self.update_handler,
+ dbus_interface=ATSPI_REGISTRY_INTERFACE,
+ signal_name="updateApplications")
+
+ obj = connection.get_object(ATSPI_REGISTRY_NAME,
+ ATSPI_REGISTRY_PATH,
+ introspect=False)
+ self._app_register = dbus.Interface(obj, ATSPI_REGISTRY_INTERFACE)
+
+ self.application_list.extend(self._app_register.getApplications())
+ for bus_name in self.application_list:
+ self.application_cache[bus_name] = AccessibleCache(self._registry, self._connection, bus_name)
+
+ def update_handler (self, update_type, bus_name):
+ if update_type == ApplicationCache._APPLICATIONS_ADD:
+ #TODO Check that app does not already exist
+ self.application_list.append(bus_name)
+ self.application_cache[bus_name] = AccessibleCache(self._registry, self._connection, bus_name)
+ event = _Event(self,
+ ApplicationCache._DESKTOP_PATH,
+ ATSPI_REGISTRY_NAME,
+ "org.freedesktop.atspi.Event.Object",
+ "children-changed",
+ ("add", 0, 0, ""))
+ elif update_type == ApplicationCache._APPLICATIONS_REMOVE:
+ #TODO Fail safely if app does not exist
+ self.application_list.remove(bus_name)
+ del(self.application_cache[bus_name])
+ event = _Event(self,
+ ApplicationCache._DESKTOP_PATH,
+ ATSPI_REGISTRY_NAME,
+ "org.freedesktop.atspi.Event.Object",
+ "children-changed",
+ ("remove", 0, 0, ""))
+
+ self._registry._notifyChildrenChange(event)
+
+ def get_cache_data(self, app_name, acc_path):
+ """
+ Returns the cache tuple for the given application and accessible
+ object path. Throws an IndexError if the cache data is not found.
+ """
+ return self.application_cache[app_name][acc_path]
+
+ def create_application(self, app_name):
+ """
+ Creates an accessible object for the root of the application
+ available at the given D-Bus name.
+ """
+ if app_name == ATSPI_REGISTRY_NAME:
+ return Desktop(self)
+ else:
+ cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
+ return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
+
+ def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
+ """
+ Creates an accessible object.
+
+ @app_name: D-Bus name of the application where the accessible object resides.
+
+ @acc_path: D-Bus path of the object within the application.
+
+ @interface: D-Bus interface of the requested object. A different accessible object
+ class will be created depending on this. Making the function much like
+ an accessible object factory.
+
+ @dbus_object: If a D-Bus object already exists for the accessible object it can be
+ provided here so that another one is not created.
+ """
+ if acc_path == ApplicationCache._DESKTOP_PATH:
+ return Desktop(self)
+ else:
+ cls = accessible_factory.get_accessible_class(interface)
+ return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
+
+ @property
+ def connection(self):
+ """
+ D-Bus connection used by the store.
+ """
+ return self._connection
+