Remove unused functions
[platform/core/uifw/at-spi2-atk.git] / libspi / 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 "accessible.h"
26
27 #define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
28
29 static dbus_bool_t
30 append_update (DBusMessageIter * iter_array, AtkObject * obj,
31                              dbus_bool_t include_children, DRouteData * data)
32 {
33   DBusMessageIter iter_struct, iter_sub_array;
34   char *path = NULL;
35   char *path_parent;
36   const char *name, *desc;
37   int i;
38   dbus_uint32_t role;
39
40   gint childcount;
41   GSList *l;
42
43   dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
44                                     &iter_struct);
45   path = spi_dbus_get_path (obj);
46   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
47   path_parent = spi_dbus_get_path (atk_object_get_parent(obj));
48   if (!path_parent) path_parent = g_strdup("/");
49   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path_parent);
50   g_free(path_parent);
51   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "o",
52                                     &iter_sub_array);
53   childcount = atk_object_get_n_accessible_children (obj);
54   for (i = 0; i < childcount; i++)
55     {
56       AtkObject *child = atk_object_ref_accessible_child (obj, i);
57       char *child_path = spi_dbus_get_path (child);
58       if (child_path)
59         {
60           dbus_message_iter_append_basic (&iter_sub_array,
61                                           DBUS_TYPE_OBJECT_PATH, &child_path);
62           g_free (child_path);
63         }
64       if (child)
65         g_object_unref (child);
66     }
67   if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
68     goto oom;
69   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s",
70                                     &iter_sub_array);
71   for (l = data->interfaces; l; l = g_slist_next (l))
72     {
73       DRouteInterface *iface_def = (DRouteInterface *) l->data;
74       void *datum = NULL;
75       if (iface_def->get_datum)
76         {
77           datum = (*iface_def->get_datum) (path, data->user_data);
78           if (!datum)
79             continue;
80         }
81       dbus_message_iter_append_basic (&iter_sub_array, DBUS_TYPE_STRING,
82                                       &iface_def->name);
83       if (iface_def->free_datum)
84         (*iface_def->free_datum) (datum);
85     }
86   if (!dbus_message_iter_close_container (&iter_struct, &iter_sub_array))
87     goto oom;
88   name = atk_object_get_name (obj);
89   if (!name)
90     name = "";
91   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
92   role = spi_accessible_role_from_atk_role (atk_object_get_role (obj));
93   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &role);
94   desc = atk_object_get_description (obj);
95   if (!desc)
96     desc = "";
97   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
98   if (!dbus_message_iter_close_container (iter_array, &iter_struct))
99     goto oom;
100   if (!include_children) childcount = 0;
101   for (i = 0; i < childcount; i++)
102     {
103       AtkObject *child = atk_object_ref_accessible_child (obj, i);
104       dbus_bool_t result;
105       if (!child)
106         continue;
107       result = append_update (iter_array, child, TRUE, data);
108       g_object_unref (child);
109       if (!result)
110         goto oom;
111     }
112   g_free (path);
113   return TRUE;
114 oom:
115   if (path) g_free(path);
116   return FALSE;
117 }
118
119 dbus_bool_t
120 spi_dbus_append_tree (DBusMessage * message, AtkObject * obj,
121                       DRouteData * data)
122 {
123   DBusMessageIter iter, iter_array;
124   dbus_bool_t result;
125
126   dbus_message_iter_init_append (message, &iter);
127   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
128                                     &iter_array);
129   result = append_update (&iter_array, obj, TRUE, data);
130   if (result)
131     result = dbus_message_iter_close_container (&iter, &iter_array);
132   return result;
133 }
134
135 static DBusMessage *
136 impl_getRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
137 {
138   AtkObject *root = atk_get_root();
139   char *path;
140   DBusMessage *reply;
141
142   if (root) path = spi_dbus_get_path(root);
143   if (!root || !path)
144     return spi_dbus_general_error (message);
145   reply = dbus_message_new_method_return (message);
146   dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path,
147                             DBUS_TYPE_INVALID);
148   g_free (path);
149   return reply;
150 }
151
152 static DBusMessage *
153 impl_getTree (DBusConnection * bus, DBusMessage * message, void *user_data)
154 {
155   DBusMessage *reply;
156   AtkObject *root = atk_get_root();
157
158   if (!root) return spi_dbus_general_error(message);
159   reply = dbus_message_new_method_return (message);
160   spi_dbus_append_tree (reply, root, (DRouteData *) user_data);
161   return reply;
162 }
163
164 static DRouteMethod methods[] = {
165   {impl_getRoot, "getRoot"},
166   {impl_getTree, "getTree", TRUE},
167   {NULL, NULL}
168 };
169
170 void
171 spi_initialize_tree (DRouteData * data)
172 {
173   droute_add_interface (data, "org.freedesktop.atspi.Tree",
174                         methods, NULL, NULL, NULL);
175 };
176
177 static GHashTable *cache_list;
178
179 #define UPDATE_NEW     1
180 #define UPDATE_REFRESH 2
181 #define UPDATE_REMOVE 3
182
183 static int update_pending = 0;
184 static gint update_pending_id;
185
186 typedef struct
187 {
188   DBusMessageIter iter;
189   DRouteData *droute;
190   gboolean removing;
191 } CacheIterData;
192
193 static void handle_cache_item(char *path, guint action, CacheIterData *d)
194 {
195   AtkObject *obj;
196
197   switch (action)
198   {
199   case UPDATE_NEW:
200   case UPDATE_REFRESH:
201   default:
202     if (d->removing) return;
203     obj = spi_dbus_get_object(path);
204 //printf("update %s\n", path);
205     append_update(&d->iter, obj, FALSE, d->droute);
206     break;
207   case UPDATE_REMOVE:
208 //printf("remove: %s\n", path);
209     if (!d->removing) return;
210     dbus_message_iter_append_basic(&d->iter, DBUS_TYPE_OBJECT_PATH, &path);
211     break;
212   }
213   g_hash_table_remove(cache_list, path);
214   }
215
216 gboolean spi_dbus_update_cache(DRouteData *data)
217 {
218   DBusMessage *message;
219   DBusMessageIter iter;
220   CacheIterData d;
221
222   if (update_pending == 0) return FALSE;
223 //printf("Sending cache\n");
224   message = dbus_message_new_signal("/org/freedesktop/atspi/tree", "org.freedesktop.atspi.Tree", "UpdateTree");
225   if (!message) goto done;
226   dbus_message_iter_init_append (message, &iter);
227   dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
228                                     &d.iter);
229   d.droute = data;
230   d.removing = FALSE;
231   do
232   {
233     /* This loop is needed because appending an item may cause new children
234      * to be registered and consequently added to the hash, so they, too,
235      * will need to be sent with the update */
236     update_pending = 0;
237     g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
238   } while (update_pending);
239   dbus_message_iter_close_container(&iter, &d.iter);
240   dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &d.iter);
241   d.removing = TRUE;
242   g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
243   dbus_message_iter_close_container(&iter, &d.iter);
244   dbus_connection_send(data->bus, message, NULL);
245 done:
246   return FALSE;
247 }
248
249 void spi_dbus_notify_change(AtkObject *obj, gboolean new, DRouteData *data)
250 {
251   guint action = (new? UPDATE_NEW: UPDATE_REFRESH);
252   char *path = spi_dbus_get_path(obj);
253
254   if (!cache_list)
255   {
256     cache_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
257     if (!cache_list)
258     {
259       g_free(path);
260       return;
261     }
262   }
263   if (g_hash_table_lookup(cache_list, path))
264   {
265     g_free(path);
266     return;
267   }
268 //printf("change: %s\n", path);
269   g_hash_table_insert(cache_list, path, (gpointer)action);
270   if (update_pending != 2 && data)
271   {
272     update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
273     update_pending = 2;
274   }
275   else if (!update_pending) update_pending = 1;
276 }
277
278 void spi_dbus_notify_remove(AtkObject *obj, DRouteData *data)
279 {
280   guint action = UPDATE_REMOVE;
281   guint cur_action;
282   gchar *path = spi_dbus_get_path(obj);
283
284 //printf("notify remove: %s\n", path);
285   if (!cache_list)
286   {
287     g_free(path);
288     return;
289   }
290   cur_action = (guint)g_hash_table_lookup(cache_list, path);
291   if (cur_action == UPDATE_NEW)
292   {
293     /* No one knew that this object ever existed, so just remove it */
294 //printf("Removing object from send queue\n");
295     g_hash_table_remove(cache_list, path);
296     g_free(path);
297   }
298   else
299   {
300     g_hash_table_insert(cache_list, path, (gpointer)action);
301     if (update_pending != 2 && data)
302     {
303       update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
304       update_pending = 2;
305     }
306     else if (!update_pending) update_pending = 1;
307   }
308 }
309
310 gboolean spi_dbus_object_is_known(AtkObject *obj)
311 {
312   guint cur_action;
313   char *path = spi_dbus_get_path(obj);
314   if (!path) return FALSE;
315   cur_action = (guint)g_hash_table_lookup(cache_list, path);
316   g_free(path);
317   return (cur_action != UPDATE_NEW);
318 }