Merge branch 'master' of git+ssh://git.codethink.co.uk/git/atspi-dbus
[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 factory import create_accessible, add_accessible_class
17 from accessible import BoundingBox
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.Terminal":"terminal",
32                 "org.freedesktop.atspi.Event.Document":"document",
33                 "org.freedesktop.atspi.Event.Focus":"focus",
34                 }
35
36 _klass_to_interface = {
37                 "object":"org.freedesktop.atspi.Event.Object",
38                 "window":"org.freedesktop.atspi.Event.Window",
39                 "mouse":"org.freedesktop.atspi.Event.Mouse",
40                 "terminal":"org.freedesktop.atspi.Event.Terminal",
41                 "document":"org.freedesktop.atspi.Event.Document",
42                 "focus":"org.freedesktop.atspi.Event.Focus",
43                 }
44
45 #------------------------------------------------------------------------------
46
47 class _ELessList(list):
48         def __getitem__(self, index):
49                 try:
50                         return list.__getitem__(self, index)
51                 except IndexError:
52                         return None
53
54 class EventType(str):
55         """
56         Wraps the AT-SPI event type string so its components can be accessed 
57         individually as klass (can't use the keyword class), major, minor, and detail 
58         (klass_major_minor_detail).
59
60         @note: All attributes of an instance of this class should be considered 
61                 public readable as it is acting a a struct.
62         @ivar klass: Most general event type identifier (object, window, mouse, etc.)
63         @type klass: string
64         @ivar major: Second level event type description
65         @type major: string
66         @ivar minor: Third level event type description
67         @type minor: string
68         @ivar detail: Lowest level event type description
69         @type detail: string
70         @ivar name: Full, unparsed event name as received from AT-SPI
71         @type name: string
72         @cvar format: Names of the event string components
73         @type format: 4-tuple of string
74         """
75
76         _SEPARATOR = ':'
77
78         def __init__(self, name):
79                 """
80                 Parses the full AT-SPI event name into its components
81                 (klass:major:minor:detail). If the provided event name is an integer
82                 instead of a string, then the event is really a device event.
83
84                 @param name: Full AT-SPI event name
85                 @type name: string
86                 @raise AttributeError: When the given event name is not a valid string 
87                 """
88                 stripped = name.strip(self._SEPARATOR)
89                 separated = stripped.split(self._SEPARATOR, 3) 
90                 self._separated = _ELessList(separated)
91
92                 self.klass = self._separated[0]
93                 self.major = self._separated[1]
94                 self.minor = self._separated[2]
95                 self.detail = self._separated[3]
96
97                 self._name = ":".join(separated)
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 self._name
117
118         @property
119         def value(self):
120                 return self._name
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 class Event(object):
150         """
151         Wraps an AT-SPI event with a more Pythonic interface managing exceptions,
152         the differences in any_data across versions, and the reference counting of
153         accessibles provided with the event.
154
155         @note: All unmarked attributes of this class should be considered public
156                 readable and writable as the class is acting as a record object.
157
158         @ivar type: The type of the AT-SPI event
159         @type type: L{EventType}
160         @ivar detail1: First AT-SPI event parameter
161         @type detail1: integer
162         @ivar detail2: Second AT-SPI event parameter
163         @type detail2: integer
164         @ivar any_data: Extra AT-SPI data payload
165         @type any_data: object
166         @ivar host_application: Application owning the event source
167         @type host_application: Accessibility.Application
168         @ivar source_name: Name of the event source at the time of event dispatch
169         @type source_name: string
170         @ivar source_role: Role of the event source at the time of event dispatch
171         @type source_role: Accessibility.Role
172         @ivar source: Source of the event
173         @type source: Accessibility.Accessible
174         """
175         def __init__(self, cache, source_path, source_application, interface, name, event):
176                 """
177                 Extracts information from the provided event. If the event is a "normal" 
178                 event, pulls the detail1, detail2, any_data, and source values out of the
179                 given object and stores it in this object. If the event is a device event,
180                 key ID is stored in detail1, scan code is stored in detail2, key name, 
181                 key modifiers (e.g. ALT, CTRL, etc.), is text flag, and timestamp are 
182                 stored as a 4-tuple in any_data, and source is None (since key events are
183                 global).
184
185                 @param event: Event from an AT-SPI callback
186                 @type event: Accessibility.Event or Accessibility.DeviceEvent
187                 """
188                 self._cache = cache
189                 self._source_path = source_path
190                 self._source_application = source_application
191
192                 self._source = None
193                 self._application = None
194
195                 self._klass = _interface_to_klass[interface]
196                 # The replace is neccessary as '-' not legal as signal name
197                 # so translated on the server side.
198                 self._major = name.replace('_', '-')
199                 self._minor = event[0]
200                 self.type = EventType(':'.join([self._klass, self._major, self._minor]))
201                 self.detail1 = event[1]
202                 self.detail2 = event[2]
203
204                 data = event[3]
205                 if name == "object_bounds_changed":
206                         self.any_data = BoundingBox(*data)
207                 else:
208                         self.any_data = data
209
210         @property
211         def host_application(self):
212                 if not self._application:
213                         application_root = self._cache[self._source_application]._get_root()
214                         return create_accessible(self._cache,
215                                                   self._source_application,
216                                                  application_root,
217                                                  interfaces.ATSPI_APPLICATION,
218                                                    connection=self._cache._connection)
219                 return self._application
220
221         @property
222         def source(self):
223                 if not self._source:
224                         self._source = create_accessible(self._cache,
225                                                             self._source_application,
226                                                            self._source_path,
227                                                            interfaces.ATSPI_ACCESSIBLE,
228                                                            connection=self._cache._connection)
229                 return self._source
230
231         @property
232         def source_name(self):
233                 return source.name
234
235         @property
236         def source_role(self):
237                 return source.getRole()
238
239         def __str__(self):
240                 """
241                 Builds a human readable representation of the event including event type,
242                 parameters, and source info.
243
244                 @return: Event description
245                 @rtype: string
246                 """
247                 return '%s(%s, %s, %s)\n\tsource: %s\n\thost_application: %s' % \
248                                          (self.type, self.detail1, self.detail2, self.any_data,
249                                                 self.source, self.host_application)
250
251 #END----------------------------------------------------------------------------