Revert "Revert "Merge remote-tracking branch 'origin/sandbox/mniesluchow/upstream_2_1...
[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: 2.9.4: Focus tracking has been dropped as a feature
104  * to be 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: 2.9.4: Focus tracking has been dropped as a feature
123  * to be implemented by ATK itself. If you need focus tracking on your
124  * implementation, subscribe to the state-changed:focused signal.
125  *
126  * Returns: added focus tracker id, or 0 on failure.
127  **/
128 guint
129 atk_add_focus_tracker (AtkEventListener   focus_tracker)
130 {
131   g_return_val_if_fail (focus_tracker, 0);
132
133   if (!init_done)
134   {
135     if (focus_tracker_init)
136     {
137       focus_tracker_init ();
138     }
139     trackers = g_array_sized_new (FALSE, TRUE, sizeof (FocusTracker), 0);
140     init_done = TRUE;
141   }
142   if (init_done)
143   {
144     FocusTracker item;
145
146     item.index = ++global_index;
147     item.func = focus_tracker;
148     trackers = g_array_append_val (trackers, item);
149     return global_index;
150   }
151   else
152   {
153     return 0;
154   }
155 }
156
157 /**
158  * atk_remove_focus_tracker:
159  * @tracker_id: the id of the focus tracker to remove
160  *
161  * Deprecated: 2.9.4: Focus tracking has been dropped as a feature
162  * to be 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: 2.9.4: Focus tracking has been dropped as a feature
199  * to be implemented by ATK itself. As #AtkObject::focus-event was
200  * deprecated in favor of a #AtkObject::state-change signal, in order
201  * to notify a focus change on your implementation, you can use
202  * atk_object_notify_state_change() instead.
203  **/
204 void
205 atk_focus_tracker_notify (AtkObject       *object)
206 {
207   FocusTracker *item;
208   guint i;
209
210   if (trackers == NULL)
211     return;
212
213   if (object == previous_focus_object)
214     return;
215   else
216     {
217       if (previous_focus_object)
218         g_object_unref (previous_focus_object);
219
220       previous_focus_object = object;
221       if (object)
222         {
223           g_object_ref (object);
224
225           for (i = 0; i < trackers->len; i++)
226             {
227               item = &g_array_index (trackers, FocusTracker, i);
228               g_return_if_fail (item != NULL);
229               item->func (object);
230             }
231         }
232     
233     }
234 }
235
236 static guint
237 add_listener (GSignalEmissionHook listener,
238               const gchar         *object_type,
239               const gchar         *signal_name,
240               const gchar         *detail_string,
241               const gchar         *hook_data)
242 {
243   GType type;
244   guint signal_id;
245   gint  rc = 0;
246   static gint listener_idx = 1;
247   GQuark detail_quark = 0;
248
249   type = g_type_from_name (object_type);
250   if (type)
251     {
252       signal_id  = g_signal_lookup (signal_name, type);
253       detail_quark = g_quark_from_string (detail_string);
254
255       if (signal_id > 0)
256         {
257           AtkUtilListenerInfo *listener_info;
258
259           rc = listener_idx;
260
261           listener_info = g_new (AtkUtilListenerInfo, 1);
262           listener_info->key = listener_idx;
263           listener_info->hook_id =
264             g_signal_add_emission_hook (signal_id, detail_quark, listener,
265                                         g_strdup (hook_data),
266                                         (GDestroyNotify) g_free);
267           listener_info->signal_id = signal_id;
268
269           g_hash_table_insert(listener_list, &(listener_info->key), listener_info);
270           listener_idx++;
271         }
272       else
273         {
274           g_debug ("Signal type %s not supported\n", signal_name);
275         }
276     }
277   else
278     {
279       g_warning("Invalid object type %s\n", object_type);
280     }
281   return rc;
282 }
283
284 static guint
285 atk_util_real_add_global_event_listener (GSignalEmissionHook listener,
286                                          const gchar *event_type)
287 {
288   guint rc = 0;
289   gchar **split_string;
290   guint length;
291
292   split_string = g_strsplit (event_type, ":", 0);
293   length = g_strv_length (split_string);
294
295   if ((length == 3) || (length == 4))
296     rc = add_listener (listener, split_string[1], split_string[2],
297                        split_string[3], event_type);
298
299   g_strfreev (split_string);
300
301   return rc;
302 }
303
304 static void
305 atk_util_real_remove_global_event_listener (guint remove_listener)
306 {
307   if (remove_listener > 0)
308     {
309       AtkUtilListenerInfo *listener_info;
310       gint tmp_idx = remove_listener;
311
312       listener_info = (AtkUtilListenerInfo *)
313         g_hash_table_lookup(listener_list, &tmp_idx);
314
315       if (listener_info != NULL)
316         {
317           /* Hook id of 0 and signal id of 0 are invalid */
318           if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
319             {
320               /* Remove the emission hook */
321               g_signal_remove_emission_hook(listener_info->signal_id,
322                                             listener_info->hook_id);
323
324               /* Remove the element from the hash */
325               g_hash_table_remove(listener_list, &tmp_idx);
326             }
327           else
328             {
329               g_warning("Invalid listener hook_id %ld or signal_id %d\n",
330                         listener_info->hook_id, listener_info->signal_id);
331             }
332         }
333       else
334         {
335           g_warning("No listener with the specified listener id %d",
336                     remove_listener);
337         }
338     }
339   else
340     {
341       g_warning("Invalid listener_id %d", remove_listener);
342     }
343 }
344
345
346 /**
347  * atk_add_global_event_listener:
348  * @listener: the listener to notify
349  * @event_type: the type of event for which notification is requested
350  *
351  * Adds the specified function to the list of functions to be called
352  * when an ATK event of type event_type occurs.
353  *
354  * The format of event_type is the following:
355  *  "ATK:&lt;atk_type&gt;:&lt;atk_event&gt;:&lt;atk_event_detail&gt;
356  *
357  * Where "ATK" works as the namespace, &lt;atk_interface&gt; is the name of
358  * the ATK type (interface or object), &lt;atk_event&gt; is the name of the
359  * signal defined on that interface and &lt;atk_event_detail&gt; is the
360  * gsignal detail of that signal. You can find more info about gsignal
361  * details here:
362  * http://developer.gnome.org/gobject/stable/gobject-Signals.html
363  *
364  * The first three parameters are mandatory. The last one is optional.
365  *
366  * For example:
367  *   ATK:AtkObject:state-change
368  *   ATK:AtkText:text-selection-changed
369  *   ATK:AtkText:text-insert:system
370  *
371  * Toolkit implementor note: ATK provides a default implementation for
372  * this virtual method. ATK implementors are discouraged from
373  * reimplementing this method.
374  *
375  * Toolkit implementor note: this method is not intended to be used by
376  * ATK implementors but by ATK consumers.
377  *
378  * ATK consumers note: as this method adds a listener for a given ATK
379  * type, that type should be already registered on the GType system
380  * before calling this method. A simple way to do that is creating an
381  * instance of #AtkNoOpObject. This class implements all ATK
382  * interfaces, so creating the instance will register all ATK types as
383  * a collateral effect.
384  *
385  * Returns: added event listener id, or 0 on failure.
386  **/
387 guint
388 atk_add_global_event_listener (GSignalEmissionHook listener,
389                                const gchar        *event_type)
390 {
391   guint retval;
392   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
393
394   if (klass->add_global_event_listener)
395     {
396       retval = klass->add_global_event_listener (listener, event_type);
397     }
398   else
399     {
400       retval = 0;
401     }
402   g_type_class_unref (klass);
403
404   return retval;
405 }
406
407 /**
408  * atk_remove_global_event_listener:
409  * @listener_id: the id of the event listener to remove
410  *
411  * @listener_id is the value returned by #atk_add_global_event_listener
412  * when you registered that event listener.
413  *
414  * Toolkit implementor note: ATK provides a default implementation for
415  * this virtual method. ATK implementors are discouraged from
416  * reimplementing this method.
417  *
418  * Toolkit implementor note: this method is not intended to be used by
419  * ATK implementors but by ATK consumers.
420  *
421  * Removes the specified event listener
422  **/
423 void
424 atk_remove_global_event_listener (guint listener_id)
425 {
426   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
427
428   if (klass && klass->remove_global_event_listener)
429     klass->remove_global_event_listener (listener_id);
430 }
431
432 /**
433  * atk_add_key_event_listener:
434  * @listener: the listener to notify
435  * @data: a #gpointer that points to a block of data that should be sent to the registered listeners,
436  *        along with the event notification, when it occurs.  
437  *
438  * Adds the specified function to the list of functions to be called
439  *        when a key event occurs.  The @data element will be passed to the
440  *        #AtkKeySnoopFunc (@listener) as the @func_data param, on notification.
441  *
442  * Returns: added event listener id, or 0 on failure.
443  **/
444 guint
445 atk_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data)
446 {
447   guint retval;
448   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
449   if (klass && klass->add_key_event_listener)
450     {
451       retval = klass->add_key_event_listener (listener, data);
452     }
453   else
454     {
455       retval = 0;
456     }
457
458   return retval;
459 }
460
461 /**
462  * atk_remove_key_event_listener:
463  * @listener_id: the id of the event listener to remove
464  *
465  * @listener_id is the value returned by #atk_add_key_event_listener
466  * when you registered that event listener.
467  *
468  * Removes the specified event listener.
469  **/
470 void
471 atk_remove_key_event_listener (guint listener_id)
472 {
473   AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
474
475   if (klass->remove_key_event_listener)
476     klass->remove_key_event_listener (listener_id);
477 }
478
479 /**
480  * atk_get_root:
481  *
482  * Gets the root accessible container for the current application.
483  *
484  * Returns: (transfer none): the root accessible container for the current
485  * application
486  **/
487 AtkObject*
488 atk_get_root (void)
489 {
490   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
491   AtkObject    *retval;
492   if (klass->get_root)
493     {
494       retval = klass->get_root ();
495     }
496   else
497     {
498       retval = NULL;
499     }
500   g_type_class_unref (klass);
501
502   return retval;
503 }
504
505 /**
506  * atk_get_focus_object:
507  *
508  * Gets the currently focused object.
509  * 
510  * Since: 1.6
511  *
512  * Returns: (transfer none): the currently focused object for the current
513  * application
514  **/
515 AtkObject*
516 atk_get_focus_object (void)
517 {
518   return previous_focus_object;
519 }
520
521 /**
522  * atk_get_toolkit_name:
523  *
524  * Gets name string for the GUI toolkit implementing ATK for this application.
525  *
526  * Returns: name string for the GUI toolkit implementing ATK for this application
527  **/
528 const gchar*
529 atk_get_toolkit_name (void)
530 {
531   const gchar *retval;
532   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
533   if (klass->get_toolkit_name)
534     {
535       retval = klass->get_toolkit_name ();
536     }
537   else
538     {
539       retval = NULL;
540     }
541   g_type_class_unref (klass);
542
543   return retval;
544 }
545
546 /**
547  * atk_get_toolkit_version:
548  *
549  * Gets version string for the GUI toolkit implementing ATK for this application.
550  *
551  * Returns: version string for the GUI toolkit implementing ATK for this application
552  **/
553 const gchar*
554 atk_get_toolkit_version (void)
555 {
556   const gchar *retval;
557   AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
558   if (klass->get_toolkit_version)
559     {
560       retval = klass->get_toolkit_version ();
561     }
562   else
563     {
564       retval = NULL;
565     }
566   g_type_class_unref (klass);
567
568   return retval;
569 }
570
571 /**
572  * atk_get_version:
573  *
574  * Gets the current version for ATK.
575  *
576  * Returns: version string for ATK
577  *
578  * Since: 1.20
579  */
580 const gchar *
581 atk_get_version (void)
582 {
583   return VERSION;
584 }
585
586 static void
587 atk_util_class_init (AtkUtilClass *klass)
588 {
589   klass->add_global_event_listener = atk_util_real_add_global_event_listener;
590   klass->remove_global_event_listener = atk_util_real_remove_global_event_listener;
591   klass->get_root = NULL;
592   klass->get_toolkit_name = NULL;
593   klass->get_toolkit_version = NULL;
594
595   listener_list = g_hash_table_new_full (g_int_hash, g_int_equal, NULL,
596                                          g_free);
597 }