-'''
-Creates functions at import time that are mixed into the
-Accessibility.Accessible base class to make it more Pythonic.
-
-Based on public domain code originally posted at
-http://wwwx.cs.unc.edu/~parente/cgi-bin/RuntimeClassMixins.
-
-@todo: PP: implement caching for basic attributes
-
-@var _CACHE_LEVEL: Current level of caching enabled. Checked dynamically by
- L{_AccessibleMixin}
-@type _CACHE_LEVEL: integer
-
-@author: Peter Parente
-@organization: IBM Corporation
-@copyright: Copyright (c) 2005, 2007 IBM Corporation
-@license: LGPL
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public
-License as published by the Free Software Foundation; either
-version 2 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with this library; if not, write to the
-Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.
-
-Portions of this code originally licensed and copyright (c) 2005, 2007
-IBM Corporation under the BSD license, available at
-U{http://www.opensource.org/licenses/bsd-license.php}
-'''
-import new
-import types
-import ORBit
-import Accessibility
-import constants
-import utils
-
-_CACHE_LEVEL = None
-
-def getCacheLevel():
- '''
- Gets the current level of caching.
-
- @return: None indicating no caching is in effect.
- L{constants.CACHE_INTERFACES} indicating all interface query results are
- cached. L{constants.CACHE_PROPERTIES} indicating all basic accessible
- properties are cached.
- @rtype: integer
- '''
- return _CACHE_LEVEL
-
-def setCacheLevel(val):
- '''
- Sets the desired level of caching for all accessible objects created after
- this function is invoked.
-
- @param val: None indicating no caching is in effect.
- L{constants.CACHE_INTERFACES} indicating all interface query results are
- cached. L{constants.CACHE_PROPERTIES} indicating all basic accessible
- properties are cached.
- @type val: integer
- '''
- global _CACHE_LEVEL
- _CACHE_LEVEL = val
-
-def _makeQuery(iid):
- '''
- Builds a function querying to a specific interface and returns it.
-
- @ivar iid: Interface identifier to use when querying
- @type iid: string
- @return: Function querying to the given interface
- @rtype: function
- '''
- def _inner(self):
- '''
- Queries an object for another interface.
-
- @return: An object with the desired interface
- @rtype: object
- @raise NotImplementedError: When the desired interface is not supported
- '''
- try:
- return self._cache[iid]
- except KeyError:
- # interface not cached
- caching = True
- except AttributeError:
- # not caching at present
- caching = False
-
- try:
- i = self.queryInterface(iid)
- except Exception, e:
- raise LookupError(e)
- if i is None:
- raise NotImplementedError
-
- # not needed according to ORBit2 spec, but makes Java queries work
- # more reliably according to Orca experience
- i._narrow(i.__class__)
- if caching:
- # cache the narrow'ed result, but only if we're caching for this object
- self._cache[iid] = i
- return i
-
- return _inner
-
-def _makeExceptionHandler(func):
- '''
- Builds a function calling the one it wraps in try/except statements catching
- CORBA exceptions.
-
- @return: Function calling the method being wrapped
- @rtype: function
- '''
- def _inner(self, *args, **kwargs):
- try:
- # try calling the original func
- return func(self, *args, **kwargs)
- except ORBit.CORBA.NO_IMPLEMENT, e:
- # raise Python exception
- raise NotImplementedError(e)
- except ORBit.CORBA.Exception, e:
- # raise Python exception
- raise LookupError(e)
- return _inner
-
-def _mixInterfaces(cls, interfaces):
- '''
- Add methods for querying to interfaces other than the base accessible to
- the given class.
-
- @param cls: Class to mix interface methods into
- @type cls: class
- @param interfaces: Classes representing AT-SPI interfaces
- @type interfaces: list of class
- '''
- # create functions in this module for all interfaces listed in constants
- for interface in interfaces:
- # build name of converter from the name of the interface
- name = 'query%s' % utils.getInterfaceName(interface)
- # build a function that queries to the given interface
- func = _makeQuery(utils.getInterfaceIID(interface))
- # build a new method that is a clone of the original function
- method = new.function(func.func_code, func.func_globals, name,
- func.func_defaults, func.func_closure)
- # add the method to the given class
- setattr(cls, name, method)
-
-def _mixExceptions(cls):
- '''
- Wraps all methods and properties in a class with handlers for CORBA
- exceptions.
-
- @param cls: Class to mix interface methods into
- @type cls: class
- '''
- # loop over all names in the new class
- for name in cls.__dict__.keys():
- obj = cls.__dict__[name]
- # check if we're on a protected or private method
- if name.startswith('_'):
- continue
- # check if we're on a method
- elif isinstance(obj, types.FunctionType):
- # wrap the function in an exception handler
- method = _makeExceptionHandler(obj)
- # add the wrpped function to the class
- setattr(cls, name, method)
- # check if we're on a property
- elif isinstance(obj, property):
- # wrap the getters and setters
- if obj.fget:
- getter = _makeExceptionHandler(getattr(cls, obj.fget.__name__))
- else:
- getter = None
- if obj.fset:
- setter = _makeExceptionHandler(getattr(cls, obj.fset.__name__))
- else:
- setter = None
- setattr(cls, name, property(getter, setter))
-
-def _mixClass(cls, new_cls):
- '''
- Adds the methods in new_cls to cls. After mixing, all instances of cls will
- have the new methods. If there is a method name clash, the method already in
- cls will be prefixed with '_mix_' before the new method of the same name is
- mixed in.
-
- @note: _ is not the prefix because if you wind up with __ in front of a
- variable, it becomes private and mangled when an instance is created.
- Difficult to invoke from the mixin class.
-
- @param cls: Existing class to mix features into
- @type cls: class
- @param new_cls: Class containing features to add
- @type new_cls: class
- '''
- # loop over all names in the new class
- for name, func in new_cls.__dict__.items():
- # get only functions from the new_class
- if (isinstance(func, types.FunctionType)):
- # build a new function that is a clone of the one from new_cls
- method = new.function(func.func_code, func.func_globals, name,
- func.func_defaults, func.func_closure)
- try:
- # check if a method of the same name already exists in the target
- old_method = getattr(cls, name)
- except AttributeError:
- pass
- else:
- # rename the old method so we can still call it if need be
- setattr(cls, '_mix_'+name, old_method)
- # add the clone to cls
- setattr(cls, name, method)
- elif isinstance(func, staticmethod):
- try:
- # check if a method of the same name already exists in the target
- old_method = getattr(cls, name)
- except AttributeError:
- pass
- else:
- # rename the old method so we can still call it if need be
- setattr(cls, '_mix_'+name, old_method)
- setattr(cls, name, func)
-
-class _AccessibleMixin(object):
- '''
- Defines methods to be added to the Accessibility.Accessible class. The
- features defined here will be added to the Accessible class at run time so
- that all instances of Accessible have them (i.e. there is no need to
- explicitly wrap an Accessible in this class or derive a new class from it.)
-
- @cvar SLOTTED_CLASSES: Mapping from raw Accessibility class to a new class
- having the slots defined by L{SLOTS}
- @type SLOTTED_CLASSES: dictionary
- @cvar SLOTS: All slots to create
- @type SLOTS: tuple
- '''
- SLOTTED_CLASSES = {}
- SLOTS = ('_cache', '_cache_level')
-
- def __new__(cls):
- '''
- Creates a new class mimicking the one requested, but with an extra _cache
- attribute set in the __slots__ tuple. This field can be set to a dictionary
- or other object to allow caching to occur.
-
- Note that we can't simply mix __slots__ into this class because __slots__
- has an effect only at class creation time.
-
- @param cls: Accessibility object class
- @type cls: class
- @return: Instance of the new class
- @rtype: object
- '''
- try:
- # check if we've already created a new version of the class
- new_cls = _AccessibleMixin.SLOTTED_CLASSES[cls]
- except KeyError:
- # create the new class if not
- new_cls = type(cls.__name__, (cls,),
- {'__module__' : cls.__module__,
- '__slots__' : _AccessibleMixin.SLOTS})
- _AccessibleMixin.SLOTTED_CLASSES[cls] = new_cls
- obj = cls._mix___new__(new_cls)
- obj._cache_level = _CACHE_LEVEL
- if obj._cache_level is not None:
- # be sure to create the cache dictionary, if we're caching
- obj._cache = {}
- return obj
-
- def __del__(self):
- '''
- Decrements the reference count on the accessible object when there are no
- Python references to this object. This provides automatic reference
- counting for AT-SPI objects.
- '''
- try:
- self.unref()
- except Exception:
- pass
-
- def __iter__(self):
- '''
- Iterator that yields one accessible child per iteration. If an exception is
- encountered, None is yielded instead.
-
- @return: A child accessible
- @rtype: Accessibility.Accessible
- '''
- for i in xrange(self.childCount):
- try:
- yield self.getChildAtIndex(i)
- except constants.CORBAException:
- yield None
-
- def __str__(self):
- '''
- Gets a human readable representation of the accessible.
-
- @return: Role and name information for the accessible
- @rtype: string
- '''
- try:
- return '[%s | %s]' % (self.getRoleName(), self.name)
- except Exception:
- return '[DEAD]'
-
- def __nonzero__(self):
- '''
- @return: True, always
- @rtype: boolean
- '''
- return True
-
- def __getitem__(self, index):
- '''
- Thin wrapper around getChildAtIndex.
-
- @param index: Index of desired child
- @type index: integer
- @return: Accessible child
- @rtype: Accessibility.Accessible
- '''
- return self.getChildAtIndex(index)
-
- def __len__(self):
- '''
- Thin wrapper around childCount.
-
- @return: Number of child accessibles
- @rtype: integer
- '''
- return self.childCount
-
- def _get_name(self):
- '''
- Gets the name of the accessible from the cache if it is available,
- otherwise, fetches it remotely.
-
- @return: Name of the accessible
- @rtype: string
- '''
- if self._cache_level != constants.CACHE_PROPERTIES:
- return self._mix__get_name()
-
- try:
- return self._cache['name']
- except KeyError:
- name = self._mix__get_name()
- self._cache['name'] = name
- return name
-
- def getRoleName(self):
- '''
- Gets the unlocalized role name of the accessible from the cache if it is
- available, otherwise, fetches it remotely.
-
- @return: Role name of the accessible
- @rtype: string
- '''
- if self._cache_level != constants.CACHE_PROPERTIES:
- return self._mix_getRoleName()
-
- try:
- return self._cache['rolename']
- except KeyError:
- name = self._mix_getRoleName()
- self._cache['rolename'] = name
- return name
-
- def _get_description(self):
- '''
- Gets the description of the accessible from the cache if it is available,
- otherwise, fetches it remotely.
-
- @return: Description of the accessible
- @rtype: string
- '''
- if self._cache_level != constants.CACHE_PROPERTIES:
- return self._mix__get_description()
-
- try:
- return self._cache['description']
- except KeyError:
- name = self._mix__get_description()
- self._cache['description'] = name
- return name
-
- def getIndexInParent(self):
- '''
- Gets the index of this accessible in its parent. Uses the implementation of
- this method provided by the Accessibility.Accessible object, but checks the
- bound of the value to ensure it is not outside the range of childCount
- reported by this accessible's parent.
-
- @return: Index of this accessible in its parent
- @rtype: integer
- '''
- i = self._mix_getIndexInParent()
- try:
- # correct for out-of-bounds index reporting
- return min(self.parent.childCount-1, i)
- except AttributeError:
- # return sentinel if there is no parent
- return -1
-
- def getApplication(self):
- '''
- Gets the most-parent accessible (the application) of this accessible. Tries
- using the getApplication method introduced in AT-SPI 1.7.0 first before
- resorting to traversing parent links.
-
- @warning: Cycles involving more than the previously traversed accessible
- are not detected by this code.
- @return: Application object
- @rtype: Accessibility.Application
- '''
- try:
- return self._mix_getApplication()
- except AttributeError:
- pass
- curr = self
- try:
- while curr.parent is not None and (not curr.parent == curr):
- curr = curr.parent
- return curr
- except Exception:
- pass
- # return None if the application isn't reachable for any reason
- return None
-
-# mix the new functions
-_mixClass(Accessibility.Accessible, _AccessibleMixin)
-# mix queryInterface convenience methods
-_mixInterfaces(Accessibility.Accessible, constants.ALL_INTERFACES)
-# mix the exception handlers into all queryable interfaces
-map(_mixExceptions, constants.ALL_INTERFACES)
-# mix the exception handlers into other Accessibility objects
-map(_mixExceptions, [Accessibility.StateSet])
+#Copyright (C) 2008 Codethink Ltd
+
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Lesser General Public
+#License version 2 as published by the Free Software Foundation.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#You should have received a copy of the GNU Lesser General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import interfaces
+from base import BaseProxy
+from factory import create_accessible, add_accessible_class
+from stateset import StateSet, _marshal_state_set
+from relation import _marshal_relation_set
+
+__all__ = [
+ "Accessible",
+ ]
+
+#------------------------------------------------------------------------------
+
+class Accessible(BaseProxy):
+ """
+ The base interface which is implemented by all accessible objects.
+ All objects support interfaces for querying their contained
+ 'children' and position in the accessible-object hierarchy,
+ whether or not they actually have children.
+ """
+
+ def getApplication(self):
+ """
+ Get the containing Application for this object.
+ @return the Application instance to which this object belongs.
+ """
+ application_root = self._cache[self._app_name]._get_root()
+ #TODO Set the desktop object as the parent of this.
+ return create_accessible(self._cache,
+ self._app_name,
+ application_root,
+ interfaces.ATSPI_APPLICATION,
+ dbus_object=self._dbus_object)
+
+ def getAttributes(self):
+ """
+ Get a list of properties applied to this object as a whole, as
+ an AttributeSet consisting of name-value pairs. As such these
+ attributes may be considered weakly-typed properties or annotations,
+ as distinct from the strongly-typed interface instance data declared
+ using the IDL "attribute" keyword.
+ Not all objects have explicit "name-value pair" AttributeSet
+ properties.
+ Attribute names and values may have any UTF-8 string value, however
+ where possible, in order to facilitate consistent use and exposure
+ of "attribute" properties by applications and AT clients, attribute
+ names and values should chosen from a publicly-specified namespace
+ where appropriate.
+ Where possible, the names and values in the name-value pairs
+ should be chosen from well-established attribute namespaces using
+ standard semantics. For example, attributes of Accessible objects
+ corresponding to XHTML content elements should correspond to
+ attribute names and values specified in the w3c XHTML specification,
+ at http://www.w3.org/TR/xhtml2, where such values are not already
+ exposed via a more strongly-typed aspect of the AT-SPI API. Metadata
+ names and values should be chosen from the 'Dublin Core' Metadata
+ namespace using Dublin Core semantics: http://dublincore.org/dcregistry/
+ Similarly, relevant structural metadata should be exposed using
+ attribute names and values chosen from the CSS2 and WICD specification:
+ http://www.w3.org/TR/1998/REC-CSS2-19980512 WICD (http://www.w3.org/TR/2005/WD-WICD-20051121/).
+
+ @return : An AttributeSet encapsulating any "attribute values"
+ currently defined for the object. An attribute set is a list of strings
+ with each string comprising an name-value pair format 'name:value'.
+ """
+ func = self.get_dbus_method("getAttributes")
+ return func()
+
+ def getChildAtIndex(self, index):
+ """
+ Get the accessible child of this object at index.
+ @param : index
+ an in parameter indicating which child is requested (zero-indexed).
+ @return : the 'nth' Accessible child of this object.
+ """
+ path = self.cached_data.children[index]
+ return create_accessible(self._cache,
+ self._app_name,
+ path,
+ interfaces.ATSPI_ACCESSIBLE,
+ dbus_object=self._dbus_object)
+
+ def getIndexInParent(self):
+ """
+ Get the index of this object in its parent's child list.
+ @return : a long integer indicating this object's index in the
+ parent's list.
+ """
+ for i in range(0, self.parent.childCount):
+ child = self.parent.getChildAtIndex(i)
+ if self.isEqual(child):
+ return i
+ raise AccessibleObjectNoLongerExists("Child not found within parent")
+
+ def getLocalizedRoleName(self):
+ """
+ Get a string indicating the type of UI role played by this object,
+ translated to the current locale.
+ @return : a UTF-8 string indicating the type of UI role played
+ by this object.
+ """
+ func = self.get_dbus_method("getLocalizedRoleName")
+ return func()
+
+ def getRelationSet(self):
+ """
+ Get a set defining this object's relationship to other accessible
+ objects.
+ @return : a RelationSet defining this object's relationships.
+ """
+ func = self.get_dbus_method("getRelationSet")
+ relation_set = func()
+ return _marshal_relation_set(self._cache, self._dbus_object, self._app_name, relation_set)
+
+ def getRole(self):
+ """
+ Get the Role indicating the type of UI role played by this object.
+ @return : a Role indicating the type of UI role played by this
+ object.
+ """
+ return self.cached_data.role
+
+ def getRoleName(self):
+ """
+ Get a string indicating the type of UI role played by this object.
+ @return : a UTF-8 string indicating the type of UI role played
+ by this object.
+ """
+ func = self.get_dbus_method("getRoleName")
+ return func()
+
+ def getState(self):
+ """
+ Get the current state of the object as a StateSet.
+ @return : a StateSet encapsulating the currently true states
+ of the object.
+ """
+ func = self.get_dbus_method("getState")
+ bitfield = func()
+ return _marshal_state_set(bitfield)
+
+ def isEqual(self, accessible):
+ """
+ Determine whether an Accessible refers to the same object as
+ another. This method should be used rather than brute-force comparison
+ of object references (i.e. "by-value" comparison), as two object
+ references may have different apparent values yet refer to the
+ same object.
+ @param : obj
+ an Accessible object reference to compare to
+ @return : a boolean indicating whether the two object references
+ point to the same object.
+ """
+ return (self._app_name == accessible._app_name) and \
+ (self._acc_path == accessible._acc_path)
+
+ def get_childCount(self):
+ return len(self.cached_data.children)
+ _childCountDoc = \
+ """
+ childCount: the number of children contained by this object.
+ """
+ childCount = property(fget=get_childCount, doc=_childCountDoc)
+
+ def get_description(self):
+ return self.cached_data.description
+ _descriptionDoc = \
+ """
+ a string describing the object in more detail than name.
+ """
+ description = property(fget=get_description, doc=_descriptionDoc)
+
+ def get_name(self):
+ return self.cached_data.name
+ _nameDoc = \
+ """
+ a (short) string representing the object's name.
+ """
+ name = property(fget=get_name, doc=_nameDoc)
+
+ def get_parent(self):
+ if self._parent:
+ return self._parent
+ else:
+ return create_accessible(self._cache,
+ self._app_name,
+ self.cached_data.parent,
+ interfaces.ATSPI_ACCESSIBLE,
+ dbus_object=self._dbus_object)
+
+ _parentDoc = \
+ """
+ an Accessible object which is this object's containing object.
+ """
+ parent = property(fget=get_parent, doc=_parentDoc)
+
+# Register the Accessible class with the accessible factory.
+add_accessible_class(interfaces.ATSPI_ACCESSIBLE, Accessible)
+
+#END----------------------------------------------------------------------------