1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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.AccessibleFactory;
14 
15 import java.lang.all;
16 
17 
18 import org.eclipse.swt.internal.accessibility.gtk.ATK;
19 import org.eclipse.swt.internal.gtk.OS;
20 import org.eclipse.swt.accessibility.Accessible;
21 import org.eclipse.swt.accessibility.ACC;
22 import org.eclipse.swt.accessibility.AccessibleObject;
23 import org.eclipse.swt.accessibility.AccessibleControlEvent;
24 import org.eclipse.swt.accessibility.AccessibleControlListener;
25 
26 import org.eclipse.swt.SWT;
27 
28 class AccessibleFactory {
29     AtkObjectFactory * handle;
30     ptrdiff_t objectParentType;
31     char* widgetTypeName;
32 
33     //Callback atkObjectFactoryCB_create_accessible;
34     //Callback gTypeInfo_base_init_factory;
35     Accessible[GtkWidget*] accessibles;
36 
37     static size_t[String] Types;
38     static AccessibleFactory[size_t] Factories;
39 
40     static ptrdiff_t DefaultParentType; //$NON-NLS-1$
41     static const String FACTORY_PARENTTYPENAME = "AtkObjectFactory";
42     static const String SWT_TYPE_PREFIX = "SWT";
43     static const String CHILD_TYPENAME = "Child";
44     static const String FACTORY_TYPENAME = "SWTFactory";
45     static const int[] actionRoles = [
46         ACC.ROLE_CHECKBUTTON, ACC.ROLE_COMBOBOX, ACC.ROLE_LINK,
47         ACC.ROLE_MENUITEM, ACC.ROLE_PUSHBUTTON, ACC.ROLE_RADIOBUTTON,
48     ];
49     static const int[] hypertextRoles = [ACC.ROLE_LINK];
50     static const int[] selectionRoles = [
51         ACC.ROLE_LIST, ACC.ROLE_TABFOLDER, ACC.ROLE_TABLE, ACC.ROLE_TREE,
52     ];
53     static const int[] textRoles = [
54         ACC.ROLE_COMBOBOX, ACC.ROLE_LINK, ACC.ROLE_LABEL, ACC.ROLE_TEXT,
55     ];
56 
57     /* AT callbacks*/
58     /* interface definitions */
59     private static GTypeInfo* ObjectIfaceDefinition;
60     private static GInterfaceInfo* ActionIfaceDefinition;
61     private static GInterfaceInfo* ComponentIfaceDefinition;
62     private static GInterfaceInfo* HypertextIfaceDefinition;
63     private static GInterfaceInfo* SelectionIfaceDefinition;
64     private static GInterfaceInfo* TextIfaceDefinition;
65 
66     private static void static_this(){
67         synchronized {
68             AccessibleObject.static_this();
69             /* Action interface */
70             if( ActionIfaceDefinition is null ){
71                 DefaultParentType = OS.g_type_from_name ("GtkAccessible"); //$NON-NLS-1$
72                 ActionIfaceDefinition = cast(GInterfaceInfo*)OS.g_malloc (GInterfaceInfo.sizeof);
73                 ActionIfaceDefinition.interface_init = &AccessibleFactory.initActionIfaceCB;
74             }
75             /* Component interface */
76             if( ComponentIfaceDefinition is null ){
77                 ComponentIfaceDefinition = cast(GInterfaceInfo*)OS.g_malloc (GInterfaceInfo.sizeof);
78                 ComponentIfaceDefinition.interface_init = &AccessibleFactory.initComponentIfaceCB;
79             }
80             /* Hypertext interface */
81             if( HypertextIfaceDefinition is null ){
82                 HypertextIfaceDefinition = cast(GInterfaceInfo*)OS.g_malloc (GInterfaceInfo.sizeof);
83                 HypertextIfaceDefinition.interface_init = &AccessibleFactory.initHypertextIfaceCB;
84             }
85             /* Selection interface */
86             if( SelectionIfaceDefinition is null ){
87                 SelectionIfaceDefinition = cast(GInterfaceInfo*)OS.g_malloc (GInterfaceInfo.sizeof);
88                 SelectionIfaceDefinition.interface_init = &AccessibleFactory.initSelectionIfaceCB;
89             }
90             /* Text interface */
91             if( TextIfaceDefinition is null ){
92                 TextIfaceDefinition =cast(GInterfaceInfo*) OS.g_malloc (GInterfaceInfo.sizeof);
93                 TextIfaceDefinition.interface_init = &AccessibleFactory.initTextIfaceCB;
94             }
95         }
96     }
97 
98     private this (int widgetType) {
99         widgetTypeName = OS.g_type_name (widgetType);
100         String factoryName = (FACTORY_TYPENAME ~ fromStringz( widgetTypeName ) ~ '\0')._idup();
101         if (OS.g_type_from_name (factoryName.ptr) is 0) {
102             /* register the factory */
103             auto registry = ATK.atk_get_default_registry ();
104             auto previousFactory = ATK.atk_registry_get_factory (registry, widgetType);
105             objectParentType = ATK.atk_object_factory_get_accessible_type (previousFactory);
106             if (objectParentType is 0) objectParentType = DefaultParentType;
107             auto factoryParentType = OS.g_type_from_name (FACTORY_PARENTTYPENAME.ptr);
108             auto typeInfo = cast(GTypeInfo*) OS.g_malloc (GTypeInfo.sizeof);
109             typeInfo.base_init = &gTypeInfo_base_init_factory;
110             typeInfo.class_size = AtkObjectFactoryClass.sizeof;
111             typeInfo.instance_size = AtkObjectFactory.sizeof;
112             auto swtFactoryType = OS.g_type_register_static (factoryParentType, factoryName.ptr, typeInfo, 0);
113             ATK.atk_registry_set_factory_type (registry, widgetType, swtFactoryType);
114             handle = ATK.atk_registry_get_factory (registry, widgetType);
115         }
116     }
117 
118     void addAccessible (Accessible accessible) {
119         auto controlHandle = accessible.getControlHandle ();
120         accessibles[controlHandle] = accessible;
121         ATK.atk_object_factory_create_accessible (handle, cast(GObject*)controlHandle);
122     }
123 
124     private static extern(C) AtkObject* atkObjectFactory_create_accessible (GObject* widget) {
125         auto widgetType = OS.G_OBJECT_TYPE ( cast(GTypeInstance*)widget);
126         if( auto factory = widgetType in Factories ){
127             with( *factory ){
128                 Accessible accessible = accessibles[ cast(GtkWidget*) widget ];
129                 if (accessible is null) {
130                     /*
131                     * we don't care about this control, so create it with the parent's
132                     * type so that its accessibility callbacks will not pass though here
133                     */
134                     auto result = cast(AtkObject*) OS.g_object_new (objectParentType, null);
135                     ATK.atk_object_initialize (result, cast(void*)widget);
136                     return result;
137                 }
138                 /* if an atk object has already been created for this widget then just return it */
139                 if (accessible.accessibleObject !is null) {
140                     return accessible.accessibleObject.handle;
141                 }
142                 String buffer = fromStringz( widgetTypeName )._idup();
143                 int type = getType (buffer, accessible, cast(uint)/*64bit*/objectParentType, ACC.CHILDID_SELF);
144                 AccessibleObject object = new AccessibleObject (type, cast(GtkWidget*)widget, accessible, cast(uint)/*64bit*/objectParentType, false);
145                 accessible.accessibleObject = object;
146                 return object.handle;
147             }
148         }
149         else{
150             getDwtLogger().info( __FILE__, __LINE__,  "AccessibleFactory.atkObjectFactoryCB_create_accessible cannot find factory instance" );
151             return null;
152         }
153     }
154 
155     static int getChildType (Accessible accessible, int childIndex) {
156         return getType (CHILD_TYPENAME, accessible, cast(int)/*64bit*/DefaultParentType, childIndex);
157     }
158 
159     static int getDefaultParentType () {
160         return cast(int)/*64bit*/DefaultParentType;
161     }
162 
163     static int getType (String widgetTypeName, Accessible accessible, int parentType, int childId) {
164         AccessibleControlEvent event = new AccessibleControlEvent (accessible);
165         event.childID = childId;
166         AccessibleControlListener[] listeners = accessible.getControlListeners ();
167         for (int i = 0; i < listeners.length; i++) {
168             listeners [i].getRole (event);
169         }
170         bool action = false, hypertext = false, selection = false, text = false;
171         if (event.detail !is 0) {    /* a role was specified */
172             for (int i = 0; i < actionRoles.length; i++) {
173                 if (event.detail is actionRoles [i]) {
174                     action = true;
175                     break;
176                 }
177             }
178             for (int i = 0; i < hypertextRoles.length; i++) {
179                 if (event.detail is hypertextRoles [i]) {
180                     hypertext = true;
181                     break;
182                 }
183             }
184             for (int i = 0; i < selectionRoles.length; i++) {
185                 if (event.detail is selectionRoles [i]) {
186                     selection = true;
187                     break;
188                 }
189             }
190             for (int i = 0; i < textRoles.length; i++) {
191                 if (event.detail is textRoles [i]) {
192                     text = true;
193                     break;
194                 }
195             }
196         } else {
197             action = hypertext = selection = text = true;
198         }
199         String swtTypeName = SWT_TYPE_PREFIX._idup();
200         swtTypeName ~= widgetTypeName;
201         if (action) swtTypeName ~= "Action"; //$NON-NLS-1$
202         if (hypertext) swtTypeName ~= "Hypertext"; //$NON-NLS-1$
203         if (selection) swtTypeName ~= "Selection"; //$NON-NLS-1$
204         if (text) swtTypeName ~= "Text"; //$NON-NLS-1$
205 
206         size_t type = 0;
207         if (swtTypeName in Types ) {
208             type = Types[swtTypeName];
209         } else {
210             /* define the type */
211             GTypeQuery* query = new GTypeQuery ();
212             OS.g_type_query (parentType, query);
213 
214             GTypeInfo* typeInfo = new GTypeInfo ();
215             typeInfo.base_init = &gTypeInfo_base_init_type;
216             typeInfo.class_size = cast(ushort) query.class_size;
217             typeInfo.instance_size = cast(ushort) query.instance_size;
218             ObjectIfaceDefinition = typeInfo;
219 
220             type = OS.g_type_register_static (parentType, toStringz( swtTypeName ), ObjectIfaceDefinition, 0);
221             OS.g_type_add_interface_static (type, AccessibleObject.ATK_COMPONENT_TYPE, ComponentIfaceDefinition);
222             if (action) OS.g_type_add_interface_static (type, AccessibleObject.ATK_ACTION_TYPE, ActionIfaceDefinition);
223             if (hypertext) OS.g_type_add_interface_static (type, AccessibleObject.ATK_HYPERTEXT_TYPE, HypertextIfaceDefinition);
224             if (selection) OS.g_type_add_interface_static (type, AccessibleObject.ATK_SELECTION_TYPE, SelectionIfaceDefinition);
225             if (text) OS.g_type_add_interface_static (type, AccessibleObject.ATK_TEXT_TYPE, TextIfaceDefinition);
226             Types[swtTypeName] = type;
227         }
228         return cast(int)/*64bit*/type;
229     }
230 
231     private static extern(C) void gTypeInfo_base_init_factory (void* klass) {
232         auto atkObjectFactoryClass = ATK.ATK_OBJECT_FACTORY_CLASS (klass);
233         atkObjectFactoryClass.create_accessible = &atkObjectFactory_create_accessible;
234     }
235 
236     private static extern(C) void gTypeInfo_base_init_type (void* klass) {
237         auto objectClass = cast(AtkObjectClass*)klass;
238         objectClass.get_name = &AccessibleObject.atkObject_get_name;
239         objectClass.get_description = &AccessibleObject.atkObject_get_description;
240         objectClass.get_n_children = &AccessibleObject.atkObject_get_n_children;
241         objectClass.get_role = &AccessibleObject.atkObject_get_role;
242         objectClass.get_parent = &AccessibleObject.atkObject_get_parent;
243         objectClass.ref_state_set = &AccessibleObject.atkObject_ref_state_set;
244         objectClass.get_index_in_parent = &AccessibleObject.atkObject_get_index_in_parent;
245         objectClass.ref_child = &AccessibleObject.atkObject_ref_child;
246 
247         GObjectClass* gObjectClass = OS.G_OBJECT_CLASS ( cast(GTypeClass*)klass);
248         gObjectClass.finalize = &AccessibleObject.gObjectClass_finalize;
249     }
250 
251     private static extern(C) void initActionIfaceCB ( void* g_iface, void* iface_data ) {
252         auto iface = cast(AtkActionIface*)g_iface;
253         iface.get_keybinding =  &AccessibleObject.atkAction_get_keybinding;
254         iface.get_name =  &AccessibleObject.atkAction_get_name;
255     }
256 
257     private static extern(C) void initComponentIfaceCB ( void* g_iface, void* iface_data ) {
258         auto iface = cast(AtkComponentIface*)g_iface;
259         iface.get_extents = &AccessibleObject.atkComponent_get_extents;
260         iface.get_position = &AccessibleObject.atkComponent_get_position;
261         iface.get_size = &AccessibleObject.atkComponent_get_size;
262         iface.ref_accessible_at_point = &AccessibleObject.atkComponent_ref_accessible_at_point;
263     }
264 
265     private static extern(C) void initHypertextIfaceCB ( void* g_iface, void* iface_data ) {
266         auto iface = cast(AtkHypertextIface*)g_iface;
267         iface.get_link = &AccessibleObject.atkHypertext_get_link;
268         iface.get_link_index = &AccessibleObject.atkHypertext_get_link_index;
269         iface.get_n_links = &AccessibleObject.atkHypertext_get_n_links;
270     }
271 
272     private static extern(C) void initSelectionIfaceCB ( void* g_iface, void* iface_data ) {
273         auto iface = cast(AtkSelectionIface*)g_iface;
274         iface.is_child_selected = &AccessibleObject.atkSelection_is_child_selected;
275         iface.ref_selection = &AccessibleObject.atkSelection_ref_selection;
276     }
277 
278     private static extern(C) void initTextIfaceCB ( void* g_iface, void* iface_data ) {
279         auto iface = cast(AtkTextIface*)g_iface;
280         iface.get_caret_offset = &AccessibleObject.atkText_get_caret_offset;
281         iface.get_character_at_offset = &AccessibleObject.atkText_get_character_at_offset;
282         iface.get_character_count = &AccessibleObject.atkText_get_character_count;
283         iface.get_n_selections = &AccessibleObject.atkText_get_n_selections;
284         iface.get_selection = &AccessibleObject.atkText_get_selection;
285         iface.get_text = &AccessibleObject.atkText_get_text;
286         iface.get_text_after_offset = &AccessibleObject.atkText_get_text_after_offset;
287         iface.get_text_at_offset = &AccessibleObject.atkText_get_text_at_offset;
288         iface.get_text_before_offset = &AccessibleObject.atkText_get_text_before_offset;
289     }
290 
291     static void registerAccessible (Accessible accessible) {
292         static_this();
293         /* If DefaultParentType is 0 then OS accessibility is not active */
294         if (DefaultParentType is 0) return;
295         auto controlHandle = accessible.getControlHandle ();
296         auto widgetType = OS.G_OBJECT_TYPE ( cast(GTypeInstance*)controlHandle);
297         AccessibleFactory factory = Factories[widgetType];
298         if (factory is null) {
299             factory = new AccessibleFactory (cast(int)/*64bit*/widgetType);
300             Factories[widgetType] = factory;
301         }
302         factory.addAccessible (accessible);
303     }
304 
305     void removeAccessible (Accessible accessible) {
306         accessibles.remove (accessible.getControlHandle ());
307     }
308 
309     static void unregisterAccessible (Accessible accessible) {
310         auto controlHandle = accessible.getControlHandle ();
311         auto widgetType = OS.G_OBJECT_TYPE (cast(GTypeInstance*)controlHandle);
312         if ( auto factory = widgetType in Factories ) {
313             factory.removeAccessible (accessible);
314         }
315     }
316 }