2f86d09158bea15558e72aeaf12ba8f770fa4812
[platform/upstream/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 Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 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  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include <string.h>
27
28 #include <atk/atk.h>
29 #include <droute/droute.h>
30
31 #include "spi-dbus.h"
32 #include "accessible-stateset.h"
33 #include "accessible-cache.h"
34 #include "bridge.h"
35 #include "object.h"
36 #include "introspection.h"
37
38 /* TODO - This should possibly be a common define */
39 #define SPI_OBJECT_PREFIX "/org/a11y/atspi"
40 #define SPI_CACHE_OBJECT_SUFFIX "/cache"
41 #define SPI_CACHE_OBJECT_PATH SPI_OBJECT_PREFIX SPI_CACHE_OBJECT_SUFFIX
42
43 #define SPI_OBJECT_REFERENCE_SIGNATURE "(" \
44                                           DBUS_TYPE_STRING_AS_STRING \
45                                           DBUS_TYPE_OBJECT_PATH_AS_STRING \
46                                        ")"
47                                           
48 #define SPI_CACHE_ITEM_SIGNATURE "(" \
49                                    SPI_OBJECT_REFERENCE_SIGNATURE \
50                                    SPI_OBJECT_REFERENCE_SIGNATURE \
51                                    SPI_OBJECT_REFERENCE_SIGNATURE \
52                                    DBUS_TYPE_INT32_AS_STRING \
53                                    DBUS_TYPE_INT32_AS_STRING \
54                                    DBUS_TYPE_ARRAY_AS_STRING \
55                                      DBUS_TYPE_STRING_AS_STRING \
56                                    DBUS_TYPE_STRING_AS_STRING \
57                                    DBUS_TYPE_UINT32_AS_STRING \
58                                    DBUS_TYPE_STRING_AS_STRING \
59                                    DBUS_TYPE_ARRAY_AS_STRING \
60                                      DBUS_TYPE_UINT32_AS_STRING \
61                                  ")"
62
63 /*---------------------------------------------------------------------------*/
64
65 static const char *
66 get_toolkit_name (AtkObject *obj)
67 {
68   static const char *toolkit_name = NULL;
69
70   if (!toolkit_name)
71     toolkit_name = atk_get_toolkit_name ();
72
73   if (!toolkit_name)
74     return "no toolkit name set yet";
75
76   /* TODO: query object attributes */
77   return toolkit_name;
78 }
79
80 static gboolean
81 should_call_index_in_parent (AtkObject *obj, AtkStateSet *set)
82 {
83   if (atk_state_set_contains_state (set, ATK_STATE_TRANSIENT))
84     return FALSE;
85
86   if (!strcmp (get_toolkit_name (obj), "gtk") &&
87       atk_object_get_role (obj) == ATK_ROLE_MENU_ITEM)
88     return FALSE;
89
90   return TRUE;
91 }
92
93 static gboolean
94 should_cache_children (AtkObject *obj, AtkStateSet *set)
95 {
96   if (atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS) ||
97       atk_state_set_contains_state (set, ATK_STATE_DEFUNCT))
98     return FALSE;
99
100   if (!strcmp (get_toolkit_name (obj), "gtk") &&
101       atk_object_get_role (obj) == ATK_ROLE_MENU)
102     return FALSE;
103
104   return TRUE;
105 }
106
107 /*
108  * Marshals the given AtkObject into the provided D-Bus iterator.
109  *
110  * The object is marshalled including all its client side cache data.
111  * The format of the structure is (o(so)iiassusau).
112  */
113 static void
114 append_cache_item (AtkObject * obj, gpointer data)
115 {
116   DBusMessageIter iter_struct, iter_sub_array;
117   dbus_uint32_t states[2];
118   dbus_int32_t count, index;
119   AtkStateSet *set;
120   DBusMessageIter *iter_array = (DBusMessageIter *) data;
121   const char *name, *desc;
122   dbus_uint32_t role;
123
124   set = atk_object_ref_state_set (obj);
125     AtkObject *application, *parent;
126
127   dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
128                                     &iter_struct);
129
130   /* Marshal object path */
131   spi_object_append_reference (&iter_struct, obj);
132
133   role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
134
135   /* Marshal application */
136   application = spi_global_app_data->root;
137   spi_object_append_reference (&iter_struct, application);
138
139   /* Marshal parent */
140   parent = atk_object_get_parent (obj);
141   if (parent == NULL)
142     {
143       /* TODO, move in to a 'Plug' wrapper. */
144       if (ATK_IS_PLUG (obj))
145         {
146           char *id = g_object_get_data (G_OBJECT (obj), "dbus-plug-parent");
147           char *bus_parent;
148           char *path_parent;
149
150           if (id)
151             {
152               bus_parent = g_strdup (id);
153               if (bus_parent && (path_parent = g_utf8_strchr (bus_parent + 1, -1, ':')))
154                 {
155                   DBusMessageIter iter_parent;
156                   *(path_parent++) = '\0';
157                   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_STRUCT, NULL,
158                                                     &iter_parent);
159                   dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_STRING, &bus_parent);
160                   dbus_message_iter_append_basic (&iter_parent, DBUS_TYPE_OBJECT_PATH, &path_parent);
161                   dbus_message_iter_close_container (&iter_struct, &iter_parent);
162                 }
163               else
164                 {
165                   spi_object_append_null_reference (&iter_struct);
166                 }
167             }
168           else
169             {
170               spi_object_append_null_reference (&iter_struct);
171             }
172         }
173       else if (role != ATSPI_ROLE_APPLICATION)
174         spi_object_append_null_reference (&iter_struct);
175       else
176         spi_object_append_desktop_reference (&iter_struct);
177     }
178   else
179     {
180       spi_object_append_reference (&iter_struct, parent);
181     }
182
183   /* Marshal index in parent */
184   index = (should_call_index_in_parent (obj, set)
185            ? atk_object_get_index_in_parent (obj) : -1);
186   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &index);
187
188   /* marshal child count */
189   count = (should_cache_children (obj, set)
190            ? atk_object_get_n_accessible_children (obj) : -1);
191
192   if (ATK_IS_SOCKET (obj) && atk_socket_is_occupied (ATK_SOCKET (obj)))
193     count = 1;
194   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_INT32, &count);
195
196   /* Marshal interfaces */
197   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
198                                     &iter_sub_array);
199   spi_object_append_interfaces (&iter_sub_array, obj);
200   dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
201
202   /* Marshal name */
203   name = atk_object_get_name (obj);
204   if (!name)
205     name = "";
206   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
207
208   /* Marshal role */
209   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
210
211   /* Marshal description */
212   desc = atk_object_get_description (obj);
213   if (!desc)
214     desc = "";
215   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
216
217   /* Marshal state set */
218   spi_atk_state_set_to_dbus_array (set, states);
219   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u",
220                                     &iter_sub_array);
221   for (count = 0; count < 2; count++)
222     {
223       dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32,
224                                       &states[count]);
225     }
226   dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
227
228   dbus_message_iter_close_container (iter_array, &iter_struct);
229   g_object_unref (set);
230 }
231
232 /*---------------------------------------------------------------------------*/
233
234 static void
235 ref_accessible_hf (gpointer key, gpointer obj_data, gpointer data)
236 {
237   g_object_ref (key);
238 }
239
240 /* For use as a GHFunc */
241 static void
242 append_accessible_hf (gpointer key, gpointer obj_data, gpointer data)
243 {
244   /* Make sure it isn't a hyperlink */
245   if (ATK_IS_OBJECT (key))
246     append_cache_item (ATK_OBJECT (key), data);
247 }
248
249 static void
250 add_to_list_hf (gpointer key, gpointer obj_data, gpointer data)
251 {
252   GSList **listptr = data;
253   *listptr = g_slist_prepend (*listptr, key);
254 }
255
256 /*---------------------------------------------------------------------------*/
257
258 static void
259 emit_cache_remove (SpiCache *cache, GObject * obj)
260 {
261   DBusMessage *message;
262
263   if ((message = dbus_message_new_signal (SPI_CACHE_OBJECT_PATH,
264                                           ATSPI_DBUS_INTERFACE_CACHE,
265                                           "RemoveAccessible")))
266     {
267       DBusMessageIter iter;
268
269       dbus_message_iter_init_append (message, &iter);
270
271       spi_object_append_reference (&iter, ATK_OBJECT (obj));
272
273       dbus_connection_send (spi_global_app_data->bus, message, NULL);
274
275       dbus_message_unref (message);
276     }
277 }
278
279 static void
280 emit_cache_add (SpiCache *cache, GObject * obj)
281 {
282   AtkObject *accessible = ATK_OBJECT (obj);
283   DBusMessage *message;
284
285   if ((message = dbus_message_new_signal (SPI_CACHE_OBJECT_PATH,
286                                           ATSPI_DBUS_INTERFACE_CACHE,
287                                           "AddAccessible")))
288     {
289       DBusMessageIter iter;
290
291       dbus_message_iter_init_append (message, &iter);
292       g_object_ref (accessible);
293       append_cache_item (accessible, &iter);
294       g_object_unref (accessible);
295
296       dbus_connection_send (spi_global_app_data->bus, message, NULL);
297
298       dbus_message_unref (message);
299     }
300 }
301
302
303 /*---------------------------------------------------------------------------*/
304
305 static DBusMessage *
306 impl_GetRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
307 {
308   return spi_object_return_reference (message, spi_global_app_data->root);
309 }
310
311 /*---------------------------------------------------------------------------*/
312
313 static DBusMessage *
314 impl_GetItems (DBusConnection * bus, DBusMessage * message, void *user_data)
315 {
316   DBusMessage *reply;
317   DBusMessageIter iter, iter_array;
318   GSList *pending_unrefs = NULL;
319
320   if (bus == spi_global_app_data->bus)
321     spi_atk_add_client (dbus_message_get_sender (message));
322
323   reply = dbus_message_new_method_return (message);
324
325   dbus_message_iter_init_append (reply, &iter);
326   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
327                                     SPI_CACHE_ITEM_SIGNATURE, &iter_array);
328   spi_cache_foreach (spi_global_cache, ref_accessible_hf, NULL);
329   spi_cache_foreach (spi_global_cache, append_accessible_hf, &iter_array);
330   spi_cache_foreach (spi_global_cache, add_to_list_hf, &pending_unrefs);
331   g_slist_free_full (pending_unrefs, g_object_unref);
332   dbus_message_iter_close_container (&iter, &iter_array);
333   return reply;
334 }
335
336 /*---------------------------------------------------------------------------*/
337
338 static DRouteMethod methods[] = {
339   {impl_GetRoot, "GetRoot"},
340   {impl_GetItems, "GetItems"},
341   {NULL, NULL}
342 };
343
344 void
345 spi_initialize_cache (DRoutePath * path)
346 {
347   droute_path_add_interface (path, ATSPI_DBUS_INTERFACE_CACHE, spi_org_a11y_atspi_Cache, methods, NULL);
348
349   g_signal_connect (spi_global_cache, "object-added",
350                     (GCallback) emit_cache_add, NULL);
351
352   g_signal_connect (spi_global_cache, "object-removed",
353                     (GCallback) emit_cache_remove, NULL);
354 };
355
356 /*END------------------------------------------------------------------------*/