Default implementation for atk_util_[add/remove]_global_event_listener
[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         *hook_data)
211 {
212   GType type;
213   guint signal_id;
214   gint  rc = 0;
215   static gint listener_idx = 1;
216
217   type = g_type_from_name (object_type);
218   if (type)
219     {
220       signal_id  = g_signal_lookup (signal_name, type);
221       if (signal_id > 0)
222         {
223           AtkUtilListenerInfo *listener_info;
224
225           rc = listener_idx;
226
227           listener_info = g_new (AtkUtilListenerInfo, 1);
228           listener_info->key = listener_idx;
229           listener_info->hook_id =
230             g_signal_add_emission_hook (signal_id, 0, listener,
231                                         g_strdup (hook_data),
232                                         (GDestroyNotify) g_free);
233           listener_info->signal_id = signal_id;
234
235           g_hash_table_insert(listener_list, &(listener_info->key), listener_info);
236           listener_idx++;
237         }
238       else
239         {
240           g_debug ("Signal type %s not supported\n", signal_name);
241         }
242     }
243   else
244     {
245       g_warning("Invalid object type %s\n", object_type);
246     }
247   return rc;
248 }
249
250 static guint
251 atk_util_real_add_global_event_listener (GSignalEmissionHook listener,
252                                          const gchar *event_type)
253 {
254   guint rc = 0;
255   gchar **split_string;
256
257   split_string = g_strsplit (event_type, ":", 3);
258
259   if (g_strv_length (split_string) == 3)
260     rc = add_listener (listener, split_string[1], split_string[2], event_type);
261
262   g_strfreev (split_string);
263
264   return rc;
265 }
266
267 static void
268 atk_util_real_remove_global_event_listener (guint remove_listener)
269 {
270   if (remove_listener > 0)
271     {
272       AtkUtilListenerInfo *listener_info;
273       gint tmp_idx = remove_listener;
274
275       listener_info = (AtkUtilListenerInfo *)
276         g_hash_table_lookup(listener_list, &tmp_idx);
277
278       if (listener_info != NULL)
279         {
280           /* Hook id of 0 and signal id of 0 are invalid */
281           if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
282             {
283               /* Remove the emission hook */
284               g_signal_remove_emission_hook(listener_info->signal_id,
285                                             listener_info->hook_id);
286
287               /* Remove the element from the hash */
288               g_hash_table_remove(listener_list, &tmp_idx);
289             }
290           else
291             {
292               g_warning("Invalid listener hook_id %ld or signal_id %d\n",
293                         listener_info->hook_id, listener_info->signal_id);
294             }
295         }
296       else
297         {
298           g_warning("No listener with the specified listener id %d",
299                     remove_listener);
300         }
301     }
302   else
303     {
304       g_warning("Invalid listener_id %d", remove_listener);
305     }
306 }
307
308
309 /**
310  * atk_add_global_event_listener:
311  * @listener: the listener to notify
312  * @event_type: the type of event for which notification is requested
313  *
314  * Adds the specified function to the list of functions to be called
315  * when an ATK event of type event_type occurs.
316  *
317  * The format of event_type is the following:
318  *  "ATK:<atk_type>:<atk_event>
319  *
320  * Where "ATK" works as the namespace, <atk_interface> is the name of
321  * the ATK type (interface or object) and <atk_event> is the name of
322  * the signal defined on that interface.
323  *
324  * For example:
325  *   ATK:AtkObject:state-change
326  *   ATK:AtkText:text-selection-changed
327  *
328  * Toolkit implementor note: Atk provides a default implementation for
329  * this virtual method, and that implementation should be enough for
330  * most of the cases. You should have a really good reason to
331  * reimplement this method.
332  *
333  * Returns: added event listener id, or 0 on failure.
334  **/
335 guint
336 atk_add_global_event_listener (GSignalEmissionHook listener,
337                                const gchar        *event_type)
338 {
339   guint retval;
340   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
341
342   if (klass->add_global_event_listener)
343     {
344       retval = klass->add_global_event_listener (listener, event_type);
345     }
346   else
347     {
348       retval = 0;
349     }
350   g_type_class_unref (klass);
351
352   return retval;
353 }
354
355 /**
356  * atk_remove_global_event_listener:
357  * @listener_id: the id of the event listener to remove
358  *
359  * @listener_id is the value returned by #atk_add_global_event_listener
360  * when you registered that event listener.
361  *
362  * Toolkit implementor note: Atk provides a default implementation for
363  * this virtual method, and that implementation should be enough for
364  * most of the cases. You should have a really good reason to
365  * reimplement this method.
366  *
367  * Removes the specified event listener
368  **/
369 void
370 atk_remove_global_event_listener (guint listener_id)
371 {
372   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
373
374   if (klass && klass->remove_global_event_listener)
375     klass->remove_global_event_listener (listener_id);
376 }
377
378 /**
379  * atk_add_key_event_listener:
380  * @listener: the listener to notify
381  * @data: a #gpointer that points to a block of data that should be sent to the registered listeners,
382  *        along with the event notification, when it occurs.  
383  *
384  * Adds the specified function to the list of functions to be called
385  *        when a key event occurs.  The @data element will be passed to the
386  *        #AtkKeySnoopFunc (@listener) as the @func_data param, on notification.
387  *
388  * Returns: added event listener id, or 0 on failure.
389  **/
390 guint
391 atk_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data)
392 {
393   guint retval;
394   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
395   if (klass && klass->add_key_event_listener)
396     {
397       retval = klass->add_key_event_listener (listener, data);
398     }
399   else
400     {
401       retval = 0;
402     }
403
404   return retval;
405 }
406
407 /**
408  * atk_remove_key_event_listener:
409  * @listener_id: the id of the event listener to remove
410  *
411  * @listener_id is the value returned by #atk_add_key_event_listener
412  * when you registered that event listener.
413  *
414  * Removes the specified event listener.
415  **/
416 void
417 atk_remove_key_event_listener (guint listener_id)
418 {
419   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
420
421   if (klass->remove_key_event_listener)
422     klass->remove_key_event_listener (listener_id);
423 }
424
425 /**
426  * atk_get_root:
427  *
428  * Gets the root accessible container for the current application.
429  *
430  * Returns: (transfer none): the root accessible container for the current
431  * application
432  **/
433 AtkObject*
434 atk_get_root (void)
435 {
436   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
437   AtkObject    *retval;
438   if (klass->get_root)
439     {
440       retval = klass->get_root ();
441     }
442   else
443     {
444       retval = NULL;
445     }
446   g_type_class_unref (klass);
447
448   return retval;
449 }
450
451 /**
452  * atk_get_focus_object:
453  *
454  * Gets the currently focused object.
455  * 
456  * Since: 1.6
457  *
458  * Returns: (transfer none): the currently focused object for the current
459  * application
460  **/
461 AtkObject*
462 atk_get_focus_object (void)
463 {
464   return previous_focus_object;
465 }
466
467 /**
468  * atk_get_toolkit_name:
469  *
470  * Gets name string for the GUI toolkit implementing ATK for this application.
471  *
472  * Returns: name string for the GUI toolkit implementing ATK for this application
473  **/
474 const gchar*
475 atk_get_toolkit_name (void)
476 {
477   const gchar *retval;
478   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
479   if (klass->get_toolkit_name)
480     {
481       retval = klass->get_toolkit_name ();
482     }
483   else
484     {
485       retval = NULL;
486     }
487   g_type_class_unref (klass);
488
489   return retval;
490 }
491
492 /**
493  * atk_get_toolkit_version:
494  *
495  * Gets version string for the GUI toolkit implementing ATK for this application.
496  *
497  * Returns: version string for the GUI toolkit implementing ATK for this application
498  **/
499 const gchar*
500 atk_get_toolkit_version (void)
501 {
502   const gchar *retval;
503   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
504   if (klass->get_toolkit_version)
505     {
506       retval = klass->get_toolkit_version ();
507     }
508   else
509     {
510       retval = NULL;
511     }
512   g_type_class_unref (klass);
513
514   return retval;
515 }
516
517 /**
518  * atk_get_version:
519  *
520  * Gets the current version for ATK.
521  *
522  * Returns: version string for ATK
523  *
524  * Since: 1.20
525  */
526 const gchar *
527 atk_get_version (void)
528 {
529   return VERSION;
530 }
531
532 static void
533 atk_util_class_init (AtkUtilClass *klass)
534 {
535   klass->add_global_event_listener = atk_util_real_add_global_event_listener;
536   klass->remove_global_event_listener = atk_util_real_remove_global_event_listener;
537   klass->get_root = NULL;
538   klass->get_toolkit_name = NULL;
539   klass->get_toolkit_version = NULL;
540
541   listener_list = g_hash_table_new_full (g_int_hash, g_int_equal, NULL,
542                                          g_free);
543 }