2009-04-23 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / base.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 from dbus.proxies import Interface
17 from dbus.exceptions import *
18
19 import interfaces
20
21 __all__ = [
22            "AccessibleObjectNoLongerExists",
23            "AccessibleObjectNotAvailable",
24            "Enum",
25            "BaseProxy",
26            "_repack_tuple",
27           ]
28
29 class AccessibleObjectNoLongerExists(Exception):
30         pass
31
32 class AccessibleObjectNotAvailable(Exception):
33         pass
34
35 #------------------------------------------------------------------------------
36
37 def _repack_tuple (tup):
38         """
39         Re-packs a tuple moving the last element to the beginning.
40         """
41         return (tup[-1] ,) + tup[:-1]
42
43 #------------------------------------------------------------------------------
44
45 class Enum(dbus.UInt32):
46         def __str__(self):
47                 return self._enum_lookup[int(self)]
48
49         def __eq__(self, other):
50                 if int(self) == int(other):
51                         return True
52                 else:
53                         return False
54
55         def __hash__(self):
56                 return int(self)
57
58 #------------------------------------------------------------------------------
59
60
61 class BaseProxyMeta(type):
62         def __new__(meta, *args, **kwargs):
63                 cls = type.__new__(meta, *args, **kwargs)
64
65                 queryable_interfaces = { 
66                         'Accessible':interfaces.ATSPI_ACCESSIBLE,
67                         'Action':interfaces.ATSPI_ACTION,
68                         'Application':interfaces.ATSPI_APPLICATION,
69                         'Collection':interfaces.ATSPI_COLLECTION,
70                         'Component':interfaces.ATSPI_COMPONENT,
71                         'Desktop':interfaces.ATSPI_DESKTOP,
72                         'Document':interfaces.ATSPI_DOCUMENT,
73                         'EditableText':interfaces.ATSPI_EDITABLE_TEXT,
74                         'Hypertext':interfaces.ATSPI_HYPERTEXT,
75                         'Hyperlink':interfaces.ATSPI_HYPERLINK,
76                         'Image':interfaces.ATSPI_IMAGE,
77                         'Selection':interfaces.ATSPI_SELECTION,
78                         'StreamableContent':interfaces.ATSPI_STREAMABLE_CONTENT,
79                         'Table':interfaces.ATSPI_TABLE,
80                         'Text':interfaces.ATSPI_TEXT,
81                         'Value':interfaces.ATSPI_VALUE,
82                 }
83
84                 def return_query(interface):
85                         def new_query(self):
86                                 return self.queryInterface(interface)
87                         return new_query
88
89                 for interface in queryable_interfaces.keys():
90                         name = 'query%s' % interface
91                         setattr(cls, name, return_query(queryable_interfaces[interface])) 
92
93                 return cls
94
95 #------------------------------------------------------------------------------
96
97 class BaseProxy(object):
98         """
99         The base D-Bus proxy for a remote object that implements one or more
100         of the AT-SPI interfaces.
101         """
102
103         __metaclass__ = BaseProxyMeta
104
105         def __init__(self, app_name, acc_path, cache, interface, dbus_object=None):
106                 """
107                 Create a D-Bus Proxy for an ATSPI interface.
108
109                 cache - ApplicationCache, where the cached data for the accessible can be obtained.
110                 app_name - D-Bus bus name of the application this accessible belongs to.
111                 acc_path - D-Bus object path of the server side accessible object.
112                 parent - Parent accessible.
113                 dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls.
114                 """
115                 self._cache = cache
116                 self._app_name = app_name
117                 self._acc_path = acc_path
118                 self._dbus_interface = interface
119
120                 if not dbus_object:
121                         dbus_object = cache.connection.get_object(self._app_name,
122                                                                   self._acc_path,
123                                                                   introspect=False)
124                 self._dbus_object = dbus_object
125
126                 self._pgetter = self.get_dbus_method("Get",
127                                                      dbus_interface="org.freedesktop.DBus.Properties")
128                 self._psetter = self.get_dbus_method("Set",
129                                                      dbus_interface="org.freedesktop.DBus.Properties")
130
131         def __str__(self):
132                     try:
133                               return '[%s | %s]' % (self.getRoleName(), self.name)
134                     except Exception:
135                               return '[DEAD]'
136
137         def __eq__(self, other):
138                 if other is None:
139                         return False
140                 try:
141                         if self._app_name == other._app_name and \
142                            self._acc_path == other._acc_path:
143                                 return True
144                         else:
145                                 return False
146                 except AttributeError:
147                         return False
148
149         def __ne__(self, other):
150                 return not self.__eq__(other)
151
152         def __hash__(self):
153                 return hash(self._app_name + self._acc_path)
154
155         def get_dbus_method(self, *args, **kwargs):
156                 method =  self._dbus_object.get_dbus_method(*args, **kwargs)
157
158                 def dbus_method_func(*iargs, **ikwargs):
159                         # TODO Need to throw an AccessibleObjectNoLongerExists exception
160                         # on D-Bus error of the same type.
161                         try:
162                                 return method(*iargs, **ikwargs)
163                         except UnknownMethodException, e:
164                                 raise NotImplementedError(e)
165                         except DBusException, e:
166                                 raise LookupError(e)
167
168                 return dbus_method_func
169
170         @property
171         def cached_data(self):
172                 try:
173                         return self._cache.get_cache_data(self._app_name, self._acc_path)
174                 except KeyError:
175                         raise AccessibleObjectNoLongerExists, \
176                                 'Cache data cannot be found for path %s in app %s' % (self._acc_path, self._app_name)
177
178         @property
179         def interfaces(self):
180                 return self.cached_data.interfaces
181
182         def queryInterface(self, interface):
183                 """
184                 Gets a different accessible interface for this object
185                 or raises a NotImplemented error if the given interface
186                 is not supported.
187                 """
188                 if interface in self.interfaces:
189                         return self._cache.create_accessible(self._app_name,
190                                                              self._acc_path,
191                                                              interface,
192                                                              dbus_object=self._dbus_object)
193                 else:
194                         raise NotImplementedError(
195                                 "%s not supported by accessible object at path %s"
196                                 % (interface, self._acc_path))
197
198         def flushCache(self):
199                 pass
200
201 #END----------------------------------------------------------------------------