Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7e8b9481d55985866c1af03e88a3e986b861a9c0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.accessibility;


import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.cocoa.*;

@SuppressWarnings("rawtypes")
class SWTAccessibleDelegate extends NSObject {

	/**
	 * Accessible Key: The string constant for looking up the accessible
	 * for a control using <code>getData(String)</code>. When an accessible
	 * is created for a control, it is stored as a property in the control
	 * using <code>setData(String, Object)</code>.
	 */
	static final String ACCESSIBLE_KEY = "Accessible"; //$NON-NLS-1$
	static final byte[] SWT_OBJECT = {'S', 'W', 'T', '_', 'O', 'B', 'J', 'E', 'C', 'T', '\0'};

	static Callback accessible2Args, accessible3Args, accessible4Args;
	static long proc2Args, proc3Args, proc4Args;

	Accessible accessible;
	long delegateJniRef;
	int childID;

	NSArray attributeNames = null;
	NSArray parameterizedAttributeNames = null;
	NSArray actionNames = null;

	static {
		Class clazz = SWTAccessibleDelegate.class;

		accessible2Args = new Callback(clazz, "accessibleProc", 2);
		proc2Args = accessible2Args.getAddress();
		if (proc2Args == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);

		accessible3Args = new Callback(clazz, "accessibleProc", 3);
		proc3Args = accessible3Args.getAddress();
		if (proc3Args == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);

		accessible4Args = new Callback(clazz, "accessibleProc", 4);
		proc4Args = accessible3Args.getAddress();
		if (proc4Args == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);

		// Accessible custom controls need to implement the NSAccessibility protocol. To do that,
		// we dynamically add the methods to the control's class that are required
		// by NSAccessibility. Then, when external assistive technology services are used,
		// those methods get called to provide the needed information.

		String className = "SWTAccessibleDelegate";

		// TODO: These should either move out of Display or be accessible to this class.
		byte[] types = {'*','\0'};
		int size = C.PTR_SIZEOF, align = C.PTR_SIZEOF == 4 ? 2 : 3;

		long cls = OS.objc_allocateClassPair(OS.class_NSObject, className, 0);
		OS.class_addIvar(cls, SWT_OBJECT, size, (byte)align, types);

		// Add the NSAccessibility overrides
		OS.class_addMethod(cls, OS.sel_accessibilityActionNames, proc2Args, "@:");
		OS.class_addMethod(cls, OS.sel_accessibilityAttributeNames, proc2Args, "@:");
		OS.class_addMethod(cls, OS.sel_accessibilityParameterizedAttributeNames, proc2Args, "@:");
		OS.class_addMethod(cls, OS.sel_accessibilityIsIgnored, proc2Args, "@:");
		OS.class_addMethod(cls, OS.sel_accessibilityFocusedUIElement, proc2Args, "@:");

		OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_, proc3Args, "@:@");
		OS.class_addMethod(cls, OS.sel_accessibilityHitTest_, proc3Args, "@:{NSPoint}");
		OS.class_addMethod(cls, OS.sel_accessibilityIsAttributeSettable_, proc3Args, "@:@");
		OS.class_addMethod(cls, OS.sel_accessibilityActionDescription_, proc3Args, "@:@");
		OS.class_addMethod(cls, OS.sel_accessibilityPerformAction_, proc3Args, "@:@");

		OS.class_addMethod(cls, OS.sel_accessibilityAttributeValue_forParameter_, proc4Args, "@:@@");
		OS.class_addMethod(cls, OS.sel_accessibilitySetValue_forAttribute_, proc4Args, "@:@@");

