2008-05-28 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / event.py
1 '''
2 Wrapper classes for AT-SPI events and device events.
3
4 @author: Peter Parente
5 @organization: IBM Corporation
6 @copyright: Copyright (c) 2005, 2007 IBM Corporation
7 @license: LGPL
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public
20 License along with this library; if not, write to the
21 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
23
24 Portions of this code originally licensed and copyright (c) 2005, 2007
25 IBM Corporation under the BSD license, available at
26 U{http://www.opensource.org/licenses/bsd-license.php}
27 '''
28 import constants
29
30 class DeviceEvent(object):
31   '''
32   Wraps an AT-SPI device event with a more Pythonic interface. Primarily adds
33   a consume attribute which can be used to cease propagation of a device event.
34   
35   @ivar consume: Should this event be consumed and not allowed to pass on to
36     observers further down the dispatch chain in this process or possibly
37     system wide?
38   @type consume: boolean
39   @ivar type: Kind of event, KEY_PRESSED_EVENT or KEY_RELEASED_EVENT
40   @type type: Accessibility.EventType
41   @ivar id: Serial identifier for this key event
42   @type id: integer
43   @ivar hw_code: Hardware scan code for the key
44   @type hw_code: integer
45   @ivar modifiers: Modifiers held at the time of the key event
46   @type modifiers: integer
47   @ivar timestamp: Time at which the event occurred relative to some platform
48     dependent starting point (e.g. XWindows start time)
49   @type timestamp: integer
50   @ivar event_string: String describing the key pressed (e.g. keysym)
51   @type event_string: string
52   @ivar is_text: Is the event representative of text to be inserted (True), or 
53     of a control key (False)?
54   @type is_text: boolean
55   '''
56   def __init__(self, event):
57     '''
58     Attaches event data to this object.
59     
60     @param event: Event object
61     @type event: Accessibility.DeviceEvent
62     '''
63     self.consume = False
64     self.type = event.type
65     self.id = event.id
66     self.hw_code = event.hw_code
67     self.modifiers = event.modifiers
68     self.timestamp = event.timestamp
69     self.event_string = event.event_string
70     self.is_text = event.is_text
71     
72   def __str__(self):
73     '''
74     Builds a human readable representation of the event.
75
76     @return: Event description
77     @rtype: string
78     '''
79     if self.type == constants.KEY_PRESSED_EVENT:
80       kind = 'pressed'
81     elif self.type == constants.KEY_RELEASED_EVENT:
82       kind = 'released'
83     return '''\
84 %s
85 \thw_code: %d
86 \tevent_string: %s
87 \tmodifiers: %d
88 \tid: %d
89 \ttimestamp: %d
90 \tis_text: %s''' % (kind, self.hw_code, self.event_string, self.modifiers,
91                     self.id, self.timestamp, self.is_text)
92
93 class Event(object):
94   '''
95   Wraps an AT-SPI event with a more Pythonic interface managing exceptions,
96   the differences in any_data across versions, and the reference counting of
97   accessibles provided with the event.
98   
99   @note: All unmarked attributes of this class should be considered public
100     readable and writable as the class is acting as a record object.
101     
102   @ivar consume: Should this event be consumed and not allowed to pass on to
103     observers further down the dispatch chain in this process?
104   @type consume: boolean
105   @ivar type: The type of the AT-SPI event
106   @type type: L{EventType}
107   @ivar detail1: First AT-SPI event parameter
108   @type detail1: integer
109   @ivar detail2: Second AT-SPI event parameter
110   @type detail2: integer
111   @ivar any_data: Extra AT-SPI data payload
112   @type any_data: object
113   @ivar host_application: Application owning the event source
114   @type host_application: Accessibility.Application
115   @ivar source_name: Name of the event source at the time of event dispatch
116   @type source_name: string
117   @ivar source_role: Role of the event source at the time of event dispatch
118   @type source_role: Accessibility.Role
119   @ivar source: Source of the event
120   @type source: Accessibility.Accessible
121   '''
122   def __init__(self, event):
123     '''
124     Extracts information from the provided event. If the event is a "normal" 
125     event, pulls the detail1, detail2, any_data, and source values out of the
126     given object and stores it in this object. If the event is a device event,
127     key ID is stored in detail1, scan code is stored in detail2, key name, 
128     key modifiers (e.g. ALT, CTRL, etc.), is text flag, and timestamp are 
129     stored as a 4-tuple in any_data, and source is None (since key events are
130     global).
131
132     @param event: Event from an AT-SPI callback
133     @type event: Accessibility.Event or Accessibility.DeviceEvent
134     '''
135     # always start out assuming no consume
136     self.consume = False
137     self.type = EventType(event.type)
138     self.detail1 = event.detail1
139     self.detail2 = event.detail2
140     # store the event source and increase the reference count since event 
141     # sources are borrowed references; the AccessibleMixin automatically
142     # decrements it later
143     try:
144       event.source.ref()
145     except AttributeError:
146       pass
147     self.source = event.source
148
149     # process any_data in a at-spi version independent manner
150     details = event.any_data.value()
151     try:
152       # see if we have a "new" any_data object which is an EventDetails struct
153       self.any_data = details.any_data.value()
154     except Exception:
155       # any kind of error means we have an "old" any_data object and None of
156       # the extra data so set them to None
157       self.any_data = details
158       self.host_application = None
159       self.source_name = None
160       self.source_role = None
161     else:
162       # the rest of the data should be here, so retrieve it
163       self.host_application = details.host_application
164       self.source_name = details.source_name
165       self.source_role = details.source_role
166     try:
167       # if we received an accessible, be sure to increment the ref count
168       self.any_data.ref()
169     except AttributeError:
170       pass
171     try:
172       # if we received a host application, be sure to increment the ref count
173       self.host_application.ref()
174     except AttributeError:
175       pass
176
177   def __str__(self):
178     '''
179     Builds a human readable representation of the event including event type,
180     parameters, and source info.
181
182     @return: Event description
183     @rtype: string
184     '''
185     return '%s(%s, %s, %s)\n\tsource: %s\n\thost_application: %s' % \
186            (self.type, self.detail1, self.detail2, self.any_data,
187             self.source, self.host_application)
188   
189 class EventType(str):
190   '''
191   Wraps the AT-SPI event type string so its components can be accessed 
192   individually as klass (can't use the keyword class), major, minor, and detail 
193   (klass:major:minor:detail).
194   
195   @note: All attributes of an instance of this class should be considered 
196     public readable as it is acting a a struct.
197   @ivar klass: Most general event type identifier (object, window, mouse, etc.)
198   @type klass: string
199   @ivar major: Second level event type description
200   @type major: string
201   @ivar minor: Third level event type description
202   @type minor: string
203   @ivar detail: Lowest level event type description
204   @type detail: string
205   @ivar name: Full, unparsed event name as received from AT-SPI
206   @type name: string
207   @cvar format: Names of the event string components
208   @type format: 4-tuple of string
209   '''
210   format = ('klass', 'major', 'minor', 'detail')
211
212   def __init__(self, name):
213     '''    
214     Parses the full AT-SPI event name into its components
215     (klass:major:minor:detail). If the provided event name is an integer
216     instead of a string, then the event is really a device event.
217     
218     @param name: Full AT-SPI event name
219     @type name: string
220     @raise AttributeError: When the given event name is not a valid string 
221     '''
222     # get rid of any leading and trailing ':' separators
223     self.value = name.strip(':')
224     self.name = self.value # Backward compatability
225     self.klass = None
226     self.major = None
227     self.minor = None
228     self.detail = None
229     
230     # split type according to delimiters
231     split = self.value.split(':', 3)
232     # loop over all the components
233     for i in xrange(len(split)):
234       # store values of attributes in this object
235       setattr(self, self.format[i], split[i])
236