1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.accessibility.Accessible;
14 
15 import java.lang.all;
16 
17 import org.eclipse.swt.accessibility.AccessibleListener;
18 import org.eclipse.swt.accessibility.AccessibleTextListener;
19 import org.eclipse.swt.accessibility.AccessibleControlListener;
20 import org.eclipse.swt.accessibility.AccessibleControlListener;
21 import org.eclipse.swt.accessibility.AccessibleFactory;
22 import org.eclipse.swt.accessibility.AccessibleObject;
23 import org.eclipse.swt.SWT;
24 //import org.eclipse.swt.events.*;
25 import org.eclipse.swt.internal.gtk.OS;
26 import org.eclipse.swt.widgets.Control;
27 import org.eclipse.swt.events.DisposeListener;
28 import org.eclipse.swt.events.DisposeEvent;
29 
30 import java.lang.Thread;
31 import java.util.Vector;
32 
33 /**
34  * Instances of this class provide a bridge between application
35  * code and assistive technology clients. Many platforms provide
36  * default accessible behavior for most widgets, and this class
37  * allows that default behavior to be overridden. Applications
38  * can get the default Accessible object for a control by sending
39  * it <code>getAccessible</code>, and then add an accessible listener
40  * to override simple items like the name and help string, or they
41  * can add an accessible control listener to override complex items.
42  * As a rule of thumb, an application would only want to use the
43  * accessible control listener to implement accessibility for a
44  * custom control.
45  *
46  * @see Control#getAccessible
47  * @see AccessibleListener
48  * @see AccessibleEvent
49  * @see AccessibleControlListener
50  * @see AccessibleControlEvent
51  * @see <a href="http://www.eclipse.org/swt/snippets/#accessibility">Accessibility snippets</a>
52  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
53  *
54  * @since 2.0
55  */
56 public class Accessible {
57     Vector accessibleListeners;
58     Vector controlListeners;
59     Vector textListeners;
60     AccessibleObject accessibleObject;
61     Control control;
62 
63     this (Control control) {
64         accessibleListeners = new Vector();
65         controlListeners = new Vector();
66         textListeners = new Vector();
67         this.control = control;
68         AccessibleFactory.registerAccessible (this);
69         control.addDisposeListener (new class () DisposeListener {
70             public void widgetDisposed (DisposeEvent e) {
71                 release ();
72             }
73         });
74     }
75 
76     /**
77      * Adds the listener to the collection of listeners who will
78      * be notified when an accessible client asks for certain strings,
79      * such as name, description, help, or keyboard shortcut. The
80      * listener is notified by sending it one of the messages defined
81      * in the <code>AccessibleListener</code> interface.
82      *
83      * @param listener the listener that should be notified when the receiver
84      * is asked for a name, description, help, or keyboard shortcut string
85      *
86      * @exception IllegalArgumentException <ul>
87      *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
88      * </ul>
89      * @exception SWTException <ul>
90      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
91      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
92      * </ul>
93      *
94      * @see AccessibleListener
95      * @see #removeAccessibleListener
96      */
97     public void addAccessibleListener (AccessibleListener listener) {
98         checkWidget ();
99         if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
100         accessibleListeners.addElement(cast(Object)listener);
101     }
102 
103     /**
104      * Adds the listener to the collection of listeners who will
105      * be notified when an accessible client asks for custom control
106      * specific information. The listener is notified by sending it
107      * one of the messages defined in the <code>AccessibleControlListener</code>
108      * interface.
109      *
110      * @param listener the listener that should be notified when the receiver
111      * is asked for custom control specific information
112      *
113      * @exception IllegalArgumentException <ul>
114      *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
115      * </ul>
116      * @exception SWTException <ul>
117      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
118      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
119      * </ul>
120      *
121      * @see AccessibleControlListener
122      * @see #removeAccessibleControlListener
123      */
124     public void addAccessibleControlListener (AccessibleControlListener listener) {
125         checkWidget ();
126         if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
127         controlListeners.addElement(cast(Object)listener);
128     }
129 
130     /**
131      * Adds the listener to the collection of listeners who will
132      * be notified when an accessible client asks for custom text control
133      * specific information. The listener is notified by sending it
134      * one of the messages defined in the <code>AccessibleTextListener</code>
135      * interface.
136      *
137      * @param listener the listener that should be notified when the receiver
138      * is asked for custom text control specific information
139      *
140      * @exception IllegalArgumentException <ul>
141      *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
142      * </ul>
143      * @exception SWTException <ul>
144      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
145      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
146      * </ul>
147      *
148      * @see AccessibleTextListener
149      * @see #removeAccessibleTextListener
150      *
151      * @since 3.0
152      */
153     public void addAccessibleTextListener (AccessibleTextListener listener) {
154         checkWidget ();
155         if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
156         textListeners.addElement(cast(Object)listener);
157     }
158 
159     /**
160      * Returns the control for this Accessible object.
161      *
162      * @return the receiver's control
163      * @since 3.0
164      */
165     public Control getControl() {
166         return control;
167     }
168 
169     /* checkWidget was copied from Widget, and rewritten to work in this package */
170     void checkWidget () {
171         if (!isValidThread ()) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS);
172         if (control.isDisposed ()) SWT.error (SWT.ERROR_WIDGET_DISPOSED);
173     }
174 
175     AccessibleListener[] getAccessibleListeners () {
176         if (accessibleListeners is null ) return null;
177         AccessibleListener[] result = new AccessibleListener [accessibleListeners.size ()];
178         accessibleListeners.copyInto ( cast(void*[])result);
179         return result;
180     }
181 
182     GtkWidget* getControlHandle () {
183         return control.handle;
184     }
185 
186     AccessibleControlListener[] getControlListeners () {
187         if (controlListeners is null) return null;
188         AccessibleControlListener[] result = new AccessibleControlListener [controlListeners.size ()];
189         controlListeners.copyInto ( cast(void*[])result);
190         return result;
191     }
192 
193     AccessibleTextListener[] getTextListeners () {
194         if (textListeners is null) return null;
195         AccessibleTextListener[] result = new AccessibleTextListener [textListeners.size ()];
196         textListeners.copyInto ( cast(void*[])result);
197         return result;
198     }
199 
200     /**
201      * Invokes platform specific functionality to allocate a new accessible object.
202      * <p>
203      * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
204      * API for <code>Accessible</code>. It is marked public only so that it
205      * can be shared within the packages provided by SWT. It is not
206      * available on all platforms, and should never be called from
207      * application code.
208      * </p>
209      *
210      * @param control the control to get the accessible object for
211      * @return the platform specific accessible object
212      */
213     public static Accessible internal_new_Accessible (Control control) {
214         return new Accessible (control);
215     }
216 
217     /* isValidThread was copied from Widget, and rewritten to work in this package */
218     bool isValidThread () {
219         return control.getDisplay ().getThread () is Thread.currentThread ();
220     }
221 
222     void release () {
223         AccessibleFactory.unregisterAccessible (/*Accessible.*/this);
224         if (accessibleObject !is null) {
225             accessibleObject.release ();
226             accessibleObject = null;
227         }
228         accessibleListeners = null;
229         controlListeners = null;
230         textListeners = null;
231     }
232     /**
233      * Removes the listener from the collection of listeners who will
234      * be notified when an accessible client asks for custom control
235      * specific information.
236      *
237      * @param listener the listener that should no longer be notified when the receiver
238      * is asked for custom control specific information
239      *
240      * @exception IllegalArgumentException <ul>
241      *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
242      * </ul>
243      * @exception SWTException <ul>
244      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
245      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
246      * </ul>
247      *
248      * @see AccessibleControlListener
249      * @see #addAccessibleControlListener
250      */
251     public void removeAccessibleControlListener (AccessibleControlListener listener) {
252         checkWidget ();
253         if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
254         controlListeners.removeElement( cast(Object)listener );
255     }
256 
257     /**
258      * Removes the listener from the collection of listeners who will
259      * be notified when an accessible client asks for certain strings,
260      * such as name, description, help, or keyboard shortcut.
261      *
262      * @param listener the listener that should no longer be notified when the receiver
263      * is asked for a name, description, help, or keyboard shortcut string
264      *
265      * @exception IllegalArgumentException <ul>
266      *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
267      * </ul>
268      * @exception SWTException <ul>
269      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
270      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
271      * </ul>
272      *
273      * @see AccessibleListener
274      * @see #addAccessibleListener
275      */
276     public void removeAccessibleListener (AccessibleListener listener) {
277         checkWidget ();
278         if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
279         accessibleListeners.removeElement( cast(Object)listener);
280     }
281 
282     /**
283      * Removes the listener from the collection of listeners who will
284      * be notified when an accessible client asks for custom text control
285      * specific information.
286      *
287      * @param listener the listener that should no longer be notified when the receiver
288      * is asked for custom text control specific information
289      *
290      * @exception IllegalArgumentException <ul>
291      *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
292      * </ul>
293      * @exception SWTException <ul>
294      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
295      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
296      * </ul>
297      *
298      * @see AccessibleTextListener
299      * @see #addAccessibleTextListener
300      *
301      * @since 3.0
302      */
303     public void removeAccessibleTextListener (AccessibleTextListener listener) {
304         checkWidget ();
305         if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
306         textListeners.removeElement( cast(Object)listener );
307     }
308 
309     /**
310      * Sends a message to accessible clients that the child selection
311      * within a custom container control has changed.
312      *
313      * @exception SWTException <ul>
314      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
315      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
316      * </ul>
317      *
318      * @since 3.0
319      */
320     public void selectionChanged () {
321         checkWidget ();
322         if (accessibleObject !is null) {
323             accessibleObject.selectionChanged ();
324         }
325     }
326 
327     /**
328      * Sends a message to accessible clients indicating that the focus
329      * has changed within a custom control.
330      *
331      * @param childID an identifier specifying a child of the control
332      *
333      * @exception SWTException <ul>
334      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
335      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
336      * </ul>
337      */
338     public void setFocus (int childID) {
339         checkWidget ();
340         if (accessibleObject !is null) {
341             accessibleObject.setFocus (childID);
342         }
343     }
344 
345     /**
346      * Sends a message to accessible clients that the text
347      * caret has moved within a custom control.
348      *
349      * @param index the new caret index within the control
350      *
351      * @exception SWTException <ul>
352      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
353      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
354      * </ul>
355      *
356      * @since 3.0
357      */
358     public void textCaretMoved (int index) {
359         checkWidget ();
360         if (accessibleObject !is null) {
361             accessibleObject.textCaretMoved (index);
362         }
363     }
364 
365     /**
366      * Sends a message to accessible clients that the text
367      * within a custom control has changed.
368      *
369      * @param type the type of change, one of <code>ACC.NOTIFY_TEXT_INSERT</code>
370      * or <code>ACC.NOTIFY_TEXT_DELETE</code>
371      * @param startIndex the text index within the control where the insertion or deletion begins
372      * @param length the non-negative length in characters of the insertion or deletion
373      *
374      * @exception SWTException <ul>
375      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
376      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
377      * </ul>
378      *
379      * @see ACC#TEXT_INSERT
380      * @see ACC#TEXT_DELETE
381      *
382      * @since 3.0
383      */
384     public void textChanged (int type, int startIndex, int length) {
385         checkWidget ();
386         if (accessibleObject !is null) {
387             accessibleObject.textChanged (type, startIndex, length);
388         }
389     }
390 
391     /**
392      * Sends a message to accessible clients that the text
393      * selection has changed within a custom control.
394      *
395      * @exception SWTException <ul>
396      *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
397      *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
398      * </ul>
399      *
400      * @since 3.0
401      */
402     public void textSelectionChanged () {
403         checkWidget ();
404         if (accessibleObject !is null) {
405             accessibleObject.textSelectionChanged ();
406         }
407     }
408 }