Merge branch 'master' of git+ssh://doffm@git.freedesktop.org/git/at-spi2/at-spi2...
[platform/core/uifw/at-spi2-atk.git] / pyatspi / applicationcache.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 import dbus
16
17 from accessiblecache import AccessibleCache
18 from desktop import Desktop
19 from factory import accessible_factory
20 from event import Event as _Event
21 from base import AccessibleObjectNotAvailable
22
23 from interfaces import *
24
25 __all__ = [
26            "ApplicationCache",
27            "TestApplicationCache",
28           ]
29
30 #------------------------------------------------------------------------------
31
32 class TestApplicationCache(object):
33         _DESKTOP_PATH = '/org/freedesktop/atspi/accessible/desktop'
34
35         """
36         Test application store, accesses a single application.
37
38         The store object acts as a central class for creating accessible objects.
39         It interfaces with the ATSPI registry to keep account of all accessible
40         applications. It contains the accessible cache objects from each application.
41
42         @registry:   Each accessible cache object must have a reference to the registry
43                      object to send update events.
44
45         @connection: D-Bus connection used to access applications.
46
47         @bus_name:   The test store only accesses one accessible application, this is its
48                      D-Bus path.
49         """
50
51         def __init__(self, registry, connection, bus_name):
52                 self._connection = connection
53
54                 self.application_list = [bus_name]
55                 self.application_cache = {bus_name:AccessibleCache(registry, connection, bus_name)}
56
57         def get_cache_data(self, app_name, acc_path):
58                 """
59                 Returns the cache tuple for the given application and accessible
60                 object path. Throws an IndexError if the cache data is not found.
61                 """
62                 return self.application_cache[app_name][acc_path]
63
64         def create_application(self, app_name):
65                 """
66                 Creates an accessible object for the root of the application
67                 available at the given D-Bus name.
68                 """
69                 cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
70                 try:
71                         return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
72                 except KeyError:
73                         raise AccessibleObjectNotAvailable ()
74
75         def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
76                 """
77                 Creates an accessible object.
78
79                 @app_name: D-Bus name of the application where the accessible object resides.
80
81                 @acc_path: D-Bus path of the object within the application.
82
83                 @interface: D-Bus interface of the requested object. A different accessible object
84                             class will be created depending on this. Making the function much like 
85                             an accessible object factory.
86
87                 @dbus_object: If a D-Bus object already exists for the accessible object it can be
88                               provided here so that another one is not created.
89                 """
90                 # An acc_path of '/' implies the desktop object, whatever the app_name.
91                 if acc_path == TestApplicationCache._DESKTOP_PATH:
92                         return Desktop(self)
93                 else:
94                         cls = accessible_factory.get_accessible_class(interface)
95                         try:
96                                 return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
97                         except KeyError:
98                                 raise AccessibleObjectNotAvailable ()
99
100         @property
101         def connection(self):
102                 """
103                 D-Bus connection used by the store.
104                 """
105                 return self._connection
106
107 #------------------------------------------------------------------------------
108
109 class ApplicationCache(object):
110         """
111         Test application store, accesses a single application.
112
113         The store object acts as a central class for creating accessible objects.
114         It interfaces with the ATSPI registry to keep account of all accessible
115         applications. It contains the accessible cache objects from each application.
116
117         @registry:   Each accessible cache object must have a reference to the registry
118                      object to send update events.
119
120         @connection: D-Bus connection used to access applications.
121
122         @bus_name:   The test store only accesses one accessible application, this is its
123                      D-Bus path.
124         """
125
126         # An accessible path of '/' implies the desktop object, whatever the application name.
127         _DESKTOP_PATH = '/org/freedesktop/atspi/accessible/root'
128
129         _APPLICATIONS_ADD = 1
130         _APPLICATIONS_REMOVE = 0
131
132         def __init__(self, registry, connection):
133                 self._connection = connection
134                 self._registry = registry
135
136                 self.application_list = []
137                 self.application_cache = {}
138
139                 self._regsig = connection.add_signal_receiver(self.update_handler,
140                                                               dbus_interface=ATSPI_REGISTRY_INTERFACE,
141                                                               signal_name="updateApplications")
142
143                 obj = connection.get_object(ATSPI_REGISTRY_NAME,
144                                             ATSPI_REGISTRY_PATH,
145                                             introspect=False)
146                 self._app_register = dbus.Interface(obj, ATSPI_REGISTRY_INTERFACE)
147
148                 self.application_list.extend(self._app_register.getApplications())
149                 for bus_name in self.application_list:
150                         self.application_cache[bus_name] = AccessibleCache(self._registry, self._connection, bus_name)
151
152         def update_handler (self, update_type, bus_name):
153                 if update_type == ApplicationCache._APPLICATIONS_ADD:
154                         #TODO Check that app does not already exist
155                         self.application_list.append(bus_name)
156                         self.application_cache[bus_name] = AccessibleCache(self._registry, self._connection, bus_name)
157                         event = _Event(self,
158                                        ApplicationCache._DESKTOP_PATH,
159                                        ATSPI_REGISTRY_NAME,
160                                        "org.freedesktop.atspi.Event.Object",
161                                        "children-changed",
162                                        ("add", 0, 0, ""))
163                 elif update_type == ApplicationCache._APPLICATIONS_REMOVE:
164                         #TODO Fail safely if app does not exist
165                         self.application_list.remove(bus_name)
166                         del(self.application_cache[bus_name])
167                         event = _Event(self,
168                                        ApplicationCache._DESKTOP_PATH,
169                                        ATSPI_REGISTRY_NAME,
170                                        "org.freedesktop.atspi.Event.Object",
171                                        "children-changed",
172                                        ("remove", 0, 0, ""))
173
174                 self._registry._notifyChildrenChange(event)
175
176         def get_cache_data(self, app_name, acc_path):
177                 """
178                 Returns the cache tuple for the given application and accessible
179                 object path. Throws an IndexError if the cache data is not found.
180                 """
181                 return self.application_cache[app_name][acc_path]
182
183         def create_application(self, app_name):
184                 """
185                 Creates an accessible object for the root of the application
186                 available at the given D-Bus name.
187                 """
188                 if app_name == ATSPI_REGISTRY_NAME:
189                         return Desktop(self)
190                 else:
191                         cls = accessible_factory.get_accessible_class(ATSPI_APPLICATION)
192                         try:
193                                 return cls(app_name, self.application_cache[app_name].root, self, ATSPI_APPLICATION)
194                         except KeyError:
195                                 raise AccessibleObjectNotAvailable ()
196
197         def create_accessible(self, app_name, acc_path, interface, dbus_object=None):
198                 """
199                 Creates an accessible object.
200
201                 @app_name: D-Bus name of the application where the accessible object resides.
202
203                 @acc_path: D-Bus path of the object within the application.
204
205                 @interface: D-Bus interface of the requested object. A different accessible object
206                             class will be created depending on this. Making the function much like 
207                             an accessible object factory.
208
209                 @dbus_object: If a D-Bus object already exists for the accessible object it can be
210                               provided here so that another one is not created.
211                 """
212                 if acc_path == ApplicationCache._DESKTOP_PATH:
213                         return Desktop(self)
214                 else:
215                         cls = accessible_factory.get_accessible_class(interface)
216                         try:
217                                 return cls(app_name, acc_path, self, interface, dbus_object=dbus_object)
218                         except KeyError:
219                                 raise AccessibleObjectNotAvailable ()
220
221         @property
222         def connection(self):
223                 """
224                 D-Bus connection used by the store.
225                 """
226                 return self._connection
227
228 #END----------------------------------------------------------------------------