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