| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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 (); |
| } |
| } |