doc: adding further explanation on atk_add_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 "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  * ATK consumers note: as this method adds a listener for a given ATK
381  * type, that type should be already registered on the GType system
382  * before calling this method. A simple way to do that is creating an
383  * instance of #AtkNoOpObject. This class implements all ATK
384  * interfaces, so creating the instance will register all ATK types as
385  * a collateral effect.
386  *
387  * Returns: added event listener id, or 0 on failure.
388  **/
389 guint
390 atk_add_global_event_listener (GSignalEmissionHook listener,
391                                const gchar        *event_type)
392 {
393   guint retval;
394   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
395
396   if (klass->add_global_event_listener)
397     {
398       retval = klass->add_global_event_listener (listener, event_type);
399     }
400   else
401     {
402       retval = 0;
403     }
404   g_type_class_unref (klass);
405
406   return retval;
407 }
408
409 /**
410  * atk_remove_global_event_listener:
411  * @listener_id: the id of the event listener to remove
412  *
413  * @listener_id is the value returned by #atk_add_global_event_listener
414  * when you registered that event listener.
415  *
416  * Toolkit implementor note: ATK provides a default implementation for
417  * this virtual method. ATK implementors are discouraged from
418  * reimplementing this method.
419  *
420  * Toolkit implementor note: this method is not intended to be used by
421  * ATK implementors but by ATK consumers.
422  *
423  * Removes the specified event listener
424  **/
425 void
426 atk_remove_global_event_listener (guint listener_id)
427 {
428   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
429
430   if (klass && klass->remove_global_event_listener)
431     klass->remove_global_event_listener (listener_id);
432 }
433
434 /**
435  * atk_add_key_event_listener:
436  * @listener: the listener to notify
437  * @data: a #gpointer that points to a block of data that should be sent to the registered listeners,
438  *        along with the event notification, when it occurs.  
439  *
440  * Adds the specified function to the list of functions to be called
441  *        when a key event occurs.  The @data element will be passed to the
442  *        #AtkKeySnoopFunc (@listener) as the @func_data param, on notification.
443  *
444  * Returns: added event listener id, or 0 on failure.
445  **/
446 guint
447 atk_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data)
448 {
449   guint retval;
450   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
451   if (klass && klass->add_key_event_listener)
452     {
453       retval = klass->add_key_event_listener (listener, data);
454     }
455   else
456     {
457       retval = 0;
458     }
459
460   return retval;
461 }
462
463 /**
464  * atk_remove_key_event_listener:
465  * @listener_id: the id of the event listener to remove
466  *
467  * @listener_id is the value returned by #atk_add_key_event_listener
468  * when you registered that event listener.
469  *
470  * Removes the specified event listener.
471  **/
472 void
473 atk_remove_key_event_listener (guint listener_id)
474 {
475   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
476
477   if (klass->remove_key_event_listener)
478     klass->remove_key_event_listener (listener_id);
479 }
480
481 /**
482  * atk_get_root:
483  *
484  * Gets the root accessible container for the current application.
485  *
486  * Returns: (transfer none): the root accessible container for the current
487  * application
488  **/
489 AtkObject*
490 atk_get_root (void)
491 {
492   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
493   AtkObject    *retval;
494   if (klass->get_root)
495     {
496       retval = klass->get_root ();
497     }
498   else
499     {
500       retval = NULL;
501     }
502   g_type_class_unref (klass);
503
504   return retval;
505 }
506
507 /**
508  * atk_get_focus_object:
509  *
510  * Gets the currently focused object.
511  * 
512  * Since: 1.6
513  *
514  * Returns: (transfer none): the currently focused object for the current
515  * application
516  **/
517 AtkObject*
518 atk_get_focus_object (void)
519 {
520   return previous_focus_object;
521 }
522
523 /**
524  * atk_get_toolkit_name:
525  *
526  * Gets name string for the GUI toolkit implementing ATK for this application.
527  *
528  * Returns: name string for the GUI toolkit implementing ATK for this application
529  **/
530 const gchar*
531 atk_get_toolkit_name (void)
532 {
533   const gchar *retval;
534   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
535   if (klass->get_toolkit_name)
536     {
537       retval = klass->get_toolkit_name ();
538     }
539   else
540     {
541       retval = NULL;
542     }
543   g_type_class_unref (klass);
544
545   return retval;
546 }
547
548 /**
549  * atk_get_toolkit_version:
550  *
551  * Gets version string for the GUI toolkit implementing ATK for this application.
552  *
553  * Returns: version string for the GUI toolkit implementing ATK for this application
554  **/
555 const gchar*
556 atk_get_toolkit_version (void)
557 {
558   const gchar *retval;
559   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
560   if (klass->get_toolkit_version)
561     {
562       retval = klass->get_toolkit_version ();
563     }
564   else
565     {
566       retval = NULL;
567     }
568   g_type_class_unref (klass);
569
570   return retval;
571 }
572
573 /**
574  * atk_get_version:
575  *
576  * Gets the current version for ATK.
577  *
578  * Returns: version string for ATK
579  *
580  * Since: 1.20
581  */
582 const gchar *
583 atk_get_version (void)
584 {
585   return VERSION;
586 }
587
588 static void
589 atk_util_class_init (AtkUtilClass *klass)
590 {
591   klass->add_global_event_listener = atk_util_real_add_global_event_listener;
592   klass->remove_global_event_listener = atk_util_real_remove_global_event_listener;
593   klass->get_root = NULL;
594   klass->get_toolkit_name = NULL;
595   klass->get_toolkit_version = NULL;
596
597   listener_list = g_hash_table_new_full (g_int_hash, g_int_equal, NULL,
598                                          g_free);
599 }