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