2001-11-19 Michael Meeks <michael@ximian.com>
[platform/core/uifw/at-spi2-atk.git] / libspi / application.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001 Sun Microsystems Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * application.c: implements SpiApplication.idl
25  *
26  */
27 #include <string.h>
28 #include <config.h>
29 #include <bonobo/Bonobo.h>
30 #include <atk/atkutil.h>
31
32 /*
33  * This pulls the CORBA definitions for the "Accessibility::Accessible" server
34  */
35 #include <libspi/Accessibility.h>
36
37 /*
38  * This pulls the definition for the BonoboObject (GObject Type)
39  */
40 #include "application.h"
41
42 /*
43  * Our parent Gtk object type
44  */
45 #define PARENT_TYPE SPI_ACCESSIBLE_TYPE
46
47 /*
48  * A pointer to our parent object class
49  */
50 static SpiAccessibleClass *spi_application_parent_class;
51
52 static SpiApplication *the_app;
53
54 /* static methods */
55
56 static void notify_listeners (GList *listeners,
57                               Accessibility_Event *e,
58                               CORBA_Environment *ev);
59
60 static char *reverse_lookup_name_for_toolkit_event (char *toolkit_name);
61
62 static const char *
63 lookup_toolkit_event_for_name (const char *generic_name)
64 {
65     char *toolkit_specific_name;
66     SpiApplicationClass *klass = g_type_class_peek (SPI_APPLICATION_TYPE);
67 #ifdef SPI_DEBUG
68     fprintf (stderr, "looking for %s in hash table.\n", generic_name);
69 #endif
70     toolkit_specific_name =
71             (char *) g_hash_table_lookup (klass->toolkit_event_names, generic_name);
72 #ifdef SPI_DEBUG
73     fprintf (stderr, "generic event %s converted to %s\n", generic_name, toolkit_specific_name);
74 #endif
75     return toolkit_specific_name;
76 }
77
78 /*
79  * Implemented GObject::finalize
80  */
81 static void
82 spi_accessible_application_finalize (GObject *object)
83 {
84   /* TODO: any necessary cleanup */
85   (G_OBJECT_CLASS (spi_application_parent_class))->finalize (object);
86 }
87
88 static CORBA_string
89 impl_accessibility_application_get_toolkit_name (PortableServer_Servant servant,
90                                                  CORBA_Environment *ev)
91 {
92         return CORBA_string_dup (atk_get_toolkit_name ());
93 }
94
95 static CORBA_string
96 impl_accessibility_application_get_version (PortableServer_Servant servant,
97                                             CORBA_Environment *ev)
98 {
99         return CORBA_string_dup (atk_get_toolkit_version ());
100 }
101
102 static CORBA_long
103 impl_accessibility_application_get_id (PortableServer_Servant servant,
104                                        CORBA_Environment *ev)
105 {
106         SpiApplication *application = SPI_APPLICATION (
107                 bonobo_object_from_servant (servant));
108         return application->id;
109 }
110
111 static void
112 impl_accessibility_application_set_id (PortableServer_Servant servant,
113                                        const CORBA_long id,
114                                        CORBA_Environment *ev)
115 {
116         SpiApplication *application = SPI_APPLICATION (
117                 bonobo_object_from_servant (servant));
118         application->id = id;
119 }
120
121 #define APP_STATIC_BUFF_SZ 64
122
123 static gboolean
124 spi_application_object_event_listener (GSignalInvocationHint *signal_hint,
125                                    guint n_param_values,
126                                    const GValue *param_values,
127                                    gpointer data)
128 {
129   Accessibility_Event *e = Accessibility_Event__alloc();
130   AtkObject *aobject;
131   GObject *gobject;
132   SpiAccessible *source;
133   CORBA_Environment ev;
134   GSignalQuery signal_query;
135   const gchar *name;
136   char sbuf[APP_STATIC_BUFF_SZ];
137   char *generic_name;
138   
139   g_signal_query (signal_hint->signal_id, &signal_query);
140   name = signal_query.signal_name;
141   fprintf (stderr, "Received (object) signal %s:%s\n",
142            g_type_name (signal_query.itype), name);
143
144   /* TODO: move GTK dependency out of app.c into bridge */
145   snprintf(sbuf, APP_STATIC_BUFF_SZ, "Gtk:%s:%s", g_type_name (signal_query.itype), name);
146
147   generic_name = reverse_lookup_name_for_toolkit_event (sbuf);
148   gobject = g_value_get_object (param_values + 0);
149
150   /* notify the actual listeners */
151   if (ATK_IS_IMPLEMENTOR (gobject))
152   {
153     aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
154   }
155   else if (ATK_IS_OBJECT (gobject))
156   {
157     aobject = ATK_OBJECT (gobject);
158     g_object_ref (G_OBJECT (aobject));
159   }
160   else
161   {
162     aobject = NULL;
163     g_error("received event from non-AtkImplementor");
164   }
165
166   g_return_val_if_fail (generic_name, FALSE);
167   if (generic_name)
168     {
169         source = spi_accessible_new (aobject);
170         e->type = CORBA_string_dup (generic_name);
171         e->source = BONOBO_OBJREF (source);
172         /*
173          * no need to dup this ref, since it's inprocess               
174          * and will be dup'ed by (inprocess) notify_listeners() call below
175          */
176         e->detail1 = 0;
177         e->detail2 = 0;
178         if (the_app) notify_listeners (the_app->toolkit_listeners, e, &ev);
179         /* unref because the in-process notify has called b_o_dup_ref (e->source) */
180         bonobo_object_release_unref (e->source, &ev); 
181     }
182   /* and, decrement the refcount on atkobject, incremented moments ago:
183    *  the call to spi_accessible_new() above should have added an extra ref */
184   g_object_unref (G_OBJECT (aobject));
185
186   return TRUE;
187 }
188
189
190 static gboolean
191 spi_application_toolkit_event_listener (GSignalInvocationHint *signal_hint,
192                                     guint n_param_values,
193                                     const GValue *param_values,
194                                     gpointer data)
195 {
196   Accessibility_Event *e = Accessibility_Event__alloc();
197   AtkObject *aobject;
198   GObject *gobject;
199   SpiAccessible *source;
200   CORBA_Environment ev;
201   GSignalQuery signal_query;
202   const char *name;
203   char sbuf[APP_STATIC_BUFF_SZ];
204
205   g_signal_query (signal_hint->signal_id, &signal_query);
206   name = signal_query.signal_name;
207   fprintf (stderr, "Received signal %s:%s\n", g_type_name (signal_query.itype), name);
208
209   /* TODO: move GTK dependency out of app.c into bridge */
210   snprintf(sbuf, APP_STATIC_BUFF_SZ, "Gtk:%s:%s", g_type_name (signal_query.itype), name);
211
212   gobject = g_value_get_object (param_values + 0);
213   /* notify the actual listeners */
214   if (ATK_IS_IMPLEMENTOR (gobject))
215     {
216       aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
217       source = spi_accessible_new (aobject);
218       e->type = CORBA_string_dup (sbuf);
219       e->source = BONOBO_OBJREF (source);
220       e->detail1 = 0;
221       e->detail2 = 0;
222       if (the_app) notify_listeners (the_app->toolkit_listeners, e, &ev);
223       bonobo_object_unref (BONOBO_OBJECT (source));
224       g_object_unref (G_OBJECT (aobject));
225     }
226   return TRUE;
227 }
228
229 static void
230 impl_accessibility_application_register_toolkit_event_listener (PortableServer_Servant servant,
231                                                                 Accessibility_EventListener listener,
232                                                                 const CORBA_char *event_name,
233                                                                 CORBA_Environment *ev)
234 {
235   guint spi_listener_id;
236   spi_listener_id =
237      atk_add_global_event_listener (spi_application_toolkit_event_listener, (char *) event_name);
238   the_app->toolkit_listeners = g_list_append (the_app->toolkit_listeners,
239                                               CORBA_Object_duplicate (listener, ev));
240 #ifdef SPI_DEBUG
241   fprintf (stderr, "registered %d for toolkit events named: %s\n",
242            spi_listener_id,
243            event_name);
244 #endif
245 }
246
247 static void
248 impl_accessibility_application_register_object_event_listener (PortableServer_Servant servant,
249                                                                Accessibility_EventListener listener,
250                                                                const CORBA_char *event_name,
251                                                                CORBA_Environment *ev)
252 {
253   guint spi_listener_id = 0;
254   const char *toolkit_specific_event_name =
255           lookup_toolkit_event_for_name (event_name);
256   if (toolkit_specific_event_name)
257   {
258     spi_listener_id =
259        atk_add_global_event_listener (spi_application_object_event_listener,
260                                       CORBA_string_dup (toolkit_specific_event_name));
261     the_app->toolkit_listeners = g_list_append (the_app->toolkit_listeners,
262                                               CORBA_Object_duplicate (listener, ev));
263   }
264 #ifdef SPI_DEBUG
265   fprintf (stderr, "registered %d for object events named: %s\n",
266            spi_listener_id,
267            event_name);
268 #endif
269 }
270
271 static void
272 notify_listeners (GList *listeners, Accessibility_Event *e, CORBA_Environment *ev)
273 {
274     int n_listeners=0;
275     int i;
276     if (listeners) n_listeners = g_list_length (listeners);
277
278     for (i=0; i<n_listeners; ++i) {
279         Accessibility_EventListener listener;
280         e->source = bonobo_object_dup_ref (e->source, ev); 
281         listener = (Accessibility_EventListener) g_list_nth_data (listeners, i);
282         Accessibility_EventListener_notifyEvent (listener, e, ev);
283         /*
284          * when this (oneway) call completes, the CORBA refcount and
285          * Bonobo_Unknown refcount will be decremented by the recipient
286          */
287     }
288 }
289
290 static char *
291 reverse_lookup_name_for_toolkit_event (char *toolkit_specific_name)
292 {
293     char *generic_name;
294     SpiApplicationClass *klass = g_type_class_peek (SPI_APPLICATION_TYPE);
295 #ifdef SPI_DEBUG
296     fprintf (stderr, "(reverse lookup) looking for %s in hash table.\n", toolkit_specific_name);
297 #endif
298     generic_name =
299             (char *) g_hash_table_lookup (klass->generic_event_names, toolkit_specific_name);
300 #ifdef SPI_DEBUG
301     fprintf (stderr, "toolkit event %s converted to %s\n", toolkit_specific_name, generic_name);
302 #endif
303     return generic_name;
304 }
305
306 static void
307 init_toolkit_names (GHashTable **generic_event_names, GHashTable **toolkit_event_names)
308 {
309         *toolkit_event_names = g_hash_table_new (g_str_hash, g_str_equal);
310         *generic_event_names = g_hash_table_new (g_str_hash, g_str_equal);
311         g_hash_table_insert (*toolkit_event_names,
312                              "object:property-change",
313                              "Gtk:AtkObject:property-change");
314         g_hash_table_insert (*generic_event_names,
315                              "Gtk:AtkObject:property-change",
316                              "object:property-change");
317 #ifdef SPI_DEBUG
318         fprintf (stderr, "inserted spi_selection_changed hash\n");
319 #endif
320 }
321
322 static void
323 spi_application_class_init (SpiApplicationClass *klass)
324 {
325   GObjectClass * object_class = (GObjectClass *) klass;
326   POA_Accessibility_Application__epv *epv = &klass->epv;
327
328   spi_application_parent_class = g_type_class_ref (SPI_ACCESSIBLE_TYPE);
329
330   object_class->finalize = spi_accessible_application_finalize;
331
332   epv->_get_toolkitName = impl_accessibility_application_get_toolkit_name;
333   epv->_get_version = impl_accessibility_application_get_version;
334   epv->_get_id = impl_accessibility_application_get_id;
335   epv->_set_id = impl_accessibility_application_set_id;
336   epv->registerToolkitEventListener = impl_accessibility_application_register_toolkit_event_listener;
337   init_toolkit_names (&klass->generic_event_names, &klass->toolkit_event_names);
338 }
339
340 static void
341 spi_application_init (SpiApplication  *application)
342 {
343   SPI_ACCESSIBLE (application)->atko = g_object_new (atk_object_get_type(), NULL);
344   application->toolkit_listeners = (GList *) NULL;
345   the_app = application;
346 }
347
348 BONOBO_TYPE_FUNC_FULL (SpiApplication,
349                        Accessibility_Application,
350                        PARENT_TYPE, spi_application);
351
352 SpiApplication *
353 spi_application_new (AtkObject *app_root)
354 {
355     SpiApplication *retval =
356                SPI_APPLICATION (g_object_new (spi_application_get_type (), NULL));
357     SPI_ACCESSIBLE (retval)->atko = app_root;
358     g_object_ref (G_OBJECT (app_root));
359     return retval;
360 }