2008-12-17 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
27 #include <atk/atk.h>
28 #include <droute/droute.h>
29
30 #include "atk-dbus.h"
31 #include "spi-common/spi-dbus.h"
32 #include "bridge.h"
33
34 static gboolean update_pending = FALSE;
35
36 /*---------------------------------------------------------------------------*/
37
38 static void
39 append_atk_object_interfaces (AtkObject *object, DBusMessageIter *iter)
40 {
41   const gchar *itf;
42
43   itf = SPI_DBUS_INTERFACE_ACCESSIBLE;
44   dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
45
46   if (ATK_IS_ACTION (object))
47     {
48       itf = SPI_DBUS_INTERFACE_ACTION;
49       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
50     }
51
52   if (ATK_IS_COMPONENT (object))
53     {
54       itf = SPI_DBUS_INTERFACE_COMPONENT;
55       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
56     }
57
58   if (ATK_IS_EDITABLE_TEXT (object))
59     {
60       itf = SPI_DBUS_INTERFACE_EDITABLE_TEXT;
61       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
62     }
63
64   if (ATK_IS_TEXT (object))
65     {
66       itf = SPI_DBUS_INTERFACE_TEXT;
67       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
68     }
69
70   if (ATK_IS_HYPERTEXT (object))
71     {
72       itf = SPI_DBUS_INTERFACE_HYPERTEXT;
73       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
74     }
75
76   if (ATK_IS_IMAGE (object))
77     {
78       itf = SPI_DBUS_INTERFACE_IMAGE;
79       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
80     }
81
82   if (ATK_IS_SELECTION (object))
83     {
84       itf = SPI_DBUS_INTERFACE_SELECTION;
85       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
86     }
87
88   if (ATK_IS_TABLE (object))
89     {
90       itf = SPI_DBUS_INTERFACE_TABLE;
91       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
92     }
93
94   if (ATK_IS_VALUE (object))
95     {
96       itf = SPI_DBUS_INTERFACE_VALUE;
97       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
98     }
99
100   if (ATK_IS_STREAMABLE_CONTENT (object))
101     {
102       itf = "org.freedesktop.atspi.StreamableContent";
103       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
104     }
105
106   if (ATK_IS_DOCUMENT (object))
107     {
108       itf = "org.freedesktop.atspi.Collection";
109       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
110       itf = SPI_DBUS_INTERFACE_DOCUMENT;
111       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
112     }
113
114   if (ATK_IS_HYPERLINK_IMPL (object))
115     {
116       itf = SPI_DBUS_INTERFACE_HYPERLINK;
117       dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &itf);
118     }
119 }
120
121 /*---------------------------------------------------------------------------*/
122
123 static const char *dumm = "/APath/1";
124
125 /*
126  * Marshals the given AtkObject into the provided D-Bus iterator.
127  *
128  * The object is marshalled including all its client side cache data.
129  * The format of the structure is (ooaoassusau).
130  * This is used in the updateTree signal and the getTree method
131  * of the org.freedesktop.atspi.Tree interface.
132  */
133 static void
134 append_accessible(gpointer ref, gpointer obj_data, gpointer iter)
135 {
136   AtkObject *obj;
137   DBusMessageIter *iter_array;
138   DBusMessageIter iter_struct, iter_sub_array;
139   dbus_int32_t states [2];
140   int count;
141
142   const char *name, *desc;
143   int i;
144   dbus_uint32_t role;
145   GSList *l;
146
147   obj = ATK_OBJECT(obj_data);
148   iter_array = (DBusMessageIter *) iter;
149
150   dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
151     {
152       AtkObject *parent;
153       gchar *path, *path_parent;
154
155       path = atk_dbus_get_path_from_ref(GPOINTER_TO_INT(ref));
156       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
157
158       parent = atk_object_get_parent(obj);
159       if (parent == NULL)
160         path_parent = g_strdup("/");
161       else
162         path_parent = atk_dbus_get_path (parent);
163       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
164       g_free(path_parent);
165
166       dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o", &iter_sub_array);
167         {
168           gint childcount, i;
169
170           childcount = atk_object_get_n_accessible_children (obj);
171           for (i = 0; i < childcount; i++)
172             {
173               AtkObject *child;
174               gchar *child_path;
175
176               child = atk_object_ref_accessible_child (obj, i);
177               child_path = atk_dbus_get_path (child);
178               g_object_unref(G_OBJECT(child));
179               dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_OBJECT_PATH, &child_path);
180               g_free (child_path);
181             }
182         }
183       dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
184
185       dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_sub_array);
186       append_atk_object_interfaces (obj, &iter_sub_array);
187       dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
188
189       name = atk_object_get_name (obj);
190       if (!name)
191         name = "";
192       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
193
194       role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
195       dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
196
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       g_free(path);
203     }
204   spi_atk_state_to_dbus_array (obj, &states);
205       dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "u", &iter_sub_array);
206   for (count = 0; count < 2; count++)
207     dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_UINT32, &states[count]);
208       dbus_message_iter_close_container (&iter_struct, &iter_sub_array);
209   dbus_message_iter_close_container (iter_array, &iter_struct);
210 }
211
212 /*---------------------------------------------------------------------------*/
213
214 /*
215  * Used to marshal array of objects to remove.
216  * Marshalls an object path onto the iter provided.
217  */
218 static void
219 append_accessible_path(gpointer ref_data, gpointer null, gpointer data)
220 {
221   guint ref;
222   gchar *path;
223   DBusMessageIter *iter_array;
224
225   iter_array = (DBusMessageIter *) data;
226   ref = GPOINTER_TO_INT(ref_data);
227   path = atk_dbus_get_path_from_ref(ref);
228   dbus_message_iter_append_basic (iter_array, DBUS_TYPE_OBJECT_PATH, &path);
229   g_free(path);
230 }
231
232 /*---------------------------------------------------------------------------*/
233
234 static gboolean
235 send_cache_update(gpointer data)
236 {
237   DBusMessage *message;
238   DBusMessageIter iter;
239   DBusMessageIter iter_array;
240   DBusConnection *bus = (DBusConnection *) data;
241
242   message = dbus_message_new_signal ("/org/freedesktop/atspi/tree", SPI_DBUS_INTERFACE_TREE, "updateTree");
243
244   dbus_message_iter_init_append (message, &iter);
245
246   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassusau)", &iter_array);
247   atk_dbus_foreach_update_list(append_accessible, &iter_array);
248   dbus_message_iter_close_container(&iter, &iter_array);
249
250   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &iter_array);
251   atk_dbus_foreach_remove_list(append_accessible_path, &iter_array);
252   dbus_message_iter_close_container(&iter, &iter_array);
253
254   dbus_connection_send(bus, message, NULL);
255   update_pending = FALSE;
256
257   return FALSE;
258 }
259
260 /*---------------------------------------------------------------------------*/
261
262 void
263 atk_tree_cache_needs_update(DBusConnection *bus)
264 {
265   if (!update_pending)
266     {
267       g_idle_add(send_cache_update, bus);
268       update_pending = TRUE;
269     }
270 }
271
272 /*---------------------------------------------------------------------------*/
273
274 static DBusMessage *
275 impl_getRoot (DBusConnection *bus, DBusMessage *message, void *user_data)
276 {
277   AtkObject *root = atk_get_root();
278   char *path;
279   DBusMessage *reply;
280
281   if (root) path = atk_dbus_get_path(root);
282   if (!root || !path)
283     return spi_dbus_general_error (message);
284   reply = dbus_message_new_method_return (message);
285   dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
286   g_free (path);
287   return reply;
288 }
289
290 /*---------------------------------------------------------------------------*/
291
292 static DBusMessage *
293 impl_getTree (DBusConnection *bus, DBusMessage *message, void *user_data)
294 {
295   DBusMessage *reply;
296   DBusMessageIter iter, iter_array;
297   AtkObject *root = atk_get_root();
298
299   if (!root) 
300      return spi_dbus_general_error(message);
301   reply = dbus_message_new_method_return (message);
302
303   dbus_message_iter_init_append (reply, &iter);
304   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ooaoassusau)", &iter_array);
305   atk_dbus_foreach_registered(append_accessible, &iter_array);
306   dbus_message_iter_close_container(&iter, &iter_array);
307   return reply;
308 }
309
310 /*---------------------------------------------------------------------------*/
311
312 static DRouteMethod methods[] = {
313   {impl_getRoot, "getRoot"},
314   {impl_getTree, "getTree"},
315   {NULL, NULL}
316 };
317
318 void
319 spi_initialize_tree (DRoutePath *path)
320 {
321   droute_path_add_interface (path,
322                              SPI_DBUS_INTERFACE_TREE,
323                              methods,
324                              NULL);
325 };
326
327 /*END------------------------------------------------------------------------*/