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