Coverage Report - orca.atspi

ModuleCoverage %
orca.atspi
70%
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()