Only query a relation set if it is non-NULL
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / adaptors / accessible-adaptor.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2008 Novell, Inc.
6  * Copyright 2001, 2002 Sun Microsystems Inc.,
7  * Copyright 2001, 2002 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <atk/atk.h>
26 #include <droute/droute.h>
27
28 #include "common/spi-dbus.h"
29 #include "common/spi-stateset.h"
30 #include "object.h"
31 #include "introspection.h"
32
33 static dbus_bool_t
34 impl_get_Name (DBusMessageIter * iter, void *user_data)
35 {
36   AtkObject *object = (AtkObject *) user_data;
37
38   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
39
40   return droute_return_v_string (iter, atk_object_get_name (object));
41 }
42
43 static dbus_bool_t
44 impl_set_Name (DBusMessageIter * iter, void *user_data)
45 {
46   AtkObject *object = (AtkObject *) user_data;
47   const char *name = droute_get_v_string (iter);
48
49   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
50
51   atk_object_set_name (object, name);
52   return TRUE;
53 }
54
55 static dbus_bool_t
56 impl_get_Description (DBusMessageIter * iter, void *user_data)
57 {
58   AtkObject *object = (AtkObject *) user_data;
59
60   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
61
62   return droute_return_v_string (iter, atk_object_get_description (object));
63 }
64
65 static dbus_bool_t
66 impl_set_Description (DBusMessageIter * iter, void *user_data)
67 {
68   AtkObject *object = (AtkObject *) user_data;
69   const char *description = droute_get_v_string (iter);
70
71   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
72
73   atk_object_set_description (object, description);
74   return TRUE;
75 }
76
77 static dbus_bool_t
78 impl_get_Parent (DBusMessageIter * iter, void *user_data)
79 {
80   AtkObject *obj = (AtkObject *) user_data;
81   AtkObject *parent;
82   DBusMessageIter iter_variant;
83   dbus_uint32_t role;
84
85   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
86
87   role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
88
89   dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, "(so)",
90                                     &iter_variant);
91
92   parent = atk_object_get_parent (obj);
93   if (parent == NULL)
94     {
95       /* TODO, move in to a 'Plug' wrapper. */
96       if (ATK_IS_PLUG (obj))
97         {
98           char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent");
99           char *bus_parent;
100           char *path_parent;
101
102           if (id)
103             {
104               bus_parent = g_strdup (id);
105               if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':')))
106                 {
107                   DBusMessageIter iter_parent;
108                   *(path_parent++) = '\0';
109                   dbus_message_iter_open_container (&iter_variant, DBUS_TYPE_STRUCT, NULL,
110                                                     &iter_parent);
111                   dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_STRING, &bus_parent);
112                   dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_OBJECT_PATH, &path_parent);
113                   dbus_message_iter_close_container (&iter_variant, &iter_parent);
114                 }
115               else
116                 {
117                   spi_object_append_null_reference (&iter_variant);
118                 }
119             }
120           else
121             {
122               spi_object_append_null_reference (&iter_variant);
123             }
124         }
125       else if (role != Accessibility_ROLE_APPLICATION)
126          spi_object_append_null_reference (&iter_variant);
127       else
128          spi_object_append_desktop_reference (&iter_variant);
129       }
130   else
131     {
132       spi_object_append_reference (&iter_variant, parent);
133     }
134
135
136   dbus_message_iter_close_container (iter, &iter_variant);
137   return TRUE;
138 }
139
140 static dbus_bool_t
141 impl_get_ChildCount (DBusMessageIter * iter, void *user_data)
142 {
143   AtkObject *object = (AtkObject *) user_data;
144
145   g_return_val_if_fail (ATK_IS_OBJECT (user_data), FALSE);
146
147   return droute_return_v_int32 (iter,
148                                 atk_object_get_n_accessible_children
149                                 (object));
150 }
151
152 static DBusMessage *
153 impl_GetChildAtIndex (DBusConnection * bus,
154                       DBusMessage * message, void *user_data)
155 {
156   AtkObject *object = (AtkObject *) user_data;
157   DBusError error;
158   dbus_int32_t i;
159   AtkObject *child;
160
161   dbus_error_init (&error);
162   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
163                         droute_not_yet_handled_error (message));
164   if (!dbus_message_get_args 
165        (message, &error, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID))
166     {
167       return droute_invalid_arguments_error (message);
168     }
169   child = atk_object_ref_accessible_child (object, i);
170   return spi_object_return_reference (message, child);
171 }
172
173 static DBusMessage *
174 impl_GetChildren (DBusConnection * bus,
175                   DBusMessage * message, void *user_data)
176 {
177   AtkObject *object = (AtkObject *) user_data;
178   gint i;
179   gint count;
180   DBusMessage *reply;
181   DBusMessageIter iter, iter_array;
182
183   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
184                         droute_not_yet_handled_error (message));
185   count = atk_object_get_n_accessible_children (object);
186   reply = dbus_message_new_method_return (message);
187   if (!reply)
188     goto oom;
189   dbus_message_iter_init_append (reply, &iter);
190   if (!dbus_message_iter_open_container
191       (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array))
192     goto oom;
193   for (i = 0; i < count; i++)
194     {
195       AtkObject *child = atk_object_ref_accessible_child (object, i);
196       spi_object_append_reference (&iter_array, child); 
197       if (child)
198         g_object_unref (child);
199     }
200   if (!dbus_message_iter_close_container (&iter, &iter_array))
201     goto oom;
202   return reply;
203 oom:
204   // TODO: handle out-of-memory
205   return reply;
206 }
207
208 static DBusMessage *
209 impl_GetIndexInParent (DBusConnection * bus,
210                        DBusMessage * message, void *user_data)
211 {
212   AtkObject *object = (AtkObject *) user_data;
213   dbus_uint32_t rv;
214   DBusMessage *reply;
215
216   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
217                         droute_not_yet_handled_error (message));
218
219   rv = atk_object_get_index_in_parent (object);
220   reply = dbus_message_new_method_return (message);
221   dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv, DBUS_TYPE_INVALID);
222   return reply;
223 }
224
225 static gboolean
226 spi_init_relation_type_table (Accessibility_RelationType * types)
227 {
228   gint i;
229
230   for (i = 0; i < ATK_RELATION_LAST_DEFINED; i++)
231     types[i] = Accessibility_RELATION_NULL;
232
233   types[ATK_RELATION_CONTROLLED_BY] = Accessibility_RELATION_CONTROLLED_BY;
234   types[ATK_RELATION_CONTROLLER_FOR] = Accessibility_RELATION_CONTROLLER_FOR;
235   types[ATK_RELATION_LABEL_FOR] = Accessibility_RELATION_LABEL_FOR;
236   types[ATK_RELATION_LABELLED_BY] = Accessibility_RELATION_LABELLED_BY;
237   types[ATK_RELATION_MEMBER_OF] = Accessibility_RELATION_MEMBER_OF;
238   types[ATK_RELATION_NODE_CHILD_OF] = Accessibility_RELATION_NODE_CHILD_OF;
239   types[ATK_RELATION_FLOWS_TO] = Accessibility_RELATION_FLOWS_TO;
240   types[ATK_RELATION_FLOWS_FROM] = Accessibility_RELATION_FLOWS_FROM;
241   types[ATK_RELATION_SUBWINDOW_OF] = Accessibility_RELATION_SUBWINDOW_OF;
242   types[ATK_RELATION_EMBEDS] = Accessibility_RELATION_EMBEDS;
243   types[ATK_RELATION_EMBEDDED_BY] = Accessibility_RELATION_EMBEDDED_BY;
244   types[ATK_RELATION_POPUP_FOR] = Accessibility_RELATION_POPUP_FOR;
245   types[ATK_RELATION_PARENT_WINDOW_OF] =
246     Accessibility_RELATION_PARENT_WINDOW_OF;
247   types[ATK_RELATION_DESCRIPTION_FOR] =
248     Accessibility_RELATION_DESCRIPTION_FOR;
249   types[ATK_RELATION_DESCRIBED_BY] = Accessibility_RELATION_DESCRIBED_BY;
250
251   return TRUE;
252 }
253
254 static Accessibility_RelationType
255 spi_relation_type_from_atk_relation_type (AtkRelationType type)
256 {
257   static gboolean is_initialized = FALSE;
258   static Accessibility_RelationType
259     spi_relation_type_table[ATK_RELATION_LAST_DEFINED];
260   Accessibility_RelationType spi_type;
261
262   if (!is_initialized)
263     is_initialized = spi_init_relation_type_table (spi_relation_type_table);
264
265   if (type > ATK_RELATION_NULL && type < ATK_RELATION_LAST_DEFINED)
266     spi_type = spi_relation_type_table[type];
267   else
268     spi_type = Accessibility_RELATION_EXTENDED;
269   return spi_type;
270 }
271
272 static DBusMessage *
273 impl_GetRelationSet (DBusConnection * bus,
274                      DBusMessage * message, void *user_data)
275 {
276   AtkObject *object = (AtkObject *) user_data;
277   DBusMessage *reply;
278   AtkRelationSet *set;
279   DBusMessageIter iter, iter_array, iter_struct, iter_targets;
280   gint count;
281   gint i, j;
282
283   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
284                         droute_not_yet_handled_error (message));
285   reply = dbus_message_new_method_return (message);
286   if (!reply)
287     return NULL;
288   set = atk_object_ref_relation_set (object);
289   dbus_message_iter_init_append (reply, &iter);
290   if (!dbus_message_iter_open_container
291       (&iter, DBUS_TYPE_ARRAY, "(ua(so))", &iter_array))
292     {
293       goto oom;
294     }
295   count = 0;
296   if (set)
297     count = atk_relation_set_get_n_relations (set);
298   for (i = 0; i < count; i++)
299     {
300       AtkRelation *r = atk_relation_set_get_relation (set, i);
301       AtkRelationType rt;
302       GPtrArray *target;
303       dbus_uint32_t type;
304       if (!r)
305         continue;
306       rt = atk_relation_get_relation_type (r);
307       type = spi_relation_type_from_atk_relation_type (rt);
308       target = atk_relation_get_target (r);
309       if (!dbus_message_iter_open_container
310           (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct))
311         {
312           goto oom;
313         }
314       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &type);
315       if (!dbus_message_iter_open_container
316           (&iter_struct, DBUS_TYPE_ARRAY, "(so)", &iter_targets))
317         {
318           goto oom;
319         }
320       for (j = 0; j < target->len; j++)
321         {
322           AtkObject *obj = target->pdata[j];
323           char *path;
324           if (!obj)
325             continue;
326           spi_object_append_reference (&iter_targets, obj);
327         }
328       dbus_message_iter_close_container (&iter_struct, &iter_targets);
329       dbus_message_iter_close_container (&iter_array, &iter_struct);
330     }
331   dbus_message_iter_close_container (&iter, &iter_array);
332 oom:
333   // TODO: handle out of memory */
334   return reply;
335 }
336
337 static DBusMessage *
338 impl_GetRole (DBusConnection * bus, DBusMessage * message, void *user_data)
339 {
340   AtkObject *object = (AtkObject *) user_data;
341   gint role;
342   dbus_uint32_t rv;
343   DBusMessage *reply;
344
345   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
346                         droute_not_yet_handled_error (message));
347   role = atk_object_get_role (object);
348   rv = spi_accessible_role_from_atk_role (role);
349   reply = dbus_message_new_method_return (message);
350   if (reply)
351     {
352       dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv,
353                                 DBUS_TYPE_INVALID);
354     }
355   return reply;
356 }
357
358 static char *
359 impl_get_role_str (void *datum)
360 {
361   g_return_val_if_fail (ATK_IS_OBJECT (datum), g_strdup (""));
362   return g_strdup_printf ("%d",
363                           spi_accessible_role_from_atk_role
364                           (atk_object_get_role ((AtkObject *) datum)));
365 }
366
367 static DBusMessage *
368 impl_GetRoleName (DBusConnection * bus,
369                   DBusMessage * message, void *user_data)
370 {
371   AtkObject *object = (AtkObject *) user_data;
372   gint role;
373   const char *role_name;
374   DBusMessage *reply;
375
376   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
377                         droute_not_yet_handled_error (message));
378   role = atk_object_get_role (object);
379   role_name = atk_role_get_name (role);
380   if (!role_name)
381     role_name = "";
382   reply = dbus_message_new_method_return (message);
383   if (reply)
384     {
385       dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name,
386                                 DBUS_TYPE_INVALID);
387     }
388   return reply;
389 }
390
391 static DBusMessage *
392 impl_GetLocalizedRoleName (DBusConnection * bus,
393                            DBusMessage * message, void *user_data)
394 {
395   AtkObject *object = (AtkObject *) user_data;
396   gint role;
397   const char *role_name;
398   DBusMessage *reply;
399
400   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
401                         droute_not_yet_handled_error (message));
402   role = atk_object_get_role (object);
403   role_name = atk_role_get_localized_name (role);
404   if (!role_name)
405     role_name = "";
406   reply = dbus_message_new_method_return (message);
407   if (reply)
408     {
409       dbus_message_append_args (reply, DBUS_TYPE_STRING, &role_name,
410                                 DBUS_TYPE_INVALID);
411     }
412   return reply;
413 }
414
415 static DBusMessage *
416 impl_GetState (DBusConnection * bus, DBusMessage * message, void *user_data)
417 {
418   AtkObject *object = (AtkObject *) user_data;
419
420   DBusMessage *reply = NULL;
421   DBusMessageIter iter, iter_array;
422
423   dbus_uint32_t states[2];
424
425   guint count;
426
427   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
428                         droute_not_yet_handled_error (message));
429
430   reply = dbus_message_new_method_return (message);
431   dbus_message_iter_init_append (reply, &iter);
432
433   spi_atk_state_to_dbus_array (object, states);
434   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "u", &iter_array);
435   for (count = 0; count < 2; count++)
436     {
437       dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_UINT32,
438                                       &states[count]);
439     }
440   dbus_message_iter_close_container (&iter, &iter_array);
441   return reply;
442 }
443
444 static DBusMessage *
445 impl_GetAttributes (DBusConnection * bus,
446                     DBusMessage * message, void *user_data)
447 {
448   AtkObject *object = (AtkObject *) user_data;
449   AtkAttributeSet *attributes;
450   DBusMessage *reply = NULL;
451   DBusMessageIter iter;
452
453   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
454                         droute_not_yet_handled_error (message));
455
456   attributes = atk_object_get_attributes (object);
457
458   reply = dbus_message_new_method_return (message);
459   dbus_message_iter_init_append (reply, &iter);
460   spi_object_append_attribute_set (&iter, attributes);
461
462   atk_attribute_set_free (attributes);
463
464   return reply;
465 }
466
467 static DBusMessage *
468 impl_GetApplication (DBusConnection * bus,
469                      DBusMessage * message, void *user_data)
470 {
471   AtkObject *root = g_object_ref (atk_get_root ());
472   return spi_object_return_reference (message, root);
473 }
474
475 static DBusMessage *
476 impl_GetInterfaces (DBusConnection * bus,
477                     DBusMessage * message, void *user_data)
478 {
479   AtkObject *object = (AtkObject *) user_data;
480   gint role;
481   const char *role_name;
482   DBusMessage *reply;
483   DBusMessageIter iter, iter_array;
484
485   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
486                         droute_not_yet_handled_error (message));
487   reply = dbus_message_new_method_return (message);
488   if (reply)
489     {
490       dbus_message_iter_init_append (reply, &iter);
491       dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s",
492                                         &iter_array);
493       spi_object_append_interfaces (&iter_array, object);
494       dbus_message_iter_close_container (&iter, &iter_array);
495     }
496   return reply;
497 }
498
499 static DBusMessage *
500 impl_Embedded (DBusConnection *bus,
501                     DBusMessage *message,
502                     void *user_data)
503 {
504   AtkObject *object = (AtkObject *) user_data;
505   char *path;
506   gchar *id;
507
508   if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID))
509     {
510       return droute_invalid_arguments_error (message);
511     }
512   id = g_object_get_data (G_OBJECT (object), "dbus-plug-parent");
513   if (id)
514     g_free (id);
515   id = g_strconcat (dbus_message_get_sender (message), ":", path, NULL);
516   g_object_set_data (G_OBJECT (object), "dbus-plug-parent", id);
517   return dbus_message_new_method_return (message);
518 }
519
520 static DRouteMethod methods[] = {
521   {impl_GetChildAtIndex, "GetChildAtIndex"},
522   {impl_GetChildren, "GetChildren"},
523   {impl_GetIndexInParent, "GetIndexInParent"},
524   {impl_GetRelationSet, "GetRelationSet"},
525   {impl_GetRole, "GetRole"},
526   {impl_GetRoleName, "GetRoleName"},
527   {impl_GetLocalizedRoleName, "GetLocalizedRoleName"},
528   {impl_GetState, "GetState"},
529   {impl_GetAttributes, "GetAttributes"},
530   {impl_GetApplication, "GetApplication"},
531   {impl_GetInterfaces, "GetInterfaces"},
532   {impl_Embedded, "Embedded"},
533   {NULL, NULL}
534 };
535
536 static DRouteProperty properties[] = {
537   {impl_get_Name, impl_set_Name, "Name"},
538   {impl_get_Description, impl_set_Description, "Description"},
539   {impl_get_Parent, NULL, "Parent"},
540   {impl_get_ChildCount, NULL, "ChildCount"},
541   {NULL, NULL, NULL}
542 };
543
544 void
545 spi_initialize_accessible (DRoutePath * path)
546 {
547   droute_path_add_interface (path,
548                              SPI_DBUS_INTERFACE_ACCESSIBLE,
549                              spi_org_a11y_atspi_Accessible,     
550                              methods, properties);
551 };