]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gailutil.c
Drop remaining uses of gail_misc api
[~andy/gtk] / gtk / a11y / gailutil.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 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 <stdlib.h>
23 #include <string.h>
24 #include <gtk/gtk.h>
25 #include "gailutil.h"
26 #include "gailtoplevel.h"
27 #include "gtkwindowaccessible.h"
28
29 static void             gail_util_class_init                    (GailUtilClass          *klass);
30 static void             gail_util_init                          (GailUtil               *utils);
31 /* atkutil.h */
32
33 static guint            gail_util_add_global_event_listener     (GSignalEmissionHook    listener,
34                                                                  const gchar*           event_type);
35 static void             gail_util_remove_global_event_listener  (guint                  remove_listener);
36 static guint            gail_util_add_key_event_listener        (AtkKeySnoopFunc        listener,
37                                                                  gpointer               data);
38 static void             gail_util_remove_key_event_listener     (guint                  remove_listener);
39 static AtkObject*       gail_util_get_root                      (void);
40 static const gchar *gail_util_get_toolkit_name          (void);
41 static const gchar *gail_util_get_toolkit_version      (void);
42
43 /* AtkMisc */
44 static void             gail_misc_class_init                    (GailMiscClass          *klass);
45 static void             gail_misc_init                          (GailMisc               *misc);
46
47 static void gail_misc_threads_enter (AtkMisc *misc);
48 static void gail_misc_threads_leave (AtkMisc *misc);
49
50 /* Misc */
51
52 static void             _listener_info_destroy                  (gpointer               data);
53 static guint            add_listener                            (GSignalEmissionHook    listener,
54                                                                  const gchar            *object_type,
55                                                                  const gchar            *signal,
56                                                                  const gchar            *hook_data);
57 static void             do_window_event_initialization          (void);
58 static gboolean         state_event_watcher                     (GSignalInvocationHint  *hint,
59                                                                  guint                  n_param_values,
60                                                                  const GValue           *param_values,
61                                                                  gpointer               data);
62 static void             window_added                             (AtkObject             *atk_obj,
63                                                                   guint                 index,
64                                                                   AtkObject             *child);
65 static void             window_removed                           (AtkObject             *atk_obj,
66                                                                   guint                 index,
67                                                                   AtkObject             *child);
68 static gboolean        window_focus                              (GtkWidget             *widget,
69                                                                   GdkEventFocus         *event);
70 static gboolean         configure_event_watcher                 (GSignalInvocationHint  *hint,
71                                                                  guint                  n_param_values,
72                                                                  const GValue           *param_values,
73                                                                  gpointer               data);
74                                                                   
75
76 static AtkObject* root = NULL;
77 static GHashTable *listener_list = NULL;
78 static gint listener_idx = 1;
79 static GSList *key_listener_list = NULL;
80 static guint key_snooper_id = 0;
81
82 typedef struct _GailUtilListenerInfo GailUtilListenerInfo;
83 typedef struct _GailKeyEventInfo GailKeyEventInfo;
84
85 struct _GailUtilListenerInfo
86 {
87    gint key;
88    guint signal_id;
89    gulong hook_id;
90 };
91
92 struct _GailKeyEventInfo
93 {
94   AtkKeyEventStruct *key_event;
95   gpointer func_data;
96 };
97
98 G_DEFINE_TYPE (GailUtil, gail_util, ATK_TYPE_UTIL)
99
100 static void      
101 gail_util_class_init (GailUtilClass *klass)
102 {
103   AtkUtilClass *atk_class;
104   gpointer data;
105
106   data = g_type_class_peek (ATK_TYPE_UTIL);
107   atk_class = ATK_UTIL_CLASS (data);
108
109   atk_class->add_global_event_listener =
110     gail_util_add_global_event_listener;
111   atk_class->remove_global_event_listener =
112     gail_util_remove_global_event_listener;
113   atk_class->add_key_event_listener =
114     gail_util_add_key_event_listener;
115   atk_class->remove_key_event_listener =
116     gail_util_remove_key_event_listener;
117   atk_class->get_root = gail_util_get_root;
118   atk_class->get_toolkit_name = gail_util_get_toolkit_name;
119   atk_class->get_toolkit_version = gail_util_get_toolkit_version;
120
121   listener_list = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, 
122      _listener_info_destroy);
123 }
124
125 static void
126 gail_util_init (GailUtil *utils)
127 {
128 }
129
130 static guint
131 gail_util_add_global_event_listener (GSignalEmissionHook listener,
132                                      const gchar *event_type)
133 {
134   guint rc = 0;
135   gchar **split_string;
136
137   split_string = g_strsplit (event_type, ":", 3);
138
139   if (split_string)
140     {
141       if (!strcmp ("window", split_string[0]))
142         {
143           static gboolean initialized = FALSE;
144
145           if (!initialized)
146             {
147               do_window_event_initialization ();
148               initialized = TRUE;
149             }
150           rc = add_listener (listener, "GtkWindowAccessible", split_string[1], event_type);
151         }
152       else
153         {
154           rc = add_listener (listener, split_string[1], split_string[2], event_type);
155         }
156
157       g_strfreev (split_string);
158     }
159
160   return rc;
161 }
162
163 static void
164 gail_util_remove_global_event_listener (guint remove_listener)
165 {
166   if (remove_listener > 0)
167   {
168     GailUtilListenerInfo *listener_info;
169     gint tmp_idx = remove_listener;
170
171     listener_info = (GailUtilListenerInfo *)
172       g_hash_table_lookup(listener_list, &tmp_idx);
173
174     if (listener_info != NULL)
175       {
176         /* Hook id of 0 and signal id of 0 are invalid */
177         if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
178           {
179             /* Remove the emission hook */
180             g_signal_remove_emission_hook(listener_info->signal_id,
181               listener_info->hook_id);
182
183             /* Remove the element from the hash */
184             g_hash_table_remove(listener_list, &tmp_idx);
185           }
186         else
187           {
188             g_warning("Invalid listener hook_id %ld or signal_id %d\n",
189               listener_info->hook_id, listener_info->signal_id);
190           }
191       }
192     else
193       {
194         g_warning("No listener with the specified listener id %d", 
195           remove_listener);
196       }
197   }
198   else
199   {
200     g_warning("Invalid listener_id %d", remove_listener);
201   }
202 }
203
204
205 static
206 AtkKeyEventStruct *
207 atk_key_event_from_gdk_event_key (GdkEventKey *key)
208 {
209   AtkKeyEventStruct *event = g_new0 (AtkKeyEventStruct, 1);
210   switch (key->type)
211     {
212     case GDK_KEY_PRESS:
213             event->type = ATK_KEY_EVENT_PRESS;
214             break;
215     case GDK_KEY_RELEASE:
216             event->type = ATK_KEY_EVENT_RELEASE;
217             break;
218     default:
219             g_assert_not_reached ();
220             return NULL;
221     }
222   event->state = key->state;
223   event->keyval = key->keyval;
224   event->length = key->length;
225   if (key->string && key->string [0] && 
226       (key->state & GDK_CONTROL_MASK ||
227        g_unichar_isgraph (g_utf8_get_char (key->string))))
228     {
229       event->string = key->string;
230     }
231   else if (key->type == GDK_KEY_PRESS ||
232            key->type == GDK_KEY_RELEASE)
233     {
234       event->string = gdk_keyval_name (key->keyval);        
235     }
236   event->keycode = key->hardware_keycode;
237   event->timestamp = key->time;
238 #ifdef GAIL_DEBUG  
239   g_print ("GailKey:\tsym %u\n\tmods %x\n\tcode %u\n\ttime %lx\n",
240            (unsigned int) event->keyval,
241            (unsigned int) event->state,
242            (unsigned int) event->keycode,
243            (unsigned long int) event->timestamp);
244 #endif
245   return event;
246 }
247
248 typedef struct {
249   AtkKeySnoopFunc func;
250   gpointer        data;
251   guint           key;
252 } KeyEventListener;
253
254 static gint
255 gail_key_snooper (GtkWidget *the_widget, GdkEventKey *event, gpointer data)
256 {
257   GSList *l;
258   AtkKeyEventStruct *atk_event;
259   gboolean result;
260
261   atk_event = atk_key_event_from_gdk_event_key (event);
262
263   result = FALSE;
264
265   for (l = key_listener_list; l; l = l->next)
266     {
267       KeyEventListener *listener = l->data;
268
269       result |= listener->func (atk_event, listener->data);
270     }
271   g_free (atk_event);
272
273   return result;
274 }
275
276 static guint
277 gail_util_add_key_event_listener (AtkKeySnoopFunc  listener_func,
278                                   gpointer         listener_data)
279 {
280   static guint key = 0;
281   KeyEventListener *listener;
282
283   if (key_snooper_id == 0)
284     key_snooper_id = gtk_key_snooper_install (gail_key_snooper, NULL);
285
286   key++;
287
288   listener = g_slice_new0 (KeyEventListener);
289   listener->func = listener_func;
290   listener->data = listener_data;
291   listener->key = key;
292
293   key_listener_list = g_slist_append (key_listener_list, listener);
294
295   return key;
296 }
297
298 static void
299 gail_util_remove_key_event_listener (guint listener_key)
300 {
301   GSList *l;
302
303   for (l = key_listener_list; l; l = l->next)
304     {
305       KeyEventListener *listener = l->data;
306
307       if (listener->key == listener_key)
308         {
309           g_slice_free (KeyEventListener, listener);
310           key_listener_list = g_slist_delete_link (key_listener_list, l);
311
312           break;
313         }
314     }
315
316   if (key_listener_list == NULL)
317     {
318       gtk_key_snooper_remove (key_snooper_id);
319       key_snooper_id = 0;
320     }
321 }
322
323 static AtkObject*
324 gail_util_get_root (void)
325 {
326   if (!root)
327     {
328       root = g_object_new (GAIL_TYPE_TOPLEVEL, NULL);
329       atk_object_initialize (root, NULL);
330     }
331
332   return root;
333 }
334
335 static const gchar *
336 gail_util_get_toolkit_name (void)
337 {
338   return "gtk";
339 }
340
341 static const gchar *
342 gail_util_get_toolkit_version (void)
343 {
344  /*
345   * Version is passed in as a -D flag when this file is
346   * compiled.
347   */
348   return GTK_VERSION;
349 }
350
351 static void
352 _listener_info_destroy (gpointer data)
353 {
354    g_free(data);
355 }
356
357 static guint
358 add_listener (GSignalEmissionHook listener,
359               const gchar         *object_type,
360               const gchar         *signal,
361               const gchar         *hook_data)
362 {
363   GType type;
364   guint signal_id;
365   gint  rc = 0;
366
367   type = g_type_from_name (object_type);
368   if (type)
369     {
370       signal_id  = g_signal_lookup (signal, type);
371       if (signal_id > 0)
372         {
373           GailUtilListenerInfo *listener_info;
374
375           rc = listener_idx;
376
377           listener_info = g_malloc(sizeof(GailUtilListenerInfo));
378           listener_info->key = listener_idx;
379           listener_info->hook_id =
380                           g_signal_add_emission_hook (signal_id, 0, listener,
381                                                       g_strdup (hook_data),
382                                                       (GDestroyNotify) g_free);
383           listener_info->signal_id = signal_id;
384
385           g_hash_table_insert(listener_list, &(listener_info->key), listener_info);
386           listener_idx++;
387         }
388       else
389         {
390           g_warning("Invalid signal type %s\n", signal);
391         }
392     }
393   else
394     {
395       g_warning("Invalid object type %s\n", object_type);
396     }
397   return rc;
398 }
399
400 static void
401 do_window_event_initialization (void)
402 {
403   AtkObject *root;
404
405   /*
406    * Ensure that GtkWindowAccessibleClass exists.
407    */
408   g_type_class_ref (GTK_TYPE_WINDOW_ACCESSIBLE);
409   g_signal_add_emission_hook (g_signal_lookup ("window-state-event", GTK_TYPE_WIDGET),
410                               0, state_event_watcher, NULL, (GDestroyNotify) NULL);
411   g_signal_add_emission_hook (g_signal_lookup ("configure-event", GTK_TYPE_WIDGET),
412                               0, configure_event_watcher, NULL, (GDestroyNotify) NULL);
413
414   root = atk_get_root ();
415   g_signal_connect (root, "children-changed::add",
416                     (GCallback) window_added, NULL);
417   g_signal_connect (root, "children-changed::remove",
418                     (GCallback) window_removed, NULL);
419 }
420
421 static gboolean
422 state_event_watcher (GSignalInvocationHint  *hint,
423                      guint                  n_param_values,
424                      const GValue           *param_values,
425                      gpointer               data)
426 {
427   GObject *object;
428   GtkWidget *widget;
429   AtkObject *atk_obj;
430   AtkObject *parent;
431   GdkEventWindowState *event;
432   gchar *signal_name;
433   guint signal_id;
434
435   object = g_value_get_object (param_values + 0);
436   /*
437    * The object can be a GtkMenu when it is popped up; we ignore this
438    */
439   if (!GTK_IS_WINDOW (object))
440     return FALSE;
441
442   event = g_value_get_boxed (param_values + 1);
443   if (event->type == GDK_WINDOW_STATE)
444     return FALSE;
445   widget = GTK_WIDGET (object);
446
447   if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
448     {
449       signal_name = "maximize";
450     }
451   else if (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)
452     {
453       signal_name = "minimize";
454     }
455   else if (event->new_window_state == 0)
456     {
457       signal_name = "restore";
458     }
459   else
460     return TRUE;
461   
462   atk_obj = gtk_widget_get_accessible (widget);
463
464   if (GTK_IS_WINDOW_ACCESSIBLE (atk_obj))
465     {
466       parent = atk_object_get_parent (atk_obj);
467       if (parent == atk_get_root ())
468         {
469           signal_id = g_signal_lookup (signal_name, GTK_TYPE_WINDOW_ACCESSIBLE);
470           g_signal_emit (atk_obj, signal_id, 0);
471         }
472
473       return TRUE;
474     }
475   else
476     {
477       return FALSE;
478     }
479 }
480
481 static void
482 window_added (AtkObject *atk_obj,
483               guint     index,
484               AtkObject *child)
485 {
486   GtkWidget *widget;
487
488   if (!GTK_IS_WINDOW_ACCESSIBLE (child)) return;
489
490   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (child));
491   if (!widget)
492     return;
493
494   g_signal_connect (widget, "focus-in-event", (GCallback) window_focus, NULL);
495   g_signal_connect (widget, "focus-out-event", (GCallback) window_focus, NULL);
496   g_signal_emit (child, g_signal_lookup ("create", GTK_TYPE_WINDOW_ACCESSIBLE), 0);
497 }
498
499
500 static void
501 window_removed (AtkObject *atk_obj,
502                  guint     index,
503                  AtkObject *child)
504 {
505   GtkWidget *widget;
506   GtkWindow *window;
507
508   if (!GTK_IS_WINDOW_ACCESSIBLE (child)) return;
509
510   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (child));
511   if (!widget)
512     return;
513
514   window = GTK_WINDOW (widget);
515   /*
516    * Deactivate window if it is still focused and we are removing it. This
517    * can happen when a dialog displayed by gok is removed.
518    */
519   if (gtk_window_is_active (window) &&
520       gtk_window_has_toplevel_focus (window))
521     {
522       gchar *signal_name;
523       AtkObject *atk_obj;
524
525       atk_obj = gtk_widget_get_accessible (widget);
526       signal_name =  "deactivate";
527       g_signal_emit (atk_obj, g_signal_lookup (signal_name, GTK_TYPE_WINDOW_ACCESSIBLE), 0);
528     }
529
530   g_signal_handlers_disconnect_by_func (widget, (gpointer) window_focus, NULL);
531   g_signal_emit (child, g_signal_lookup ("destroy", GTK_TYPE_WINDOW_ACCESSIBLE), 0);
532 }
533
534 static gboolean
535 window_focus (GtkWidget     *widget,
536               GdkEventFocus *event)
537 {
538   gchar *signal_name;
539   AtkObject *atk_obj;
540
541   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
542
543   atk_obj = gtk_widget_get_accessible (widget);
544   signal_name =  (event->in) ? "activate" : "deactivate";
545   g_signal_emit (atk_obj, g_signal_lookup (signal_name, GTK_TYPE_WINDOW_ACCESSIBLE), 0);
546
547   return FALSE;
548 }
549
550 static gboolean 
551 configure_event_watcher (GSignalInvocationHint  *hint,
552                          guint                  n_param_values,
553                          const GValue           *param_values,
554                          gpointer               data)
555 {
556   GtkAllocation allocation;
557   GObject *object;
558   GtkWidget *widget;
559   AtkObject *atk_obj;
560   AtkObject *parent;
561   GdkEvent *event;
562   gchar *signal_name;
563   guint signal_id;
564
565   object = g_value_get_object (param_values + 0);
566   if (!GTK_IS_WINDOW (object))
567     /*
568      * GtkDrawingArea can send a GDK_CONFIGURE event but we ignore here
569      */
570     return FALSE;
571
572   event = g_value_get_boxed (param_values + 1);
573   if (event->type != GDK_CONFIGURE)
574     return FALSE;
575   widget = GTK_WIDGET (object);
576   gtk_widget_get_allocation (widget, &allocation);
577   if (allocation.x == ((GdkEventConfigure *)event)->x &&
578       allocation.y == ((GdkEventConfigure *)event)->y &&
579       allocation.width == ((GdkEventConfigure *)event)->width &&
580       allocation.height == ((GdkEventConfigure *)event)->height)
581     return TRUE;
582
583   if (allocation.width != ((GdkEventConfigure *)event)->width ||
584       allocation.height != ((GdkEventConfigure *)event)->height)
585     {
586       signal_name = "resize";
587     }
588   else
589     {
590       signal_name = "move";
591     }
592
593   atk_obj = gtk_widget_get_accessible (widget);
594   if (GTK_IS_WINDOW_ACCESSIBLE (atk_obj))
595     {
596       parent = atk_object_get_parent (atk_obj);
597       if (parent == atk_get_root ())
598         {
599           signal_id = g_signal_lookup (signal_name, GTK_TYPE_WINDOW_ACCESSIBLE);
600           g_signal_emit (atk_obj, signal_id, 0);
601         }
602
603       return TRUE;
604     }
605   else
606     {
607       return FALSE;
608     }
609 }
610
611 G_DEFINE_TYPE (GailMisc, gail_misc, ATK_TYPE_MISC)
612
613 static void      
614 gail_misc_class_init (GailMiscClass *klass)
615 {
616   AtkMiscClass *miscclass = ATK_MISC_CLASS (klass);
617   miscclass->threads_enter =
618     gail_misc_threads_enter;
619   miscclass->threads_leave =
620     gail_misc_threads_leave;
621   atk_misc_instance = g_object_new (GAIL_TYPE_MISC, NULL);
622 }
623
624 static void
625 gail_misc_init (GailMisc *misc)
626 {
627 }
628
629 static void gail_misc_threads_enter (AtkMisc *misc)
630 {
631   GDK_THREADS_ENTER ();
632 }
633
634 static void gail_misc_threads_leave (AtkMisc *misc)
635 {
636   GDK_THREADS_LEAVE ();
637 }