1 /******************************************************************************* 2 * Copyright (c) 2000, 2012 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.swt.accessibility; 15 16 17 import org.eclipse.swt.*; 18 import org.eclipse.swt.internal.*; 19 import org.eclipse.swt.internal.cocoa.*; 20 21 @SuppressWarnings("rawtypes") 22 class SWTAccessibleDelegate extends NSObject { 23 24 /** 25 * Accessible Key: The string constant for looking up the accessible 26 * for a control using <code>getData(String)</code>. When an accessible 27 * is created for a control, it is stored as a property in the control 28 * using <code>setData(String, Object)</code>. 29 */ 30 static final String ACCESSIBLE_KEY = "Accessible"; //$NON-NLS-1$ 31 static final byte[] SWT_OBJECT = {'S', 'W', 'T', '_', 'O', 'B', 'J', 'E', 'C', 'T', '\0'}; 32 33 static Callback accessible2Args, accessible3Args, accessible4Args; 34 static long proc2Args, proc3Args, proc4Args; 35 36 Accessible accessible; 37 long delegateJniRef; 38 int childID; 39 40 NSArray attributeNames = null; 41 NSArray parameterizedAttributeNames = null; 42 NSArray actionNames = null; 43 44 static { 45 Class clazz = SWTAccessibleDelegate.class; 46 47 accessible2Args = new Callback(clazz, "accessibleProc", 2); 48 proc2Args = accessible2Args.getAddress(); 49 50 accessible3Args = new Callback(clazz, "accessibleProc", 3); 51 proc3Args = accessible3Args.getAddress(); 52 53 accessible4Args = new Callback(clazz, "accessibleProc", 4); 54 proc4Args = accessible3Args.getAddress(); 55 56 // Accessible custom controls need to implement the NSAccessibility protocol. To do that, 57 // we dynamically add the methods to the control's class that are required 58 // by NSAccessibility. Then, when external assistive technology services are used, 59 // those methods get called to provide the needed information. 60 61 String className = "SWTAccessibleDelegate"; 62 63 // TODO: These should either move out of Display or be accessible to this class. 64 byte[] types = {'*','\0'}; 65 int size = C.PTR_SIZEOF, align = C.PTR_SIZEOF == 4 ? 2 : 3; 66 67 long cls = OS.objc_allocateClassPair(OS.class_NSObject, className, 0); OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types)68 OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types); 69 70 // Add the NSAccessibility overrides OS.class_addMethod(cls, OS.sel_accessibilityActionNames, proc2Args, R)71 OS.class_addMethod(cls, OS.sel_accessibilityActionNames, proc2Args, "@:"); OS.class_addMethod(cls, OS.sel_accessibilityAttributeNames, proc2Args, R)72 OS.class_addMethod(cls, OS.sel_accessibilityAttributeNames, proc2Args, "@:"); OS.class_addMethod(cls, OS.sel_accessibilityParameterizedAttributeNames, proc2Args, R)73 OS.class_addMethod(cls, OS.sel_accessibilityParameterizedAttributeNames, proc2Args, "@:"); OS.class_addMethod(cls, OS.sel_accessibilityIsIgnored, proc2Args, R)74 OS.class_addMethod(cls, OS.sel_accessibilityIsIgnored, proc2Args, "@:"); OS.class_addMethod(cls, OS.sel_accessibilityFocusedUIElement, proc2Args, R)75 OS.class_addMethod(cls, OS.sel_accessibilityFocusedUIElement, proc2Args, "@:"); 76 OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_, proc3Args, R)77 OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_, proc3Args, "@:@"); OS.class_addMethod(cls, OS.sel_accessibilityHitTest_, proc3Args, R)78 OS.class_addMethod(cls, OS.sel_accessibilityHitTest_, proc3Args, "@:{NSPoint}"); OS.class_addMethod(cls, OS.sel_accessibilityIsAttributeSettable_, proc3Args, R)79 OS.class_addMethod(cls, OS.sel_accessibilityIsAttributeSettable_, proc3Args, "@:@"); OS.class_addMethod(cls, OS.sel_accessibilityActionDescription_, proc3Args, R)80 OS.class_addMethod(cls, OS.sel_accessibilityActionDescription_, proc3Args, "@:@"); OS.class_addMethod(cls, OS.sel_accessibilityPerformAction_, proc3Args, R)81 OS.class_addMethod(cls, OS.sel_accessibilityPerformAction_, proc3Args, "@:@"); 82 OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_forParameter_, proc4Args, R)83 OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_forParameter_, proc4Args, "@:@@"); OS.class_addMethod(cls, OS.sel_accessibilitySetValue_forAttribute_, proc4Args, R)84 OS.class_addMethod(cls, OS.sel_accessibilitySetValue_forAttribute_, proc4Args, "@:@@"); 85 86 OS.objc_registerClassPair(cls); 87 } 88 89 SWTAccessibleDelegate(Accessible accessible, int childID)90 public SWTAccessibleDelegate(Accessible accessible, int childID) { 91 super(0); 92 this.accessible = accessible; 93 this.childID = childID; 94 alloc().init(); 95 delegateJniRef = OS.NewGlobalRef(this); 96 if (delegateJniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES); 97 OS.object_setInstanceVariable(this.id, SWT_OBJECT, delegateJniRef); 98 } 99 accessibilityActionNames()100 NSArray accessibilityActionNames() { 101 102 if (actionNames != null) 103 return retainedAutoreleased(actionNames); 104 105 actionNames = accessible.internal_accessibilityActionNames(childID); 106 actionNames.retain(); 107 return retainedAutoreleased(actionNames); 108 } 109 accessibilityAttributeNames()110 NSArray accessibilityAttributeNames() { 111 112 if (attributeNames != null) 113 return retainedAutoreleased(attributeNames); 114 115 attributeNames = accessible.internal_accessibilityAttributeNames(childID); 116 attributeNames.retain(); 117 return retainedAutoreleased(attributeNames); 118 } 119 accessibilityAttributeValue(NSString attribute)120 id accessibilityAttributeValue(NSString attribute) { 121 return accessible.internal_accessibilityAttributeValue(attribute, childID); 122 } 123 124 // parameterized attribute methods accessibilityParameterizedAttributeNames()125 NSArray accessibilityParameterizedAttributeNames() { 126 127 if (parameterizedAttributeNames != null) 128 return retainedAutoreleased(parameterizedAttributeNames); 129 130 parameterizedAttributeNames = accessible.internal_accessibilityParameterizedAttributeNames(childID); 131 parameterizedAttributeNames.retain(); 132 return retainedAutoreleased(parameterizedAttributeNames); 133 } 134 accessibilityAttributeValue_forParameter(NSString attribute, id parameter)135 id accessibilityAttributeValue_forParameter(NSString attribute, id parameter) { 136 return accessible.internal_accessibilityAttributeValue_forParameter(attribute, parameter, childID); 137 } 138 139 // Return YES if the UIElement doesn't show up to the outside world - i.e. its parent should return the UIElement's children as its own - cutting the UIElement out. E.g. NSControls are ignored when they are single-celled. accessibilityIsIgnored()140 boolean accessibilityIsIgnored() { 141 return accessible.internal_accessibilityIsIgnored(childID); 142 } 143 accessibilityIsAttributeSettable(NSString attribute)144 boolean accessibilityIsAttributeSettable(NSString attribute) { 145 return accessible.internal_accessibilityIsAttributeSettable(attribute, childID); 146 } 147 148 // Returns the deepest descendant of the UIElement hierarchy that contains the point. You can assume the point has already been determined to lie within the receiver. Override this method to do deeper hit testing within a UIElement - e.g. a NSMatrix would test its cells. The point is bottom-left relative screen coordinates. accessibilityHitTest(NSPoint point)149 id accessibilityHitTest(NSPoint point) { 150 return accessible.internal_accessibilityHitTest(point, childID); 151 } 152 153 // Returns the UI Element that has the focus. You can assume that the search for the focus has already been narrowed down to the reciever. Override this method to do a deeper search with a UIElement - e.g. a NSMatrix would determine if one of its cells has the focus. accessibilityFocusedUIElement()154 id accessibilityFocusedUIElement() { 155 return accessible.internal_accessibilityFocusedUIElement(childID); 156 } 157 accessibilityPerformAction(NSString action)158 void accessibilityPerformAction(NSString action) { 159 accessible.internal_accessibilityPerformAction(action, childID); 160 } 161 accessibilityActionDescription(NSString action)162 id accessibilityActionDescription(NSString action) { 163 return accessible.internal_accessibilityActionDescription(action, childID); 164 } 165 accessibilitySetValue_forAttribute(id value, NSString attribute)166 void accessibilitySetValue_forAttribute(id value, NSString attribute) { 167 accessible.internal_accessibilitySetValue_forAttribute(value, attribute, childID); 168 } 169 retainedAutoreleased(NSArray inObject)170 static NSArray retainedAutoreleased(NSArray inObject) { 171 id temp = inObject.retain(); 172 id temp2 = new NSObject(temp.id).autorelease(); 173 return new NSArray(temp2.id); 174 } 175 accessibleProc(long id, long sel)176 static long accessibleProc(long id, long sel) { 177 SWTAccessibleDelegate swtAcc = getAccessibleDelegate(id); 178 if (swtAcc == null) return 0; 179 180 if (sel == OS.sel_accessibilityAttributeNames) { 181 NSArray retObject = swtAcc.accessibilityAttributeNames(); 182 return (retObject == null ? 0 : retObject.id); 183 } else if (sel == OS.sel_accessibilityActionNames) { 184 NSArray retObject = swtAcc.accessibilityActionNames(); 185 return (retObject == null ? 0 : retObject.id); 186 } else if (sel == OS.sel_accessibilityParameterizedAttributeNames) { 187 NSArray retObject = swtAcc.accessibilityParameterizedAttributeNames(); 188 return (retObject == null ? 0 : retObject.id); 189 } else if (sel == OS.sel_accessibilityIsIgnored) { 190 boolean retVal = swtAcc.accessibilityIsIgnored(); 191 return (retVal ? 1 : 0); 192 } else if (sel == OS.sel_accessibilityFocusedUIElement) { 193 id retObject = swtAcc.accessibilityFocusedUIElement(); 194 return (retObject == null ? 0 : retObject.id); 195 } 196 197 return 0; 198 } 199 accessibleProc(long id, long sel, long arg0)200 static long accessibleProc(long id, long sel, long arg0) { 201 SWTAccessibleDelegate swtAcc = getAccessibleDelegate(id); 202 if (swtAcc == null) return 0; 203 204 if (sel == OS.sel_accessibilityAttributeValue_) { 205 NSString attribute = new NSString(arg0); 206 id retObject = swtAcc.accessibilityAttributeValue(attribute); 207 return (retObject == null ? 0 : retObject.id); 208 } else if (sel == OS.sel_accessibilityHitTest_) { 209 NSPoint point= new NSPoint(); 210 OS.memmove(point, arg0, NSPoint.sizeof); 211 id retObject = swtAcc.accessibilityHitTest(point); 212 return (retObject == null ? 0 : retObject.id); 213 } else if (sel == OS.sel_accessibilityIsAttributeSettable_) { 214 NSString attribute = new NSString(arg0); 215 return (swtAcc.accessibilityIsAttributeSettable(attribute) ? 1 : 0); 216 } else if (sel == OS.sel_accessibilityActionDescription_) { 217 NSString action = new NSString(arg0); 218 id retObject = swtAcc.accessibilityActionDescription(action); 219 return (retObject == null ? 0 : retObject.id); 220 } else if (sel == OS.sel_accessibilityPerformAction_) { 221 NSString action = new NSString(arg0); 222 swtAcc.accessibilityPerformAction(action); 223 } 224 225 return 0; 226 } 227 accessibleProc(long id, long sel, long arg0, long arg1)228 static long accessibleProc(long id, long sel, long arg0, long arg1) { 229 SWTAccessibleDelegate swtAcc = getAccessibleDelegate(id); 230 if (swtAcc == null) return 0; 231 232 if (sel == OS.sel_accessibilityAttributeValue_forParameter_) { 233 NSString attribute = new NSString(arg0); 234 id parameter = new id(arg1); 235 id retObject = swtAcc.accessibilityAttributeValue_forParameter(attribute, parameter); 236 return (retObject == null ? 0 : retObject.id); 237 } else if (sel == OS.sel_accessibilitySetValue_forAttribute_) { 238 id value = new id(arg0); 239 NSString attribute = new NSString(arg1); 240 swtAcc.accessibilitySetValue_forAttribute(value, attribute); 241 } 242 243 return 0; 244 } 245 getAccessibleDelegate(long id)246 static SWTAccessibleDelegate getAccessibleDelegate(long id) { 247 if (id == 0) return null; 248 long [] jniRef = new long [1]; 249 OS.object_getInstanceVariable(id, SWT_OBJECT, jniRef); 250 if (jniRef[0] == 0) return null; 251 return (SWTAccessibleDelegate)OS.JNIGetObject(jniRef[0]); 252 } 253 internal_dispose_SWTAccessibleDelegate()254 public void internal_dispose_SWTAccessibleDelegate() { 255 if (actionNames != null) actionNames.release(); 256 actionNames = null; 257 if (attributeNames != null) attributeNames.release(); 258 attributeNames = null; 259 if (parameterizedAttributeNames != null) parameterizedAttributeNames.release(); 260 parameterizedAttributeNames = null; 261 262 if (delegateJniRef != 0) OS.DeleteGlobalRef(delegateJniRef); 263 delegateJniRef = 0; 264 OS.object_setInstanceVariable(this.id, SWT_OBJECT, 0); 265 } 266 267 }