1dce58cca7f005a1fd08c6ff439546fffd2e65ac
[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* lookup_toolkit_event_for_name (char *generic_name);
61
62 static char* reverse_lookup_name_for_toolkit_event (char *toolkit_name);
63
64 /*
65  * Implemented GObject::finalize
66  */
67 static void
68 spi_accessible_application_finalize (GObject *object)
69 {
70   /* TODO: any necessary cleanup */
71   (G_OBJECT_CLASS (spi_application_parent_class))->finalize (object);
72 }
73
74 static CORBA_string
75 impl_accessibility_application_get_toolkit_name (PortableServer_Servant servant,
76                                                  CORBA_Environment *ev)
77 {
78         return CORBA_string_dup (atk_get_toolkit_name ());
79 }
80
81 static CORBA_string
82 impl_accessibility_application_get_version (PortableServer_Servant servant,
83                                             CORBA_Environment *ev)
84 {
85         return CORBA_string_dup (atk_get_toolkit_version ());
86 }
87
88 static CORBA_long
89 impl_accessibility_application_get_id (PortableServer_Servant servant,
90                                        CORBA_Environment *ev)
91 {
92         SpiApplication *application = SPI_APPLICATION (
93                 bonobo_object_from_servant (servant));
94         return application->id;
95 }
96
97 static void
98 impl_accessibility_application_set_id (PortableServer_Servant servant,
99                                        const CORBA_long id,
100                                        CORBA_Environment *ev)
101 {
102         SpiApplication *application = SPI_APPLICATION (
103                 bonobo_object_from_servant (servant));
104         application->id = id;
105 }
106
107 #define APP_STATIC_BUFF_SZ 64
108
109 static gboolean
110 spi_application_object_event_listener (GSignalInvocationHint *signal_hint,
111                                    guint n_param_values,
112                                    const GValue *param_values,
113                                    gpointer data)
114 {
115   Accessibility_Event *e = Accessibility_Event__alloc();
116   AtkObject *aobject;
117   GObject *gobject;
118   SpiAccessible *source;
119   CORBA_Environment ev;
120   GSignalQuery signal_query;
121   gchar *name;
122   char sbuf[APP_STATIC_BUFF_SZ];
123   char *generic_name;
124   
125   g_signal_query (signal_hint->signal_id, &signal_query);
126   name = signal_query.signal_name;
127   fprintf (stderr, "Received (object) signal %s:%s\n",
128            g_type_name (signal_query.itype), name);
129
130   /* TODO: move GTK dependency out of app.c into bridge */
131   snprintf(sbuf, APP_STATIC_BUFF_SZ, "Gtk:%s:%s", g_type_name (signal_query.itype), name);
132
133   generic_name = reverse_lookup_name_for_toolkit_event (sbuf);
134   gobject = g_value_get_object (param_values + 0);
135
136   /* notify the actual listeners */
137   if (ATK_IS_IMPLEMENTOR (gobject))
138   {
139     aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
140   }
141   else if (ATK_IS_OBJECT (gobject))
142   {
143     aobject = ATK_OBJECT (gobject);
144     g_object_ref (G_OBJECT (aobject));
145   }
146   else
147   {
148     g_error("received event from non-AtkImplementor");
149   }
150
151   g_return_val_if_fail (generic_name, FALSE);
152   if (generic_name)
153     {
154         source = spi_accessible_new (aobject);
155         e->type = CORBA_string_dup (generic_name);
156         e->source = BONOBO_OBJREF (source);
157         /*
158          * no need to dup this ref, since it's inprocess               
159          * and will be dup'ed by (inprocess) notify_listeners() call below
160          */
161         e->detail1 = 0;
162         e->detail2 = 0;
163         if (the_app) notify_listeners (the_app->toolkit_listeners, e, &ev);
164         /* unref because the in-process notify has called b_o_dup_ref (e->source) */
165         bonobo_object_release_unref (e->source, &ev); 
166     }
167   /* and, decrement the refcount on atkobject, incremented moments ago:
168    *  the call to spi_accessible_new() above should have added an extra ref */
169   g_object_unref (G_OBJECT (aobject));
170
171   return TRUE;
172 }
173
174
175 static gboolean
176 spi_application_toolkit_event_listener (GSignalInvocationHint *signal_hint,
177                                     guint n_param_values,
178                                     const GValue *param_values,
179                                     gpointer data)
180 {
181   Accessibility_Event *e = Accessibility_Event__alloc();
182   AtkObject *aobject;
183   GObject *gobject;
184   SpiAccessible *source;
185   CORBA_Environment ev;
186   GSignalQuery signal_query;
187   gchar *name;
188   char sbuf[APP_STATIC_BUFF_SZ];
189
190   g_signal_query (signal_hint->signal_id, &signal_query);
191   name = signal_query.signal_name;
192   fprintf (stderr, "Received signal %s:%s\n", g_type_name (signal_query.itype), name);
193
194   /* TODO: move GTK dependency out of app.c into bridge */
195   snprintf(sbuf, APP_STATIC_BUFF_SZ, "Gtk:%s:%s", g_type_name (signal_query.itype), name);
196
197   gobject = g_value_get_object (param_values + 0);
198   /* notify the actual listeners */
199   if (ATK_IS_IMPLEMENTOR (gobject))
200     {
201       aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
202       source = spi_accessible_new (aobject);
203       e->type = CORBA_string_dup (sbuf);
204       e->source = BONOBO_OBJREF (source);
205       e->detail1 = 0;
206       e->detail2 = 0;
207       if (the_app) notify_listeners (the_app->toolkit_listeners, e, &ev);
208       bonobo_object_unref (source);
209       g_object_unref (G_OBJECT (aobject));
210     }
211   return TRUE;
212 }
213
214 static void
215 impl_accessibility_application_register_toolkit_event_listener (PortableServer_Servant servant,
216                                                                 Accessibility_EventListener listener,
217                                                                 const CORBA_char *event_name,
218                                                                 CORBA_Environment *ev)
219 {
220   guint spi_listener_id;
221   spi_listener_id =
222      atk_add_global_event_listener (spi_application_toolkit_event_listener, event_name);
223   the_app->toolkit_listeners = g_list_append (the_app->toolkit_listeners,
224                                               CORBA_Object_duplicate (listener, ev));
225 #ifdef SPI_DEBUG
226   fprintf (stderr, "registered %d for toolkit events named: %s\n",
227            spi_listener_id,
228            event_name);
229 #endif
230 }
231
232 static void
233 impl_accessibility_application_register_object_event_listener (PortableServer_Servant servant,
234                                                                Accessibility_EventListener listener,
235                                                                const CORBA_char *event_name,
236                                                                CORBA_Environment *ev)
237 {
238   guint spi_listener_id;
239   char *toolkit_specific_event_name = lookup_toolkit_event_for_name (event_name);
240   if (toolkit_specific_event_name)
241   {
242     spi_listener_id =
243        atk_add_global_event_listener (spi_application_object_event_listener,
244                                       CORBA_string_dup (toolkit_specific_event_name));
245     the_app->toolkit_listeners = g_list_append (the_app->toolkit_listeners,
246                                               CORBA_Object_duplicate (listener, ev));
247   }
248 #ifdef SPI_DEBUG
249   fprintf (stderr, "registered %d for object events named: %s\n",
250            spi_listener_id,
251            event_name);
252 #endif
253 }
254
255 static void
256 notify_listeners (GList *listeners, Accessibility_Event *e, CORBA_Environment *ev)
257 {
258     int n_listeners=0;
259     int i;
260     if (listeners) n_listeners = g_list_length (listeners);
261
262     for (i=0; i<n_listeners; ++i) {
263         Accessibility_EventListener listener;
264         e->source = bonobo_object_dup_ref (e->source, ev); 
265         listener = (Accessibility_EventListener) g_list_nth_data (listeners, i);
266         Accessibility_EventListener_notifyEvent (listener, e, ev);
267         /*
268          * when this (oneway) call completes, the CORBA refcount and
269          * Bonobo_Unknown refcount will be decremented by the recipient
270          */
271     }
272 }
273
274 static char *
275 lookup_toolkit_event_for_name (char *generic_name)
276 {
277     char *toolkit_specific_name;
278     SpiApplicationClass *klass = g_type_class_peek (SPI_APPLICATION_TYPE);
279 #ifdef SPI_DEBUG
280     fprintf (stderr, "looking for %s in hash table.\n", generic_name);
281 #endif
282     toolkit_specific_name =
283             (char *) g_hash_table_lookup (klass->toolkit_event_names, generic_name);
284 #ifdef SPI_DEBUG
285     fprintf (stderr, "generic event %s converted to %s\n", generic_name, toolkit_specific_name);
286 #endif
287     return toolkit_specific_name;
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 GType
349 spi_application_get_type (void)
350 {
351         static GType type = 0;
352
353         if (!type) {
354                 static const GTypeInfo tinfo = {
355                         sizeof (SpiApplicationClass),
356                         (GBaseInitFunc) NULL,
357                         (GBaseFinalizeFunc) NULL,
358                         (GClassInitFunc) spi_application_class_init,
359                         (GClassFinalizeFunc) NULL,
360                         NULL, /* class data */
361                         sizeof (SpiApplication),
362                         0, /* n preallocs */
363                         (GInstanceInitFunc) spi_application_init,
364                         NULL /* value table */
365                 };
366                 /*
367                  * Bonobo_type_unique auto-generates a load of
368                  * CORBA structures for us. All derived types must
369                  * use bonobo_type_unique.
370                  */
371                 type = bonobo_type_unique (
372                         PARENT_TYPE,
373                         POA_Accessibility_Application__init,
374                         NULL,
375                         G_STRUCT_OFFSET (SpiApplicationClass, epv),
376                         &tinfo,
377                         "SpiApplication");
378         }
379
380         return type;
381 }
382
383 SpiApplication *
384 spi_application_new (AtkObject *app_root)
385 {
386     SpiApplication *retval =
387                SPI_APPLICATION (g_object_new (spi_application_get_type (), NULL));
388     SPI_ACCESSIBLE (retval)->atko = app_root;
389     g_object_ref (G_OBJECT (app_root));
390     return retval;
391 }