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