2008-11-5 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / event.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008, Codethink Ltd.
6  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
7  * Copyright 2001, 2002, 2003 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <atk/atk.h>
26 #include <string.h>
27 #include "accessible.h"
28 #include "bridge.h"
29 #include "atk-dbus.h"
30
31 extern SpiAppData *app_data;
32
33 static GArray *listener_ids = NULL;
34
35 static gint atk_bridge_key_event_listener_id;
36 static gint atk_bridge_focus_tracker_id;
37
38
39 /*---------------------------------------------------------------------------*/
40
41 #define ITF_EVENT_OBJECT   "org.freedesktop.atspi.Event.Object"
42 #define ITF_EVENT_WINDOW   "org.freedesktop.atspi.Event.Window"
43 #define ITF_EVENT_DOCUMENT "org.freedekstop.atspi.Event.Document"
44 #define ITF_EVENT_FOCUS    "org.freedesktop.atspi.Event.Focus"
45
46 /*---------------------------------------------------------------------------*/
47
48 static gboolean
49 Accessibility_DeviceEventController_notifyListenersSync(const Accessibility_DeviceEvent *key_event)
50 {
51   DBusMessage *message;
52   DBusError error;
53   dbus_bool_t consumed = FALSE;
54
55   message = 
56   dbus_message_new_method_call(SPI_DBUS_NAME_REGISTRY, 
57                                SPI_DBUS_PATH_REGISTRY,
58                                SPI_DBUS_INTERFACE_DEC,
59                                "notifyListenersSync");
60
61   dbus_error_init(&error);
62   if (spi_dbus_marshal_deviceEvent(message, key_event))
63   {
64     DBusMessage *reply = dbus_connection_send_with_reply_and_block(app_data->droute.bus, message, 1000, &error);
65     if (reply)
66     {
67       DBusError error;
68       dbus_error_init(&error);
69       dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID);
70       dbus_message_unref(reply);
71     }
72   }
73   dbus_message_unref(message);
74   return consumed;
75 }
76
77 static void
78 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
79                                        AtkKeyEventStruct          *event)
80 {
81   keystroke->id        = (dbus_int32_t) event->keyval;
82   keystroke->hw_code   = (dbus_int16_t) event->keycode;
83   keystroke->timestamp = (dbus_uint32_t) event->timestamp;
84   keystroke->modifiers = (dbus_uint16_t) (event->state & 0xFFFF);
85   if (event->string)
86     {
87       gunichar c;
88
89       keystroke->event_string = g_strdup (event->string);
90       c = g_utf8_get_char_validated (event->string, -1);
91       if (c > 0 && g_unichar_isprint (c))
92         keystroke->is_text = TRUE;
93       else
94         keystroke->is_text = FALSE;
95     }
96   else
97     {
98       keystroke->event_string = g_strdup ("");
99       keystroke->is_text = FALSE;
100     }
101   switch (event->type)
102     {
103     case (ATK_KEY_EVENT_PRESS):
104       keystroke->type = Accessibility_KEY_PRESSED_EVENT;
105       break;
106     case (ATK_KEY_EVENT_RELEASE):
107       keystroke->type = Accessibility_KEY_RELEASED_EVENT;
108       break;
109     default:
110       keystroke->type = 0;
111       break;
112     }
113 #if 0  
114   g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
115            (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
116            (int) keystroke->modifiers,
117            keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
118 #endif
119 }
120
121
122 static gint
123 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
124 {
125   gboolean             result;
126   Accessibility_DeviceEvent key_event;
127
128   spi_init_keystroke_from_atk_key_event (&key_event, event);
129
130   result = Accessibility_DeviceEventController_notifyListenersSync (&key_event);
131
132   if (key_event.event_string) g_free (key_event.event_string);
133
134   return result;
135 }
136
137
138 /*---------------------------------------------------------------------------*/
139
140 /*
141  * Emits an AT-SPI event.
142  * AT-SPI events names are split into three parts:
143  * class:major:minor
144  * This is mapped onto D-Bus events as:
145  * D-Bus Interface:Signal Name:Detail argument
146  *
147  * Marshals a basic type into the 'any_data' attribute of
148  * the AT-SPI event.
149  */
150
151 /*
152  * This is a rather annoying function needed to replace
153  * NULL values of strings with the empty string. Null string
154  * values can be created by the atk_object_get_name or text selection
155  */
156 static const void *
157 provide_defaults(const gint type,
158                  const void *val)
159 {
160   switch (type)
161     {
162       case DBUS_TYPE_STRING:
163       case DBUS_TYPE_OBJECT_PATH:
164            if (!val)
165               return "";
166            else
167               return val;
168       default:
169            return val;
170     }
171 }
172
173
174 /* TODO Should we bother emiting events for objects that have not yet
175  * been added to the tree?
176  * 
177  * This gets difficult. Its entirely possible that an Accessible would have been
178  * added to the tree, but not yet reached the clients.
179  * In this case we would be wrongly surpressing an event.
180  */
181 static void 
182 emit(AtkObject  *accessible,
183      const char *klass,
184      const char *major,
185      const char *minor,
186      dbus_int32_t detail1,
187      dbus_int32_t detail2,
188      const char *type,
189      const void *val)
190 {
191   DBusMessage *sig;
192   DBusMessageIter iter, sub;
193   gchar *path, *cname, *t;
194
195   if (!klass) klass = "";
196   if (!major) major = "";
197   if (!minor) minor = "";
198
199   /*
200    * This is very annoying, but as '-' isn't a legal signal
201    * name in D-Bus (Why not??!?) The names need converting
202    * on this side, and again on the client side.
203    */
204   cname = g_strdup(major);
205   while ((t = strchr(cname, '-')) != NULL) *t = '_';
206
207   path = atk_dbus_get_path(accessible);
208   sig = dbus_message_new_signal(path, klass, cname);
209   g_free(cname);
210   g_free(path);
211
212   dbus_message_iter_init_append(sig, &iter);
213
214   dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
215   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail1);
216   dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &detail2);
217
218   dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type, &sub);
219   /*
220    * I need to convert the string signature to an integer type signature.
221    * DBUS_TYPE_INT32 is defined as 'i' whereas the string is "i".
222    * I should just be able to cast the first character of the string to an
223    * integer.
224    */
225   val = provide_defaults((int) *type, val);
226   dbus_message_iter_append_basic(&sub, (int) *type, &val);
227   dbus_message_iter_close_container(&iter, &sub);
228
229   dbus_connection_send(app_data->droute.bus, sig, NULL);
230   dbus_message_unref(sig);
231 }
232
233 /*---------------------------------------------------------------------------*/
234
235 /*
236  * Emits an AT-SPI event, marshalling a BoundingBox structure into the 
237  * 'any_data' variant of the event.
238  */
239 static void
240 emit_rect(AtkObject  *accessible,
241           const char *klass,
242           const char *major,
243           const char *minor,
244           AtkRectangle *rect)
245 {
246   DBusMessage *sig;
247   DBusMessageIter iter, variant, sub;
248   gchar *path, *cname, *t;
249   dbus_int32_t dummy = 0;
250
251   if (!klass) klass = "";
252   if (!major) major = "";
253   if (!minor) minor = "";
254
255   /*
256    * This is very annoying, but as '-' isn't a legal signal
257    * name in D-Bus (Why not??!?) The names need converting
258    * on this side, and again on the client side.
259    */
260   cname = g_strdup(major);
261   while ((t = strchr(cname, '-')) != NULL) *t = '_';
262
263   path = atk_dbus_get_path(accessible);
264   sig = dbus_message_new_signal(path, klass, cname);
265   g_free(path);
266   g_free(cname);
267
268   dbus_message_iter_init_append (sig, &iter);
269   dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &minor);
270   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
271   dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &dummy);
272
273   dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "(iiii)", &variant);
274     dbus_message_iter_open_container (&variant, DBUS_TYPE_STRUCT, NULL, &sub);
275       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->x));
276       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->y));
277       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->width));
278       dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &(rect->height));
279     dbus_message_iter_close_container (&variant, &sub);
280   dbus_message_iter_close_container (&iter, &variant);
281
282   dbus_connection_send(app_data->droute.bus, sig, NULL);
283 }
284
285 /*---------------------------------------------------------------------------*/
286
287 /*
288  * The tree update listener handles the following Atk signals:
289  *
290  * Gtk:AtkObject:property-change
291  * 
292  * With the folowing property names:
293  *
294  * accessible-name
295  * accessible-description
296  * accessible-parent
297  *
298  * It updates the server side accessible-object database, which
299  * will then syncronize with the client-side accessible cache.
300  * 
301  */
302 static gboolean
303 tree_update_listener (GSignalInvocationHint *signal_hint,
304                       guint                  n_param_values,
305                       const GValue          *param_values,
306                       gpointer               data)
307 {
308   AtkObject *accessible;
309   AtkPropertyValues *values;
310   const gchar *pname = NULL;
311
312   accessible = g_value_get_object (&param_values[0]);
313   values = (AtkPropertyValues*) g_value_get_pointer (&param_values[1]);
314
315   pname = values[0].property_name;
316
317   if (strcmp (pname, "accessible-name") == 0)
318     {
319       atk_dbus_notify_change(accessible);
320     }
321   else if (strcmp (pname, "accessible-description") == 0)
322     {
323       atk_dbus_notify_change(accessible);
324     }
325   else if (strcmp (pname, "accessible-parent") == 0)
326     {
327       atk_dbus_notify_change(accessible);
328     }
329   return TRUE;
330 }
331
332 /* 
333  * Handles the ATK signal 'Gtk:AtkObject:children-changed'.
334  *
335  * It updates the server side accessible-object database, which
336  * will then syncronize with the client-side accessible cache.
337  *
338  */
339 static gboolean
340 tree_update_children_listener (GSignalInvocationHint *signal_hint,
341                                guint                  n_param_values,
342                                const GValue          *param_values,
343                                gpointer               data)
344 {
345   AtkObject *accessible;
346
347   accessible = g_value_get_object (&param_values[0]);
348   atk_dbus_register_subtree(accessible);
349   return TRUE;
350 }
351
352 /*---------------------------------------------------------------------------*/
353
354 /*
355  * The focus listener handles the ATK 'focus' signal and forwards it
356  * as the AT-SPI event, 'focus:'
357  */
358 static void
359 focus_tracker (AtkObject *accessible)
360 {
361   emit(accessible, ITF_EVENT_FOCUS, "focus", "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
362 }
363
364 /*---------------------------------------------------------------------------*/
365
366 #define PCHANGE "property-change"
367
368 /* 
369  * This handler handles the following ATK signals and
370  * converts them to AT-SPI events:
371  *  
372  * Gtk:AtkObject:property-change -> object:property-change:(property-name)
373  *
374  * The property-name is part of the ATK property-change signal.
375  */
376 static gboolean
377 property_event_listener (GSignalInvocationHint *signal_hint,
378                          guint                  n_param_values,
379                          const GValue          *param_values,
380                          gpointer               data)
381 {
382   AtkObject *accessible;
383   AtkPropertyValues *values;
384
385   const gchar *pname = NULL;
386
387   AtkObject *otemp;
388   const gchar *stemp;
389   gint i;
390   
391   accessible = g_value_get_object (&param_values[0]);
392   values = (AtkPropertyValues*) g_value_get_pointer (&param_values[1]);
393
394   pname = values[0].property_name;
395
396   /* TODO Could improve this control statement by matching
397    * on only the end of the signal names,
398    */
399   if (strcmp (pname, "accessible-table-summary") == 0)
400     {
401       otemp = atk_table_get_summary(ATK_TABLE (accessible));
402       stemp = atk_dbus_get_path(otemp);
403       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
404     }
405   else if (strcmp (pname, "accessible-table-column-header") == 0)
406     {
407       i = g_value_get_int (&(values->new_value));
408       otemp = atk_table_get_column_header(ATK_TABLE (accessible), i);
409       stemp = atk_dbus_get_path(otemp);
410       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
411     }
412   else if (strcmp (pname, "accessible-table-row-header") == 0)
413     {
414       i = g_value_get_int (&(values->new_value));
415       otemp = atk_table_get_row_header(ATK_TABLE (accessible), i);
416       stemp = atk_dbus_get_path(otemp);
417       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, stemp);
418     }
419   else if (strcmp (pname, "accessible-table-row-description") == 0)
420     {
421       i = g_value_get_int (&(values->new_value));
422       stemp = atk_table_get_row_description(ATK_TABLE (accessible), i);
423       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
424     }
425   else if (strcmp (pname, "accessible-table-column-description") == 0)
426     {
427       i = g_value_get_int (&(values->new_value));
428       stemp = atk_table_get_column_description(ATK_TABLE (accessible), i);
429       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
430     }
431   else if (strcmp (pname, "accessible-table-caption-object") == 0)
432     {
433       otemp = atk_table_get_caption(ATK_TABLE(accessible));
434       stemp = atk_object_get_name(otemp);
435       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_STRING_AS_STRING, stemp);
436     }
437   else
438     {
439       emit(accessible, ITF_EVENT_OBJECT, PCHANGE, pname, 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
440     }
441   return TRUE;
442 }
443
444 /*---------------------------------------------------------------------------*/
445
446 #define STATE_CHANGED "state-changed"
447
448 /*
449  * The state event listener handles 'Gtk:AtkObject:state-change' ATK signals
450  * and forwards them as object:state-changed:(param-name) AT-SPI events. Where
451  * the param-name is part of the ATK state-change signal.
452  */
453 static gboolean
454 state_event_listener (GSignalInvocationHint *signal_hint,
455                       guint n_param_values,
456                       const GValue *param_values,
457                       gpointer data)
458 {
459   AtkObject *accessible;
460   gchar *pname;
461   guint detail1;
462
463   accessible = ATK_OBJECT(g_value_get_object (&param_values[0]));
464   pname = g_strdup (g_value_get_string (&param_values[1]));
465
466   /* TODO - Possibly ignore a change to the 'defunct' state.
467    * This is because without reference counting defunct objects should be removed.
468    */
469   detail1 = (g_value_get_boolean (&param_values[2])) ? 1 : 0;
470   emit(accessible, ITF_EVENT_OBJECT, STATE_CHANGED, pname, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
471   g_free (pname);
472   return TRUE;
473 }
474
475 /*---------------------------------------------------------------------------*/
476
477 /*
478  * The window event listener handles the following ATK signals and forwards
479  * them as AT-SPI events:
480  *
481  * window:create     -> window:create
482  * window:destroy    -> window:destroy
483  * window:minimize   -> window:minimize
484  * window:maximize   -> window:maximize
485  * window:activate   -> window:activate
486  * window:deactivate -> window:deactivate
487  */
488 static gboolean
489 window_event_listener (GSignalInvocationHint *signal_hint,
490                        guint n_param_values,
491                        const GValue *param_values,
492                        gpointer data)
493 {
494   AtkObject *accessible;
495   GSignalQuery signal_query;
496   const gchar *name, *s;
497   
498   g_signal_query (signal_hint->signal_id, &signal_query);
499   name = signal_query.signal_name;
500
501   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
502   s = atk_object_get_name (accessible);
503   emit(accessible, ITF_EVENT_WINDOW, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
504
505   return TRUE;
506 }
507
508 /*---------------------------------------------------------------------------*/
509
510 /* 
511  * The document event listener handles the following ATK signals
512  * and converts them to AT-SPI events:
513  *
514  * Gtk:AtkDocument:load-complete ->  document:load-complete
515  * Gtk:AtkDocument:load-stopped  ->  document:load-stopped
516  * Gtk:AtkDocument:reload        ->  document:reload
517  */
518 static gboolean
519 document_event_listener (GSignalInvocationHint *signal_hint,
520                          guint n_param_values,
521                          const GValue *param_values,
522                          gpointer data)
523 {
524   AtkObject *accessible;
525   GSignalQuery signal_query;
526   const gchar *name, *s;
527
528   g_signal_query (signal_hint->signal_id, &signal_query);
529   name = signal_query.signal_name;
530
531   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
532   s = atk_object_get_name (accessible);
533   emit(accessible, ITF_EVENT_DOCUMENT, name, "", 0, 0, DBUS_TYPE_STRING_AS_STRING, s);
534
535   return TRUE;
536 }
537
538 /*---------------------------------------------------------------------------*/
539
540 /*
541  * Signal handler for  "Gtk:AtkComponent:bounds-changed". Converts
542  * this to an AT-SPI event - "object:bounds-changed".
543  */
544 static gboolean
545 bounds_event_listener (GSignalInvocationHint *signal_hint,
546                        guint n_param_values,
547                        const GValue *param_values,
548                        gpointer data)
549 {
550   AtkObject *accessible;
551   AtkRectangle *atk_rect;
552   GSignalQuery signal_query;
553   const gchar *name, *s;
554
555   g_signal_query (signal_hint->signal_id, &signal_query);
556   name = signal_query.signal_name;
557
558   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
559
560   if (G_VALUE_HOLDS_BOXED (param_values + 1))
561     atk_rect = g_value_get_boxed (param_values + 1);
562
563   emit_rect(accessible, ITF_EVENT_OBJECT, name, "", atk_rect);
564   return TRUE;
565 }
566
567 /*---------------------------------------------------------------------------*/
568
569 /* 
570  * Handles the ATK signal 'Gtk:AtkObject:active-descendant-changed' and 
571  * converts it to the AT-SPI signal - 'object:active-descendant-changed'.
572  *
573  */
574 static gboolean
575 active_descendant_event_listener (GSignalInvocationHint *signal_hint,
576                                   guint n_param_values,
577                                   const GValue *param_values,
578                                   gpointer data)
579 {
580   AtkObject *accessible;
581   AtkObject *child;
582   GSignalQuery signal_query;
583   const gchar *name, *minor;
584   gchar *s;
585   gint detail1;
586
587   g_signal_query (signal_hint->signal_id, &signal_query);
588   name = signal_query.signal_name;
589
590   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
591   child = ATK_OBJECT(g_value_get_pointer (&param_values[1]));
592   g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
593   minor = g_quark_to_string (signal_hint->detail);
594
595   detail1 = atk_object_get_index_in_parent (child);
596   s = atk_dbus_get_path(child);
597
598   emit(accessible, ITF_EVENT_OBJECT, name, "", detail1, 0, DBUS_TYPE_OBJECT_PATH_AS_STRING, s);
599   g_free(s);
600   return TRUE;
601 }
602
603 /*---------------------------------------------------------------------------*/
604
605 /* 
606  * Handles the ATK signal 'Gtk:AtkHypertext:link-selected' and
607  * converts it to the AT-SPI signal - 'object:link-selected'
608  *
609  */
610 static gboolean
611 link_selected_event_listener (GSignalInvocationHint *signal_hint,
612                               guint n_param_values,
613                               const GValue *param_values,
614                               gpointer data)
615 {
616   AtkObject *accessible;
617   GSignalQuery signal_query;
618   const gchar *name, *minor;
619   gint detail1;
620
621   g_signal_query (signal_hint->signal_id, &signal_query);
622   name = signal_query.signal_name;
623
624   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
625   minor = g_quark_to_string (signal_hint->detail);
626
627   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
628         detail1 = g_value_get_int (&param_values[1]);
629
630   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, 0, DBUS_TYPE_INT32_AS_STRING, 0);
631   return TRUE;
632 }
633
634 /*---------------------------------------------------------------------------*/
635
636 /* 
637  * Handles the ATK signal 'Gtk:AtkText:text-changed' and
638  * converts it to the AT-SPI signal - 'object:text-changed'
639  *
640  */
641 static gboolean
642 text_changed_event_listener (GSignalInvocationHint *signal_hint,
643                              guint n_param_values,
644                              const GValue *param_values,
645                              gpointer data)
646 {
647   AtkObject *accessible;
648   GSignalQuery signal_query;
649   const gchar *name, *minor;
650   gchar *selected;
651   gint detail1, detail2;
652
653   g_signal_query (signal_hint->signal_id, &signal_query);
654   name = signal_query.signal_name;
655
656   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
657   minor = g_quark_to_string (signal_hint->detail);
658
659   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
660         detail1 = g_value_get_int (&param_values[1]);
661
662   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
663         detail2 = g_value_get_int (&param_values[2]);
664
665   selected = atk_text_get_text (ATK_TEXT (accessible), detail1, detail1+detail2);
666
667   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, selected);
668   return TRUE;
669 }
670
671 /*---------------------------------------------------------------------------*/
672
673 /* 
674  * Handles the ATK signal 'Gtk:AtkText:text-selection-changed' and
675  * converts it to the AT-SPI signal - 'object:text-selection-changed'
676  *
677  */
678 static gboolean
679 text_selection_changed_event_listener (GSignalInvocationHint *signal_hint,
680                                        guint n_param_values,
681                                        const GValue *param_values,
682                                        gpointer data)
683 {
684   AtkObject *accessible;
685   GSignalQuery signal_query;
686   const gchar *name, *minor;
687   gint detail1, detail2;
688
689   g_signal_query (signal_hint->signal_id, &signal_query);
690   name = signal_query.signal_name;
691
692   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
693   minor = g_quark_to_string (signal_hint->detail);
694
695   if (G_VALUE_TYPE (&param_values[1]) == G_TYPE_INT)
696         detail1 = g_value_get_int (&param_values[1]);
697
698   if (G_VALUE_TYPE (&param_values[2]) == G_TYPE_INT)
699         detail2 = g_value_get_int (&param_values[2]);
700
701   emit(accessible, ITF_EVENT_OBJECT, name, minor, detail1, detail2, DBUS_TYPE_STRING_AS_STRING, "");
702   return TRUE;
703 }
704
705 /*---------------------------------------------------------------------------*/
706
707 /*
708  * Generic signal converter and forwarder.
709  *
710  * Klass (Interface) org.freedesktop.atspi.Event.Object
711  * Major is the signal name.
712  * Minor is NULL.
713  * detail1 is 0.
714  * detail2 is 0.
715  * any_data is NULL.
716  */
717 static gboolean
718 generic_event_listener (GSignalInvocationHint *signal_hint,
719                         guint n_param_values,
720                         const GValue *param_values,
721                         gpointer data)
722 {
723   AtkObject *accessible;
724   GSignalQuery signal_query;
725   const gchar *name;
726
727   g_signal_query (signal_hint->signal_id, &signal_query);
728   name = signal_query.signal_name;
729
730   accessible = ATK_OBJECT(g_value_get_object(&param_values[0]));
731   emit(accessible, ITF_EVENT_OBJECT, name, "", 0, 0, DBUS_TYPE_INT32_AS_STRING, 0);
732   return TRUE;
733 }
734
735 /*---------------------------------------------------------------------------*/
736
737 /*
738  * Registers the provided function as a handler for the given signal name
739  * and stores the signal id returned so that the function may be
740  * de-registered later.
741  */
742 static void
743 add_signal_listener (GSignalEmissionHook listener, const char *signal_name)
744 {
745   guint id;
746
747   id = atk_add_global_event_listener (listener, signal_name);
748   g_array_append_val (listener_ids, id);
749 }
750
751 /*
752  * Initialization for the signal handlers.
753  *
754  * Registers all required signal handlers.
755  */
756 void
757 spi_atk_register_event_listeners (void)
758 {
759   /*
760    * Kludge to make sure the Atk interface types are registered, otherwise
761    * the AtkText signal handlers below won't get registered
762    */
763   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
764   AtkObject *bo = atk_no_op_object_new (ao);
765
766   g_object_unref (G_OBJECT (bo));
767   g_object_unref (ao);
768
769   /* Register for focus event notifications, and register app with central registry  */
770   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
771
772   atk_bridge_focus_tracker_id = atk_add_focus_tracker (focus_tracker);
773
774   add_signal_listener (tree_update_listener,          "Gtk:AtkObject:property-change");
775   add_signal_listener (tree_update_children_listener, "Gtk:AtkObject:children-changed");
776
777   add_signal_listener (property_event_listener,               "Gtk:AtkObject:property-change");
778   add_signal_listener (window_event_listener,                 "window:create");
779   add_signal_listener (window_event_listener,                 "window:destroy");
780   add_signal_listener (window_event_listener,                 "window:minimize");
781   add_signal_listener (window_event_listener,                 "window:maximize");
782   add_signal_listener (window_event_listener,                 "window:restore");
783   add_signal_listener (window_event_listener,                 "window:activate");
784   add_signal_listener (window_event_listener,                 "window:deactivate");
785   add_signal_listener (document_event_listener,               "Gtk:AtkDocument:load-complete");
786   add_signal_listener (document_event_listener,               "Gtk:AtkDocument:reload");
787   add_signal_listener (document_event_listener,               "Gtk:AtkDocument:load-stopped");
788   add_signal_listener (state_event_listener,                  "Gtk:AtkObject:state-change");
789   add_signal_listener (active_descendant_event_listener,      "Gtk:AtkObject:active-descendant-changed");
790   add_signal_listener (bounds_event_listener,                 "Gtk:AtkComponent:bounds-changed");
791   add_signal_listener (text_selection_changed_event_listener, "Gtk:AtkText:text-selection-changed");
792   add_signal_listener (text_changed_event_listener,           "Gtk:AtkText:text-changed");
793   add_signal_listener (link_selected_event_listener,          "Gtk:AtkHypertext:link-selected");
794   add_signal_listener (generic_event_listener,                "Gtk:AtkObject:visible-data-changed");
795   add_signal_listener (generic_event_listener,                "Gtk:AtkSelection:selection-changed");
796   add_signal_listener (generic_event_listener,                "Gtk:AtkText:text-caret-moved");
797   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-inserted");
798   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-reordered");
799   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:row-deleted");
800   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-inserted");
801   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-reordered");
802   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:column-deleted");
803   add_signal_listener (generic_event_listener,                "Gtk:AtkTable:model-changed");
804
805   /*
806    * May add the following listeners to implement preemptive key listening for GTK+
807    *
808    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
809    * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
810    */
811   atk_bridge_key_event_listener_id = atk_add_key_event_listener (spi_atk_bridge_key_listener, NULL);
812 }
813
814 /*---------------------------------------------------------------------------*/
815
816 /* 
817  * De-registers all ATK signal handlers.
818  */
819 void
820 spi_atk_deregister_event_listeners (void)
821 {
822   gint i;
823   GArray *ids = listener_ids;
824   listener_ids = NULL;
825
826   if (atk_bridge_focus_tracker_id)
827         atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
828   
829   for (i = 0; ids && i < ids->len; i++)
830     {
831           atk_remove_global_event_listener (g_array_index (ids, guint, i));
832     }
833   
834   if (atk_bridge_key_event_listener_id)
835           atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
836 }
837
838 /*---------------------------------------------------------------------------*/
839
840 /*
841  * TODO This function seems out of place here.
842  *
843  * Emits fake deactivate signals on all top-level windows.
844  * Used when shutting down AT-SPI, ensuring that all
845  * windows have been removed on the client side.
846  */
847 void
848 spi_atk_tidy_windows (void)
849 {
850   AtkObject *root;
851   gint n_children;
852   gint i;
853
854   root = atk_get_root ();
855   n_children = atk_object_get_n_accessible_children (root);
856   for (i = 0; i < n_children; i++)
857     {
858       AtkObject *child;
859       AtkStateSet *stateset;
860       const gchar *name;
861      
862       child = atk_object_ref_accessible_child (root, i);
863       stateset = atk_object_ref_state_set (child);
864       
865       name = atk_object_get_name (child);
866       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
867         {
868           emit(child, ITF_EVENT_WINDOW, "deactivate", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
869         }
870       g_object_unref (stateset);
871
872       emit(child, ITF_EVENT_WINDOW, "destroy", NULL, 0, 0, DBUS_TYPE_STRING_AS_STRING, name);
873       g_object_unref (child);
874     }
875 }
876
877 /*END------------------------------------------------------------------------*/