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