		OS.objc_registerClassPair(cls);
	}


	public SWTAccessibleDelegate(Accessible accessible, int childID) {
		super(0);
		this.accessible = accessible;
		this.childID = childID;
		alloc().init();
		delegateJniRef = OS.NewGlobalRef(this);
		if (delegateJniRef == 0) SWT.error(SWT.ERROR_NO_HANDLES);
		OS.object_setInstanceVariable(this.id, SWT_OBJECT, delegateJniRef);
	}

	NSArray accessibilityActionNames() {

		if (actionNames != null)
			return retainedAutoreleased(actionNames);

		actionNames = accessible.internal_accessibilityActionNames(childID);
		actionNames.retain();
		return retainedAutoreleased(actionNames);
	}

	NSArray accessibilityAttributeNames() {

		if (attributeNames != null)
			return retainedAutoreleased(attributeNames);

		attributeNames = accessible.internal_accessibilityAttributeNames(childID);
		attributeNames.retain();
		return retainedAutoreleased(attributeNames);
	}

	id accessibilityAttributeValue(NSString attribute) {
		return accessible.internal_accessibilityAttributeValue(attribute, childID);
	}

	// parameterized attribute methods
	NSArray accessibilityParameterizedAttributeNames() {

		if (parameterizedAttributeNames != null)
			return retainedAutoreleased(parameterizedAttributeNames);

		parameterizedAttributeNames = accessible.internal_accessibilityParameterizedAttributeNames(childID);
		parameterizedAttributeNames.retain();
		return retainedAutoreleased(parameterizedAttributeNames);
	}

	id accessibilityAttributeValue_forParameter(NSString attribute, id parameter) {
		return accessible.internal_accessibilityAttributeValue_forParameter(attribute, parameter, childID);
	}

	// 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.
	boolean accessibilityIsIgnored() {
		return accessible.internal_accessibilityIsIgnored(childID);
	}

	boolean accessibilityIsAttributeSettable(NSString attribute) {
		return accessible.internal_accessibilityIsAttributeSettable(attribute, childID);
	}

	// 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.
	id accessibilityHitTest(NSPoint point) {
		return accessible.internal_accessibilityHitTest(point, childID);
	}

	// 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.
	id accessibilityFocusedUIElement() {
		return accessible.internal_accessibilityFocusedUIElement(childID);
	}

	void accessibilityPerformAction(NSString action) {
		accessible.internal_accessibilityPerformAction(action, childID);
	}

	id accessibilityActionDescription(NSString action) {
		return accessible.internal_accessibilityActionDescription(action, childID);
	}

	void accessibilitySetValue_forAttribute(id value, NSString attribute) {
		accessible.internal_accessibilitySetValue_forAttribute(value, attribute, childID);
	}

	static NSArray retainedAutoreleased(NSArray inObject) {
		id temp = inObject.retain();
		id temp2 = new NSObject(temp.id).autorelease();
		return new NSArray(temp2.id);
	}

	static long accessibleProc(long id, long sel) {
		SWTAccessibleDelegate swtAcc = getAccessibleDelegate(id);
		if (swtAcc == null) return 0;

		if (sel == OS.sel_accessibilityAttributeNames) {
			NSArray retObject = swtAcc.accessibilityAttributeNames();
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilityActionNames) {
			NSArray retObject = swtAcc.accessibilityActionNames();
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilityParameterizedAttributeNames) {
			NSArray retObject = swtAcc.accessibilityParameterizedAttributeNames();
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilityIsIgnored) {
			boolean retVal = swtAcc.accessibilityIsIgnored();
			return (retVal ? 1 : 0);
		} else if (sel == OS.sel_accessibilityFocusedUIElement) {
			id retObject = swtAcc.accessibilityFocusedUIElement();
			return (retObject == null ? 0 : retObject.id);
		}

		return 0;
	}

	static long accessibleProc(long id, long sel, long arg0) {
		SWTAccessibleDelegate swtAcc = getAccessibleDelegate(id);
		if (swtAcc == null) return 0;

		if (sel == OS.sel_accessibilityAttributeValue_) {
			NSString attribute = new NSString(arg0);
			id retObject = swtAcc.accessibilityAttributeValue(attribute);
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilityHitTest_) {
			NSPoint point= new NSPoint();
			OS.memmove(point, arg0, NSPoint.sizeof);
			id retObject = swtAcc.accessibilityHitTest(point);
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilityIsAttributeSettable_) {
			NSString attribute = new NSString(arg0);
			return (swtAcc.accessibilityIsAttributeSettable(attribute) ? 1 : 0);
		} else if (sel == OS.sel_accessibilityActionDescription_) {
			NSString action = new NSString(arg0);
			id retObject = swtAcc.accessibilityActionDescription(action);
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilityPerformAction_) {
			NSString action = new NSString(arg0);
			swtAcc.accessibilityPerformAction(action);
		}

		return 0;
	}

	static long accessibleProc(long id, long sel, long arg0, long arg1) {
		SWTAccessibleDelegate swtAcc = getAccessibleDelegate(id);
		if (swtAcc == null) return 0;

		if (sel == OS.sel_accessibilityAttributeValue_forParameter_) {
			NSString attribute = new NSString(arg0);
			id parameter = new id(arg1);
			id retObject = swtAcc.accessibilityAttributeValue_forParameter(attribute, parameter);
			return (retObject == null ? 0 : retObject.id);
		} else if (sel == OS.sel_accessibilitySetValue_forAttribute_) {
			id value = new id(arg0);
			NSString attribute = new NSString(arg1);
			swtAcc.accessibilitySetValue_forAttribute(value, attribute);
		}

		return 0;
	}

	static SWTAccessibleDelegate getAccessibleDelegate(long id) {
		if (id == 0) return null;
		long [] jniRef = new long [1];
		OS.object_getInstanceVariable(id, SWT_OBJECT, jniRef);
		if (jniRef[0] == 0) return null;
		return (SWTAccessibleDelegate)OS.JNIGetObject(jniRef[0]);
	}

	public void internal_dispose_SWTAccessibleDelegate() {
		if (actionNames != null) actionNames.release();
		actionNames = null;
		if (attributeNames != null) attributeNames.release();
		attributeNames = null;
		if (parameterizedAttributeNames != null) parameterizedAttributeNames.release();
		parameterizedAttributeNames = null;

		if (delegateJniRef != 0) OS.DeleteGlobalRef(delegateJniRef);
		delegateJniRef = 0;
		OS.object_setInstanceVariable(this.id, SWT_OBJECT, 0);
	}

}

Back to the top