LibreOffice Developer's Guide: Chapter 17 - Accessibility
There are certain circumstances where LibreOffice applications can not be used with the usual input and output devices, such as a mouse, keyboard, monitor and printer. This may be because the user is sitting in a car and can only occasionally look at the screen and has no keyboard at all. Maybe the user is disabled and can not see or hear or use traditional keyboards. Alternative input and output devices are called assistive technology, or AT. Examples of AT are Braille terminals, which are used mainly for display of single text lines where each character is represented by raised or lowered dots and can be read by touching them with the fingertips, screen magnifiers, which magnify the screen contents and optionally change color, and screen readers, which use speech synthesis to read displayed text or descriptions of objects out loud in a human language.
To make LibreOffice applications accessible to the disabled or to people in mobile environments, alternative input and output devices have to be supported. In order to support a wide variety of ATs, the approach taken by Java and Gnome has been adopted: an API tailored to the specific needs of AT and modeled closely after its Java counterpart is used as an interface between the available data of the elements visible on screen and the AT, which transforms that data and presents an alternative view of the screen contents.
Overview
As previously stated, the UNO Accessibility API, or UAA, is closely modeled after the Java Accessibility API, and to some extent the Gnome Accessibility API. This section describes some differences with common UNO styles and standards.
The purpose of the accessibility API is to represent what is currently visible on screen. To be kept up-to-date, users of the accessibility API are informed of any relevant changes of the screen content by events. This focus on visual appearance is another point in which the accessibility API differs from other parts of UNO, which are strictly model centered. The accessibility API provides a tree structure or, to be more specific, a forest of accessibility objects that, as a whole, represent the on-screen data.
The transition point from the UNO API to the accessibility API is windows, which both support the com.sun.star.awt.XWindow and com.sun.star.accessibility.XAccessible interfaces. A list of all top-level windows, from which you can get the roots of the associated accessibility trees, can be retrieved from the toolkit through the com.sun.star.awt.XExtendedToolkit interface.
The com.sun.star.accessibility.XAccessible interface can be queried for the actual accessibility object with its only function getAccessibleContext(). With this technique, the implementations of the accessibility interfaces can be kept apart from that of the other UNO interfaces. The getAccessibleContext() function returns a reference to an object that implements the com.sun.star.accessibility.XAccessibleContext interface. This interface is the center of the accessibility API. On the one hand it provides the functionality to traverse the accessibility tree and on the other hand gives access to basic information that represents the object that is being made accessible. These two aspects are described in more detail in the following sections Accessibility Tree and Content Information.
The accessibility API is as self-contained as possible. You should not need to use the UNO API outside its accessibility API subset. However, there are some exceptions to this. The most important one is the initial access to the root nodes of the accessibility tree over the toolkit.
Bridges
There are several ways that ATs can be realized. They differ in two important points. The first one is how the AT represents the information it obtains from LibreOffice and presents it to the user. The second difference is how ATs obtain this information in the first place.
In its simplest form, the communication between AT and LibreOffice involves only the accessibility API and, where necessary, some additional features from other parts of the UNO API. Existing ATs, however, do not know anything yet about the accessibility API. They use one of several ways to access LibreOffice by using one or more bridges that translate between different APIs:
- The UNO access bridge translates between the accessibility API and the Java Accessibility API. Note that this is not the same as the Java version of the accessibility API.
- The Windows/Java access bridge translates between the Java and the C versions of the Java Accessibility API.
- The Gnome access bridge translates between the accessibility API and the Gnome Accessibility API.
In order to make LibreOffice accessible, it is necessary to support the accessibility API. The characteristics of the bridges have to be taken into account as well.
Under Windows, LibreOffice itself has a switch that can turn on or off the accessibility support. This switch can be reached through Tools – Options – Accessibility. Under Linux and Solaris, an equivalent setting can be made in the Gnome environment. When accessibility is activated, on every launch LibreOffice will start its own Java VM, which in turn starts all registered AT tools. This will be explained in the next section.
Accessibility Tree
The screen content is presented to AT as a tree - or a forest, to be more specific - of accessibility objects. Each displayed object that wants to be accessible has to support the com.sun.star.accessibility.XAccessible interface. From this interface, you obtain the actual accessibility object by calling the getAccessibleContext() function. The returned object has to at least support the com.sun.star.accessibility.XAccessibleContext interface.
Accessibility objects are organized in one or more hierarchies, one for each top-level window. So there is a tree for a single top-level window, and a forest when there is more than one top-level window. Internal nodes of a tree are containers of other accessibility objects. A container can represent window frames, toolbars, menus, group shapes, or shapes that contain text. Leaves represent objects like menu entries without sub-menus, buttons, icons, shapes without text, or text paragraphs.
You can move up and down within the tree of a given accessibility object. All functions for obtaining an object's parent and children are part of the com.sun.star.accessibility.XAccessibleContext interface. The ability to move up towards the tree root is provided by the getAccessibleParent() function. Like all other accessibility functions that return a reference to another accessibility object, it returns a reference to a com.sun.star.accessibility.XAccessible object. Moving down the tree towards the leaves requires two functions. The getAccessibleChildCount() function returns the number of children. The getAccessibleChild() function allows you to access any child by specifying the appropriate index.
When children are added or removed from an accessibility object, the indices of the new and remaining children may change. You can use the getAccessibleIndexInParent() function to get the current indices.
Content Information
Content information of accessibility objects establishes the connection to the objects that are visible on the screen. This information gives a detailed description of what is visible on the screen, the size of the object, and its location on the screen. Access to the content information is provided by several interfaces of the UNO accessibility API, which are described in the following sections.
The accessibility API allows you to divide the implementation of an accessible object into an accessibility related and an accessibility unrelated part. This is done with the com.sun.star.accessibility.XAccessible interface. The getAccessibleContext() method returns an object that implements the other interfaces related to accessibility. This object may be the same as the object that is made accessible, but it can be a different object as well. Once you have the accessibility object, you can use the usual UNO type cast mechanisms to change from one interface to another.
Listeners and Broadcasters
The com.sun.star.accessibility.XAccessibleEventBroadcaster and com.sun.star.accessibility.XAccessibleEventListener interface combo lets you register and unregister listeners. Events are represented by com.sun.star.accessibility.AccessibleEventObject structure. The different event types are listed and explained in the com.sun.star.accessibility.AccessibleEventId constants group.
Again, event types can be divided into two classes depending on whether they describe changes in the structure of the accessibility tree or changes in the internal state or the visual appearance of an accessible object. The first group consists of CHILD and INVALIDATE_ALL_CHILDREN. The first denotes a newly inserted or a removed child. The second is used in cases where more than one child has been inserted or removed and tells the listener to re-fetch a complete list of children.
The second group comprises all other event types. Typical members are VISIBLE_DATA_CHANGED and STATE_CHANGED, which inform listeners that the visual appearance of an object has changed (for example, to a different text color) or that one of its states has been switched on or off (for example, when an object becomes focused or selected).
The event types CONTROLLED_BY_RELATION_CHANGED, CONTROLLER_FOR_RELATION_CHANGED, LABEL_FOR_RELATION_CHANGED, LABELED_BY_RELATION_CHANGED, MEMBER_OF_RELATION_CHANGED, CONTENT_FLOWS_FROM_RELATION_CHANGED and CONTENT_FLOWS_TO_RELATION_CHANGED may be thought of as constituting a third group. They describe changes of the more virtual structure formed by relations between accessible objects in different parts of an accessibility tree.
Events are sent after the respective change of an accessible object took place. This enables the listener to retrieve up-to-date values that are sent with the event.
Implementing Accessible Objects
Implementation Rules
There are some rules to observe when implementing the UNO accessibility API that go beyond simply following the specifications in the IDL files of the accessibility API's interfaces. These rules have to do with what kind of data ATs expect from an application.
One such rule is that only objects that are visible on the screen are included into the accessibility tree. If, for example, you have a text document with a large number of pages, usually only parts of one or two pages are visible, and only accessibility objects for these parts should be part of the accessibility hierarchy. Another closely related rule is that the bounding boxes of objects are clipped to the visible area.
However, there are exceptions to these rules. For reasons of consistency with the behavior of Java tables represented through the com.sun.star.accessibility.XAccessibleTable interface, access is granted to all of their cells regardless of whether they are visible or not. Menus are another example. The whole menu structure is represented, even when only the menu bar is visible.
Another rule is that bounding boxes of accessibility objects as returned by getBounds() must not overlap the bounding boxes of their parents. This is crucial to enable ATs to find the accessibility object that lies under a given screen coordinate, such as the mouse position. With properly nesting bounding boxes, ATs can prune whole sub-trees from the search when the bounding box of the root object does not contain the point.
Services
There are only two services in the accessibility API, which have to be supported by any accessible object.
The com.sun.star.accessibility.Accessible service contains the com.sun.star.accessibility.XAccessible interface and must be supported by every UNO object that is accessible.
The com.sun.star.accessibility.AccessibleContext service contains the com.sun.star.accessibility.XAccessibleContext interface and, optionally, the com.sun.star.accessibility.XAccessibleEventBroadcaster interface. This service must be supported by every accessible object that is returned by the getAccessibleContext() function.
Using the Accessibility API
When you are writing your own ATs and want to use the UNO accessibility API directly, you must first connect to LibreOffice. Connecting to LibreOffice is explained elsewhere in this document. Once a connection is established, the toolkit with its com.sun.star.awt.XExtendedToolkit interface can be used to retrieve a list of all currently open top-level windows. From these, you can then get the accessible root nodes of the accessibility object trees associated with the windows. When you register a com.sun.star.awt.XTopWindowListener you will then be informed about new top-level windows, as well as top-level windows that have disappeared.
With the top-level accessible objects at hand, you can use the Java version of the accessibility API as it is described in detail in the following sections. To be informed about focus changes - so that, for example, a screen reader can track the currently focused object and read it to the user - an AT has to register all non-transient objects of an accessibility tree.
The general operation of a simple AT consists of the following steps:
- Connect to LibreOffice.
- Retrieve the currently visible top-level windows and register as top window listener to keep the list up-to-date.
- Traverse the trees by getting their root elements from each window. See the description of the com.sun.star.accessibility.XAccessibleContext for a code example for this.
- Register each accessible object as com.sun.star.accessibility.XAccessibleEventListener.
- If called back with an com.sun.star.accessibility.AccessibleEventObject object, then process two kinds of events:
- Events that denote a state change with either OldValue or NewValue containing the FOCUSED constant indicate that the source object of this event got either focused or unfocused.
- When receiving events of type CHILD, register as listener at the object that is specified by the event, as well as all the object's children.
A Simple Screen Reader
To illustrate the use of the UNO accessibility API, we will describe how to implement a simple AT. The simple screen reader, or SSR, will display some information about the currently focused object. As you can see in Illustration 18.1, the SSR consists of three windows. The bottom window logs all events that the SSR receives from any of the accessibility objects to which it is registered.
![](/images/thumb/5/5c/SSR_screen_shot.png/400px-SSR_screen_shot.png)
The two upper windows display information about the currently focused object, which in this screen shot is a shape in a presentation document. The left window displays the names of all the ancestors of the focused object and the path from that object to the root object of the accessibility tree. The left window also displays the focused object's description, its state, its location, and its size on the screen. In this example, the focused object is named “Rectangle2”, which corresponds to the red rectangular shape. Its parent is called “Drawing View” and the root of the tree has the name of the document, which is “Untitled1”, followed by the product name and some debug information.
The upper right window displays similar information graphically. The focused object is shown as a green rectangle, while its ancestors are drawn as gray rectangles. You can see how the objects are nested. This corresponds to the requirement that the bounding of child objects must not overlap that of their parents. Note that the blue rectangular shape is not visible in this window, because it is a sibling of the focused red rectangle, but does not lie on that object's path to the root of the root object. Also note that some of the rectangles are off-center and smaller than they should be. This is because the rectangles that represent accessible objects, which in turn represent part of the GUI, are drawn with their screen location and size relative to the whole screen; the outermost rectangle that is enclosed by the gray background represents the screen of which the screen shot shows only a part.
The bottom window logs all the events that the SSR receives from the accessible objects it has register as event listener at.
Features
The SSR was designed to be a simple program that illustrates how to use the UNO accessibility API. However, we have not always chosen the most simple way to do something. There are therefore some features that may be useful in the “real” AT:
- The SSR has a background connection timer task that waits for a LibreOffice application to start and then connects automatically to that application.
- It uses independent threads to register as listener at a whole accessibility (sub-) tree of new top-level application windows or newly created accessible objects. The same is true for removing the listener from windows not visible anymore or accessible objects that are removed from their tree.
- There are two different views that display information about the currently focused object. This illustrates how information of a certain accessibility object is retrieved by using different interfaces of the accessibility API.
- A message area shows all received events regardless of whether they are necessary to keep track of the currently focused object. With this, the SSR serves as simple event monitor as well.
Class Overview
The SSR is implemented with the following classes and interfaces:
SSR
- This is the main class of the tool. It is responsible for setting up and managing the GUI
ConnectionTask
- The connection task is a thread that waits in the background for a LibreOffice application to start. As soon as there is one, it connects to it and initiates the registration at all of its accessible objects.
RegistrationThread
- Each object of this class creates one thread that adds or removes the event listener at all accessible objects in one tree, with one tree per application window. This is done in separate threads so that the normal operation of the tool is not blocked while registering to complex applications with many objects.
EventListenerProxy
- This is a singleton class. Its only instance is registered as listener at all accessible objects. It runs in its own thread and delivers the received events eventually to the actual event handler. This de-coupling between event reception and event handling is necessary to avoid deadlocks. Soon this will become obsolete.
EventHandler
- There is usually only one object of this class. It prints log messages for all the events it receives and provides special handling some of events:
- Top window events denoting new or removed application windows are not accessibility events in a strict sense. They have to be listened to, however, to add or remove the event listener to the accessibility objects that correspond to these windows.
- State events that inform the listener of a set or reset FOCUSED state. This is the most important event for the SSR in order to keep track of the currently focused object.
- Events that signal a change of the geometric property of the currently focused object trigger a redisplay of the two windows that display that object. This ensures that you always see the current position of the object.
TextualDisplay
- This widget displays textual information about the focused object as described previously.
GraphicalDisplay
- This widget displays graphical information about the focused object as described previously.
IaccessibleObjectDisplay
- This is the interface supported by the two display widgets. It defines how the event handler tells the displays about focus and geometry changes. You can add other displays as well by adding a widget that implements this interface to the event handler and to the GUI.
MessageArea
- The message area at the bottom is a simple text widget that scrolls its content so that the last line that contains the most recent message is always visible.
NameProvider
- This is a useful helper class that converts numerical IDs into names, roles, events, or states.
Parts of some of these classes will be explained at later points in this text. Others, like the ConnectionTask
class, are a mere technicality with respect to the accessibility API and will not be detailed any further.
Putting the Accessibility Interfaces to Work
Once an accessible object has been obtained by a call to getAccessibleContext(), you can switch between the interfaces belonging to the accessibility API by using the usual UNO cast mechanisms. There is, however, no way back to the object from which the accessible object has been obtained through procedures provided by the accessibility API.
XAccessibleContext
com.sun.star.accessibility.XAccessibleContext is the central interface of the accessibility API. In addition to the hierarchy information described previously, it provides access to some important information. The functions getAccessibleRole(), getAccessibleName() and getAccessibleDescription() return descriptions of the object in increasing detail:
Role
- The role classifies all accessibility objects into a handful of different classes. Most roles are taken from the Java accessibility API, such as PUSH_BUTTON, RADIO_BUTTON, SCROLL_BAR or TEXT. Some have been defined for the accessibility API so that, in addition to GUI elements, documents can be made accessible. These roles are DOCUMENT for document windows or views, PARAGRAPH for text sections, or SHAPE for graphical objects. Roles are described in the com.sun.star.accessibility.AccessibleRole constants group.
Name
- Names allow you to distinguish between objects with the same role. For example, the buttons at the bottom of a dialog all have the role PUSH_BUTTON. Their names may be "OK", "Cancel" or "Help". Where necessary, names are made unique with respect to the object's siblings. Names for shapes can be "Rectangle 0", "Ellipse 1", "Rectangle 2", or "Curve 3".
Description
- To further describe the purpose of accessibility objects, description strings are provided. Descriptions of shapes can contain their style and some properties whose values differ from that style. If you have changed the color of a rectangle to red, its description may look like "Rectangle with style=default and color=red".
Names and descriptions are strings localized according to the locale returned by getLocale().
Two functions of the interface have not been mentioned so far. The function getAccessibleRelationSet() returns the set of relations defined for an accessibility object. Likewise getAccessibleStateSet() returns a set of states that are active for an object.
The showParents()
method from the TextualDisplay
class of the SSR displays the path from a given object to the root of the accessibility tree by printing each object's accessible name indented relative to its father.
private String showParents (XAccessibleContext xContext) {
The method first obtains references to all the objects that belong to this path.
Vector aPathToRoot = new Vector();
while (xContext != null) {
aPathToRoot.add (xContext);
// Go up the hierarchy one level to the object's parent.
try {
XAccessible xParent = xContext.getAccessibleParent();
if (xParent != null)
xContext = xParent.getAccessibleContext();
else
xContext = null;
}
catch (Exception e) {
System.err.println ("caught exception " + e + " while getting path to root");
}
}
This is done in two steps. First, a call to the getAccessibleParent() method to get the parent of the object and thereby moving to the previous level in the accessibility tree. Second, from the returned XAccessible reference, the accessible context is retrieved by calling getAccessibleContext(). This is repeated until an object is reached that has no parent and getAccessibleParent() returns null.
The path of the accessibility tree is now printed by appending text to the msTextContext
member variable, which later is displayed in a JtextArea
widget. To cope with accessibility objects that return an empty name, the role of these objects is used to represent them. Note how the indentation string is updated after every object by appending the msIndentation
member.
String sIndentation = new String ();
for (int i=aPathToRoot.size()-1; i>=0; i--) {
XAccessibleContext xParentContext = (XAccessibleContext)aPathToRoot.get(i);
String sParentName = xParentContext.getAccessibleName();
if (sParentName.length() == 0)
sParentName = "<unnamed> / Role "
+ NameProvider.getRoleName(xParentContext.getAccessibleRole());
msTextContent += sIndentation + sParentName + "\n";
sIndentation += msIndentation;
}
The indentation is returned so that further output can be properly aligned.
return sIndentation;
}
XAccessibleComponent
The XAccessibleComponent interface gives access to geometric properties, such as size and position on the screen. This interface should be implemented by every object that has a visible representation, that is, by all objects that are not simple containers.
The coordinates used by the functions of this interface are returned and and are expected in pixel values and not, as is elsewhere in the UDK, in internal coordinates (100th of mm). There are three different origins to which coordinates may be specified:
- Relative to an object's bounding box. This is relative to the object itself. Used by the containsPoint() and getAccessibleAtPoint() functions.
- Relative to an object's parent. Used by the getBounds() and getLocation() functions.
- Absolute or relative to the screen origin. Used by the getLocationOnScreen().
Because all three coordinate systems are based on pixel values, the getSize() function is independent of the coordinate system.
The bounding rectangle that encloses the visual presentation of an object can be retrieved by calling getBounds(). If you only need the location or the size, then call getLocation() or getSize(). The function getLocationOnScreen() returns the absolute screen coordinates.
There or two functions that determine whether the bounding boxes of the object or one of its children contain a given test point. The function containsPoint() checks whether the test point lies within the bounding box of the object. Children can be tested with getAccessibleAtPoint(). When one of the direct children contains the test point, a reference to this object is returned.
In addition to the geometrical functions, there are the two getForeground() and getBackground() functions that describe an object's appearance. Keep in mind that an object does not necessarily have a monochrome background color. There can be a hatching, gradient, or bitmap as well. The returned background color in this case is an approximation.
The showComponentInfo()
method of the TextualDisplay
class takes as argument a reference to the accessible object for which geometrical information is shown, as well as the indentation string computed in the showParents()
method.
private void showComponentInfo (XAccessibleContext xContext, String sIndentation) {
When given an XAccessibleContext reference, you must cast it to a XAccessibleComponent reference in order to access geometrical information about an accessible object.
XAccessibleComponent xComponent = (XComponent)UnoRuntime.queryInterface(
XAccessibleComponent.class, xContext);
if (xComponent != null) {
If the cast was successful, then simply call the getLocationOnScreen() and getSize() methods to obtain the object's bounding box in screen coordinates. If getBounds() is called instead, the parent's screen coordinates must be added to obtain an absolute position.
Point aLocation = xComponent.getLocationOnScreen();
msTextContent += sIndentation + "Position : "
+ aLocation.X + ", " + aLocation.Y + "\n";
Size aSize = xComponent.getSize();
msTextContent += sIndentation + "Size : "
+ aSize.Width + ", " + aSize.Height + "\n";
}
}
XAccessibleExtendedComponent
While the com.sun.star.accessibility.XAccessibleComponent interface should be implemented by almost every accessible object, the support of the XAccessibleExtendedComponent interface is optional. Its most important function, getFont() returns the font used to display text.
The getTitledBorderText() function returns the text that is displayed on an object's window borders, which in the case of LibreOffice is only relevant for top-level windows.
The getToolTipText() function returns the tool tip text that is displayed when the mouse pointer rests long enough over one point of the object.
XAccessibleText
The interface XAccessibleText handles read-only text. It serves three purposes: to obtain certain parts of a text, to obtain the text's size and location on the screen, and to handle the caret position. An accessibility object that implements this interface usually represents only a part of a larger text. Typically, this is a single or a small number of paragraphs. You can use the relation types CONTENT_FLOWS_FROM and CONTENT_FLOWS_TO to explicitly represent the text flow from one text part to another. Without these relations, the text flow has to be determined from the structure of the accessibility tree alone.
Selection
Represented text may contain a selected text portion, which is typically displayed highlighted (inverse). There are four functions to access and modify the selection. The selected text, as well as its start and end index, can be accessed with the getSelectedText(), getSelectionStart() and getSelectionEnd() functions respectively. To modify the selection, call setSelection() with the new start and end indices.
Text type
The functions getTextAtIndex(), getTextBeforeIndex() and getTextBehindIndex() return parts of the text where the part or length of text returned is specified by the text type. Defined in the com.sun.star.accessibility.AccessibleTextType constants collection, some of these text types further explanation:
- The type LINE indicates all text on a single line as it is displayed on the screen. This may include a hyphen at the line end. The hyphen is not actually part of the text, but is visible on the screen. They are only included in text parts of type LINE.
- Glyph is used in this context to mean that everything between two adjacent cursor positions is considered to be a glyph. In Thai, for example, you can stack up to four (Unicode) characters upon each other. When moving with the cursor keys, those character groups are interpreted as single glyphs, and the cursor can only be set in front or after such glyphs, but not inside.
- The definition of words, sentences and paragraphs are implementation and locale dependent.
- An attribute run is a sequence of characters of maximal length, where all characters have the same attributes. For example the text "This is an example" consists of three attribute runs: "This", "is an" and "example". The first and last one each have only a single attribute, italic respectively bold. The second run has both attributes at the same time. Note that all three spaces belong to one of the attribute runs.
There are other functions to access text. To retrieve the whole text represented by an object, use getText(). Call getTextRange() to get only a part of text between and including two given indices. A single character can be accessed with the getCharacter() function.
To copy a text range to the clipboard, call the copyText() function. See also the related cutText() and pasteText() functions of the com.sun.star.accessibility.XAccessibleEditableText interface.
Caret and Text Indices
The caret is often referred to as the cursor and is typically displayed as a vertical line. On the screen, it is always placed between two adjacent glyphs, or at the beginning or the end of a text line. When specifying its position in terms of character positions, the position of the character to its right is used. Thus, the index 0 says that the caret is at the very beginning of the text. When the caret is located behind the last character, then its position equals the text length, that is, the number of characters of the text as returned by getCharacterCount().
When the caret changes its position, an event must be sent to all listeners so that the old and new position can be indicated.
The caret position is returned by the getCaretPosition() function, and can be set by calling setCaretPosition().
Other index-related functions are getCharacterAttributes(), which returns the attributes at the specified index, getCharacterBounds(), which returns the bounding box of the character at the specified index, and getIndexAtPoint(), which returns the index of the character at the specified position.
XAccessibleEditableText
The XAccessibleEditableText interface extends the XAccessibleText interface by adding functions that let you modify the text. The interface is therefore only implemented when the text represented by the implementing object is readable and writable.
With the functions deleteText(), insertText() and replaceText(), you can delete, insert, and replace text. The setText() function is a special case of replaceText() and replaces the whole text at once.
The cutText() and pasteText() functions, together with copyText(), from the com.sun.star.accessibility.XAccessibleText interface provide access to the clipboard.
Finally, the setAttributes() function is the counterpart of getCharacterAttributes() in the com.sun.star.accessibility.XAccessibleText interface. With this function, you can replace the existing attributes with the given set. To add one attribute, first use getCharacterAttributes() to get the current set of attributes, add the attribute to that set, and finally call setAttributes() to set the new set of attributes.
XAccessibleTable
The com.sun.star.accessibility.XAccessibleTable interface represents two-dimensional tables of data. The Calc application is one example of its implementation. It grants access to individual cells or groups of cells.
Global information about a table can be accessed with two functions: getAccessibleCaption() returns the caption and getAccessibleSummary() returns a summary describing the content of a table.
A table is organized in horizontal rows and vertical columns. The number of rows and columns - and indirectly the number of cells - can be determined by calling the functions getAccessibleRowCount() and getAccessibleColumnCount(). Here, in contrast to the general rule of only giving access to visible objects, all cells are represented by a table. This exception is necessary to stay consistent with Java tables.
Information on rows and columns is returned by the getAccessibleRowDescription() and getAccessibleColumnDescription() functions. Note that both functions return objects implementing the com.sun.star.accessibility.XAccessibleTable interface themselves. The headers of rows and columns can be retrieved by calling getAccessibleRowHeaders() and getAccessibleColumnHeaders().
To obtain a reference to a certain cell specified by its row and column indices, use getAccessibleCellAt(). A table cell may span multiple rows and columns. You can determine the number of rows and columns that a cell spans with the getAccessibleRowExtentAt() and getAccessibleColumnExtentAt() functions.
Selections in tables can have two different forms. You can have a multi-selection of rectangular areas of single or multiple cells, or you can select whole rows and columns. The function isAccessibleSelected() determines whether a single cell that spans a position specified by a row and a column index is selected. To determine whether certain rows or columns are selected, use the isAccessibleRowSelected() and isAccessibleColumnSelected() functions. Finally, the functions getSelectedAccessibleRows() and getSelectedAccessibleColumns() each return a sequence of indices of the currently selected row and columns.
There are three functions that can be used to switch between cell indices and row and column indices. Cell indices are the same as the child indices used by the getAccessibleChildCount() and getAccessibleChild() functions of the com.sun.star.accessibility.XAccessibleContext interface. Row and column indices have been used previously, and specify each cell by stating its row and column. The getAccessibleIndex() function returns the corresponding cell index for a given row and column index. The getAccessibleRow() and getAccessibleColumn() functions return the corresponding row and column index, respectively, for a given cell index.
XAccessibleEventBroadcaster
Most accessible objects need to broadcast events that describe changes of its internal state or visual appearance - this can be done with the com.sun.star.accessibility.XAccessibleEventBroadcasterinterface. If you want to be informed of such changes, you can register a listener with the addEventListener() function, or remove it with removeEventListener().
The SSR registers the event listener in separate threads. The major work is done by a method called traverseTree()
that takes an accessible context and traverses the whole tree rooted in this object.
public long traverseTree (XAccessibleContext xRoot) {
After casting the context reference to a reference of the XAccessibleEventBroadcaster interface, it either adds or removes the listener maListener
at the accessibility object.
long nNodeCount = 0;
if (xRoot != null) {
// Register the root node.
XAccessibleEventBroadcaster xBroadcaster =
UnoRuntime.queryInterface (
XAccessibleEventBroadcaster.class,
xRoot);
if (xBroadcaster != null) {
if (mbRegister)
xBroadcaster.addEventListener (maListener);
else
xBroadcaster.removeEventListener (maListener);
nNodeCount += 1;
}
Once the given object is handled, the traversing of the tree continues by calling this method recursively for every child.
try {
int nChildCount = xRoot.getAccessibleChildCount();
for (int i=0; i<nChildCount; i++) {
XAccessible xChild = xRoot.getAccessibleChild (i);
if (xChild != null)
nNodeCount += traverseTree (xChild.getAccessibleContext());
}
}
Because the iteration over the direct children of the given accessible context may take a while, there is the possibility that some children do not exist anymore. It is therefore important to catch IndexOutOfBoundsException and DisposedException exceptions. Note that this is not a perfect solution, because children that are added to the given object are not handled properly. A better algorithm would listen to events of the new and removed children of the object to which it was recently registered:
catch (com.sun.star.lang.IndexOutOfBoundsException aException) {
// The set of children has changed since our last call to
// getAccesibleChildCount(). Don't try any further on this
// sub-tree.
}
catch (com.sun.star.lang.DisposedException aException) {
// The child has been destroyed since our last call to
// getAccesibleChildCount(). That is OK. Don't try any
// further on this sub-tree.
}
}
return nNodeCount;
}
This method keeps track of how many objects it has added to the listener. This number is used in the run()
method to write an informative message.
public void run () {
System.out.println ("starting registration");
long nNodeCount = traverseTree (mxRoot);
System.out.println ("ending registration");
if (mbShowMessages) {
if (!mbRegister)
MessageArea.print ("un");
MessageArea.println ("registered at " + nNodeCount
+ " objects in accessibility tree of " + mxRoot.getAccessibleName());
}
}
XAccessibleEventListener
The interface com.sun.star.accessibility.XAccessibleEventListener is the counterpart to the accessible event broadcaster, and is called for every change of any accessible object at which it has been registered. The notifyEvent() function is called with an com.sun.star.accessibility.AccessibleEventObject structure. That structure comprises four fields: the EventId field, which is one of the com.sun.star.accessibility.AccessibleEventId constants, describes the type of the event. The object that sent the event is referenced from the Source field. The OldValue and NewValue fields contain event type specific values that contain the changed value before and after the modification took place. The type of content that is expected in the OldValue and NewValue fields is explained together with the AccessibleEventId event types.
The notifyEvent()
method of the EventHandler
class is not called directly from the accessibility objects. There is an instance of the EventListenerProxy
class in between. That class simply forwards the events at the right time and is therefore not explained in more detail here.
public void notifyEvent (com.sun.star.accessibility.AccessibleEventObject aEvent) {
The one event type that is covered here is the CHILD event, which is sent when a new accessibility object has been created, or an existing one has been removed.
// Guard against disposed objects.
try {
switch (aEvent.EventId) {
case AccessibleEventId.CHILD:
handleChildEvent (
objectToContext (aEvent.OldValue),
objectToContext (aEvent.NewValue));
break;
The handling of the rest of the event types is omitted here to keep this explanation simple.
Again, it is important to guard against the possibility of events arriving after the object they were sent for has been destroyed. For this simple tool, it is sufficient to silently ignore the resulting exception.
}
}
catch (com.sun.star.lang.DisposedException e) {
}
}
Before showing you the actual handling of child events, you can look at the objectToContext()
method that is used to convert the OldValue and NewValue fields of the event structure from UNO Anys to XAccessibleContext references. It takes a weakness of the accessibility API IDL specification into account: the type of the content of the Source field is not explicitly stated. As a result, it contains references to both the XAccessible and the XAccessibleContext interfaces. To cope with this, the conversion method first uses the com.sun.star.uno.AnyConverter
class to retrieve an XAccessible reference from the event source.
private XAccessibleContext objectToContext (Object aObject) {
XAccessibleContext xContext = null;
XAccessible xAccessible = null;
try {
xAccessible = (XAccessible)AnyConverter.toObject(
new Type(XAccessible.class), aObject);
}
catch (com.sun.star.lang.IllegalArgumentException e) {
}
If that was successful, the accessible context from the object is returned.
if (xAccessible != null)
xContext = xAccessible.getAccessibleContext();
If retrieving an XAccessible reference from the event's source field failed, this is repeated directly with the XAccessibleContext interface.
else
try {
xContext = (XAccessibleContext)AnyConverter.toObject(
new Type(XAccessibleContext.class), aObject);
}
catch (com.sun.star.lang.IllegalArgumentException e) {
}
return xContext;
}
Handling the child event itself is comparably simple: create a new RegistrationThread
object that adds (or removes) the listener to the object and all of its children.
private void handleChildEvent (XAccessibleContext aOldChild, XAccessibleContext aNewChild) {
if (aOldChild != null)
// Remove event listener from the child and all of its descendants.
new RegistrationThread (maListenerProxy, aOldChild, false, false);
else if (aNewChild != null)
// Add event listener to the new child and all of its descendants.
new RegistrationThread (maListenerProxy, aNewChild, true, false);
}
XAccessibleSelection
While the com.sun.star.accessibility.XAccessibleText and com.sun.star.accessibility.XAccessibleTable interfaces already support selection of text and table cells, respectively, there is a special interface for the general case. The XAccessibleSelection interface manages a sub-set of an object's children that form the selection. The number of selected children is returned by getSelectedAccessibleChildCount(), which, of course, is smaller than or equal to the total number of children as returned by getAccessibleChildCount() of the com.sun.star.accessibility.XAccessibleContext interface. The selected children can be retrieved by calling the getSelectedAccessibleChild() function. Note that the same index passed to getSelectedAccessibleChild() and to getAccessibleChild() will generally return different objects.
The selection can be modified with various functions. The functions selectAllAccessibleChildren() and clearAccessibleSelection() select or deselect, respectively, all of the children. To select or deselect a single child, use selectAccessibleChild() or deselectAccessibleChild(). Whether a child belongs to the selection can be determined by calling the isAccessibleChildSelected() function.
Each child that belongs to the selection is expected to have the SELECTED state set. When the selection changes, two kinds of events are expected to be broadcast. One for each child that is selected or deselected that tells the listeners about the toggled SELECTED state, and one event from their parent that informs the listeners of the modified selection as represented by the com.sun.star.accessibility.XAccessibleSelection interface.
XAccessibleRelationSet
In addition to the parent-child relationship that defines the accessibility object tree, each accessible object may have one or more relations to other accessible objects, independent of that hierarchy. The types of possible relations are defined and explained in the com.sun.star.accessibility.AccessibleRelationType set of constants.
One example is the LABEL_FOR and LABELED_BY pair of relations that is used to express the case where one object is the label for a one or more controls of the GUI. The label and its controls may be spatially adjacent to make their relationship clear to the sighted user. But in the accessibility object tree, they may belong to different sub-trees. With a set of relations, these objects can be linked together.
Each relation is an com.sun.star.accessibility.AccessibleRelation structure that contains the relation type and a set of references to its target objects. In the previous example, there would be one LABEL_FOR relation for the label with all its controls in the target set and one LABELED_BY relation from each control to the label.
For each relation type there may be one relation belonging to a relation set as represented by the com.sun.star.accessibility.XAccessibleRelationSet interface. Therefore, the number of relations that is returned by getRelationCount() can not be greater than the number of relation types. Relations can be accessed either by calling the getRelation() function with an index, or by calling the RelationType() function with a relation type. Note that there are no fixed mappings from relation types to indices. You can test whether a relation set contains a relation of a given type by using the containsRelation() function.
XAccessibleValue
In a typical GUI, there are many controls whose characteristic feature can be represented by a single numerical value. Examples are spin boxes, scales and sliders, or combo boxes that contain only numbers, such as the font size. These objects should support the XAccessibleValue interface.
The current value can be read and set with the getCurrentValue() and setCurrentValue() functions. The valid range of values is returned by the getMaximumValue() and getMinimumValue() functions.
XAccessibleImage
The main purpose of the XAccessibleImage interface is to be an indicator that an object represents an image or a bitmap. The functions of this interface do not add functionality that is not already present in the com.sun.star.accessibility.XAccessibleContext interface.
The getAccessibleImageDescription() function returns a localized description of the image. The functions getAccessibleImageHeight() and getAccessibleImageWidth() return the size of the image in pixels.
XAccessibleAction
With the XAccessibleAction interface, an accessible object can provide access to actions that can be performed on the object. These actions may or may not correspond to actions that are already available over the GUI.
The number of available actions is returned by getAccessibleActionCount(). To execute an action call doAccessibleAction() with the index of the desired action. The description of an action is returned by the getAccessibleActionDescription().
The getAccessibleActionKeyBinding() function tells you what key bindings exist for a certain action. See the description of the XAccessibleKeyBinding interface below.
XAccessibleKeyBinding
The purpose of the XAccessibleKeyBinding interface is to represent an arbitrary set of key strokes. When a key binding is associated with an action (see the description of the interface above) then each of its key strokes executes that action. A key stroke itself consists of one or more keys to be pressed. See com.sun.star.awt.KeyStroke for details.
XAccessibleHypertext
The interface com.sun.star.accessibility.XAccessibleHypertext is derived from com.sun.star.accessibility.XAccessibleText. This interface represents text that contains hyperlinks. Those hyperlinks are represented by com.sun.star.accessibility.XAccessibleHyperlink objects, which are described next.
To iterate over all hyperlinks in a text, use getHyperLinkCount() to determine the number of links. The getHyperLink() function returns the com.sun.star.accessibility.XAccessibleHyperlink object for a specific index. If you want to know whether there is a link at a certain text position, use the getHyperLinkIndex() function to obtain the corresponding object. When the returned reference is empty then there is no hyperlink at that position.
XAccessibleHyperlink
Hyperlinks contained in a hypertext document are modeled by the XAccessibleHyperlink interface. In its simplest form, a hyperlink corresponds to an HTML link. However, there may be more complex hyperlinks where there is more than one action assigned to a single hyperlink. An example of this is HTML image maps. To give access to the actions, the interface is derived from the com.sun.star.accessibility.XAccessibleAction interface. In addition to the information provided by the com.sun.star.accessibility.XAccessibleAction interface, you can request an action's anchor and object. A typical return value of the getAccessibleActionAnchor() function for HTML links would be the text between the <a href…>
and </a>
tags. Likewise, the getAccessibleActionObject() function returns a HTML link's URL.
A hyperlink specifies its text relative to the enclosing hypertext by providing a start- and end index through the getStartIndex() and getEndIndex() functions. You can ask a link about the validity of the referenced target by calling its isValid() function. Note that this state is volatile and may change without notice.