924d0a0808f08e7cfbc38df55f95cbdf313c84db
[platform/upstream/atk.git] / atk / atkutil.c
1 /* ATK -  Accessibility Toolkit
2  * Copyright 2001 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "atkutil.h"
21 #include "atkmarshal.c"
22 #include "config.h"
23
24 static void atk_util_class_init (AtkUtilClass *klass);
25
26 static AtkObject *previous_focus_object = NULL;
27
28 typedef struct _AtkUtilListenerInfo AtkUtilListenerInfo;
29 struct _AtkUtilListenerInfo
30 {
31   gint key;
32   guint signal_id;
33   gulong hook_id;
34 };
35 static GHashTable *listener_list = NULL;
36
37 GType
38 atk_util_get_type (void)
39 {
40   static GType type = 0;
41
42   if (!type)
43     {
44       static const GTypeInfo typeInfo =
45       {
46         sizeof (AtkUtilClass),
47         (GBaseInitFunc) NULL,
48         (GBaseFinalizeFunc) NULL,
49         (GClassInitFunc) atk_util_class_init,
50         (GClassFinalizeFunc) NULL,
51         NULL,
52         sizeof (AtkUtil),
53         0,
54         (GInstanceInitFunc) NULL,
55       } ;
56       type = g_type_register_static (G_TYPE_OBJECT, "AtkUtil", &typeInfo, 0) ;
57     }
58   return type;
59 }
60
61 /*
62  * This file supports the addition and removal of multiple focus handlers
63  * as long as they are all called in the same thread.
64  */
65 static AtkEventListenerInit  focus_tracker_init = (AtkEventListenerInit) NULL;
66
67 static gboolean init_done = FALSE;
68
69 /*
70  * Array of FocusTracker structs
71  */
72 static GArray *trackers = NULL;
73 static guint  global_index = 0;
74
75 typedef struct _FocusTracker FocusTracker;
76
77 struct _FocusTracker {
78   guint index;
79   AtkEventListener func;
80 };
81
82 /**
83  * atk_focus_tracker_init:
84  * @init: Function to be called for focus tracker initialization
85  *
86  * Specifies the function to be called for focus tracker initialization.
87  * This function should be called by an implementation of the
88  * ATK interface if any specific work needs to be done to enable
89  * focus tracking.
90  **/
91 void
92 atk_focus_tracker_init (AtkEventListenerInit    init)
93 {
94   if (!focus_tracker_init)
95     focus_tracker_init = init;
96 }
97
98 /**
99  * atk_add_focus_tracker:
100  * @focus_tracker: Function to be added to the list of functions to be called
101  * when an object receives focus.
102  *
103  * Adds the specified function to the list of functions to be called
104  * when an object receives focus.
105  *
106  * Returns: added focus tracker id, or 0 on failure.
107  **/
108 guint
109 atk_add_focus_tracker (AtkEventListener   focus_tracker)
110 {
111   g_return_val_if_fail (focus_tracker, 0);
112
113   if (!init_done)
114   {
115     if (focus_tracker_init)
116     {
117       focus_tracker_init ();
118     }
119     trackers = g_array_sized_new (FALSE, TRUE, sizeof (FocusTracker), 0);
120     init_done = TRUE;
121   }
122   if (init_done)
123   {
124     FocusTracker item;
125
126     item.index = ++global_index;
127     item.func = focus_tracker;
128     trackers = g_array_append_val (trackers, item);
129     return global_index;
130   }
131   else
132   {
133     return 0;
134   }
135 }
136
137 /**
138  * atk_remove_focus_tracker:
139  * @tracker_id: the id of the focus tracker to remove
140  *
141  * Removes the specified focus tracker from the list of functions
142  * to be called when any object receives focus.
143  **/
144 void
145 atk_remove_focus_tracker (guint            tracker_id)
146 {
147   FocusTracker *item;
148   guint i;
149
150   if (trackers == NULL)
151     return;
152
153   if (tracker_id == 0)
154     return;
155
156   for (i = 0; i < trackers->len; i++)
157   {
158     item = &g_array_index (trackers, FocusTracker, i);
159     if (item->index == tracker_id)
160     {
161       trackers = g_array_remove_index (trackers, i);
162       break;
163     }
164   }
165 }
166
167 /**
168  * atk_focus_tracker_notify:
169  * @object: an #AtkObject
170  *
171  * Cause the focus tracker functions which have been specified to be
172  * executed for the object.
173  **/
174 void
175 atk_focus_tracker_notify (AtkObject       *object)
176 {
177   FocusTracker *item;
178   guint i;
179
180   if (trackers == NULL)
181     return;
182
183   if (object == previous_focus_object)
184     return;
185   else
186     {
187       if (previous_focus_object)
188         g_object_unref (previous_focus_object);
189
190       previous_focus_object = object;
191       if (object)
192         {
193           g_object_ref (object);
194
195           for (i = 0; i < trackers->len; i++)
196             {
197               item = &g_array_index (trackers, FocusTracker, i);
198               g_return_if_fail (item != NULL);
199               item->func (object);
200             }
201         }
202     
203     }
204 }
205
206 static guint
207 add_listener (GSignalEmissionHook listener,
208               const gchar         *object_type,
209               const gchar         *signal_name,
210               const gchar         *detail_string,
211               const gchar         *hook_data)
212 {
213   GType type;
214   guint signal_id;
215   gint  rc = 0;
216   static gint listener_idx = 1;
217   GQuark detail_quark = 0;
218
219   type = g_type_from_name (object_type);
220   if (type)
221     {
222       signal_id  = g_signal_lookup (signal_name, type);
223       detail_quark = g_quark_from_string (detail_string);
224
225       if (signal_id > 0)
226         {
227           AtkUtilListenerInfo *listener_info;
228
229           rc = listener_idx;
230
231           listener_info = g_new (AtkUtilListenerInfo, 1);
232           listener_info->key = listener_idx;
233           listener_info->hook_id =
234             g_signal_add_emission_hook (signal_id, detail_quark, listener,
235                                         g_strdup (hook_data),
236                                         (GDestroyNotify) g_free);
237           listener_info->signal_id = signal_id;
238
239           g_hash_table_insert(listener_list, &(listener_info->key), listener_info);
240           listener_idx++;
241         }
242       else
243         {
244           g_debug ("Signal type %s not supported\n", signal_name);
245         }
246     }
247   else
248     {
249       g_warning("Invalid object type %s\n", object_type);
250     }
251   return rc;
252 }
253
254 static guint
255 atk_util_real_add_global_event_listener (GSignalEmissionHook listener,
256                                          const gchar *event_type)
257 {
258   guint rc = 0;
259   gchar **split_string;
260   guint length;
261
262   split_string = g_strsplit (event_type, ":", 0);
263   length = g_strv_length (split_string);
264
265   if ((length == 3) || (length == 4))
266     rc = add_listener (listener, split_string[1], split_string[2],
267                        split_string[3], event_type);
268
269   g_strfreev (split_string);
270
271   return rc;
272 }
273
274 static void
275 atk_util_real_remove_global_event_listener (guint remove_listener)
276 {
277   if (remove_listener > 0)
278     {
279       AtkUtilListenerInfo *listener_info;
280       gint tmp_idx = remove_listener;
281
282       listener_info = (AtkUtilListenerInfo *)
283         g_hash_table_lookup(listener_list, &tmp_idx);
284
285       if (listener_info != NULL)
286         {
287           /* Hook id of 0 and signal id of 0 are invalid */
288           if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
289             {
290               /* Remove the emission hook */
291               g_signal_remove_emission_hook(listener_info->signal_id,
292                                             listener_info->hook_id);
293
294               /* Remove the element from the hash */
295               g_hash_table_remove(listener_list, &tmp_idx);
296             }
297           else
298             {
299               g_warning("Invalid listener hook_id %ld or signal_id %d\n",
300                         listener_info->hook_id, listener_info->signal_id);
301             }
302         }
303       else
304         {
305           g_warning("No listener with the specified listener id %d",
306                     remove_listener);
307         }
308     }
309   else
310     {
311       g_warning("Invalid listener_id %d", remove_listener);
312     }
313 }
314
315
316 /**
317  * atk_add_global_event_listener:
318  * @listener: the listener to notify
319  * @event_type: the type of event for which notification is requested
320  *
321  * Adds the specified function to the list of functions to be called
322  * when an ATK event of type event_type occurs.
323  *
324  * The format of event_type is the following:
325  *  "ATK:<atk_type>:<atk_event>:<atk_event_detail>
326  *
327  * Where "ATK" works as the namespace, <atk_interface> is the name of
328  * the ATK type (interface or object), <atk_event> is the name of the
329  * signal defined on that interface and <atk_event_detail> is the
330  * gsignal detail of that signal. You can find more info about gsignal
331  * details here:
332  * http://developer.gnome.org/gobject/stable/gobject-Signals.html
333  *
334  * The first three parameters are mandatory. The last one is optional.
335  *
336  * For example:
337  *   ATK:AtkObject:state-change
338  *   ATK:AtkText:text-selection-changed
339  *   ATK:AtkText:text-insert:system
340  *
341  * Toolkit implementor note: Atk provides a default implementation for
342  * this virtual method, and that implementation should be enough for
343  * most of the cases. You should have a really good reason to
344  * reimplement this method.
345  *
346  * Returns: added event listener id, or 0 on failure.
347  **/
348 guint
349 atk_add_global_event_listener (GSignalEmissionHook listener,
350                                const gchar        *event_type)
351 {
352   guint retval;
353   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
354
355   if (klass->add_global_event_listener)
356     {
357       retval = klass->add_global_event_listener (listener, event_type);
358     }
359   else
360     {
361       retval = 0;
362     }
363   g_type_class_unref (klass);
364
365   return retval;
366 }
367
368 /**
369  * atk_remove_global_event_listener:
370  * @listener_id: the id of the event listener to remove
371  *
372  * @listener_id is the value returned by #atk_add_global_event_listener
373  * when you registered that event listener.
374  *
375  * Toolkit implementor note: Atk provides a default implementation for
376  * this virtual method, and that implementation should be enough for
377  * most of the cases. You should have a really good reason to
378  * reimplement this method.
379  *
380  * Removes the specified event listener
381  **/
382 void
383 atk_remove_global_event_listener (guint listener_id)
384 {
385   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
386
387   if (klass && klass->remove_global_event_listener)
388     klass->remove_global_event_listener (listener_id);
389 }
390
391 /**
392  * atk_add_key_event_listener:
393  * @listener: the listener to notify
394  * @data: a #gpointer that points to a block of data that should be sent to the registered listeners,
395  *        along with the event notification, when it occurs.  
396  *
397  * Adds the specified function to the list of functions to be called
398  *        when a key event occurs.  The @data element will be passed to the
399  *        #AtkKeySnoopFunc (@listener) as the @func_data param, on notification.
400  *
401  * Returns: added event listener id, or 0 on failure.
402  **/
403 guint
404 atk_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data)
405 {
406   guint retval;
407   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
408   if (klass && klass->add_key_event_listener)
409     {
410       retval = klass->add_key_event_listener (listener, data);
411     }
412   else
413     {
414       retval = 0;
415     }
416
417   return retval;
418 }
419
420 /**
421  * atk_remove_key_event_listener:
422  * @listener_id: the id of the event listener to remove
423  *
424  * @listener_id is the value returned by #atk_add_key_event_listener
425  * when you registered that event listener.
426  *
427  * Removes the specified event listener.
428  **/
429 void
430 atk_remove_key_event_listener (guint listener_id)
431 {
432   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
433
434   if (klass->remove_key_event_listener)
435     klass->remove_key_event_listener (listener_id);
436 }
437
438 /**
439  * atk_get_root:
440  *
441  * Gets the root accessible container for the current application.
442  *
443  * Returns: (transfer none): the root accessible container for the current
444  * application
445  **/
446 AtkObject*
447 atk_get_root (void)
448 {
449   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
450   AtkObject    *retval;
451   if (klass->get_root)
452     {
453       retval = klass->get_root ();
454     }
455   else
456     {
457       retval = NULL;
458     }
459   g_type_class_unref (klass);
460
461   return retval;
462 }
463
464 /**
465  * atk_get_focus_object:
466  *
467  * Gets the currently focused object.
468  * 
469  * Since: 1.6
470  *
471  * Returns: (transfer none): the currently focused object for the current
472  * application
473  **/
474 AtkObject*
475 atk_get_focus_object (void)
476 {
477   return previous_focus_object;
478 }
479
480 /**
481  * atk_get_toolkit_name:
482  *
483  * Gets name string for the GUI toolkit implementing ATK for this application.
484  *
485  * Returns: name string for the GUI toolkit implementing ATK for this application
486  **/
487 const gchar*
488 atk_get_toolkit_name (void)
489 {
490   const gchar *retval;
491   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
492   if (klass->get_toolkit_name)
493     {
494       retval = klass->get_toolkit_name ();
495     }
496   else
497     {
498       retval = NULL;
499     }
500   g_type_class_unref (klass);
501
502   return retval;
503 }
504
505 /**
506  * atk_get_toolkit_version:
507  *
508  * Gets version string for the GUI toolkit implementing ATK for this application.
509  *
510  * Returns: version string for the GUI toolkit implementing ATK for this application
511  **/
512 const gchar*
513 atk_get_toolkit_version (void)
514 {
515   const gchar *retval;
516   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
517   if (klass->get_toolkit_version)
518     {
519       retval = klass->get_toolkit_version ();
520     }
521   else
522     {
523       retval = NULL;
524     }
525   g_type_class_unref (klass);
526
527   return retval;
528 }
529
530 /**
531  * atk_get_version:
532  *
533  * Gets the current version for ATK.
534  *
535  * Returns: version string for ATK
536  *
537  * Since: 1.20
538  */
539 const gchar *
540 atk_get_version (void)
541 {
542   return VERSION;
543 }
544
545 static void
546 atk_util_class_init (AtkUtilClass *klass)
547 {
548   klass->add_global_event_listener = atk_util_real_add_global_event_listener;
549   klass->remove_global_event_listener = atk_util_real_remove_global_event_listener;
550   klass->get_root = NULL;
551   klass->get_toolkit_name = NULL;
552   klass->get_toolkit_version = NULL;
553
554   listener_list = g_hash_table_new_full (g_int_hash, g_int_equal, NULL,
555                                          g_free);
556 }