source: trunk/third/gail/gail/gailutil.c @ 20902

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