1 |
|
# Orca |
2 |
|
# |
3 |
|
# Copyright 2005-2007 Sun Microsystems Inc. |
4 |
|
# |
5 |
|
# This library is free software; you can redistribute it and/or |
6 |
|
# modify it under the terms of the GNU Library General Public |
7 |
|
# License as published by the Free Software Foundation; either |
8 |
|
# version 2 of the License, or (at your option) any later version. |
9 |
|
# |
10 |
|
# This library is distributed in the hope that it will be useful, |
11 |
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 |
|
# Library General Public License for more details. |
14 |
|
# |
15 |
|
# You should have received a copy of the GNU Library General Public |
16 |
|
# License along with this library; if not, write to the |
17 |
|
# Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 |
|
# Boston, MA 02111-1307, USA. |
19 |
|
|
20 |
1 |
"""Provides the interface to the AT-SPI Registry.""" |
21 |
|
|
22 |
1 |
__id__ = "$Id: atspi.py 2503 2007-06-26 20:25:55Z lmonsanto $" |
23 |
1 |
__version__ = "$Revision: 2503 $" |
24 |
1 |
__date__ = "$Date: 2007-06-26 16:25:55 -0400 (Tue, 26 Jun 2007) $" |
25 |
1 |
__copyright__ = "Copyright (c) 2005-2007 Sun Microsystems Inc." |
26 |
1 |
__license__ = "LGPL" |
27 |
|
|
28 |
1 |
import signal |
29 |
1 |
import time |
30 |
|
|
31 |
1 |
import gobject |
32 |
1 |
gobject.threads_init() |
33 |
|
|
34 |
1 |
import bonobo |
35 |
1 |
import ORBit |
36 |
|
|
37 |
1 |
ORBit.load_typelib("Accessibility") |
38 |
|
|
39 |
|
# We will pass "orbit-io-thread" to initialize the ORB in threaded mode. |
40 |
|
# This should hopefully help address bug 319652: |
41 |
|
# |
42 |
|
# http://bugzilla.gnome.org/show_bug.cgi?id=319652 |
43 |
|
# |
44 |
|
# See also: |
45 |
|
# |
46 |
|
# http://bugzilla.gnome.org/show_bug.cgi?id=342614 |
47 |
|
# http://mail.gnome.org/archives/orbit-list/2005-December/msg00001.html |
48 |
|
# |
49 |
1 |
ORBit.CORBA.ORB_init(orb_id="orbit-io-thread") |
50 |
|
|
51 |
1 |
import Accessibility |
52 |
1 |
import Accessibility__POA |
53 |
|
|
54 |
1 |
import debug |
55 |
1 |
import rolenames |
56 |
1 |
import settings |
57 |
|
|
58 |
2 |
class Event: |
59 |
|
"""Converts the source of an event to an Accessible object. We |
60 |
|
need this since the event object we get from the atspi is |
61 |
|
read-only. So, we create this dummy event object to contain a copy |
62 |
|
of all the event members with the source converted to an |
63 |
|
Accessible. It is perfectly OK for event handlers to annotate this |
64 |
|
object with their own attributes. |
65 |
1 |
""" |
66 |
|
|
67 |
1 |
def __init__(self, e=None): |
68 |
13853 |
if e: |
69 |
13852 |
self.source = Accessible.makeAccessible(e.source) |
70 |
13852 |
self.type = e.type |
71 |
13852 |
self.detail1 = e.detail1 |
72 |
13852 |
self.detail2 = e.detail2 |
73 |
|
|
74 |
|
# If were talking to AT-SPI 1.7.0 or greater, we can get the |
75 |
|
# application information right away because it is tucked in |
76 |
|
# the EventDetails data new for 1.7.0. |
77 |
|
# |
78 |
13852 |
if e.any_data and (e.any_data.typecode().name) == "EventDetails": |
79 |
13851 |
details = e.any_data.value() |
80 |
13851 |
self.any_data = details.any_data |
81 |
13851 |
if self.source and details.host_application: |
82 |
13851 |
self.source.app = Accessible.makeAccessible( |
83 |
13851 |
details.host_application) |
84 |
|
else: |
85 |
1 |
self.any_data = e.any_data |
86 |
|
|
87 |
|
# We need to make sure we reference any object that comes |
88 |
|
# to us via an any_data because we process events |
89 |
|
# asynchronously. If we don't reference them, we may |
90 |
|
# end up with OBJECT_NOT_EXIST errors. Please see |
91 |
|
# http://bugzilla.gnome.org/show_bug.cgi?id=395749 for |
92 |
|
# more information. |
93 |
|
# |
94 |
13852 |
if self.type == "object:active-descendant-changed": |
95 |
69 |
self.any_data = Accessible.makeAccessible( |
96 |
69 |
self.any_data.value()) |
97 |
13783 |
elif self.type == "object:text-changed:insert": |
98 |
1296 |
self.any_data = self.any_data.value() |
99 |
12487 |
elif self.type == "object:text-changed:delete": |
100 |
252 |
self.any_data = self.any_data.value() |
101 |
|
else: |
102 |
1 |
self.source = None |
103 |
1 |
self.type = None |
104 |
1 |
self.detail1 = None |
105 |
1 |
self.detail2 = None |
106 |
1 |
self.any_data = None |
107 |
|
|
108 |
2 |
class Registry: |
109 |
|
"""Delegates to the actual AT-SPI Regisitry. |
110 |
1 |
""" |
111 |
|
|
112 |
|
# The "Borg" singleton model - ensures we're really |
113 |
|
# only connecting to the registry once. |
114 |
|
# |
115 |
1 |
__sharedState = {} |
116 |
1 |
__instanceCount = 0 |
117 |
|
|
118 |
1 |
__listeners=[] |
119 |
1 |
__keystrokeListeners=[] |
120 |
|
|
121 |
1 |
def __init__(self): |
122 |
|
|
123 |
|
# The "Borg" singleton model - ensures we're really |
124 |
|
# only connecting to the registry once. |
125 |
|
# |
126 |
3619 |
self.__dict__ = self.__sharedState |
127 |
3619 |
self.__instanceCount += 1 |
128 |
3619 |
if not self.__dict__.has_key("registry"): |
129 |
1 |
self.registry = bonobo.get_object( |
130 |
1 |
"OAFIID:Accessibility_Registry:1.0", |
131 |
1 |
"Accessibility/Registry") |
132 |
3619 |
if not self.__dict__.has_key("desktop"): |
133 |
1 |
self.desktop = self.registry.getDesktop(0) |
134 |
|
|
135 |
1 |
def __blockPreventor(self): |
136 |
|
"""[[[TODO: HACK to attempt to prevent deadlocks. We call time.sleep |
137 |
|
here as a means to sidestep the global interpreter lock (GIL).]]] |
138 |
|
""" |
139 |
0 |
if settings.gilSleepTime: |
140 |
0 |
time.sleep(settings.gilSleepTime) |
141 |
0 |
return True |
142 |
|
|
143 |
1 |
def start(self): |
144 |
|
"""Starts event notification with the AT-SPI Registry. This method |
145 |
|
only returns after 'stop' has been called. |
146 |
|
""" |
147 |
1 |
Accessible.init(self) |
148 |
|
|
149 |
|
# We'll try our own main loop to help debug things. Code borrowed |
150 |
|
# "The Whole PyGtk FAQ": http://www.async.com.br/faq/pygtk/ |
151 |
|
# |
152 |
1 |
if settings.useBonoboMain: |
153 |
1 |
debug.println(debug.LEVEL_CONFIGURATION, |
154 |
1 |
"atspi.start: using bonobo.main; " |
155 |
1 |
+ "gilSleepTime=%f" % settings.gilSleepTime) |
156 |
1 |
if settings.useBlockPreventor and settings.gilSleepTime: |
157 |
0 |
gobject.idle_add(self.__blockPreventor) |
158 |
1 |
bonobo.main() |
159 |
|
else: |
160 |
0 |
debug.println(debug.LEVEL_CONFIGURATION, |
161 |
0 |
"atspi.start: using our custom main loop; " |
162 |
0 |
+ "gilSleepTime=%f" % settings.gilSleepTime) |
163 |
0 |
self.running = True |
164 |
0 |
context = gobject.MainLoop().get_context() |
165 |
0 |
while self.running: |
166 |
0 |
if settings.gilSleepTime: |
167 |
0 |
time.sleep(settings.gilSleepTime) |
168 |
0 |
context.iteration(False) |
169 |
|
|
170 |
1 |
def stop(self): |
171 |
|
"""Unregisters any event or keystroke listeners registered with |
172 |
|
the AT-SPI Registry and then stops event notification with the |
173 |
|
AT-SPI Registry. |
174 |
|
""" |
175 |
1 |
Accessible.shutdown(self) |
176 |
257 |
for listener in (self.__listeners + self.__keystrokeListeners): |
177 |
256 |
listener.deregister() |
178 |
1 |
if settings.useBonoboMain: |
179 |
1 |
bonobo.main_quit() |
180 |
|
else: |
181 |
0 |
self.running = False |
182 |
|
|
183 |
1 |
def registerEventListener(self, callback, eventType): |
184 |
|
"""Registers the given eventType and callback with the Registry. |
185 |
|
|
186 |
|
Arguments: |
187 |
|
- callback: function to call with an AT-SPI event instance |
188 |
|
- eventType: string representing the type of event |
189 |
|
""" |
190 |
35 |
listener = EventListener(self.registry, callback, eventType) |
191 |
35 |
self.__listeners.append(listener) |
192 |
|
|
193 |
1 |
def deregisterEventListener(self, callback, eventType): |
194 |
|
"""Unregisters the given eventType and callback with the Registry. |
195 |
|
|
196 |
|
Arguments: |
197 |
|
- callback: function to call with an AT-SPI event instance |
198 |
|
- eventType: string representing the type of event |
199 |
|
""" |
200 |
35 |
found = True |
201 |
35 |
while len(self.__listeners) and found: |
202 |
687 |
for i in range(0, len(self.__listeners)): |
203 |
653 |
if (self.__listeners[i].callback == callback) \ |
204 |
409 |
and (self.__listeners[i].eventType == eventType): |
205 |
|
# The __del__ method of the listener will unregister it. |
206 |
|
# |
207 |
35 |
self.__listeners.pop(i) |
208 |
35 |
found = True |
209 |
35 |
break |
210 |
|
else: |
211 |
618 |
found = False |
212 |
|
|
213 |
1 |
def registerKeystrokeListeners(self, callback): |
214 |
|
"""Registers a single callback for all possible keystrokes. |
215 |
|
""" |
216 |
257 |
for i in range(0, (1 << (Accessibility.MODIFIER_NUMLOCK + 1))): |
217 |
256 |
self.__keystrokeListeners.append( |
218 |
256 |
KeystrokeListener(self.registry, |
219 |
256 |
callback, # callback |
220 |
256 |
[], # keyset |
221 |
256 |
i, # modifier mask |
222 |
256 |
[Accessibility.KEY_PRESSED_EVENT, |
223 |
256 |
Accessibility.KEY_RELEASED_EVENT], |
224 |
256 |
True, # synchronous |
225 |
256 |
True, # preemptive |
226 |
256 |
False)) # global |
227 |
|
|
228 |
|
######################################################################## |
229 |
|
# # |
230 |
|
# Event listener classes for global and keystroke events # |
231 |
|
# # |
232 |
|
######################################################################## |
233 |
|
|
234 |
2 |
class EventListener(Accessibility__POA.EventListener): |
235 |
|
"""Registers a callback directly with the AT-SPI Registry for the |
236 |
|
given event type. Most users of this module will not use this |
237 |
|
class directly, but will instead use the registerEventListener method |
238 |
1 |
of the Registry.""" |
239 |
|
|
240 |
1 |
def __init__(self, registry, callback, eventType): |
241 |
35 |
self.registry = registry |
242 |
35 |
self.callback = callback |
243 |
35 |
self.eventType = eventType |
244 |
35 |
self.register() |
245 |
|
|
246 |
36 |
def ref(self): pass |
247 |
|
|
248 |
36 |
def unref(self): pass |
249 |
|
|
250 |
1 |
def queryInterface(self, repo_id): |
251 |
0 |
thiz = None |
252 |
0 |
if repo_id == "IDL:Accessibility/EventListener:1.0": |
253 |
0 |
thiz = self._this() |
254 |
0 |
return thiz |
255 |
|
|
256 |
1 |
def register(self): |
257 |
35 |
self._default_POA().the_POAManager.activate() |
258 |
35 |
self.registry.registerGlobalEventListener(self._this(), |
259 |
35 |
self.eventType) |
260 |
35 |
self.__registered = True |
261 |
35 |
return self.__registered |
262 |
|
|
263 |
1 |
def deregister(self): |
264 |
35 |
if not self.__registered: |
265 |
0 |
return |
266 |
35 |
self.registry.deregisterGlobalEventListener(self._this(), |
267 |
35 |
self.eventType) |
268 |
35 |
self.__registered = False |
269 |
|
|
270 |
1 |
def notifyEvent(self, event): |
271 |
43865 |
if settings.timeoutCallback and (settings.timeoutTime > 0): |
272 |
43865 |
signal.signal(signal.SIGALRM, settings.timeoutCallback) |
273 |
43865 |
signal.alarm(settings.timeoutTime) |
274 |
|
|
275 |
43865 |
try: |
276 |
43865 |
self.callback(event) |
277 |
0 |
except: |
278 |
0 |
debug.printException(debug.LEVEL_WARNING) |
279 |
|
|
280 |
43865 |
if settings.timeoutCallback and (settings.timeoutTime > 0): |
281 |
43865 |
signal.alarm(0) |
282 |
|
|
283 |
1 |
def __del__(self): |
284 |
35 |
self.deregister() |
285 |
|
|
286 |
2 |
class KeystrokeListener(Accessibility__POA.DeviceEventListener): |
287 |
|
"""Registers a callback directly with the AT-SPI Registry for the |
288 |
|
given keystroke. Most users of this module will not use this |
289 |
|
class directly, but will instead use the registerKeystrokeListeners |
290 |
1 |
method of the Registry.""" |
291 |
|
|
292 |
1 |
def keyEventToString(event): |
293 |
|
return ("KEYEVENT: type=%d\n" % event.type) \ |
294 |
|
+ (" hw_code=%d\n" % event.hw_code) \ |
295 |
|
+ (" modifiers=%d\n" % event.modifiers) \ |
296 |
|
+ (" event_string=(%s)\n" % event.event_string) \ |
297 |
|
+ (" is_text=%s\n" % event.is_text) \ |
298 |
3673 |
+ (" time=%f" % time.time()) |
299 |
|
|
300 |
1 |
keyEventToString = staticmethod(keyEventToString) |
301 |
|
|
302 |
1 |
def __init__(self, registry, callback, |
303 |
|
keyset, mask, type, synchronous, preemptive, isGlobal): |
304 |
256 |
self._default_POA().the_POAManager.activate() |
305 |
|
|
306 |
256 |
self.registry = registry |
307 |
256 |
self.callback = callback |
308 |
256 |
self.keyset = keyset |
309 |
256 |
self.mask = mask |
310 |
256 |
self.type = type |
311 |
256 |
self.mode = Accessibility.EventListenerMode() |
312 |
256 |
self.mode.synchronous = synchronous |
313 |
256 |
self.mode.preemptive = preemptive |
314 |
256 |
self.mode._global = isGlobal |
315 |
256 |
self.register() |
316 |
|
|
317 |
513 |
def ref(self): pass |
318 |
|
|
319 |
513 |
def unref(self): pass |
320 |
|
|
321 |
1 |
def queryInterface(self, repo_id): |
322 |
0 |
thiz = None |
323 |
0 |
if repo_id == "IDL:Accessibility/EventListener:1.0": |
324 |
0 |
thiz = self._this() |
325 |
0 |
return thiz |
326 |
|
|
327 |
1 |
def register(self): |
328 |
256 |
d = self.registry.getDeviceEventController() |
329 |
256 |
if d.registerKeystrokeListener(self._this(), |
330 |
256 |
self.keyset, |
331 |
256 |
self.mask, |
332 |
256 |
self.type, |
333 |
256 |
self.mode): |
334 |
256 |
self.__registered = True |
335 |
|
else: |
336 |
0 |
self.__registered = False |
337 |
256 |
return self.__registered |
338 |
|
|
339 |
1 |
def deregister(self): |
340 |
256 |
if not self.__registered: |
341 |
0 |
return |
342 |
256 |
d = self.registry.getDeviceEventController() |
343 |
256 |
d.deregisterKeystrokeListener(self._this(), |
344 |
256 |
self.keyset, |
345 |
256 |
self.mask, |
346 |
256 |
self.type) |
347 |
256 |
self.__registered = False |
348 |
|
|
349 |
1 |
def notifyEvent(self, event): |
350 |
|
"""Called by the at-spi registry when a key is pressed or released. |
351 |
|
|
352 |
|
Arguments: |
353 |
|
- event: an at-spi DeviceEvent |
354 |
|
|
355 |
|
Returns True if the event has been consumed. |
356 |
|
""" |
357 |
3579 |
if settings.timeoutCallback and (settings.timeoutTime > 0): |
358 |
3579 |
signal.signal(signal.SIGALRM, settings.timeoutCallback) |
359 |
3579 |
signal.alarm(settings.timeoutTime) |
360 |
|
|
361 |
3579 |
try: |
362 |
3579 |
consumed = self.callback(event) |
363 |
0 |
except: |
364 |
0 |
debug.printException(debug.LEVEL_WARNING) |
365 |
0 |
consumed = False |
366 |
|
|
367 |
3579 |
if settings.timeoutCallback and (settings.timeoutTime > 0): |
368 |
3579 |
signal.alarm(0) |
369 |
|
|
370 |
3579 |
return consumed |
371 |
|
|
372 |
1 |
def __del__(self): |
373 |
0 |
self.deregister() |
374 |
|
|
375 |
|
######################################################################## |
376 |
|
# # |
377 |
|
# The Accessible class. # |
378 |
|
# # |
379 |
|
######################################################################## |
380 |
|
|
381 |
2 |
class Accessible: |
382 |
|
"""Wraps AT-SPI Accessible objects and caches properties such as |
383 |
|
name, description, and parent. |
384 |
|
|
385 |
|
It also adds some properties to the AT-SPI Accessible including |
386 |
|
the Application to which the object belongs. |
387 |
|
|
388 |
|
For efficiency purposes, this class also maintains a cache of all |
389 |
|
Accessible objects obtained so far, and will return an element |
390 |
|
from that cache instead of creating a duplicate object. |
391 |
1 |
""" |
392 |
|
|
393 |
|
# The cache of the currently active accessible objects. The key is |
394 |
|
# the AT-SPI Accessible, and the value is the Python Accessible. |
395 |
|
# [[[TODO: WDW - probably should look at the __new__ method as a means |
396 |
|
# to handle singletons.]]] |
397 |
|
# |
398 |
1 |
_cache = {} |
399 |
|
|
400 |
1 |
def init(registry): |
401 |
|
"""Registers various event listeners with the Registry to keep |
402 |
|
the Accessible cache up to date. |
403 |
|
|
404 |
|
Arguments: |
405 |
|
- registry: an instance of Registry |
406 |
|
""" |
407 |
1 |
registry.registerEventListener( |
408 |
1 |
Accessible._onRoleChanged, |
409 |
1 |
"object:property-change:accessible-role") |
410 |
1 |
registry.registerEventListener( |
411 |
1 |
Accessible._onNameChanged, |
412 |
1 |
"object:property-change:accessible-name") |
413 |
1 |
registry.registerEventListener( |
414 |
1 |
Accessible._onDescriptionChanged, |
415 |
1 |
"object:property-change:accessible-description") |
416 |
1 |
registry.registerEventListener( |
417 |
1 |
Accessible._onParentChanged, |
418 |
1 |
"object:property-change:accessible-parent") |
419 |
1 |
registry.registerEventListener( |
420 |
1 |
Accessible._onStateChanged, |
421 |
1 |
"object:state-changed:") |
422 |
1 |
registry.registerEventListener( |
423 |
1 |
Accessible._onChildrenChanged, |
424 |
1 |
"object:children-changed:") |
425 |
|
|
426 |
1 |
init = staticmethod(init) |
427 |
|
|
428 |
1 |
def shutdown(registry): |
429 |
|
"""Unregisters the event listeners that were registered in the |
430 |
|
init method. |
431 |
|
|
432 |
|
Arguments: |
433 |
|
- registry: an instance of Registry |
434 |
|
""" |
435 |
1 |
registry.deregisterEventListener( |
436 |
1 |
Accessible._onRoleChanged, |
437 |
1 |
"object:property-change:accessible-role") |
438 |
1 |
registry.deregisterEventListener( |
439 |
1 |
Accessible._onNameChanged, |
440 |
1 |
"object:property-change:accessible-name") |
441 |
1 |
registry.deregisterEventListener( |
442 |
1 |
Accessible._onDescriptionChanged, |
443 |
1 |
"object:property-change:accessible-description") |
444 |
1 |
registry.deregisterEventListener( |
445 |
1 |
Accessible._onParentChanged, |
446 |
1 |
"object:property-change:accessible-parent") |
447 |
1 |
registry.deregisterEventListener( |
448 |
1 |
Accessible._onStateChanged, |
449 |
1 |
"object:state-changed:") |
450 |
1 |
registry.deregisterEventListener( |
451 |
1 |
Accessible._onChildrenChanged, |
452 |
1 |
"object:children-changed:") |
453 |
|
|
454 |
1 |
shutdown = staticmethod(shutdown) |
455 |
|
|
456 |
1 |
def _onRoleChanged(e): |
457 |
|
"""Core module event listener called when an object's role |
458 |
|
changes. Updates the cache accordingly. |
459 |
|
|
460 |
|
Arguments: |
461 |
|
- e: AT-SPI event from the AT-SPI registry |
462 |
|
""" |
463 |
|
|
464 |
13 |
if Accessible._cache.has_key(e.source): |
465 |
0 |
obj = Accessible._cache[e.source] |
466 |
0 |
if obj.__dict__.has_key("role"): |
467 |
0 |
del obj.__dict__["role"] |
468 |
|
|
469 |
1 |
_onRoleChanged = staticmethod(_onRoleChanged) |
470 |
|
|
471 |
1 |
def _onNameChanged(e): |
472 |
|
"""Core module event listener called when an object's name |
473 |
|
changes. Updates the cache accordingly. |
474 |
|
|
475 |
|
Arguments: |
476 |
|
- e: AT-SPI event from the AT-SPI registry |
477 |
|
""" |
478 |
|
|
479 |
1684 |
if Accessible._cache.has_key(e.source): |
480 |
1676 |
obj = Accessible._cache[e.source] |
481 |
1676 |
if obj.__dict__.has_key("name"): |
482 |
565 |
del obj.__dict__["name"] |
483 |
1676 |
if obj.__dict__.has_key("label"): |
484 |
0 |
del obj.__dict__["label"] |
485 |
|
|
486 |
1 |
_onNameChanged = staticmethod(_onNameChanged) |
487 |
|
|
488 |
1 |
def _onDescriptionChanged(e): |
489 |
|
"""Core module event listener called when an object's description |
490 |
|
changes. Updates the cache accordingly. |
491 |
|
|
492 |
|
Arguments: |
493 |
|
- e: AT-SPI event from the AT-SPI registry |
494 |
|
""" |
495 |
|
|
496 |
0 |
if Accessible._cache.has_key(e.source): |
497 |
0 |
obj = Accessible._cache[e.source] |
498 |
0 |
if obj.__dict__.has_key("description"): |
499 |
0 |
del obj.__dict__["description"] |
500 |
0 |
if obj.__dict__.has_key("label"): |
501 |
0 |
del obj.__dict__["label"] |
502 |
|
|
503 |
1 |
_onDescriptionChanged = staticmethod(_onDescriptionChanged) |
504 |
|
|
505 |
1 |
def _onParentChanged(e): |
506 |
|
"""Core module event listener called when an object's parent |
507 |
|
changes. Updates the cache accordingly. |
508 |
|
|
509 |
|
Arguments: |
510 |
|
- e: AT-SPI event from the AT-SPI registry |
511 |
|
""" |
512 |
|
|
513 |
|
# When the parent of an object changes, just forget about the |
514 |
|
# parent -- we'll lazily ask for it the next time someone is |
515 |
|
# really interested. |
516 |
|
# |
517 |
3118 |
try: |
518 |
3118 |
obj = Accessible._cache[e.source] |
519 |
1033 |
del obj.__dict__["parent"] |
520 |
2299 |
except: |
521 |
2299 |
pass |
522 |
|
|
523 |
3118 |
return |
524 |
|
|
525 |
1 |
_onParentChanged = staticmethod(_onParentChanged) |
526 |
|
|
527 |
1 |
def _onStateChanged(e): |
528 |
|
"""Core module event listener called when an object's state |
529 |
|
changes. Updates the cache accordingly. |
530 |
|
|
531 |
|
Arguments: |
532 |
|
- e: AT-SPI event from the AT-SPI registry |
533 |
|
""" |
534 |
|
|
535 |
6252 |
if Accessible._cache.has_key(e.source): |
536 |
|
# Let's get rid of defunct objects. We hate them. |
537 |
|
# |
538 |
4634 |
if e.type == "object:state-changed:defunct": |
539 |
1503 |
Accessible.deleteAccessible(e.source) |
540 |
|
else: |
541 |
3131 |
obj = Accessible._cache[e.source] |
542 |
3131 |
if obj.__dict__.has_key("state"): |
543 |
0 |
del obj.state |
544 |
|
|
545 |
1 |
_onStateChanged = staticmethod(_onStateChanged) |
546 |
|
|
547 |
1 |
def _onChildrenChanged(e): |
548 |
|
"""Core module event listener called when an object's child count |
549 |
|
changes. Updates the cache accordingly. |
550 |
|
|
551 |
|
Arguments: |
552 |
|
- e: AT-SPI event from the AT-SPI registry |
553 |
|
""" |
554 |
|
|
555 |
3611 |
if Accessible._cache.has_key(e.source): |
556 |
706 |
obj = Accessible._cache[e.source] |
557 |
706 |
if obj.__dict__.has_key("childCount"): |
558 |
0 |
del obj.childCount |
559 |
|
|
560 |
1 |
_onChildrenChanged = staticmethod(_onChildrenChanged) |
561 |
|
|
562 |
1 |
def makeAccessible(acc): |
563 |
|
"""Make an Accessible. This is used instead of a simple calls to |
564 |
|
Accessible's constructor because the object may already be in the |
565 |
|
cache. |
566 |
|
|
567 |
|
Arguments: |
568 |
|
- acc: the AT-SPI Accessibility_Accessible |
569 |
|
|
570 |
|
Returns a Python Accessible. |
571 |
|
""" |
572 |
|
|
573 |
43126 |
obj = None |
574 |
|
|
575 |
43126 |
if not acc: |
576 |
32 |
return obj |
577 |
|
|
578 |
43094 |
if isinstance(acc, Accessible): |
579 |
0 |
debug.println( |
580 |
0 |
debug.LEVEL_WARNING, |
581 |
0 |
"WARNING: atspi.Accessible.makeAccessible:\n" |
582 |
|
" Parameter acc passed in is a\n" \ |
583 |
|
" Python Accessible instead of an\n" \ |
584 |
|
" AT-SPI Accessible.\n" |
585 |
|
" Returning Python Accessible.") |
586 |
0 |
return acc |
587 |
|
|
588 |
|
# [[[TODO: WDW - the AT-SPI appears to give us a different |
589 |
|
# accessible when we repeatedly ask for the same child of a |
590 |
|
# parent that manages its descendants. So...we probably |
591 |
|
# shouldn't cache those kind of children because we're likely |
592 |
|
# to cause a memory leak. Logged as bugzilla bug 319675.]]] |
593 |
|
# |
594 |
43094 |
if not settings.cacheAccessibles: |
595 |
0 |
obj = Accessible(acc) |
596 |
0 |
return obj |
597 |
|
|
598 |
43094 |
if Accessible._cache.has_key(acc): |
599 |
40868 |
obj = Accessible._cache[acc] |
600 |
40868 |
if not obj.valid: |
601 |
0 |
del Accessible._cache[acc] |
602 |
0 |
obj = None |
603 |
|
|
604 |
43094 |
if not obj: |
605 |
2226 |
obj = Accessible(acc) |
606 |
|
|
607 |
43094 |
if obj.valid: |
608 |
43094 |
Accessible._cache[acc] = obj |
609 |
|
else: |
610 |
0 |
obj = None |
611 |
|
|
612 |
43094 |
return obj |
613 |
|
|
614 |
1 |
makeAccessible = staticmethod(makeAccessible) |
615 |
|
|
616 |
1 |
def deleteAccessible(acc): |
617 |
|
"""Delete an Accessible from the cache if it exists. |
618 |
|
|
619 |
|
Arguments: |
620 |
|
- acc: the AT-SPI Accessibility_Accessible |
621 |
|
""" |
622 |
|
|
623 |
|
# Just a little sanity check in case someone sent us a |
624 |
|
# Python Accessible instance instead of a CORBA object. |
625 |
|
# |
626 |
4941 |
if isinstance(acc, Accessible): |
627 |
1484 |
acc = acc._acc |
628 |
|
|
629 |
4941 |
if acc and Accessible._cache.has_key(acc): |
630 |
2185 |
try: |
631 |
2185 |
del Accessible._cache[acc] |
632 |
0 |
except: |
633 |
0 |
pass |
634 |
|
|
635 |
1 |
deleteAccessible = staticmethod(deleteAccessible) |
636 |
|
|
637 |
1 |
def __init__(self, acc): |
638 |
|
"""Obtains, and creates if necessary, a Python Accessible from |
639 |
|
an AT-SPI Accessibility_Accessible. Applications should not |
640 |
|
call this method, but should instead call makeAccessible. |
641 |
|
|
642 |
|
Arguments: |
643 |
|
- acc: the AT-SPI Accessibility_Accessible to back this object |
644 |
|
|
645 |
|
Returns the associated Python Accessible. |
646 |
|
""" |
647 |
|
|
648 |
|
# The setting of self._acc to None here is to help with manual |
649 |
|
# and unit testing of this module. Furthermore, it helps us |
650 |
|
# determine if this particular instance is really backed by an |
651 |
|
# object or not. |
652 |
|
# |
653 |
2226 |
self._acc = None |
654 |
|
|
655 |
|
# We'll also keep track of whether this object is any good to |
656 |
|
# us or not. This object will be deleted when a defunct event |
657 |
|
# is received for it, but anything could happen prior to that |
658 |
|
# event, so we keep this extra field around to help us. |
659 |
|
# |
660 |
2226 |
self.valid = False |
661 |
|
|
662 |
|
# [[[TODO: WDW - should do an assert here to make sure we're |
663 |
|
# getting a raw AT-SPI Accessible and not one of our own locally |
664 |
|
# cached Accessible instances. Logged as bugzilla bug 319673.]]] |
665 |
|
# |
666 |
2226 |
assert (not Accessible._cache.has_key(acc)), \ |
667 |
0 |
"Attempt to create an Accessible that's already been made." |
668 |
|
|
669 |
|
# See if we have an application. Via bridges such as the Java |
670 |
|
# access bridge, we might be getting a CORBA::Object, which is |
671 |
|
# of little use to us. We need to narrow it down to something |
672 |
|
# we can use. The first attempt is to see if we can get an |
673 |
|
# application out of it. Then we go for an accessible. |
674 |
|
# |
675 |
2226 |
self.accessible = None |
676 |
2226 |
try: |
677 |
2226 |
self.accessible = acc._narrow(Accessibility.Application) |
678 |
58 |
try: |
679 |
58 |
self.toolkitName = self.accessible.toolkitName |
680 |
0 |
except: |
681 |
0 |
self.toolkitName = None |
682 |
58 |
try: |
683 |
58 |
self.version = self.accessible.version |
684 |
0 |
except: |
685 |
0 |
self.version = None |
686 |
2168 |
except: |
687 |
2168 |
try: |
688 |
2168 |
self.accessible = acc._narrow(Accessibility.Accessible) |
689 |
0 |
except: |
690 |
0 |
debug.printException(debug.LEVEL_WARNING) |
691 |
0 |
debug.println(debug.LEVEL_WARNING, |
692 |
0 |
"atspi.py:Accessible.__init__" \ |
693 |
0 |
+ " NOT GIVEN AN ACCESSIBLE!") |
694 |
0 |
self.accessible = None |
695 |
|
|
696 |
|
# Save a reference to the AT-SPI object. |
697 |
|
# |
698 |
2226 |
if self.accessible: |
699 |
2226 |
try: |
700 |
2226 |
self.accessible.ref() |
701 |
2226 |
self._acc = acc |
702 |
2226 |
self.valid = True |
703 |
0 |
except: |
704 |
0 |
debug.printException(debug.LEVEL_SEVERE) |
705 |
|
|
706 |
1 |
def getRelationString(self): |
707 |
|
"""Returns a space-delimited string composed of the given object's |
708 |
|
Accessible relations attribute. This is for debug purposes. |
709 |
|
""" |
710 |
|
|
711 |
6284 |
relations = self.relations |
712 |
6096 |
relString = " " |
713 |
6131 |
for relation in relations: |
714 |
35 |
if relation.getRelationType() == Accessibility.RELATION_LABEL_FOR: |
715 |
0 |
relString += "LABEL_FOR " |
716 |
35 |
if relation.getRelationType() == Accessibility.RELATION_LABELLED_BY: |
717 |
0 |
relString += "LABELLED_BY " |
718 |
35 |
if relation.getRelationType() == \ |
719 |
35 |
Accessibility.RELATION_CONTROLLER_FOR: |
720 |
0 |
relString += "CONTROLLER_FOR " |
721 |
35 |
if relation.getRelationType() == \ |
722 |
35 |
Accessibility.RELATION_CONTROLLED_BY: |
723 |
0 |
relString += "CONTROLLED_BY " |
724 |
35 |
if relation.getRelationType() == Accessibility.RELATION_MEMBER_OF: |
725 |
0 |
relString += "MEMBER_OF " |
726 |
35 |
if relation.getRelationType() == Accessibility.RELATION_TOOLTIP_FOR: |
727 |
0 |
relString += "TOOLTIP_FOR " |
728 |
35 |
if relation.getRelationType() == \ |
729 |
35 |
Accessibility.RELATION_NODE_CHILD_OF: |
730 |
35 |
relString += "NODE_CHILD_OF " |
731 |
35 |
if relation.getRelationType() == Accessibility.RELATION_EXTENDED: |
732 |
0 |
relString += "RELATION_EXTENDED " |
733 |
35 |
if relation.getRelationType() == Accessibility.RELATION_FLOWS_TO: |
734 |
0 |
relString += "FLOWS_TO " |
735 |
35 |
if relation.getRelationType() == Accessibility.RELATION_FLOWS_FROM: |
736 |
0 |
relString += "FLOWS_FROM " |
737 |
35 |
if relation.getRelationType() == \ |
738 |
35 |
Accessibility.RELATION_SUBWINDOW_OF: |
739 |
0 |
relString += "SUBWINDOW_OF " |
740 |
35 |
if relation.getRelationType() == Accessibility.RELATION_EMBEDS: |
741 |
0 |
relString += "EMBEDS " |
742 |
35 |
if relation.getRelationType() == Accessibility.RELATION_EMBEDDED_BY: |
743 |
0 |
relString += "EMBEDDED_BY " |
744 |
35 |
if relation.getRelationType() == Accessibility.RELATION_POPUP_FOR: |
745 |
0 |
relString += "POPUP_FOR " |
746 |
35 |
if relation.getRelationType() == \ |
747 |
35 |
Accessibility.RELATION_PARENT_WINDOW_OF: |
748 |
0 |
relString += "WINDOW_OF " |
749 |
|
|
750 |
6096 |
return relString.strip() |
751 |
|
|
752 |
1 |
def getStateString(self): |
753 |
|
"""Returns a space-delimited string composed of the given object's |
754 |
|
Accessible state attribute. This is for debug purposes. |
755 |
|
""" |
756 |
|
|
757 |
6284 |
stateSet = self.state |
758 |
6284 |
stateString = " " |
759 |
6284 |
if stateSet.count(Accessibility.STATE_INVALID): |
760 |
0 |
stateString += "INVALID " |
761 |
6284 |
if stateSet.count(Accessibility.STATE_ACTIVE): |
762 |
114 |
stateString += "ACTIVE " |
763 |
6284 |
if stateSet.count(Accessibility.STATE_ARMED): |
764 |
0 |
stateString += "ARMED " |
765 |
6284 |
if stateSet.count(Accessibility.STATE_BUSY): |
766 |
0 |
stateString += "BUSY " |
767 |
6284 |
if stateSet.count(Accessibility.STATE_CHECKED): |
768 |
0 |
stateString += "CHECKED " |
769 |
6284 |
if stateSet.count(Accessibility.STATE_COLLAPSED): |
770 |
0 |
stateString += "COLLAPSED " |
771 |
6284 |
if stateSet.count(Accessibility.STATE_DEFUNCT): |
772 |
0 |
stateString += "DEFUNCT " |
773 |
6284 |
if stateSet.count(Accessibility.STATE_EDITABLE): |
774 |
6 |
stateString += "EDITABLE " |
775 |
6284 |
if stateSet.count(Accessibility.STATE_ENABLED): |
776 |
3410 |
stateString += "ENABLED " |
777 |
6284 |
if stateSet.count(Accessibility.STATE_EXPANDABLE): |
778 |
3 |
stateString += "EXPANDABLE " |
779 |
6284 |
if stateSet.count(Accessibility.STATE_EXPANDED): |
780 |
0 |
stateString += "EXPANDED " |
781 |
6284 |
if stateSet.count(Accessibility.STATE_FOCUSABLE): |
782 |
520 |
stateString += "FOCUSABLE " |
783 |
6284 |
if stateSet.count(Accessibility.STATE_FOCUSED): |
784 |
305 |
stateString += "FOCUSED " |
785 |
6284 |
if stateSet.count(Accessibility.STATE_HAS_TOOLTIP): |
786 |
0 |
stateString += "HAS_TOOLTIP " |
787 |
6284 |
if stateSet.count(Accessibility.STATE_HORIZONTAL): |
788 |
156 |
stateString += "HORIZONTAL " |
789 |
6284 |
if stateSet.count(Accessibility.STATE_ICONIFIED): |
790 |
0 |
stateString += "ICONIFIED " |
791 |
6284 |
if stateSet.count(Accessibility.STATE_MODAL): |
792 |
6 |
stateString += "MODAL " |
793 |
6284 |
if stateSet.count(Accessibility.STATE_MULTI_LINE): |
794 |
8 |
stateString += "MULTI_LINE " |
795 |
6284 |
if stateSet.count(Accessibility.STATE_MULTISELECTABLE): |
796 |
0 |
stateString += "MULTISELECTABLE " |
797 |
6284 |
if stateSet.count(Accessibility.STATE_OPAQUE): |
798 |
0 |
stateString += "OPAQUE " |
799 |
6284 |
if stateSet.count(Accessibility.STATE_PRESSED): |
800 |
0 |
stateString += "PRESSED " |
801 |
6284 |
if stateSet.count(Accessibility.STATE_RESIZABLE): |
802 |
604 |
stateString += "RESIZABLE " |
803 |
6284 |
if stateSet.count(Accessibility.STATE_SELECTABLE): |
804 |
193 |
stateString += "SELECTABLE " |
805 |
6284 |
if stateSet.count(Accessibility.STATE_SELECTED): |
806 |
164 |
stateString += "SELECTED " |
807 |
6284 |
if stateSet.count(Accessibility.STATE_SENSITIVE): |
808 |
3262 |
stateString += "SENSITIVE " |
809 |
6284 |
if stateSet.count(Accessibility.STATE_SHOWING): |
810 |
3397 |
stateString += "SHOWING " |
811 |
6284 |
if stateSet.count(Accessibility.STATE_SINGLE_LINE): |
812 |
41 |
stateString += "SINGLE_LINE " |
813 |
6284 |
if stateSet.count(Accessibility.STATE_STALE): |
814 |
0 |
stateString += "STALE " |
815 |
6284 |
if stateSet.count(Accessibility.STATE_TRANSIENT): |
816 |
35 |
stateString += "TRANSIENT " |
817 |
6284 |
if stateSet.count(Accessibility.STATE_VERTICAL): |
818 |
154 |
stateString += "VERTICAL " |
819 |
6284 |
if stateSet.count(Accessibility.STATE_VISIBLE): |
820 |
3402 |
stateString += "VISIBLE " |
821 |
6284 |
if stateSet.count(Accessibility.STATE_MANAGES_DESCENDANTS): |
822 |
9 |
stateString += "MANAGES_DESCENDANTS " |
823 |
6284 |
if stateSet.count(Accessibility.STATE_INDETERMINATE): |
824 |
0 |
stateString += "INDETERMINATE " |
825 |
|
|
826 |
6284 |
return stateString.strip() |
827 |
|
|
828 |
1 |
def accessibleNameToString(self): |
829 |
|
"""Returns the accessible's name in single quotes or |
830 |
|
the string None if the accessible does not have a name. |
831 |
|
""" |
832 |
|
|
833 |
18467 |
if self.name: |
834 |
13024 |
return "'" + self.name + "'" |
835 |
|
else: |
836 |
5003 |
return "None" |
837 |
|
|
838 |
1 |
def __del__(self): |
839 |
|
"""Unrefs the AT-SPI Accessible associated with this object. |
840 |
|
""" |
841 |
|
|
842 |
1954 |
if self.accessible: |
843 |
1954 |
try: |
844 |
1954 |
self.accessible.unref() |
845 |
411 |
except: |
846 |
411 |
pass |
847 |
|
|
848 |
1954 |
try: |
849 |
1954 |
Accessible.deleteAccessible(self._acc) |
850 |
0 |
except: |
851 |
0 |
pass |
852 |
|
|
853 |
1954 |
self.accessible = None |
854 |
1954 |
self._acc = None |
855 |
1954 |
self.app = None |
856 |
|
|
857 |
1 |
def __get_name(self): |
858 |
|
"""Returns the object's accessible name as a string. |
859 |
|
""" |
860 |
|
|
861 |
|
# Combo boxes don't seem to issue accessible-name changed |
862 |
|
# events, so we can't cache their names. The main culprit |
863 |
|
# here seems to be the combo box in gaim's "Join Chat" window. |
864 |
|
# |
865 |
17654 |
name = self.accessible.name |
866 |
|
|
867 |
17214 |
if name and settings.cacheValues \ |
868 |
1241 |
and (self.role != rolenames.ROLE_COMBO_BOX): |
869 |
1114 |
self.name = name |
870 |
|
|
871 |
17214 |
return name |
872 |
|
|
873 |
1 |
def __get_description(self): |
874 |
|
"""Returns the object's accessible description as a string. |
875 |
|
""" |
876 |
|
|
877 |
179 |
description = self.accessible.description |
878 |
|
|
879 |
179 |
if description and settings.cacheValues and settings.cacheDescriptions: |
880 |
0 |
self.description = description |
881 |
|
|
882 |
179 |
return description |
883 |
|
|
884 |
1 |
def __get_parent(self): |
885 |
|
"""Returns the object's parent as a Python Accessible. If |
886 |
|
this object has no parent, None will be returned. |
887 |
|
""" |
888 |
|
|
889 |
|
# We will never set self.parent if the backing accessible doesn't |
890 |
|
# have a parent. The reason we do this is that we may sometimes |
891 |
|
# get events for objects without a parent, but then the object ends |
892 |
|
# up getting a parent later on. |
893 |
|
# |
894 |
13084 |
accParent = self.accessible.parent |
895 |
|
|
896 |
12862 |
if not accParent: |
897 |
11888 |
return None |
898 |
|
else: |
899 |
974 |
parent = Accessible.makeAccessible(accParent); |
900 |
|
|
901 |
974 |
if settings.cacheValues: |
902 |
974 |
self.parent = parent |
903 |
|
|
904 |
974 |
return parent; |
905 |
|
|
906 |
1 |
def __get_child_count(self): |
907 |
|
"""Returns the number of children for this object. |
908 |
|
""" |
909 |
|
|
910 |
11083 |
childCount = self.accessible.childCount |
911 |
|
|
912 |
|
# We don't want to cache this value because it's possible that it |
913 |
|
# will continually change. |
914 |
|
# if settings.cacheValues: |
915 |
|
# self.childCount = childCount |
916 |
|
|
917 |
11083 |
return childCount |
918 |
|
|
919 |
1 |
def __get_index(self): |
920 |
|
"""Returns the index of this object in its parent's child list. |
921 |
|
""" |
922 |
|
|
923 |
2213 |
index = self.accessible.getIndexInParent() |
924 |
|
|
925 |
|
# We don't want to cache this value because it's possible that it |
926 |
|
# will continually change. |
927 |
|
# if settings.cacheValues: |
928 |
|
# self.index = index |
929 |
|
|
930 |
2213 |
return index |
931 |
|
|
932 |
1 |
def __get_role(self): |
933 |
|
"""Returns the Accessible role name of this object as a string. |
934 |
|
This string is not localized and can be used for comparison. |
935 |
|
|
936 |
|
Note that this fudges the rolename of the object to match more closely |
937 |
|
what it is. The only thing that is being fudged right now is to |
938 |
|
coalesce radio and check menu items that are also submenus; gtk-demo |
939 |
|
has an example of this in its menus demo. |
940 |
|
""" |
941 |
|
|
942 |
2639 |
role = self.accessible.getRoleName() |
943 |
|
|
944 |
|
# [[[TODO: WDW - HACK to handle the situation where some |
945 |
|
# things might not be quite lined up with the ATK and AT-SPI. |
946 |
|
# That is, some roles might have ids but no string yet. See |
947 |
|
# http://bugzilla.gnome.org/show_bug.cgi?id=361757. |
948 |
|
# |
949 |
2639 |
if not len(role): |
950 |
0 |
try: |
951 |
0 |
roleId = self.accessible.getRole() |
952 |
0 |
if roleId == Accessibility.ROLE_LINK: |
953 |
0 |
role = rolenames.ROLE_LINK |
954 |
0 |
elif roleId == Accessibility.ROLE_INPUT_METHOD_WINDOW: |
955 |
0 |
role = rolenames.ROLE_INPUT_METHOD_WINDOW |
956 |
0 |
except: |
957 |
0 |
pass |
958 |
|
|
959 |
|
# [[[TODO: HACK to coalesce menu items with children into |
960 |
|
# menus. The menu demo in gtk-demo does this, and one |
961 |
|
# might view that as an edge case. But, in |
962 |
|
# gnome-terminal, "Terminal" -> "Set Character Encoding" |
963 |
|
# is a menu item with children, but it behaves like a |
964 |
|
# menu.]]] |
965 |
|
# |
966 |
2639 |
if (role == rolenames.ROLE_CHECK_MENU_ITEM) \ |
967 |
9 |
and (self.childCount > 0): |
968 |
0 |
role = rolenames.ROLE_CHECK_MENU |
969 |
2639 |
elif (role == rolenames.ROLE_RADIO_MENU_ITEM) \ |
970 |
1 |
and (self.childCount > 0): |
971 |
1 |
role = rolenames.ROLE_RADIO_MENU |
972 |
2638 |
elif (role == rolenames.ROLE_MENU_ITEM) \ |
973 |
34 |
and (self.childCount > 0): |
974 |
0 |
role = rolenames.ROLE_MENU |
975 |
|
|
976 |
|
# [[[TODO: HACK because Java gives us radio button role and |
977 |
|
# check box role for menu item objects, instead of giving us |
978 |
|
# radio menu item role and check menu item role (see SwingSet |
979 |
|
# menus).]]] |
980 |
|
# |
981 |
2639 |
if (self.parent) and (self.parent.role == rolenames.ROLE_MENU): |
982 |
70 |
if (role == rolenames.ROLE_RADIO_BUTTON): |
983 |
0 |
role = rolenames.ROLE_RADIO_MENU_ITEM |
984 |
70 |
elif (role == rolenames.ROLE_CHECK_BOX): |
985 |
0 |
role = rolenames.ROLE_CHECK_MENU_ITEM |
986 |
|
|
987 |
|
# [[[TODO: HACK because we sometimes get an object with an |
988 |
|
# unknown role but it's role changes later on and we are not |
989 |
|
# notified. An example of this is gnome-terminal. So...to |
990 |
|
# help with this, we will not cache the role if it is unknown. |
991 |
|
# See http://bugzilla.gnome.org/show_bug.cgi?id=344218 for |
992 |
|
# more info.]]] |
993 |
|
# |
994 |
2639 |
if role and settings.cacheValues \ |
995 |
2639 |
and (role != rolenames.ROLE_UNKNOWN): |
996 |
2134 |
self.role = role |
997 |
|
|
998 |
2639 |
return role |
999 |
|
|
1000 |
1 |
def __get_localized_rolename(self): |
1001 |
|
"""Returns the Accessible role name of this object as a |
1002 |
|
localized string. Most callers should use __get_role instead |
1003 |
|
since it returns a non-localized string that can be used for |
1004 |
|
comparison. |
1005 |
|
""" |
1006 |
|
|
1007 |
0 |
localizedRoleName = self.accessible.getLocalizedRoleName() |
1008 |
|
|
1009 |
0 |
if localizedRoleName and settings.cacheValues: |
1010 |
0 |
self.localizedRoleName = localizedRoleName |
1011 |
|
|
1012 |
0 |
return localizedRoleName |
1013 |
|
|
1014 |
1 |
def __get_state(self): |
1015 |
|
"""Returns the Accessible StateSeq of this object, which is a |
1016 |
|
sequence of Accessible StateTypes. |
1017 |
|
""" |
1018 |
|
|
1019 |
43458 |
try: |
1020 |
43458 |
stateSet = self.accessible.getState() |
1021 |
816 |
except: |
1022 |
816 |
stateSet = None |
1023 |
43458 |
if stateSet: |
1024 |
42642 |
try: |
1025 |
42642 |
state = stateSet._narrow(Accessibility.StateSet).getStates() |
1026 |
0 |
except: |
1027 |
0 |
state = [] |
1028 |
|
else: |
1029 |
816 |
state = [] |
1030 |
|
|
1031 |
|
# [[[WDW - we don't seem to always get appropriate state changed |
1032 |
|
# information, so we will not cache state information.]]] |
1033 |
|
# |
1034 |
|
#if state and settings.cacheValues: |
1035 |
|
# self.state = state |
1036 |
|
|
1037 |
43458 |
return state |
1038 |
|
|
1039 |
1 |
def __get_relations(self): |
1040 |
|
"""Returns the Accessible RelationSet of this object as a list. |
1041 |
|
""" |
1042 |
|
|
1043 |
26652 |
relations = [] |
1044 |
|
|
1045 |
26652 |
relationSet = self.accessible.getRelationSet() |
1046 |
|
|
1047 |
27402 |
for relation in relationSet: |
1048 |
1160 |
try: |
1049 |
1160 |
relations.append(relation._narrow(Accessibility.Relation)) |
1050 |
0 |
except: |
1051 |
0 |
pass |
1052 |
|
|
1053 |
26242 |
return relations |
1054 |
|
|
1055 |
1 |
def __get_app(self): |
1056 |
|
"""Returns the AT-SPI Accessibility_Application associated with this |
1057 |
|
object. Returns None if the application cannot be found (usually |
1058 |
|
the indication of an AT-SPI bug). |
1059 |
|
""" |
1060 |
|
|
1061 |
|
# [[[TODO: WDW - this code seems like it might break if this |
1062 |
|
# object is an application to begin with. Logged as bugzilla |
1063 |
|
# bug 319677.]]] |
1064 |
|
# |
1065 |
339 |
debug.println(debug.LEVEL_FINEST, |
1066 |
339 |
"Finding app for source.name=" \ |
1067 |
339 |
+ self.accessibleNameToString()) |
1068 |
306 |
obj = self |
1069 |
306 |
while obj.parent and (obj != obj.parent): |
1070 |
1100 |
obj = obj.parent |
1071 |
1100 |
debug.println(debug.LEVEL_FINEST, |
1072 |
1100 |
"--> parent.name=" + obj.accessibleNameToString()) |
1073 |
|
|
1074 |
273 |
if (obj == obj.parent): |
1075 |
0 |
debug.println(debug.LEVEL_SEVERE, |
1076 |
0 |
"ERROR in Accessible.__get_app: obj == obj.parent!") |
1077 |
0 |
return None |
1078 |
273 |
elif (obj.role != rolenames.ROLE_APPLICATION): |
1079 |
0 |
debug.println(debug.LEVEL_FINEST, |
1080 |
0 |
"ERROR in Accessible.__get_app: top most parent " \ |
1081 |
0 |
"(name='%s') is of role %s" % (obj.name, obj.role)) |
1082 |
|
|
1083 |
|
# [[[TODO: We'll let this fall through for some cases. It |
1084 |
|
# seems as though we don't always end up with an |
1085 |
|
# application, but we do end up with *something* that is |
1086 |
|
# uniquely identifiable as the app. |
1087 |
|
# |
1088 |
0 |
if (obj.role != rolenames.ROLE_INVALID) \ |
1089 |
0 |
and (obj.role != rolenames.ROLE_FRAME): |
1090 |
0 |
return None |
1091 |
|
|
1092 |
273 |
debug.println(debug.LEVEL_FINEST, "Accessible app for %s is %s" \ |
1093 |
273 |
% (self.accessibleNameToString(), \ |
1094 |
273 |
obj.accessibleNameToString())) |
1095 |
|
|
1096 |
273 |
if settings.cacheValues: |
1097 |
273 |
self.app = obj |
1098 |
|
|
1099 |
273 |
return obj |
1100 |
|
|
1101 |
1 |
def __get_extents(self, coordinateType = 0): |
1102 |
|
"""Returns the object's accessible extents as an |
1103 |
|
Accessibility.BoundingBox object, or None if the object doesn't |
1104 |
|
implement the Accessibility Component interface. |
1105 |
|
|
1106 |
|
Arguments: |
1107 |
|
- coordinateType: 0 = get the extents in screen coordinates, |
1108 |
|
1 = get the extents in window coordinates |
1109 |
|
|
1110 |
|
Returns: |
1111 |
|
This object's accessible extents as an Accessibility.BoundingBox |
1112 |
|
object, or None if the object doesn't implement the Accessibility |
1113 |
|
Component interface. |
1114 |
|
""" |
1115 |
|
|
1116 |
52 |
component = self.component |
1117 |
|
|
1118 |
52 |
if not component: |
1119 |
0 |
return None |
1120 |
|
|
1121 |
52 |
extents = component.getExtents(coordinateType) |
1122 |
|
|
1123 |
|
# [[[TODO: WDW - caching the extents is dangerous because |
1124 |
|
# the object may move, resulting in the current extents |
1125 |
|
# becoming way out of date. Perhaps need to cache just |
1126 |
|
# the component interface and suffer the hit for getting |
1127 |
|
# the extents if we cannot figure out how to determine if |
1128 |
|
# the cached extents is out of date. Logged as bugzilla |
1129 |
|
# bug 319678.]]] |
1130 |
|
# |
1131 |
|
#if settings.cacheValues: |
1132 |
|
# self.extents = extents |
1133 |
|
|
1134 |
52 |
return extents |
1135 |
|
|
1136 |
1 |
def __get_action(self): |
1137 |
|
"""Returns an object that implements the Accessibility_Action |
1138 |
|
interface for this object, or None if this object doesn't implement |
1139 |
|
the Accessibility_Action interface. |
1140 |
|
""" |
1141 |
|
|
1142 |
321 |
action = self.accessible.queryInterface("IDL:Accessibility/Action:1.0") |
1143 |
|
|
1144 |
321 |
if action: |
1145 |
199 |
try: |
1146 |
199 |
action = action._narrow(Accessibility.Action) |
1147 |
0 |
except: |
1148 |
0 |
action = None |
1149 |
|
|
1150 |
321 |
if action and settings.cacheValues: |
1151 |
199 |
self.action = action |
1152 |
|
|
1153 |
321 |
return action |
1154 |
|
|
1155 |
1 |
def __get_component(self): |
1156 |
|
"""Returns an object that implements the Accessibility_Component |
1157 |
|
interface for this object, or None if this object doesn't implement |
1158 |
|
the Accessibility_Component interface. |
1159 |
|
""" |
1160 |
|
|
1161 |
97 |
component = self.accessible.queryInterface(\ |
1162 |
97 |
"IDL:Accessibility/Component:1.0") |
1163 |
|
|
1164 |
97 |
if component: |
1165 |
97 |
try: |
1166 |
97 |
component = component._narrow(Accessibility.Component) |
1167 |
0 |
except: |
1168 |
0 |
component = None |
1169 |
|
|
1170 |
97 |
if component and settings.cacheValues: |
1171 |
97 |
self.component = component |
1172 |
|
|
1173 |
97 |
return component |
1174 |
|
|
1175 |
1 |
def __get_hyperlink(self): |
1176 |
|
"""Returns an object that implements the Accessibility_Hyperlink |
1177 |
|
interface for this object, or None if this object doesn't implement |
1178 |
|
the Accessibility_Hyperlink interface. |
1179 |
|
""" |
1180 |
|
|
1181 |
0 |
hyperlink = self.accessible.queryInterface(\ |
1182 |
0 |
"IDL:Accessibility/Hyperlink:1.0") |
1183 |
|
|
1184 |
0 |
if hyperlink: |
1185 |
0 |
try: |
1186 |
0 |
hyperlink = hyperlink._narrow(Accessibility.Hyperlink) |
1187 |
0 |
except: |
1188 |
0 |
hyperlink = None |
1189 |
|
|
1190 |
0 |
if hyperlink and settings.cacheValues: |
1191 |
0 |
self.hyperlink = hyperlink |
1192 |
|
|
1193 |
0 |
return hyperlink |
1194 |
|
|
1195 |
1 |
def __get_hypertext(self): |
1196 |
|
"""Returns an object that implements the Accessibility_Hypertext |
1197 |
|
interface for this object, or None if this object doesn't implement |
1198 |
|
the Accessibility_Hypertext interface. |
1199 |
|
""" |
1200 |
|
|
1201 |
14 |
hypertext = self.accessible.queryInterface(\ |
1202 |
14 |
"IDL:Accessibility/Hypertext:1.0") |
1203 |
|
|
1204 |
14 |
if hypertext: |
1205 |
0 |
try: |
1206 |
0 |
hypertext = hypertext._narrow(Accessibility.Hypertext) |
1207 |
0 |
except: |
1208 |
0 |
hypertext = None |
1209 |
|
|
1210 |
14 |
if hypertext and settings.cacheValues: |
1211 |
0 |
self.hypertext = hypertext |
1212 |
|
|
1213 |
14 |
return hypertext |
1214 |
|
|
1215 |
1 |
def __get_image(self): |
1216 |
|
"""Returns an object that implements the Accessibility_Image |
1217 |
|
interface for this object, or None if this object doesn't implement |
1218 |
|
the Accessibility_Image interface. |
1219 |
|
""" |
1220 |
|
|
1221 |
9 |
image = self.accessible.queryInterface(\ |
1222 |
9 |
"IDL:Accessibility/Image:1.0") |
1223 |
|
|
1224 |
9 |
if image: |
1225 |
9 |
try: |
1226 |
9 |
image = image._narrow(Accessibility.Image) |
1227 |
0 |
except: |
1228 |
0 |
image = None |
1229 |
|
|
1230 |
9 |
if image and settings.cacheValues: |
1231 |
9 |
self.image = image |
1232 |
|
|
1233 |
9 |
return image |
1234 |
|
|
1235 |
1 |
def __get_selection(self): |
1236 |
|
"""Returns an object that implements the Accessibility_Selection |
1237 |
|
interface for this object, or None if this object doesn't implement |
1238 |
|
the Accessibility_Selection interface. |
1239 |
|
""" |
1240 |
|
|
1241 |
89 |
selection = self.accessible.queryInterface(\ |
1242 |
89 |
"IDL:Accessibility/Selection:1.0") |
1243 |
|
|
1244 |
89 |
if selection: |
1245 |
62 |
try: |
1246 |
62 |
selection = selection._narrow(Accessibility.Selection) |
1247 |
0 |
except: |
1248 |
0 |
selection = None |
1249 |
|
|
1250 |
89 |
if selection and settings.cacheValues: |
1251 |
62 |
self.selection = selection |
1252 |
|
|
1253 |
89 |
return selection |
1254 |
|
|
1255 |
1 |
def __get_document(self): |
1256 |
|
"""Returns an object that implements the Accessibility_Document |
1257 |
|
interface for this object, or None if this object doesn't implement |
1258 |
|
the Accessibility_Document interface. |
1259 |
|
""" |
1260 |
0 |
document = self.accessible.queryInterface(\ |
1261 |
0 |
"IDL:Accessibility/Document:1.0") |
1262 |
|
|
1263 |
0 |
if document: |
1264 |
0 |
try: |
1265 |
0 |
document = document._narrow(Accessibility.Document) |
1266 |
0 |
except: |
1267 |
0 |
document = None |
1268 |
|
|
1269 |
0 |
if document and settings.cacheValues: |
1270 |
0 |
self.document = document |
1271 |
|
|
1272 |
0 |
return document |
1273 |
|
|
1274 |
1 |
def __get_table(self): |
1275 |
|
"""Returns an object that implements the Accessibility_Table |
1276 |
|
interface for this object, or None if this object doesn't implement |
1277 |
|
the Accessibility_Table interface. |
1278 |
|
""" |
1279 |
|
|
1280 |
1784 |
table = self.accessible.queryInterface("IDL:Accessibility/Table:1.0") |
1281 |
|
|
1282 |
1784 |
if table: |
1283 |
42 |
try: |
1284 |
42 |
table = table._narrow(Accessibility.Table) |
1285 |
0 |
except: |
1286 |
0 |
table = None |
1287 |
|
|
1288 |
1784 |
if table and settings.cacheValues: |
1289 |
42 |
self.table = table |
1290 |
|
|
1291 |
1784 |
return table |
1292 |
|
|
1293 |
1 |
def __get_text(self): |
1294 |
|
"""Returns an object that implements the Accessibility_Text |
1295 |
|
interface for this object, or None if this object doesn't implement |
1296 |
|
the Accessibility_Text interface. |
1297 |
|
""" |
1298 |
12429 |
text = self.accessible.queryInterface("IDL:Accessibility/Text:1.0") |
1299 |
|
|
1300 |
12429 |
if text: |
1301 |
406 |
try: |
1302 |
406 |
text = text._narrow(Accessibility.Text) |
1303 |
0 |
except: |
1304 |
0 |
text = None |
1305 |
|
|
1306 |
12429 |
if text and settings.cacheValues: |
1307 |
406 |
self.text = text |
1308 |
|
|
1309 |
12429 |
return text |
1310 |
|
|
1311 |
1 |
def __get_value(self): |
1312 |
|
"""Returns an object that implements the Accessibility_Value |
1313 |
|
interface for this object, or None if this object doesn't implement |
1314 |
|
the Accessibility_Value interface. |
1315 |
|
""" |
1316 |
|
|
1317 |
4976 |
value = self.accessible.queryInterface("IDL:Accessibility/Value:1.0") |
1318 |
|
|
1319 |
4976 |
if value: |
1320 |
9 |
try: |
1321 |
9 |
value = value._narrow(Accessibility.Value) |
1322 |
0 |
except: |
1323 |
0 |
value = None |
1324 |
|
|
1325 |
4976 |
if value and settings.cacheValues: |
1326 |
9 |
self.value = value |
1327 |
|
|
1328 |
4976 |
return value |
1329 |
|
|
1330 |
1 |
def __get_attributes(self): |
1331 |
|
"""Returns an Accessibility_AttributeSet of the object. |
1332 |
|
""" |
1333 |
|
|
1334 |
110 |
try: |
1335 |
110 |
attributes = self.accessible.getAttributes() |
1336 |
0 |
except: |
1337 |
0 |
attributes = None |
1338 |
|
|
1339 |
110 |
return attributes |
1340 |
|
|
1341 |
1 |
def __getattr__(self, attr): |
1342 |
|
"""Created virtual attributes for the Accessible object to make |
1343 |
|
the syntax a bit nicer (e.g., acc.name rather than acc.name()). |
1344 |
|
This method is also called if and only if the given attribute |
1345 |
|
does not exist in the object. Thus, we're effectively lazily |
1346 |
|
building a cache to the remote object attributes here. |
1347 |
|
|
1348 |
|
Arguments: |
1349 |
|
- attr: a string indicating the attribute name to retrieve |
1350 |
|
|
1351 |
|
Returns the value of the given attribute. |
1352 |
|
""" |
1353 |
|
|
1354 |
1010736 |
if attr == "name": |
1355 |
17654 |
return self.__get_name() |
1356 |
993082 |
elif attr == "description": |
1357 |
179 |
return self.__get_description() |
1358 |
992903 |
elif attr == "parent": |
1359 |
13084 |
return self.__get_parent() |
1360 |
979819 |
elif attr == "childCount": |
1361 |
11083 |
return self.__get_child_count() |
1362 |
968736 |
elif attr == "index": |
1363 |
2213 |
return self.__get_index() |
1364 |
966523 |
elif attr == "role": |
1365 |
2639 |
return self.__get_role() |
1366 |
963884 |
elif attr == "localizedRoleName": |
1367 |
0 |
return self.__get_localized_rolename() |
1368 |
963884 |
elif attr == "state": |
1369 |
43458 |
return self.__get_state() |
1370 |
920426 |
elif attr == "relations": |
1371 |
26652 |
return self.__get_relations() |
1372 |
893774 |
elif attr == "app": |
1373 |
339 |
return self.__get_app() |
1374 |
893435 |
elif attr == "extents": |
1375 |
52 |
return self.__get_extents() |
1376 |
893383 |
elif attr == "action": |
1377 |
321 |
return self.__get_action() |
1378 |
893062 |
elif attr == "component": |
1379 |
97 |
return self.__get_component() |
1380 |
892965 |
elif attr == "hyperlink": |
1381 |
0 |
return self.__get_hyperlink() |
1382 |
892965 |
elif attr == "hypertext": |
1383 |
14 |
return self.__get_hypertext() |
1384 |
892951 |
elif attr == "image": |
1385 |
9 |
return self.__get_image() |
1386 |
892942 |
elif attr == "selection": |
1387 |
89 |
return self.__get_selection() |
1388 |
892853 |
elif attr == "table": |
1389 |
1784 |
return self.__get_table() |
1390 |
891069 |
elif attr == "text": |
1391 |
12429 |
return self.__get_text() |
1392 |
878640 |
elif attr == "value": |
1393 |
4976 |
return self.__get_value() |
1394 |
873664 |
elif attr == "attributes": |
1395 |
110 |
return self.__get_attributes() |
1396 |
873554 |
elif attr == "document": |
1397 |
0 |
return self.__get_document() |
1398 |
873554 |
elif attr.startswith('__') and attr.endswith('__'): |
1399 |
873547 |
raise AttributeError, attr |
1400 |
|
else: |
1401 |
7 |
return self.__dict__[attr] |
1402 |
|
|
1403 |
1 |
def child(self, index): |
1404 |
|
"""Returns the specified child of this object. |
1405 |
|
|
1406 |
|
Arguments: |
1407 |
|
- index: an integer specifying which child to obtain |
1408 |
|
|
1409 |
|
Returns the child at the given index or raise an exception if the |
1410 |
|
index is out of bounds or the child is invalid. |
1411 |
|
""" |
1412 |
|
|
1413 |
|
# [[[TODO: WDW - the AT-SPI appears to give us a different accessible |
1414 |
|
# when we repeatedly ask for the same child of a parent that manages |
1415 |
|
# its descendants. So...we probably shouldn't cache those kind of |
1416 |
|
# children because we're likely to cause a memory leak.]]] |
1417 |
|
# |
1418 |
|
# Save away details we now know about this child |
1419 |
|
# |
1420 |
10027 |
newChild = None |
1421 |
10027 |
if index >= 0 and index < self.accessible.childCount: |
1422 |
10027 |
accChild = self.accessible.getChildAtIndex(index) |
1423 |
10027 |
if accChild: |
1424 |
10027 |
newChild = Accessible.makeAccessible(accChild) |
1425 |
10027 |
newChild.index = index |
1426 |
10027 |
newChild.parent = self |
1427 |
10027 |
newChild.app = self.app |
1428 |
|
|
1429 |
10027 |
if not newChild: |
1430 |
|
# The problem with a child not existing is a bad one. |
1431 |
|
# We want to issue a warning and we also want to know |
1432 |
|
# where it happens. |
1433 |
|
# |
1434 |
0 |
debug.printStack(debug.LEVEL_WARNING) |
1435 |
0 |
debug.println(debug.LEVEL_WARNING, |
1436 |
0 |
"Child at index %d is not an Accessible" % index) |
1437 |
|
|
1438 |
10027 |
return newChild |
1439 |
|
|
1440 |
1 |
def toString(self, indent="", includeApp=True): |
1441 |
|
|
1442 |
|
"""Returns a string, suitable for printing, that describes the |
1443 |
|
given accessible. |
1444 |
|
|
1445 |
|
Arguments: |
1446 |
|
- indent: A string to prefix the output with |
1447 |
|
- includeApp: If True, include information about the app |
1448 |
|
for this accessible. |
1449 |
|
""" |
1450 |
|
|
1451 |
6724 |
if includeApp: |
1452 |
6724 |
if self.app: |
1453 |
6491 |
string = indent + "app.name=%-20s " \ |
1454 |
6491 |
% self.app.accessibleNameToString() |
1455 |
|
else: |
1456 |
167 |
string = indent + "app=None " |
1457 |
|
else: |
1458 |
0 |
string = indent |
1459 |
|
|
1460 |
6658 |
string += "name=%s role='%s' state='%s' relations='%s'" \ |
1461 |
6658 |
% (self.accessibleNameToString(), |
1462 |
6284 |
self.role, |
1463 |
6284 |
self.getStateString(), |
1464 |
6284 |
self.getRelationString()) |
1465 |
|
|
1466 |
6096 |
return string |
1467 |
|
|
1468 |
|
######################################################################## |
1469 |
|
# # |
1470 |
|
# Testing functions. # |
1471 |
|
# # |
1472 |
|
######################################################################## |
1473 |
|
|
1474 |
1 |
def __printTopObject(child): |
1475 |
0 |
parent = child |
1476 |
0 |
while parent: |
1477 |
0 |
if not parent.parent: |
1478 |
0 |
print "RAW TOP:", parent.name, parent.role |
1479 |
0 |
parent = parent.parent |
1480 |
0 |
if (child.parent): |
1481 |
0 |
accessible = Accessible.makeAccessible(child) |
1482 |
0 |
app = accessible.app |
1483 |
0 |
print "ACC TOP:", app.name, app.role |
1484 |
|
|
1485 |
1 |
def __printDesktops(): |
1486 |
0 |
registry = Registry().registry |
1487 |
0 |
print "There are %d desktops" % registry.getDesktopCount() |
1488 |
0 |
for i in range(0,registry.getDesktopCount()): |
1489 |
0 |
desktop = registry.getDesktop(i) |
1490 |
0 |
print " Desktop %d (name=%s) has %d apps" \ |
1491 |
0 |
% (i, desktop.name, desktop.childCount) |
1492 |
0 |
for j in range(0, desktop.childCount): |
1493 |
0 |
app = desktop.getChildAtIndex(j) |
1494 |
0 |
print " App %d: name=%s role=%s" \ |
1495 |
0 |
% (j, app.name, app.getRoleName()) |
1496 |
|
|
1497 |
1 |
def __notifyEvent(event): |
1498 |
0 |
print event.type, event.source.name, \ |
1499 |
0 |
event.detail1, event.detail2, \ |
1500 |
0 |
event.any_data |
1501 |
0 |
__printTopObject(event.source) |
1502 |
0 |
if not event.source.parent: |
1503 |
0 |
print "NO PARENT:", event.source.name, event.source.role |
1504 |
|
|
1505 |
1 |
def __notifyKeystroke(event): |
1506 |
0 |
print "keystroke type=%d hw_code=%d modifiers=%d event_string=(%s) " \ |
1507 |
|
"is_text=%s" \ |
1508 |
0 |
% (event.type, event.hw_code, event.modifiers, event.event_string, |
1509 |
0 |
event.is_text) |
1510 |
0 |
if event.event_string == "F12": |
1511 |
0 |
__shutdownAndExit(None, None) |
1512 |
0 |
return False |
1513 |
|
|
1514 |
1 |
def __shutdownAndExit(signum, frame): |
1515 |
0 |
Registry().stop() |
1516 |
0 |
print "Goodbye." |
1517 |
|
|
1518 |
1 |
def __test(): |
1519 |
|
eventTypes = [ |
1520 |
0 |
"focus:", |
1521 |
0 |
"mouse:rel", |
1522 |
0 |
"mouse:button", |
1523 |
0 |
"mouse:abs", |
1524 |
0 |
"keyboard:modifiers", |
1525 |
0 |
"object:property-change", |
1526 |
0 |
"object:property-change:accessible-name", |
1527 |
0 |
"object:property-change:accessible-description", |
1528 |
0 |
"object:property-change:accessible-parent", |
1529 |
0 |
"object:state-changed", |
1530 |
0 |
"object:state-changed:focused", |
1531 |
0 |
"object:selection-changed", |
1532 |
0 |
"object:children-changed" |
1533 |
|
"object:active-descendant-changed" |
1534 |
|
"object:visible-data-changed" |
1535 |
|
"object:text-selection-changed", |
1536 |
0 |
"object:text-caret-moved", |
1537 |
0 |
"object:text-changed", |
1538 |
0 |
"object:column-inserted", |
1539 |
0 |
"object:row-inserted", |
1540 |
0 |
"object:column-reordered", |
1541 |
0 |
"object:row-reordered", |
1542 |
0 |
"object:column-deleted", |
1543 |
0 |
"object:row-deleted", |
1544 |
0 |
"object:model-changed", |
1545 |
0 |
"object:link-selected", |
1546 |
0 |
"object:bounds-changed", |
1547 |
0 |
"window:minimize", |
1548 |
0 |
"window:maximize", |
1549 |
0 |
"window:restore", |
1550 |
0 |
"window:activate", |
1551 |
0 |
"window:create", |
1552 |
0 |
"window:deactivate", |
1553 |
0 |
"window:close", |
1554 |
0 |
"window:lower", |
1555 |
0 |
"window:raise", |
1556 |
0 |
"window:resize", |
1557 |
0 |
"window:shade", |
1558 |
0 |
"window:unshade", |
1559 |
0 |
"object:property-change:accessible-table-summary", |
1560 |
0 |
"object:property-change:accessible-table-row-header", |
1561 |
0 |
"object:property-change:accessible-table-column-header", |
1562 |
0 |
"object:property-change:accessible-table-summary", |
1563 |
0 |
"object:property-change:accessible-table-row-description", |
1564 |
0 |
"object:property-change:accessible-table-column-description", |
1565 |
0 |
"object:test", |
1566 |
0 |
"window:restyle", |
1567 |
0 |
"window:desktop-create", |
1568 |
0 |
"window:desktop-destroy" |
1569 |
|
] |
1570 |
|
|
1571 |
0 |
__printDesktops() |
1572 |
|
|
1573 |
0 |
registry = Registry() |
1574 |
0 |
for eventType in eventTypes: |
1575 |
0 |
registry.registerEventListener(__notifyEvent, eventType) |
1576 |
0 |
registry.registerKeystrokeListeners(__notifyKeystroke) |
1577 |
0 |
registry.start() |
1578 |
|
|
1579 |
1 |
if __name__ == "__main__": |
1580 |
0 |
signal.signal(signal.SIGINT, __shutdownAndExit) |
1581 |
0 |
signal.signal(signal.SIGQUIT, __shutdownAndExit) |
1582 |
0 |
__test() |