2008-08-29 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / atk-adaptor / tree.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 <string.h>
26 #include <droute/introspect-loader.h>
27
28 #include "accessible.h"
29 #include "bridge.h"
30
31 extern SpiAppData *this_app;
32 static gboolean update_pending = FALSE;
33
34 /*---------------------------------------------------------------------------*/
35
36 static const char *dumm = "/APath/1";
37
38 /*
39  * Marshals the given AtkObject into the provided D-Bus iterator.
40  *
41  * The object is marshalled including all its client side cache data.
42  * The format of the strucuture is (ooaoassus).
43  * This is used in the updateTree signal and the getTree method
44  * of the org.freedesktop.atspi.Tree interface.
45  */
46 static void
47 append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
48 {
49   AtkObject *obj;
50   DBusMessageIter *iter_array;
51   DBusMessageIter iter_struct, iter_sub_array;
52   DRouteData *data;
53
54   const char *name, *desc;
55   int i;
56   dbus_uint32_t role;
57   GSList *l;
58
59   obj = ATK_OBJECT(obj_data);
60   iter_array = (DBusMessageIter *) iter;
61   data = &(this_app->droute);
62
63   dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
64     {
65       AtkObject *parent;
66       gchar *path, *path_parent;
67
68       path = atk_dbus_get_path_from_ref(GPOINTER_TO_INT(ref));
69       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
70       g_free(path);
71
72       parent = atk_object_get_parent(obj);
73       if (parent == NULL)
74         path_parent = g_strdup("/");
75       else
76         path_parent = atk_dbus_get_path (parent);
77       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
78       g_free(path_parent);
79
80       dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o", &iter_sub_array);
81         {
82           gint childcount, i;
83
84           childcount = atk_object_get_n_accessible_children (obj);
85           for (i = 0; i < childcount; i++)
86             {
87               AtkObject *child;
88               gchar *child_path;
89               
90               child = atk_object_ref_accessible_child (obj, i);
91               child_path = atk_dbus_get_path (child);
92               g_object_unref(G_OBJECT(child));
93               dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_OBJECT_PATH, &child_path);
94               g_free (child_path);
95             }
96         }
97       dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
98
99       dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_sub_array);
100         {
101           for (l = data->interfaces; l; l = g_slist_next (l))
102             {
103               DRouteInterface *iface_def = (DRouteInterface *) l->data;
104               void *datum = NULL;
105
106               if (iface_def->get_datum)
107                 {
108                   datum = (*iface_def->get_datum) (path, data->user_data);
109                   if (!datum)
110                     continue;
111                 }
112               dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_STRING, &iface_def->name);
113               if (iface_def->free_datum)
114                 (*iface_def->free_datum) (datum);
115             }
116         }
117       dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
118
119       name = atk_object_get_name (obj);
120       if (!name)
121         name = "";
122       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
123
124       role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
125       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
126
127       desc = atk_object_get_description (obj);
128       if (!desc)
129         desc = "";
130       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
131     }      
132   dbus_message_iter_close_container (iter_array, &iter_struct);
133 }
134
135 /*---------------------------------------------------------------------------*/
136
137 /*
138  * Used to marshal array of objects to remove.
139  * Marshalls an object path onto the iter provided.
140  */
141 static void
142 append_accessible_path(gpointer ref_data, gpointer null, gpointer data)
143 {
144   guint ref;
145   gchar *path;
146   DBusMessageIter *iter_array;
147
148   iter_array = (DBusMessageIter *) data;
149   ref = GPOINTER_TO_INT(ref_data);
150   path = atk_dbus_get_path_from_ref(ref);
151   dbus_message_iter_append_basic (iter_array, DBUS_TYPE_OBJECT_PATH, &path);
152   g_free(path);
153 }
154
155 /*---------------------------------------------------------------------------*/
156
157 static gboolean
158 send_cache_update(gpointer d)
159 {
160   DBusMessage *message;
161   DBusMessageIter iter;
162   DBusMessageIter iter_array;
163   DRouteData *data;
164
165   data = &(this_app->droute);
166
167   message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateTree");
168
169   dbus_message_iter_init_append (message, &iter);
170
171   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)", &iter_array);
172   atk_dbus_foreach_update_list(append_accessible, &iter_array);
173   dbus_message_iter_close_container(&iter, &iter_array);
174
175   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &iter_array);
176   atk_dbus_foreach_remove_list(append_accessible_path, &iter_array);
177   dbus_message_iter_close_container(&iter, &iter_array);
178
179   dbus_connection_send(data->bus, message, NULL);
180   update_pending = FALSE;
181
182   return FALSE;
183 }
184
185 /*---------------------------------------------------------------------------*/
186
187 void
188 atk_tree_cache_needs_update(void)
189 {
190   if (!update_pending)
191     {
192       g_idle_add(send_cache_update, NULL);
193       update_pending = TRUE;
194     }
195 }
196
197 /*---------------------------------------------------------------------------*/
198
199 static DBusMessage *
200 impl_getRoot (DBusConnection *bus, DBusMessage *message, void *user_data)
201 {
202   AtkObject *root = atk_get_root();
203   char *path;
204   DBusMessage *reply;
205
206   if (root) path = atk_dbus_get_path(root);
207   if (!root || !path)
208     return spi_dbus_general_error (message);
209   reply = dbus_message_new_method_return (message);
210   dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
211   g_free (path);
212   return reply;
213 }
214
215 /*---------------------------------------------------------------------------*/
216
217 static DBusMessage *
218 impl_getTree (DBusConnection *bus, DBusMessage *message, void *user_data)
219 {
220   DBusMessage *reply;
221   DBusMessageIter iter, iter_array;
222   AtkObject *root = atk_get_root();
223
224   if (!root) 
225      return spi_dbus_general_error(message);
226   reply = dbus_message_new_method_return (message);
227
228   dbus_message_iter_init_append (reply, &iter);
229   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ooaoassus)", &iter_array);
230   atk_dbus_foreach_registered(append_accessible, &iter_array);
231   dbus_message_iter_close_container(&iter, &iter_array);
232   return reply;
233 }
234
235 /*---------------------------------------------------------------------------*/
236
237 static DBusMessage *
238 impl_introspect (DBusConnection *bus, DBusMessage *message, void *user_data)
239 {
240   const char *path;
241   GString *output;
242   char *final;
243
244   DBusMessage *reply;
245
246   path = dbus_message_get_path(message);
247
248   output = g_string_new(spi_introspection_header);
249   
250   g_string_append_printf(output, spi_introspection_node_element, path);
251
252   spi_append_interface(output, SPI_DBUS_INTERFACE_TREE);
253
254   g_string_append(output, spi_introspection_footer);
255   final = g_string_free(output, FALSE);
256
257   reply = dbus_message_new_method_return (message);
258   g_assert(reply != NULL);
259   dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
260                            DBUS_TYPE_INVALID);
261
262   g_free(final);
263   return reply;
264 }
265
266 /*---------------------------------------------------------------------------*/
267
268 static DBusHandlerResult
269 message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
270 {
271   const char *iface = dbus_message_get_interface (message);
272   const char *member = dbus_message_get_member (message);
273
274   DBusMessage *reply = NULL;
275
276   g_return_val_if_fail(iface != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
277   
278   if (!strcmp(iface, SPI_DBUS_INTERFACE_TREE))
279     {
280       if (!strcmp(member, "getRoot"))
281         {
282           reply = impl_getRoot(bus, message, user_data);
283         }
284
285       if (!strcmp(member, "getTree"))
286         {
287           reply = impl_getTree(bus, message, user_data);
288         }
289     }
290
291   if (!strcmp(iface, "org.freedesktop.DBus.Introspectable"))
292     {
293       if (!strcmp(member, "Introspect"))
294         {
295           reply = impl_introspect(bus, message, user_data);
296         }
297     }
298
299   if (reply)
300     {
301       dbus_connection_send (bus, reply, NULL);
302       dbus_message_unref (reply);
303     }
304
305   return DBUS_HANDLER_RESULT_HANDLED;
306 }
307
308 /*---------------------------------------------------------------------------*/
309
310 static DBusObjectPathVTable tree_vtable =
311 {
312   NULL,
313   &message_handler,
314   NULL, NULL, NULL, NULL
315 };
316
317 /*---------------------------------------------------------------------------*/
318
319 void
320 spi_register_tree_object(DBusConnection *bus,
321                          DRouteData *data,
322                          const char *path)
323 {
324   dbus_bool_t mem = FALSE;
325   mem = dbus_connection_register_object_path(bus, path, &tree_vtable, data);
326   g_assert(mem == TRUE);
327 }
328
329 /*END------------------------------------------------------------------------*/