2008-11-13 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / event.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 interfaces
16 from accessible import BoundingBox
17
18 __all__ = [
19                 "Event",
20                 "EventType",
21                 "event_type_to_signal_reciever",
22           ]
23
24 #------------------------------------------------------------------------------
25
26 _interface_to_klass = {
27                 "org.freedesktop.atspi.Event.Object":"object",
28                 "org.freedesktop.atspi.Event.Window":"window",
29                 "org.freedesktop.atspi.Event.Mouse":"mouse",
30                 "org.freedesktop.atspi.Event.Terminal":"terminal",
31                 "org.freedesktop.atspi.Event.Document":"document",
32                 "org.freedesktop.atspi.Event.Focus":"focus",
33                 }
34
35 _klass_to_interface = {
36                 "object":"org.freedesktop.atspi.Event.Object",
37                 "window":"org.freedesktop.atspi.Event.Window",
38                 "mouse":"org.freedesktop.atspi.Event.Mouse",
39                 "terminal":"org.freedesktop.atspi.Event.Terminal",
40                 "document":"org.freedesktop.atspi.Event.Document",
41                 "focus":"org.freedesktop.atspi.Event.Focus",
42                 }
43
44 #------------------------------------------------------------------------------
45
46 class _ELessList(list):
47         def __getitem__(self, index):
48                 try:
49                         return list.__getitem__(self, index)
50                 except IndexError:
51                         return None
52
53 class EventType(str):
54         """
55         Wraps the AT-SPI event type string so its components can be accessed 
56         individually as klass (can't use the keyword class), major, minor, and detail 
57         (klass_major_minor_detail).
58
59         @note: All attributes of an instance of this class should be considered 
60                 public readable as it is acting a a struct.
61         @ivar klass: Most general event type identifier (object, window, mouse, etc.)
62         @type klass: string
63         @ivar major: Second level event type description
64         @type major: string
65         @ivar minor: Third level event type description
66         @type minor: string
67         @ivar detail: Lowest level event type description
68         @type detail: string
69         @ivar name: Full, unparsed event name as received from AT-SPI
70         @type name: string
71         @cvar format: Names of the event string components
72         @type format: 4-tuple of string
73         """
74
75         _SEPARATOR = ':'
76
77         def __init__(self, name):
78                 """
79                 Parses the full AT-SPI event name into its components
80                 (klass:major:minor:detail). If the provided event name is an integer
81                 instead of a string, then the event is really a device event.
82
83                 @param name: Full AT-SPI event name
84                 @type name: string
85                 @raise AttributeError: When the given event name is not a valid string 
86                 """
87                 stripped = name.strip(self._SEPARATOR)
88                 separated = stripped.split(self._SEPARATOR, 3) 
89                 self._separated = _ELessList(separated)
90
91                 self.klass = self._separated[0]
92                 self.major = self._separated[1]
93                 self.minor = self._separated[2]
94                 self.detail = self._separated[3]
95
96         def is_subtype(self, event_type):
97                 """
98                 Determines if the passed event type is a subtype
99                 of this event.
100                 """
101                 if event_type.klass and event_type.klass !=  self.klass:
102                         return False
103                 else:
104                         if event_type.major and event_type.major != self.major:
105                                 return False
106                         else:
107                                 if event_type.minor and event_type.minor != self.minor:
108                                         return False
109                 return True
110
111         @property
112         def name(self):
113                 return str(self)
114
115         @property
116         def value(self):
117                 return str(self)
118
119 #------------------------------------------------------------------------------
120
121 def event_type_to_signal_reciever(bus, cache, event_handler, event_type):
122         kwargs = {
123                         'sender_keyword':'sender',
124                         'interface_keyword':'interface',
125                         'member_keyword':'member',
126                         'path_keyword':'path',
127                  }
128         if event_type.major:
129                 major = event_type.major.replace('-', '_')
130         if event_type.klass:
131                 kwargs['dbus_interface'] = _klass_to_interface[event_type.klass]
132         if event_type.major:
133                 kwargs['signal_name'] = major
134         if event_type.minor:
135                 kwargs['arg0'] = event_type.minor
136
137         def handler_wrapper(minor, detail1, detail2, any_data, 
138                             sender=None, interface=None, member=None, path=None):
139                 event = Event(cache, path, sender, interface, member, (minor, detail1, detail2, any_data))
140                 return event_handler(event)
141
142         return bus.add_signal_receiver(handler_wrapper, **kwargs) 
143
144 #------------------------------------------------------------------------------
145
146 def signal_spec_to_event_string (interface, name, minor):
147         interface = _interface_to_klass[interface]
148         name = name.replace('_', '-')
149
150         if interface == "focus":
151                 return "focus:"
152
153         result = interface + ':'
154         if name:
155                 result += name + ':'
156         if minor:
157                 result += minor
158         return result
159
160 #------------------------------------------------------------------------------
161
162 class Event(object):
163         """
164         Wraps an AT-SPI event with a more Pythonic interface managing exceptions,
165         the differences in any_data across versions, and the reference counting of
166         accessibles provided with the event.
167
168         @note: All unmarked attributes of this class should be considered public
169                 readable and writable as the class is acting as a record object.
170
171         @ivar type: The type of the AT-SPI event
172         @type type: L{EventType}
173         @ivar detail1: First AT-SPI event parameter
174         @type detail1: integer
175         @ivar detail2: Second AT-SPI event parameter
176         @type detail2: integer
177         @ivar any_data: Extra AT-SPI data payload
178         @type any_data: object
179         @ivar host_application: Application owning the event source
180         @type host_application: Accessibility.Application
181         @ivar source_name: Name of the event source at the time of event dispatch
182         @type source_name: string
183         @ivar source_role: Role of the event source at the time of event dispatch
184         @type source_role: Accessibility.Role
185         @ivar source: Source of the event
186         @type source: Accessibility.Accessible
187         """
188         def __init__(self, cache, source_path, source_application, interface, name, event):
189                 """
190                 Extracts information from the provided event. If the event is a "normal" 
191                 event, pulls the detail1, detail2, any_data, and source values out of the
192                 given object and stores it in this object. If the event is a device event,
193                 key ID is stored in detail1, scan code is stored in detail2, key name, 
194                 key modifiers (e.g. ALT, CTRL, etc.), is text flag, and timestamp are 
195                 stored as a 4-tuple in any_data, and source is None (since key events are
196                 global).
197
198                 @param event: Event from an AT-SPI callback
199                 @type event: Accessibility.Event or Accessibility.DeviceEvent
200                 """
201                 self._cache = cache
202                 self._source_path = source_path
203                 self._source_application = source_application
204
205                 self._source = None
206                 self._application = None
207
208                 self.type = EventType(signal_spec_to_event_string(interface, name, event[0]))
209
210                 self.detail1 = event[1]
211                 self.detail2 = event[2]
212
213                 data = event[3]
214                 if name == "object_bounds_changed":
215                         self.any_data = BoundingBox(*data)
216                 else:
217                         self.any_data = data
218
219         @property
220         def host_application(self):
221                 if not self._application:
222                         return self._cache.create_application(self._source_application)
223                 return self._application
224
225         @property
226         def source(self):
227                 if not self._source:
228                         self._source = self._cache.create_accessible(self._source_application,
229                                                                      self._source_path,
230                                                                      interfaces.ATSPI_ACCESSIBLE)
231                 return self._source
232
233         @property
234         def source_name(self):
235                 return source.name
236
237         @property
238         def source_role(self):
239                 return source.getRole()
240
241         def __str__(self):
242                 """
243                 Builds a human readable representation of the event including event type,
244                 parameters, and source info.
245
246                 @return: Event description
247                 @rtype: string
248                 """
249                 return '%s(%s, %s, %s)\n\tsource: %s\n\thost_application: %s' % \
250                                          (self.type, self.detail1, self.detail2, self.any_data,
251                                                 self.source, self.host_application)
252
253 #END----------------------------------------------------------------------------