Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT Accessibility/carbon/org/eclipse/swt/accessibility/Accessible.java')
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT Accessibility/carbon/org/eclipse/swt/accessibility/Accessible.java1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Accessibility/carbon/org/eclipse/swt/accessibility/Accessible.java b/bundles/org.eclipse.swt/Eclipse SWT Accessibility/carbon/org/eclipse/swt/accessibility/Accessible.java
new file mode 100644
index 0000000000..43e797e555
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Accessibility/carbon/org/eclipse/swt/accessibility/Accessible.java
@@ -0,0 +1,1382 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.accessibility;
+
+
+import java.util.Vector;
+import org.eclipse.swt.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.internal.carbon.*;
+
+/**
+ * Instances of this class provide a bridge between application
+ * code and assistive technology clients. Many platforms provide
+ * default accessible behavior for most widgets, and this class
+ * allows that default behavior to be overridden. Applications
+ * can get the default Accessible object for a control by sending
+ * it <code>getAccessible</code>, and then add an accessible listener
+ * to override simple items like the name and help string, or they
+ * can add an accessible control listener to override complex items.
+ * As a rule of thumb, an application would only want to use the
+ * accessible control listener to implement accessibility for a
+ * custom control.
+ *
+ * @see Control#getAccessible
+ * @see AccessibleListener
+ * @see AccessibleEvent
+ * @see AccessibleControlListener
+ * @see AccessibleControlEvent
+ * @see <a href="http://www.eclipse.org/swt/snippets/#accessibility">Accessibility snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ *
+ * @since 2.0
+ */
+public class Accessible {
+ static final String [] requiredAttributes = {
+ OS.kAXRoleAttribute,
+ OS.kAXSubroleAttribute,
+ OS.kAXRoleDescriptionAttribute,
+ OS.kAXHelpAttribute,
+ OS.kAXTitleAttribute,
+ OS.kAXValueAttribute,
+ OS.kAXEnabledAttribute,
+ OS.kAXFocusedAttribute,
+ OS.kAXParentAttribute,
+ OS.kAXChildrenAttribute,
+ OS.kAXSelectedChildrenAttribute,
+ OS.kAXVisibleChildrenAttribute,
+ OS.kAXWindowAttribute,
+ OS.kAXTopLevelUIElementAttribute,
+ OS.kAXPositionAttribute,
+ OS.kAXSizeAttribute,
+ OS.kAXDescriptionAttribute,
+ };
+ static final String [] textAttributes = {
+ OS.kAXNumberOfCharactersAttribute,
+ OS.kAXSelectedTextAttribute,
+ OS.kAXSelectedTextRangeAttribute,
+ OS.kAXStringForRangeParameterizedAttribute,
+ OS.kAXInsertionPointLineNumberAttribute,
+ OS.kAXRangeForLineParameterizedAttribute,
+ };
+
+ Vector accessibleListeners = new Vector();
+ Vector accessibleControlListeners = new Vector();
+ Vector accessibleTextListeners = new Vector ();
+ Control control;
+ int axuielementref = 0;
+ int[] osChildIDCache = new int[0];
+
+ /**
+ * @since 3.5
+ */
+ protected Accessible() {
+ }
+
+ Accessible(Control control) {
+ this.control = control;
+ axuielementref = OS.AXUIElementCreateWithHIObjectAndIdentifier(control.handle, 0);
+ OS.HIObjectSetAccessibilityIgnored(control.handle, false);
+ }
+
+ /**
+ * Invokes platform specific functionality to allocate a new accessible object.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Accessible</code>. It is marked public only so that it
+ * can be shared within the packages provided by SWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ *
+ * @param control the control to get the accessible object for
+ * @return the platform specific accessible object
+ */
+ public static Accessible internal_new_Accessible(Control control) {
+ return new Accessible(control);
+ }
+
+ /**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an accessible client asks for certain strings,
+ * such as name, description, help, or keyboard shortcut. The
+ * listener is notified by sending it one of the messages defined
+ * in the <code>AccessibleListener</code> interface.
+ *
+ * @param listener the listener that should be notified when the receiver
+ * is asked for a name, description, help, or keyboard shortcut string
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see AccessibleListener
+ * @see #removeAccessibleListener
+ */
+ public void addAccessibleListener(AccessibleListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ accessibleListeners.addElement(listener);
+ }
+
+ /**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an accessible client asks for custom control
+ * specific information. The listener is notified by sending it
+ * one of the messages defined in the <code>AccessibleControlListener</code>
+ * interface.
+ *
+ * @param listener the listener that should be notified when the receiver
+ * is asked for custom control specific information
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see AccessibleControlListener
+ * @see #removeAccessibleControlListener
+ */
+ public void addAccessibleControlListener(AccessibleControlListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ accessibleControlListeners.addElement(listener);
+ }
+
+ /**
+ * Adds the listener to the collection of listeners who will
+ * be notified when an accessible client asks for custom text control
+ * specific information. The listener is notified by sending it
+ * one of the messages defined in the <code>AccessibleTextListener</code>
+ * interface.
+ *
+ * @param listener the listener that should be notified when the receiver
+ * is asked for custom text control specific information
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see AccessibleTextListener
+ * @see #removeAccessibleTextListener
+ *
+ * @since 3.0
+ */
+ public void addAccessibleTextListener (AccessibleTextListener listener) {
+ checkWidget ();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ accessibleTextListeners.addElement (listener);
+ }
+
+ /**
+ * Returns the control for this Accessible object.
+ *
+ * @return the receiver's control
+ * @since 3.0
+ */
+ public Control getControl() {
+ return control;
+ }
+
+ /**
+ * Invokes platform specific functionality to dispose an accessible object.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Accessible</code>. It is marked public only so that it
+ * can be shared within the packages provided by SWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ */
+ public void internal_dispose_Accessible() {
+ if (axuielementref != 0) {
+ OS.CFRelease(axuielementref);
+ axuielementref = 0;
+ for (int index = 1; index < osChildIDCache.length; index += 2) {
+ OS.CFRelease(osChildIDCache [index]);
+ }
+ osChildIDCache = new int[0];
+ }
+ }
+
+ /**
+ * Invokes platform specific functionality to handle a window message.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Accessible</code>. It is marked public only so that it
+ * can be shared within the packages provided by SWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ */
+ public int internal_kEventAccessibleGetChildAtPoint (int nextHandler, int theEvent, int userData) {
+ if (axuielementref != 0) {
+ OS.CallNextEventHandler (nextHandler, theEvent);
+ //TODO: check error?
+ int childID = getChildIDFromEvent(theEvent);
+ CGPoint pt = new CGPoint ();
+ OS.GetEventParameter (theEvent, OS.kEventParamMouseLocation, OS.typeHIPoint, null, CGPoint.sizeof, null, pt);
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.x = (int) pt.x;
+ event.y = (int) pt.y;
+ event.childID = ACC.CHILDID_SELF;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getChildAtPoint(event);
+ }
+ if (event.accessible != null) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleChild, OS.typeCFTypeRef, 4, new int[] {event.accessible.axuielementref});
+ return OS.noErr;
+ }
+ if (event.childID == ACC.CHILDID_SELF || event.childID == ACC.CHILDID_NONE || event.childID == childID) {
+ /*
+ * From the Carbon doc for kEventAccessibleGetChildAtPoint: "If there is no child at the given point,
+ * you should still return noErr, but leave the parameter empty (do not call SetEventParameter)."
+ */
+ return OS.noErr;
+ }
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleChild, OS.typeCFTypeRef, 4, new int[] {childIDToOs(event.childID)});
+ return OS.noErr;
+ }
+ return OS.eventNotHandledErr;
+ }
+
+ /**
+ * Invokes platform specific functionality to handle a window message.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Accessible</code>. It is marked public only so that it
+ * can be shared within the packages provided by SWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ */
+ public int internal_kEventAccessibleGetFocusedChild (int nextHandler, int theEvent, int userData) {
+ if (axuielementref != 0) {
+ int result = OS.CallNextEventHandler (nextHandler, theEvent);
+ //TODO: check error?
+ int childID = getChildIDFromEvent(theEvent);
+ if (childID != ACC.CHILDID_SELF) {
+ /* From the Carbon doc for kEventAccessibleGetFocusedChild:
+ * "Only return immediate children; do not return grandchildren of yourself."
+ */
+ return OS.noErr; //TODO: should this return eventNotHandledErr?
+ }
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = ACC.CHILDID_MULTIPLE; // set to invalid value, to test if the application sets it in getFocus()
+ event.accessible = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getFocus(event);
+ }
+
+ /* The application can optionally answer an accessible. */
+ if (event.accessible != null) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleChild, OS.typeCFTypeRef, 4, new int[] {event.accessible.axuielementref});
+ return OS.noErr;
+ }
+
+ /* Or the application can answer a valid child ID, including CHILDID_SELF and CHILDID_NONE. */
+ if (event.childID == ACC.CHILDID_SELF) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleChild, OS.typeCFTypeRef, 4, new int[] {0});
+ return OS.noErr;
+ }
+ if (event.childID == ACC.CHILDID_NONE) {
+ /*
+ * From the Carbon doc for kEventAccessibleGetFocusedChild: "If there is no child in the
+ * focus chain, your handler should leave the kEventParamAccessibleChild parameter empty
+ * and return noErr."
+ */
+ return OS.noErr;
+ }
+ if (event.childID != ACC.CHILDID_MULTIPLE) {
+ /* Other valid childID. */
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleChild, OS.typeCFTypeRef, 4, new int[] {childIDToOs(event.childID)});
+ return OS.noErr;
+ }
+
+ /* Invalid childID means the application did not implement getFocus, so just go with the default handler. */
+ return result;
+ }
+ return OS.eventNotHandledErr;
+ }
+
+ /**
+ * Invokes platform specific functionality to handle a window message.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Accessible</code>. It is marked public only so that it
+ * can be shared within the packages provided by SWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ */
+ public int internal_kEventAccessibleGetAllAttributeNames (int nextHandler, int theEvent, int userData) {
+ int code = userData; // userData flags whether nextHandler has already been called
+ if (axuielementref != 0) {
+ if (code == OS.eventNotHandledErr) OS.CallNextEventHandler (nextHandler, theEvent);
+ int [] arrayRef = new int[1];
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeNames, OS.typeCFMutableArrayRef, null, 4, null, arrayRef);
+ int stringArrayRef = arrayRef[0];
+ int length = OS.CFArrayGetCount(stringArrayRef);
+ String [] osAllAttributes = new String [length];
+ for (int i = 0; i < length; i++) {
+ int stringRef = OS.CFArrayGetValueAtIndex(stringArrayRef, i);
+ osAllAttributes[i] = stringRefToString (stringRef);
+ }
+ /* Add our list of supported attributes to the array.
+ * Make sure each attribute name is not already in the array before appending.
+ */
+ for (int i = 0; i < requiredAttributes.length; i++) {
+ if (!contains(osAllAttributes, requiredAttributes[i])) {
+ int stringRef = stringToStringRef(requiredAttributes[i]);
+ OS.CFArrayAppendValue(stringArrayRef, stringRef);
+ OS.CFRelease(stringRef);
+ }
+ }
+ if (accessibleTextListeners.size() > 0) {
+ for (int i = 0; i < textAttributes.length; i++) {
+ if (!contains(osAllAttributes, textAttributes[i])) {
+ int stringRef = stringToStringRef(textAttributes[i]);
+ OS.CFArrayAppendValue(stringArrayRef, stringRef);
+ OS.CFRelease(stringRef);
+ }
+ }
+ }
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ boolean contains (String [] array, String element) {
+ for (int i = 0; i < array.length; i++) {
+ if (array[i].equals(element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Invokes platform specific functionality to handle a window message.
+ * <p>
+ * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+ * API for <code>Accessible</code>. It is marked public only so that it
+ * can be shared within the packages provided by SWT. It is not
+ * available on all platforms, and should never be called from
+ * application code.
+ * </p>
+ */
+ public int internal_kEventAccessibleGetNamedAttribute (int nextHandler, int theEvent, int userData) {
+ if (axuielementref != 0) {
+ int [] stringRef = new int [1];
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeName, OS.typeCFStringRef, null, 4, null, stringRef);
+ int length = 0;
+ if (stringRef [0] != 0) length = OS.CFStringGetLength (stringRef [0]);
+ char [] buffer= new char [length];
+ CFRange range = new CFRange ();
+ range.length = length;
+ OS.CFStringGetCharacters (stringRef [0], range, buffer);
+ String attributeName = new String(buffer);
+ if (attributeName.equals(OS.kAXRoleAttribute)) return getRoleAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXSubroleAttribute)) return getSubroleAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXRoleDescriptionAttribute)) return getRoleDescriptionAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXHelpAttribute)) return getHelpAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXTitleAttribute)) return getTitleAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXValueAttribute)) return getValueAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXEnabledAttribute)) return getEnabledAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXFocusedAttribute)) return getFocusedAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXParentAttribute)) return getParentAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXChildrenAttribute)) return getChildrenAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXSelectedChildrenAttribute)) return getSelectedChildrenAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXVisibleChildrenAttribute)) return getVisibleChildrenAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXPositionAttribute)) return getPositionAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXSizeAttribute)) return getSizeAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXDescriptionAttribute)) return getDescriptionAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXNumberOfCharactersAttribute)) return getNumberOfCharactersAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXSelectedTextAttribute)) return getSelectedTextAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXSelectedTextRangeAttribute)) return getSelectedTextRangeAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXStringForRangeParameterizedAttribute)) return getStringForRangeAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXInsertionPointLineNumberAttribute)) return getInsertionPointLineNumberAttribute(nextHandler, theEvent, userData);
+ if (attributeName.equals(OS.kAXRangeForLineParameterizedAttribute)) return getRangeForLineParameterizedAttribute(nextHandler, theEvent, userData);
+ return getAttribute(nextHandler, theEvent, userData);
+ }
+ return userData;
+ }
+
+ int getAttribute (int nextHandler, int theEvent, int userData) {
+ /* Generic handler: first try just calling the default handler. */
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ if (code != OS.noErr && getChildIDFromEvent(theEvent) != ACC.CHILDID_SELF) {
+ /*
+ * If the childID is unknown to the control, then it was created by the application,
+ * so delegate to the application's accessible UIElement for the control.
+ */
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleObject, OS.typeCFTypeRef, 4, new int [] {axuielementref});
+ code = OS.CallNextEventHandler (nextHandler, theEvent);
+ }
+ return code;
+ }
+
+ int getHelpAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ String osHelpAttribute = null;
+ int [] stringRef = new int [1];
+ if (code == OS.noErr) {
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, null, 4, null, stringRef);
+ osHelpAttribute = stringRefToString (stringRef [0]);
+ }
+ AccessibleEvent event = new AccessibleEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.result = osHelpAttribute;
+ for (int i = 0; i < accessibleListeners.size(); i++) {
+ AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+ listener.getHelp(event);
+ }
+ if (event.result != null) {
+ stringRef [0] = stringToStringRef (event.result);
+ if (stringRef [0] != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, stringRef);
+ OS.CFRelease(stringRef [0]);
+ code = OS.noErr;
+ }
+ }
+ return code;
+ }
+
+ int getRoleAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.detail = -1;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getRole(event);
+ }
+ if (event.detail != -1) {
+ String appRole = roleToOs (event.detail);
+ int index = appRole.indexOf(':');
+ if (index != -1) appRole = appRole.substring(0, index);
+ int stringRef = stringToStringRef (appRole);
+ if (stringRef != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, new int [] {stringRef});
+ OS.CFRelease(stringRef);
+ code = OS.noErr;
+ }
+ }
+ return code;
+ }
+
+ int getSubroleAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.detail = -1;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getRole(event);
+ }
+ if (event.detail != -1) {
+ String appRole = roleToOs (event.detail);
+ int index = appRole.indexOf(':');
+ if (index != -1) {
+ appRole = appRole.substring(index + 1);
+ int stringRef = stringToStringRef (appRole);
+ if (stringRef != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, new int [] {stringRef});
+ OS.CFRelease(stringRef);
+ }
+ }
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getRoleDescriptionAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.detail = -1;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getRole(event);
+ }
+ if (event.detail != -1) {
+ String appRole = roleToOs (event.detail);
+ String appSubrole = null;
+ int index = appRole.indexOf(':');
+ if (index != -1) {
+ appSubrole = appRole.substring(index + 1);
+ appRole = appRole.substring(0, index);
+ }
+ int stringRef1 = stringToStringRef (appRole);
+ if (stringRef1 != 0) {
+ int stringRef2 = 0;
+ if (appSubrole != null) stringRef2 = stringToStringRef (appSubrole);
+ int stringRef3 = OS.HICopyAccessibilityRoleDescription (stringRef1, stringRef2);
+ OS.CFRelease(stringRef1);
+ if (stringRef2 != 0) OS.CFRelease(stringRef2);
+ if (stringRef3 != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, new int [] {stringRef3});
+ OS.CFRelease(stringRef3);
+ code = OS.noErr;
+ }
+ }
+ }
+ return code;
+ }
+
+ int getTitleAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ int childID = getChildIDFromEvent(theEvent);
+
+ /*
+ * Feature of the Macintosh. The text of a Label is returned in its value,
+ * not its title, so ensure that the role is not Label before asking for the title.
+ */
+ AccessibleControlEvent roleEvent = new AccessibleControlEvent(this);
+ roleEvent.childID = childID;
+ roleEvent.detail = -1;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getRole(roleEvent);
+ }
+ if (roleEvent.detail != ACC.ROLE_LABEL) {
+ String osTitleAttribute = null;
+ int [] stringRef = new int [1];
+ if (code == OS.noErr) {
+ int status = OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, null, 4, null, stringRef);
+ if (status == OS.noErr) {
+ osTitleAttribute = stringRefToString (stringRef [0]);
+ }
+ }
+ AccessibleEvent event = new AccessibleEvent(this);
+ event.childID = childID;
+ event.result = osTitleAttribute;
+ for (int i = 0; i < accessibleListeners.size(); i++) {
+ AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+ listener.getName(event);
+ }
+ if (event.result != null) {
+ stringRef [0] = stringToStringRef (event.result);
+ if (stringRef [0] != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, stringRef);
+ OS.CFRelease(stringRef [0]);
+ code = OS.noErr;
+ }
+ }
+ }
+ return code;
+ }
+
+ int getValueAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ int childID = getChildIDFromEvent(theEvent);
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = childID;
+ event.detail = -1;
+ event.result = null; //TODO: could pass the OS value to the app
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getRole(event);
+ listener.getValue(event);
+ }
+ int role = event.detail;
+ String value = event.result;
+ if (value != null || role == ACC.ROLE_LABEL) {
+ int stringRef = 0;
+ switch (role) {
+ case ACC.ROLE_RADIOBUTTON: // 1 = on, 0 = off
+ case ACC.ROLE_CHECKBUTTON: // 1 = checked, 0 = unchecked, 2 = mixed
+ case ACC.ROLE_SCROLLBAR: // numeric value representing the position of the scroller
+ case ACC.ROLE_TABITEM: // 1 = selected, 0 = not selected
+ case ACC.ROLE_SLIDER: // the value associated with the position of the slider thumb
+ case ACC.ROLE_PROGRESSBAR: // the value associated with the fill level of the progress bar
+ try {
+ int number = Integer.parseInt(value);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeSInt32, 4, new int [] {number});
+ code = OS.noErr;
+ } catch (NumberFormatException ex) {
+ if (value.equalsIgnoreCase("true")) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {true});
+ code = OS.noErr;
+ } else if (value.equalsIgnoreCase("false")) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {false});
+ code = OS.noErr;
+ }
+ }
+ break;
+ case ACC.ROLE_TABFOLDER: // the accessibility object representing the currently selected tab item
+ //break;
+ case ACC.ROLE_COMBOBOX: // text of the currently selected item
+ case ACC.ROLE_TEXT: // text in the text field
+ stringRef = stringToStringRef(value);
+ break;
+ case ACC.ROLE_LABEL: // text in the label
+ /* On a Mac, the 'value' of a label is the same as the 'name' of the label. */
+ AccessibleEvent e = new AccessibleEvent(this);
+ e.childID = childID;
+ e.result = null;
+ for (int i = 0; i < accessibleListeners.size(); i++) {
+ AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+ listener.getName(e);
+ }
+ if (e.result != null) {
+ stringRef = stringToStringRef(e.result);
+ } else {
+ if (value != null) stringRef = stringToStringRef(value);
+ }
+ break;
+ }
+ if (stringRef != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, new int [] {stringRef});
+ OS.CFRelease(stringRef);
+ code = OS.noErr;
+ }
+ }
+ return code;
+ }
+
+ int getEnabledAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ if (code == OS.eventNotHandledErr) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {control.isEnabled()});
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getFocusedAttribute (int nextHandler, int theEvent, int userData) {
+ int[] osChildID = new int[1];
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleObject, OS.typeCFTypeRef, null, 4, null, osChildID);
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = ACC.CHILDID_MULTIPLE; // set to invalid value, to test if the application sets it in getFocus()
+ event.accessible = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getFocus(event);
+ }
+
+ /* The application can optionally answer an accessible. */
+ if (event.accessible != null) {
+ boolean hasFocus = OS.CFEqual(event.accessible.axuielementref, osChildID[0]);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {hasFocus});
+ return OS.noErr;
+ }
+
+ /* Or the application can answer a valid child ID, including CHILDID_SELF and CHILDID_NONE. */
+ if (event.childID == ACC.CHILDID_SELF) {
+ boolean hasFocus = OS.CFEqual(axuielementref, osChildID[0]);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {hasFocus});
+ return OS.noErr;
+ }
+ if (event.childID == ACC.CHILDID_NONE) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {false});
+ return OS.noErr;
+ }
+ if (event.childID != ACC.CHILDID_MULTIPLE) {
+ /* Other valid childID. */
+ int childID = osToChildID(osChildID[0]);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {event.childID == childID});
+ return OS.noErr;
+ }
+
+ /* Invalid childID at this point means the application did not implement getFocus, so return the native focus. */
+ boolean hasFocus = false;
+ int focusWindow = OS.GetUserFocusWindow ();
+ if (focusWindow != 0) {
+ int [] focusControl = new int [1];
+ OS.GetKeyboardFocus (focusWindow, focusControl);
+ if (focusControl [0] == control.handle) hasFocus = true;
+ }
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeBoolean, 4, new boolean [] {hasFocus});
+ return OS.noErr;
+ }
+
+ int getParentAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ if (code == OS.eventNotHandledErr) {
+ /* If the childID was created by the application, the parent is the accessible for the control. */
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFTypeRef, 4, new int [] {axuielementref});
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getChildrenAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ int childID = getChildIDFromEvent(theEvent);
+ if (childID == ACC.CHILDID_SELF) {
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = childID;
+ event.detail = -1; // set to impossible value to test if app resets
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getChildCount(event);
+ }
+ if (event.detail == 0) {
+ code = OS.noErr;
+ } else if (event.detail > 0) {
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getChildren(event);
+ }
+ Object [] appChildren = event.children;
+ if (appChildren != null && appChildren.length > 0) {
+ /* return a CFArrayRef of AXUIElementRefs */
+ int children = OS.CFArrayCreateMutable (OS.kCFAllocatorDefault, 0, 0);
+ if (children != 0) {
+ for (int i = 0; i < appChildren.length; i++) {
+ Object child = appChildren[i];
+ if (child instanceof Integer) {
+ OS.CFArrayAppendValue (children, childIDToOs(((Integer)child).intValue()));
+ } else {
+ OS.CFArrayAppendValue (children, ((Accessible)child).axuielementref);
+ }
+ }
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFMutableArrayRef, 4, new int [] {children});
+ OS.CFRelease(children);
+ code = OS.noErr;
+ }
+ }
+ }
+ }
+ return code;
+ }
+
+ int getSelectedChildrenAttribute (int nextHandler, int theEvent, int userData) {
+ //TODO
+ return getAttribute (nextHandler, theEvent, userData);
+ }
+
+ int getVisibleChildrenAttribute (int nextHandler, int theEvent, int userData) {
+ //TODO
+ return getAttribute (nextHandler, theEvent, userData);
+ }
+
+ int getPositionAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ CGPoint osPositionAttribute = new CGPoint ();
+ if (code == OS.noErr) {
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeHIPoint, null, CGPoint.sizeof, null, osPositionAttribute);
+ }
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.x = (int) osPositionAttribute.x;
+ event.y = (int) osPositionAttribute.y;
+ if (code != OS.noErr) event.width = -1;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getLocation(event);
+ }
+ if (event.width != -1) {
+ osPositionAttribute.x = event.x;
+ osPositionAttribute.y = event.y;
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeHIPoint, CGPoint.sizeof, osPositionAttribute);
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getSizeAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ CGPoint osSizeAttribute = new CGPoint ();
+ if (code == OS.noErr) {
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeHISize, null, CGPoint.sizeof, null, osSizeAttribute);
+ }
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.width = (code != OS.noErr) ? -1 : (int) osSizeAttribute.x;
+ event.height = (int) osSizeAttribute.y;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getLocation(event);
+ }
+ if (event.width != -1) {
+ osSizeAttribute.x = event.width;
+ osSizeAttribute.y = event.height;
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeHISize, CGPoint.sizeof, osSizeAttribute);
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getDescriptionAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData != OS.eventNotHandledErr ? userData : OS.CallNextEventHandler (nextHandler, theEvent);
+ String osDescriptionAttribute = null;
+ int [] stringRef = new int [1];
+ if (code == OS.noErr) {
+ int status = OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, null, 4, null, stringRef);
+ if (status == OS.noErr) {
+ osDescriptionAttribute = stringRefToString (stringRef [0]);
+ }
+ }
+ AccessibleEvent event = new AccessibleEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.result = osDescriptionAttribute;
+ for (int i = 0; i < accessibleListeners.size(); i++) {
+ AccessibleListener listener = (AccessibleListener) accessibleListeners.elementAt(i);
+ listener.getDescription(event);
+ }
+ if (event.result != null) {
+ stringRef [0] = stringToStringRef (event.result);
+ if (stringRef [0] != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, stringRef);
+ OS.CFRelease(stringRef [0]);
+ code = OS.noErr;
+ }
+ }
+ return code;
+ }
+
+ int getInsertionPointLineNumberAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleControlEvent controlEvent = new AccessibleControlEvent(this);
+ controlEvent.childID = getChildIDFromEvent(theEvent);
+ controlEvent.result = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getValue(controlEvent);
+ }
+ AccessibleTextEvent textEvent = new AccessibleTextEvent(this);
+ textEvent.childID = getChildIDFromEvent(theEvent);
+ textEvent.offset = -1;
+ for (int i = 0; i < accessibleTextListeners.size(); i++) {
+ AccessibleTextListener listener = (AccessibleTextListener) accessibleTextListeners.elementAt(i);
+ listener.getCaretOffset(textEvent);
+ }
+ if (controlEvent.result != null && textEvent.offset != -1) {
+ int lineNumber = lineNumberForOffset (controlEvent.result, textEvent.offset);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeSInt32, 4, new int [] {lineNumber});
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getNumberOfCharactersAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.result = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getValue(event);
+ }
+ String appValue = event.result;
+ if (appValue != null) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeSInt32, 4, new int [] {appValue.length()});
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getRangeForLineParameterizedAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ int lineNumber [] = new int [1];
+ int status = OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeParameter, OS.typeSInt32, null, 4, null, lineNumber);
+ if (status == OS.noErr) {
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.result = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getValue(event);
+ }
+ if (event.result != null) {
+ CFRange range = rangeForLineNumber (lineNumber [0], event.result);
+ if (range.location != -1) {
+ int valueRef = OS.AXValueCreate(OS.kAXValueCFRangeType, range);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFTypeRef, 4, new int [] {valueRef});
+ OS.CFRelease(valueRef);
+ code = OS.noErr;
+ }
+ }
+ }
+ return code;
+ }
+
+ int getSelectedTextAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleTextEvent event = new AccessibleTextEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.offset = -1;
+ event.length = -1;
+ for (int i = 0; i < accessibleTextListeners.size(); i++) {
+ AccessibleTextListener listener = (AccessibleTextListener) accessibleTextListeners.elementAt(i);
+ listener.getSelectionRange(event);
+ }
+ int offset = event.offset;
+ int length = event.length;
+ if (offset != -1 && length != -1 && length != 0) { // TODO: do we need the && length != 0 ?
+ AccessibleControlEvent event2 = new AccessibleControlEvent(this);
+ event2.childID = event.childID;
+ event2.result = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getValue(event2);
+ }
+ String appValue = event2.result;
+ if (appValue != null) {
+ int stringRef = stringToStringRef (appValue.substring(offset, offset + length));
+ if (stringRef != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, new int [] {stringRef});
+ OS.CFRelease(stringRef);
+ code = OS.noErr;
+ }
+ }
+ }
+ return code;
+ }
+
+ int getSelectedTextRangeAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ AccessibleTextEvent event = new AccessibleTextEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.offset = -1;
+ event.length = -1;
+ for (int i = 0; i < accessibleTextListeners.size(); i++) {
+ AccessibleTextListener listener = (AccessibleTextListener) accessibleTextListeners.elementAt(i);
+ listener.getSelectionRange(event);
+ }
+ if (event.offset != -1) {
+ CFRange range = new CFRange();
+ range.location = event.offset;
+ range.length = event.length;
+ int valueRef = OS.AXValueCreate(OS.kAXValueCFRangeType, range);
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFTypeRef, 4, new int [] {valueRef});
+ OS.CFRelease(valueRef);
+ code = OS.noErr;
+ }
+ return code;
+ }
+
+ int getStringForRangeAttribute (int nextHandler, int theEvent, int userData) {
+ int code = userData;
+ int valueRef [] = new int [1];
+ int status = OS.GetEventParameter (theEvent, OS.kEventParamAccessibleAttributeParameter, OS.typeCFTypeRef, null, 4, null, valueRef);
+ if (status == OS.noErr) {
+ CFRange range = new CFRange();
+ boolean ok = OS.AXValueGetValue(valueRef[0], OS.kAXValueCFRangeType, range);
+ if (ok) {
+ AccessibleControlEvent event = new AccessibleControlEvent(this);
+ event.childID = getChildIDFromEvent(theEvent);
+ event.result = null;
+ for (int i = 0; i < accessibleControlListeners.size(); i++) {
+ AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.elementAt(i);
+ listener.getValue(event);
+ }
+ String appValue = event.result;
+ if (appValue != null) {
+ int stringRef = stringToStringRef (appValue.substring(range.location, range.location + range.length));
+ if (stringRef != 0) {
+ OS.SetEventParameter (theEvent, OS.kEventParamAccessibleAttributeValue, OS.typeCFStringRef, 4, new int [] {stringRef});
+ OS.CFRelease(stringRef);
+ code = OS.noErr;
+ }
+ }
+ }
+ }
+ return code;
+ }
+
+ int lineNumberForOffset (String text, int offset) {
+ int lineNumber = 1;
+ int length = text.length();
+ for (int i = 0; i < offset; i++) {
+ switch (text.charAt (i)) {
+ case '\r':
+ if (i + 1 < length) {
+ if (text.charAt (i + 1) == '\n') ++i;
+ }
+ // FALL THROUGH
+ case '\n':
+ lineNumber++;
+ }
+ }
+ return lineNumber;
+ }
+
+ CFRange rangeForLineNumber (int lineNumber, String text) {
+ CFRange range = new CFRange();
+ range.location = -1;
+ int line = 1;
+ int count = 0;
+ int length = text.length ();
+ for (int i = 0; i < length; i++) {
+ if (line == lineNumber) {
+ if (count == 0) {
+ range.location = i;
+ }
+ count++;
+ }
+ if (line > lineNumber) break;
+ switch (text.charAt (i)) {
+ case '\r':
+ if (i + 1 < length && text.charAt (i + 1) == '\n') i++;
+ // FALL THROUGH
+ case '\n':
+ line++;
+ }
+ }
+ range.length = count;
+ return range;
+ }
+
+ /**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an accessible client asks for certain strings,
+ * such as name, description, help, or keyboard shortcut.
+ *
+ * @param listener the listener that should no longer be notified when the receiver
+ * is asked for a name, description, help, or keyboard shortcut string
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see AccessibleListener
+ * @see #addAccessibleListener
+ */
+ public void removeAccessibleListener(AccessibleListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ accessibleListeners.removeElement(listener);
+ }
+
+ /**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an accessible client asks for custom control
+ * specific information.
+ *
+ * @param listener the listener that should no longer be notified when the receiver
+ * is asked for custom control specific information
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see AccessibleControlListener
+ * @see #addAccessibleControlListener
+ */
+ public void removeAccessibleControlListener(AccessibleControlListener listener) {
+ checkWidget();
+ if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ accessibleControlListeners.removeElement(listener);
+ }
+
+ /**
+ * Removes the listener from the collection of listeners who will
+ * be notified when an accessible client asks for custom text control
+ * specific information.
+ *
+ * @param listener the listener that should no longer be notified when the receiver
+ * is asked for custom text control specific information
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see AccessibleTextListener
+ * @see #addAccessibleTextListener
+ *
+ * @since 3.0
+ */
+ public void removeAccessibleTextListener (AccessibleTextListener listener) {
+ checkWidget ();
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ accessibleTextListeners.removeElement (listener);
+ }
+
+ /**
+ * Sends a message to accessible clients that the child selection
+ * within a custom container control has changed.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+ public void selectionChanged () {
+ checkWidget();
+ int stringRef = stringToStringRef(OS.kAXSelectedChildrenChangedNotification);
+ OS.AXNotificationHIObjectNotify(stringRef, control.handle, 0);
+ OS.CFRelease(stringRef);
+ }
+
+ /**
+ * Sends a message to accessible clients indicating that the focus
+ * has changed within a custom control.
+ *
+ * @param childID an identifier specifying a child of the control
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ */
+ public void setFocus(int childID) {
+ checkWidget();
+ childIDToOs(childID); // Make sure the childID is cached
+ int stringRef = stringToStringRef(OS.kAXFocusedUIElementChangedNotification);
+ OS.AXNotificationHIObjectNotify(stringRef, control.handle, 0);
+ OS.CFRelease(stringRef);
+ }
+
+ /**
+ * Sends a message to accessible clients that the text
+ * caret has moved within a custom control.
+ *
+ * @param index the new caret index within the control
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+ public void textCaretMoved (int index) {
+ checkWidget();
+ int stringRef = stringToStringRef(OS.kAXSelectedTextChangedNotification);
+ OS.AXNotificationHIObjectNotify(stringRef, control.handle, 0);
+ OS.CFRelease(stringRef);
+ }
+
+ /**
+ * Sends a message to accessible clients that the text
+ * within a custom control has changed.
+ *
+ * @param type the type of change, one of <code>ACC.NOTIFY_TEXT_INSERT</code>
+ * or <code>ACC.NOTIFY_TEXT_DELETE</code>
+ * @param startIndex the text index within the control where the insertion or deletion begins
+ * @param length the non-negative length in characters of the insertion or deletion
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @see ACC#TEXT_INSERT
+ * @see ACC#TEXT_DELETE
+ *
+ * @since 3.0
+ */
+ public void textChanged (int type, int startIndex, int length) {
+ checkWidget();
+ int stringRef = stringToStringRef(OS.kAXValueChangedNotification);
+ OS.AXNotificationHIObjectNotify(stringRef, control.handle, 0);
+ OS.CFRelease(stringRef);
+ }
+
+ /**
+ * Sends a message to accessible clients that the text
+ * selection has changed within a custom control.
+ *
+ * @exception SWTException <ul>
+ * <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+ * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+ public void textSelectionChanged () {
+ checkWidget();
+ int stringRef = stringToStringRef(OS.kAXSelectedTextChangedNotification);
+ OS.AXNotificationHIObjectNotify(stringRef, control.handle, 0);
+ OS.CFRelease(stringRef);
+ }
+
+ int getChildIDFromEvent(int theEvent) {
+ int[] ref = new int[1];
+ OS.GetEventParameter (theEvent, OS.kEventParamAccessibleObject, OS.typeCFTypeRef, null, 4, null, ref);
+ return osToChildID(ref[0]);
+ }
+
+ int childIDToOs(int childID) {
+ if (childID == ACC.CHILDID_SELF) {
+ return axuielementref;
+ }
+ /* Check cache for childID, if found, return corresponding osChildID. */
+ int index;
+ for (index = 0; index < osChildIDCache.length; index += 2) {
+ if (childID == osChildIDCache [index]) {
+ return osChildIDCache [index + 1];
+ }
+ }
+ /* If childID not in cache, create osChildID, grow cache by 2,
+ * add childID/osChildID to cache, and return new osChildID. */
+ int osChildID = OS.AXUIElementCreateWithHIObjectAndIdentifier(control.handle, childID + 1);
+ int [] newCache = new int [osChildIDCache.length + 2];
+ System.arraycopy (osChildIDCache, 0, newCache, 0, osChildIDCache.length);
+ osChildIDCache = newCache;
+ osChildIDCache [index] = childID;
+ osChildIDCache [index + 1] = osChildID;
+ return osChildID;
+ }
+
+ int osToChildID(int osChildID) {
+ if (OS.CFEqual(osChildID, axuielementref)) {
+ return ACC.CHILDID_SELF;
+ }
+
+ /* osChildID is an AXUIElementRef containing the control handle and a long identifier. */
+ long[] childID = new long[1];
+ OS.AXUIElementGetIdentifier(osChildID, childID);
+ if (childID[0] == 0) {
+ return ACC.CHILDID_SELF;
+ }
+ return (int) childID[0] - 1;
+ }
+
+ int stateToOs(int state) {
+// int osState = 0;
+// if ((state & ACC.STATE_SELECTED) != 0) osState |= OS.;
+// return osState;
+ return state;
+ }
+
+ int osToState(int osState) {
+// int state = ACC.STATE_NORMAL;
+// if ((osState & OS.) != 0) state |= ACC.STATE_SELECTED;
+// return state;
+ return osState;
+ }
+
+ String roleToOs(int role) {
+ switch (role) {
+ case ACC.ROLE_CLIENT_AREA: return OS.kAXGroupRole;
+ case ACC.ROLE_WINDOW: return OS.kAXWindowRole;
+ case ACC.ROLE_MENUBAR: return OS.kAXMenuBarRole;
+ case ACC.ROLE_MENU: return OS.kAXMenuRole;
+ case ACC.ROLE_MENUITEM: return OS.kAXMenuItemRole;
+ case ACC.ROLE_SEPARATOR: return OS.kAXSplitterRole;
+ case ACC.ROLE_TOOLTIP: return OS.kAXHelpTagRole;
+ case ACC.ROLE_SCROLLBAR: return OS.kAXScrollBarRole;
+ case ACC.ROLE_DIALOG: return OS.kAXWindowRole + ':' + OS.kAXDialogSubrole;
+ case ACC.ROLE_LABEL: return OS.kAXStaticTextRole;
+ case ACC.ROLE_PUSHBUTTON: return OS.kAXButtonRole;
+ case ACC.ROLE_CHECKBUTTON: return OS.kAXCheckBoxRole;
+ case ACC.ROLE_RADIOBUTTON: return OS.kAXRadioButtonRole;
+ case ACC.ROLE_SPLITBUTTON: return OS.kAXMenuButtonRole;
+ case ACC.ROLE_COMBOBOX: return OS.kAXComboBoxRole;
+ case ACC.ROLE_TEXT: return (control.getStyle () & SWT.MULTI) != 0 ? OS.kAXTextAreaRole : OS.kAXTextFieldRole;
+ case ACC.ROLE_TOOLBAR: return OS.kAXToolbarRole;
+ case ACC.ROLE_LIST: return OS.kAXOutlineRole;
+ case ACC.ROLE_LISTITEM: return OS.kAXStaticTextRole;
+ case ACC.ROLE_TABLE: return OS.kAXTableRole;
+ case ACC.ROLE_TABLECELL: return OS.kAXRowRole + ':' + OS.kAXTableRowSubrole;
+ case ACC.ROLE_TABLECOLUMNHEADER: return OS.kAXButtonRole + ':' + OS.kAXSortButtonSubrole;
+ case ACC.ROLE_TABLEROWHEADER: return OS.kAXRowRole + ':' + OS.kAXTableRowSubrole;
+ case ACC.ROLE_TREE: return OS.kAXOutlineRole;
+ case ACC.ROLE_TREEITEM: return OS.kAXOutlineRole + ':' + OS.kAXOutlineRowSubrole;
+ case ACC.ROLE_TABFOLDER: return OS.kAXTabGroupRole;
+ case ACC.ROLE_TABITEM: return OS.kAXRadioButtonRole;
+ case ACC.ROLE_PROGRESSBAR: return OS.kAXProgressIndicatorRole;
+ case ACC.ROLE_SLIDER: return OS.kAXSliderRole;
+ case ACC.ROLE_LINK: return OS.kAXLinkRole;
+ }
+ return OS.kAXUnknownRole;
+ }
+
+ int osToRole(String osRole) {
+ if (osRole == null) return 0;
+ if (osRole.equals(OS.kAXWindowRole)) return ACC.ROLE_WINDOW;
+ if (osRole.equals(OS.kAXMenuBarRole)) return ACC.ROLE_MENUBAR;
+ if (osRole.equals(OS.kAXMenuRole)) return ACC.ROLE_MENU;
+ if (osRole.equals(OS.kAXMenuItemRole)) return ACC.ROLE_MENUITEM;
+ if (osRole.equals(OS.kAXSplitterRole)) return ACC.ROLE_SEPARATOR;
+ if (osRole.equals(OS.kAXHelpTagRole)) return ACC.ROLE_TOOLTIP;
+ if (osRole.equals(OS.kAXScrollBarRole)) return ACC.ROLE_SCROLLBAR;
+ if (osRole.equals(OS.kAXScrollAreaRole)) return ACC.ROLE_LIST;
+ if (osRole.equals(OS.kAXWindowRole + ':' + OS.kAXDialogSubrole)) return ACC.ROLE_DIALOG;
+ if (osRole.equals(OS.kAXWindowRole + ':' + OS.kAXSystemDialogSubrole)) return ACC.ROLE_DIALOG;
+ if (osRole.equals(OS.kAXStaticTextRole)) return ACC.ROLE_LABEL;
+ if (osRole.equals(OS.kAXButtonRole)) return ACC.ROLE_PUSHBUTTON;
+ if (osRole.equals(OS.kAXCheckBoxRole)) return ACC.ROLE_CHECKBUTTON;
+ if (osRole.equals(OS.kAXRadioButtonRole)) return ACC.ROLE_RADIOBUTTON;
+ if (osRole.equals(OS.kAXMenuButtonRole)) return ACC.ROLE_SPLITBUTTON;
+ if (osRole.equals(OS.kAXComboBoxRole)) return ACC.ROLE_COMBOBOX;
+ if (osRole.equals(OS.kAXTextFieldRole)) return ACC.ROLE_TEXT;
+ if (osRole.equals(OS.kAXTextAreaRole)) return ACC.ROLE_TEXT;
+ if (osRole.equals(OS.kAXToolbarRole)) return ACC.ROLE_TOOLBAR;
+ if (osRole.equals(OS.kAXListRole)) return ACC.ROLE_LIST;
+ if (osRole.equals(OS.kAXTableRole)) return ACC.ROLE_TABLE;
+ if (osRole.equals(OS.kAXColumnRole)) return ACC.ROLE_TABLECOLUMNHEADER;
+ if (osRole.equals(OS.kAXButtonRole + ':' + OS.kAXSortButtonSubrole)) return ACC.ROLE_TABLECOLUMNHEADER;
+ if (osRole.equals(OS.kAXRowRole + ':' + OS.kAXTableRowSubrole)) return ACC.ROLE_TABLEROWHEADER;
+ if (osRole.equals(OS.kAXOutlineRole)) return ACC.ROLE_TREE;
+ if (osRole.equals(OS.kAXOutlineRole + ':' + OS.kAXOutlineRowSubrole)) return ACC.ROLE_TREEITEM;
+ if (osRole.equals(OS.kAXTabGroupRole)) return ACC.ROLE_TABFOLDER;
+ if (osRole.equals(OS.kAXProgressIndicatorRole)) return ACC.ROLE_PROGRESSBAR;
+ if (osRole.equals(OS.kAXSliderRole)) return ACC.ROLE_SLIDER;
+ if (osRole.equals(OS.kAXLinkRole)) return ACC.ROLE_LINK;
+ return ACC.ROLE_CLIENT_AREA;
+ }
+
+ /* Return a CFStringRef representing the given java String.
+ * Note that the caller is responsible for calling OS.CFRelease
+ * when they are done with the stringRef.
+ */
+ int stringToStringRef(String string) {
+ char [] buffer = new char [string.length ()];
+ string.getChars (0, buffer.length, buffer, 0);
+ return OS.CFStringCreateWithCharacters (OS.kCFAllocatorDefault, buffer, buffer.length);
+ }
+
+ /* Return a Java String representing the given CFStringRef.
+ * Note that this method does not call OS.CFRelease(stringRef).
+ */
+ String stringRefToString(int stringRef) {
+ if (stringRef == 0) return "";
+ int length = OS.CFStringGetLength (stringRef);
+ char [] buffer= new char [length];
+ CFRange range = new CFRange ();
+ range.length = length;
+ OS.CFStringGetCharacters (stringRef, range, buffer);
+ return new String(buffer);
+ }
+
+ /* checkWidget was copied from Widget, and rewritten to work in this package */
+ void checkWidget () {
+ if (!isValidThread ()) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS);
+ if (control.isDisposed ()) SWT.error (SWT.ERROR_WIDGET_DISPOSED);
+ }
+
+ /* isValidThread was copied from Widget, and rewritten to work in this package */
+ boolean isValidThread () {
+ return control.getDisplay ().getThread () == Thread.currentThread ();
+ }
+}

Back to the top