Only query a relation set if it is non-NULL
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / adaptors / cache-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  * Copyright 2008, 2009 Codethink Ltd.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include <string.h>
27
28 #include <atk/atk.h>
29 #include <droute/droute.h>
30
31 #include "common/spi-dbus.h"
32 #include "accessible-cache.h"
33 #include "bridge.h"
34 #include "object.h"
35 #include "introspection.h"
36
37 /* TODO - This should possibly be a common define */
38 #define SPI_OBJECT_PREFIX "/org/at_spi"
39 #define SPI_CACHE_OBJECT_SUFFIX "/cache"
40 #define SPI_CACHE_OBJECT_PATH SPI_OBJECT_PREFIX SPI_CACHE_OBJECT_SUFFIX
41
42 #define SPI_OBJECT_REFERENCE_SIGNATURE "(" \
43                                           DBUS_TYPE_STRING_AS_STRING \
44                                           DBUS_TYPE_OBJECT_PATH_AS_STRING \
45                                        ")"
46                                           
47 #define SPI_CACHE_ITEM_SIGNATURE "(" \
48                                    SPI_OBJECT_REFERENCE_SIGNATURE \
49                                    SPI_OBJECT_REFERENCE_SIGNATURE \
50                                    SPI_OBJECT_REFERENCE_SIGNATURE \
51                                    DBUS_TYPE_ARRAY_AS_STRING \
52                                      SPI_OBJECT_REFERENCE_SIGNATURE \
53                                    DBUS_TYPE_ARRAY_AS_STRING \
54                                      DBUS_TYPE_STRING_AS_STRING \
55                                    DBUS_TYPE_STRING_AS_STRING \
56                                    DBUS_TYPE_UINT32_AS_STRING \
57                                    DBUS_TYPE_STRING_AS_STRING \
58                                    DBUS_TYPE_ARRAY_AS_STRING \
59                                      DBUS_TYPE_UINT32_AS_STRING \
60                                  ")"
61
62 /*---------------------------------------------------------------------------*/
63
64 /*
65  * Marshals the given AtkObject into the provided D-Bus iterator.
66  *
67  * The object is marshalled including all its client side cache data.
68  * The format of the structure is (o(so)a(so)assusau).
69  */
70 static void
71 append_cache_item (AtkObject * obj, gpointer data)
72 {
73   DBusMessageIter iter_struct, iter_sub_array;
74   dbus_uint32_t states[2];
75   int count;
76   AtkStateSet *set;
77   DBusMessageIter *iter_array = (DBusMessageIter *) data;
78
79   const char *name, *desc;
80   dbus_uint32_t role;
81
82   set = atk_object_ref_state_set (obj);
83   {
84     AtkObject *application, *parent;
85
86     dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
87                                       &iter_struct);
88
89     /* Marshall object path */
90     spi_object_append_reference (&iter_struct, obj);
91
92     role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
93
94     /* Marshall application */
95     application = spi_global_app_data->root; 
96     spi_object_append_reference (&iter_struct, application);
97
98     /* Marshall parent */
99     parent = atk_object_get_parent (obj);
100     if (parent == NULL)
101       {
102         /* TODO, move in to a 'Plug' wrapper. */
103         if (ATK_IS_PLUG (obj))
104           {
105             char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent");
106             char *bus_parent;
107             char *path_parent;
108
109             if (id)
110               {
111                 bus_parent = g_strdup (id);
112                 if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':')))
113                   {
114                     DBusMessageIter iter_parent;
115                     *(path_parent++) = '\0';
116                     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_STRUCT, NULL,
117                                                       &iter_parent);
118                     dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_STRING, &bus_parent);
119                     dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_OBJECT_PATH, &path_parent);
120                     dbus_message_iter_close_container (&iter_struct, &iter_parent);
121                   }
122                 else
123                   {
124                     spi_object_append_null_reference (&iter_struct);
125                   }
126               }
127             else
128               {
129                 spi_object_append_null_reference (&iter_struct);
130               }
131           }
132         else if (role != Accessibility_ROLE_APPLICATION)
133           spi_object_append_null_reference (&iter_struct);
134         else
135           spi_object_append_desktop_reference (&iter_struct);
136       }
137     else
138       {
139         spi_object_append_reference (&iter_struct, parent);
140       }
141
142     /* Marshall children */
143     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "(so)",
144                                       &iter_sub_array);
145     if (!atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS))
146       {
147         gint childcount, i;
148
149         childcount = atk_object_get_n_accessible_children (obj);
150         for (i = 0; i < childcount; i++)
151           {
152             AtkObject *child;
153             gchar *child_path;
154
155             child = atk_object_ref_accessible_child (obj, i);
156             spi_object_append_reference (&iter_sub_array, child);
157             g_object_unref (G_OBJECT (child));
158           }
159       }
160     if (ATK_IS_SOCKET (obj) && atk_socket_is_occupied (ATK_SOCKET (obj)))
161       {
162         AtkSocket *socket = ATK_SOCKET (obj);
163         gchar *child_name, *child_path;
164         child_name = g_strdup (socket->embedded_plug_id);
165         child_path = g_utf8_strchr (child_name + 1, -1, ':');
166         if (child_path)
167           {
168             DBusMessageIter iter_socket;
169             *(child_path++) = '\0';
170             dbus_message_iter_open_container (&iter_sub_array, DBUS_TYPE_STRUCT, NULL,
171                                               &iter_socket);
172             dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_STRING, &child_name);
173             dbus_message_iter_append_basic (&iter_socket, DBUS_TYPE_OBJECT_PATH, &child_path);
174             dbus_message_iter_close_container (&iter_sub_array, &iter_socket);
175           }
176         g_free (child_name);
177       }
178
179     dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
180
181     /* Marshall interfaces */
182     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
183                                       &iter_sub_array);
184     spi_object_append_interfaces (&iter_sub_array, obj);
185     dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
186
187     /* Marshall name */
188     name = atk_object_get_name (obj);
189     if (!name)
190       name = "";
191     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
192
193     /* Marshall role */
194     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
195
196     /* Marshall description */
197     desc = atk_object_get_description (obj);
198     if (!desc)
199       desc = "";
200     dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
201
202     /* Marshall state set */
203     spi_atk_state_set_to_dbus_array (set, states);
204     dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u",
205                                       &iter_sub_array);
206     for (count = 0; count < 2; count++)
207       {
208         dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32,
209                                         &states[count]);
210       }
211     dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
212   }
213   dbus_message_iter_close_container (iter_array, &iter_struct);
214   g_object_unref (set);
215 }
216
217 /*---------------------------------------------------------------------------*/
218
219 /* For use as a GHFunc */
220 static void
221 append_accessible_hf (gpointer key, gpointer obj_data, gpointer data)
222 {
223   /* Make sure it isn't a hyperlink */
224   if (ATK_IS_OBJECT (key))
225     append_cache_item (ATK_OBJECT (key), data);
226 }
227
228 /*---------------------------------------------------------------------------*/
229
230 static void
231 emit_cache_remove (SpiCache *cache, GObject * obj)
232 {
233   DBusMessage *message;
234
235   if ((message = dbus_message_new_signal (SPI_CACHE_OBJECT_PATH,
236                                           SPI_DBUS_INTERFACE_CACHE,
237                                           "RemoveAccessible")))
238     {
239       DBusMessageIter iter;
240       gchar *path;
241
242       dbus_message_iter_init_append (message, &iter);
243
244       spi_object_append_reference (&iter, ATK_OBJECT (obj));
245
246       dbus_connection_send (spi_global_app_data->bus, message, NULL);
247
248       dbus_message_unref (message);
249     }
250 }
251
252 static void
253 emit_cache_add (SpiCache *cache, GObject * obj)
254 {
255   AtkObject *accessible = ATK_OBJECT (obj);
256   DBusMessage *message;
257
258   if ((message = dbus_message_new_signal (SPI_CACHE_OBJECT_PATH,
259                                           SPI_DBUS_INTERFACE_CACHE,
260                                           "AddAccessible")))
261     {
262       DBusMessageIter iter;
263
264       dbus_message_iter_init_append (message, &iter);
265       append_cache_item (accessible, &iter);
266
267       dbus_connection_send (spi_global_app_data->bus, message, NULL);
268
269       dbus_message_unref (message);
270     }
271 }
272
273
274 /*---------------------------------------------------------------------------*/
275
276 static DBusMessage *
277 impl_GetRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
278 {
279   return spi_object_return_reference (message,
280                                       g_object_ref (G_OBJECT (spi_global_app_data->root)));
281 }
282
283 /*---------------------------------------------------------------------------*/
284
285 static DBusMessage *
286 impl_GetItems (DBusConnection * bus, DBusMessage * message, void *user_data)
287 {
288   DBusMessage *reply;
289   DBusMessageIter iter, iter_array;
290
291   reply = dbus_message_new_method_return (message);
292
293   dbus_message_iter_init_append (reply, &iter);
294   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
295                                     SPI_CACHE_ITEM_SIGNATURE, &iter_array);
296   spi_cache_foreach (spi_global_cache, append_accessible_hf, &iter_array);
297   dbus_message_iter_close_container (&iter, &iter_array);
298   return reply;
299 }
300
301 /*---------------------------------------------------------------------------*/
302
303 static DRouteMethod methods[] = {
304   {impl_GetRoot, "GetRoot"},
305   {impl_GetItems, "GetItems"},
306   {NULL, NULL}
307 };
308
309 void
310 spi_initialize_cache (DRoutePath * path)
311 {
312   droute_path_add_interface (path, SPI_DBUS_INTERFACE_CACHE, spi_org_a11y_atspi_Cache, methods, NULL);
313
314   g_signal_connect (spi_global_cache,
315                     "object-added",
316                     (GCallback) emit_cache_add,
317                     NULL);
318
319   g_signal_connect (spi_global_cache,
320                     "object-removed",
321                     (GCallback) emit_cache_remove,
322                     NULL);
323 };
324
325 /*END------------------------------------------------------------------------*/