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