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 }