2008-09-08 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 from factory import create_accessible
21
22 __all__ = [
23            "AccessibleObjectNoLongerExists",
24            "Enum",
25            "BaseProxy",
26           ]
27
28 class AccessibleObjectNoLongerExists(Exception):
29         pass
30
31 #------------------------------------------------------------------------------
32
33 class Enum(int):
34         def __str__(self):
35                 return self._enum_lookup[int(self)]
36
37 #------------------------------------------------------------------------------
38
39
40 class BaseProxyMeta(type):
41         def __new__(meta, *args, **kwargs):
42                 cls = type.__new__(meta, *args, **kwargs)
43
44                 queryable_interfaces = { 
45                         'Accessible':interfaces.ATSPI_ACCESSIBLE,
46                         'Action':interfaces.ATSPI_ACTION,
47                         'Application':interfaces.ATSPI_APPLICATION,
48                         'Collection':interfaces.ATSPI_COLLECTION,
49                         'Component':interfaces.ATSPI_COMPONENT,
50                         'Desktop':interfaces.ATSPI_DESKTOP,
51                         'Document':interfaces.ATSPI_DOCUMENT,
52                         'EditableText':interfaces.ATSPI_EDITABLE_TEXT,
53                         'Hypertext':interfaces.ATSPI_HYPERTEXT,
54                         'Hyperlink':interfaces.ATSPI_HYPERLINK,
55                         'Image':interfaces.ATSPI_IMAGE,
56                         'Selection':interfaces.ATSPI_SELECTION,
57                         'StreamableContent':interfaces.ATSPI_STREAMABLE_CONTENT,
58                         'Table':interfaces.ATSPI_TABLE,
59                         'Text':interfaces.ATSPI_TEXT,
60                         'Value':interfaces.ATSPI_VALUE,
61                 }
62
63                 def return_query(interface):
64                         def new_query(self):
65                                 return self.queryInterface(interface)
66                         return new_query
67
68                 for interface in queryable_interfaces.keys():
69                         name = 'query%s' % interface
70                         setattr(cls, name, return_query(queryable_interfaces[interface])) 
71
72                 return cls
73
74 #------------------------------------------------------------------------------
75
76 class BaseProxy(Interface):
77         """
78         The base D-Bus proxy for a remote object that implements one or more
79         of the AT-SPI interfaces.
80         """
81
82         __metaclass__ = BaseProxyMeta
83
84         def __init__(self, cache, app_name, acc_path, interface, dbus_object=None, connection=None, parent=None):
85                 """
86                 Create a D-Bus Proxy for an ATSPI interface.
87
88                 cache - ApplicationCache, where the cached data for the accessible can be obtained.
89                 app_name - D-Bus bus name of the application this accessible belongs to.
90                 acc_path - D-Bus object path of the server side accessible object.
91                 parent - Parent accessible.
92                 interface - D-Bus interface of the object. Used to decide which accessible class to instanciate.
93                 dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls.
94                 """
95                 self._cache = cache
96                 self._app_name = app_name
97                 self._acc_path = acc_path
98                 self._parent = parent
99
100                 if not dbus_object:
101                         dbus_object = connection.get_object(self._app_name, self._acc_path, introspect=False)
102                 self._dbus_object = dbus_object
103
104                 Interface.__init__(self, self._dbus_object, interface)
105
106                 self._pgetter = self.get_dbus_method("Get", dbus_interface="org.freedesktop.DBus.Properties")
107                 self._psetter = self.get_dbus_method("Set", dbus_interface="org.freedesktop.DBus.Properties")
108
109         def __getattr__(self, attr):
110                 raise AttributeError("\'%s\' has no attribute \'%s\'" % (self.__class__.__name__, attr))
111
112         def __str__(self):
113                 try:
114                         return '[%s | %s]' % (self.getRoleName(), self.name)
115                 except Exception:
116                         return '[DEAD]'
117
118         def get_dbus_method(self, *args, **kwargs):
119                 method =  Interface.get_dbus_method(self, *args, **kwargs)
120
121                 def dbus_method_func(*args, **kwargs):
122                         # TODO Need to throw an AccessibleObjectNoLongerExists exception
123                         # on D-Bus error of the same type.
124                         try:
125                                 return method(*args, **kwargs)
126                         except UnknownMethodException, e:
127                                 raise NotImplementedError(e)
128                         except DBusException, e:
129                                 raise LookupError(e)
130
131                 return dbus_method_func
132
133         @property
134         def cached_data(self):
135                 try:
136                         return self._cache[self._app_name][self._acc_path]
137                 except KeyError:
138                         raise AccessibleObjectNoLongerExists, \
139                                 'Cache data cannot be found for path %s in app %s' % (self._acc_path, self._app_name)
140
141         @property
142         def interfaces(self):
143                 return self.cached_data.interfaces
144
145         def queryInterface(self, interface):
146                 """
147                 Gets a different accessible interface for this object
148                 or raises a NotImplemented error if the given interface
149                 is not supported.
150                 """
151                 if interface in self.interfaces:
152                         return create_accessible(self._cache,
153                                                  self._app_name,
154                                                  self._acc_path,
155                                                  interface,
156                                                  dbus_object=self._dbus_object)
157                 else:
158                         raise NotImplementedError(
159                                 "%s not supported by accessible object at path %s"
160                                 % (interface, self._acc_path))
161
162 #END----------------------------------------------------------------------------