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