2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2009, 2010 Codethink Ltd.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #include "accessible-cache.h"
26 #include "accessible-register.h"
29 SpiCache *spi_global_cache = NULL;
32 child_added_listener (GSignalInvocationHint * signal_hint,
34 const GValue * param_values, gpointer data);
37 toplevel_added_listener (AtkObject * accessible,
38 guint index, AtkObject * child);
41 remove_object (gpointer data, GObject * gobj);
44 add_object (SpiCache * cache, GObject * gobj);
47 add_subtree (SpiCache *cache, AtkObject * accessible);
49 /*---------------------------------------------------------------------------*/
52 spi_cache_finalize (GObject * object);
55 spi_cache_dispose (GObject * object);
57 /*---------------------------------------------------------------------------*/
65 static guint cache_signals[LAST_SIGNAL] = { 0 };
67 /*---------------------------------------------------------------------------*/
69 G_DEFINE_TYPE (SpiCache, spi_cache, G_TYPE_OBJECT)
71 static void spi_cache_class_init (SpiCacheClass * klass)
73 GObjectClass *object_class = (GObjectClass *) klass;
75 spi_cache_parent_class = g_type_class_ref (G_TYPE_OBJECT);
77 object_class->finalize = spi_cache_finalize;
78 object_class->dispose = spi_cache_dispose;
80 cache_signals [OBJECT_ADDED] = \
81 g_signal_new ("object-added",
87 g_cclosure_marshal_VOID__OBJECT,
92 cache_signals [OBJECT_REMOVED] = \
93 g_signal_new ("object-removed",
99 g_cclosure_marshal_VOID__OBJECT,
106 spi_cache_init (SpiCache * cache)
108 cache->objects = g_hash_table_new (g_direct_hash, g_direct_equal);
111 if (g_thread_supported ())
112 g_message ("AT-SPI: Threads enabled");
114 g_debug ("AT-SPI: Initial Atk tree regisration");
117 g_signal_connect (spi_global_register,
118 "object-deregistered",
119 (GCallback) remove_object, cache);
121 add_subtree (cache, spi_global_app_data->root);
123 atk_add_global_event_listener (child_added_listener,
124 "Gtk:AtkObject:children-changed");
126 g_signal_connect (G_OBJECT (spi_global_app_data->root),
127 "children-changed::add",
128 (GCallback) toplevel_added_listener, NULL);
132 spi_cache_finalize (GObject * object)
134 SpiCache *cache = SPI_CACHE (object);
136 g_free (cache->objects);
138 G_OBJECT_CLASS (spi_cache_parent_class)->finalize (object);
142 spi_cache_dispose (GObject * object)
144 SpiCache *cache = SPI_CACHE (object);
146 G_OBJECT_CLASS (spi_cache_parent_class)->dispose (object);
149 /*---------------------------------------------------------------------------*/
152 remove_object (gpointer data, GObject * gobj)
154 SpiCache *cache = SPI_CACHE (data);
156 if (spi_cache_in (cache, gobj))
158 g_signal_emit (cache, cache_signals [OBJECT_REMOVED], 0, gobj);
159 g_hash_table_remove (cache->objects, gobj);
164 add_object (SpiCache * cache, GObject * gobj)
166 g_return_if_fail (G_IS_OBJECT (gobj));
168 g_hash_table_insert (cache->objects, gobj, NULL);
170 g_signal_emit (cache, cache_signals [OBJECT_ADDED], 0, gobj);
173 /*---------------------------------------------------------------------------*/
175 static GStaticRecMutex cache_mutex = G_STATIC_REC_MUTEX_INIT;
176 static GStaticMutex recursion_check_guard = G_STATIC_MUTEX_INIT;
178 static gboolean recursion_check = FALSE;
181 recursion_check_and_set ()
184 g_static_mutex_lock (&recursion_check_guard);
185 ret = recursion_check;
186 recursion_check = TRUE;
187 g_static_mutex_unlock (&recursion_check_guard);
192 recursion_check_unset ()
194 g_static_mutex_lock (&recursion_check_guard);
195 recursion_check = FALSE;
196 g_static_mutex_unlock (&recursion_check_guard);
199 /*---------------------------------------------------------------------------*/
202 append_children (AtkObject * accessible, GQueue * traversal)
206 gint count = atk_object_get_n_accessible_children (accessible);
210 for (i = 0; i < count; i++)
212 current = atk_object_ref_accessible_child (accessible, i);
215 g_queue_push_tail (traversal, current);
221 * Adds a subtree of accessible objects
222 * to the cache at the accessible object provided.
224 * The leaf nodes do not have their children
225 * registered. A node is considered a leaf
226 * if it has the state "manages-descendants"
227 * or if it has already been registered.
230 add_subtree (SpiCache *cache, AtkObject * accessible)
236 g_return_if_fail (ATK_IS_OBJECT (accessible));
238 traversal = g_queue_new ();
239 to_add = g_queue_new ();
241 g_object_ref (accessible);
242 g_queue_push_tail (traversal, accessible);
244 while (!g_queue_is_empty (traversal))
248 current = g_queue_pop_head (traversal);
249 set = atk_object_ref_state_set (current);
251 if (!atk_state_set_contains_state (set, ATK_STATE_TRANSIENT))
253 g_queue_push_tail (to_add, current);
254 if (!spi_cache_in (cache, G_OBJECT (current)) &&
255 !atk_state_set_contains_state (set, ATK_STATE_MANAGES_DESCENDANTS))
258 g_debug ("REG - %s - %d - %s", atk_object_get_name (current),
259 atk_object_get_role (current),
260 atk_dbus_object_to_path (current));
262 append_children (current, traversal);
266 g_object_unref (set);
269 while (!g_queue_is_empty (to_add))
271 current = g_queue_pop_head (to_add);
272 add_object (cache, G_OBJECT(current));
273 g_object_unref (G_OBJECT (current));
276 g_queue_free (traversal);
277 g_queue_free (to_add);
280 /*---------------------------------------------------------------------------*/
283 child_added_listener (GSignalInvocationHint * signal_hint,
284 guint n_param_values,
285 const GValue * param_values, gpointer data)
287 SpiCache *cache = spi_global_cache;
289 AtkObject *accessible;
292 const gchar *detail = NULL;
294 g_static_rec_mutex_lock (&cache_mutex);
297 * Ensure that only accessibles already in the cache
298 * have their signals processed.
300 accessible = ATK_OBJECT (g_value_get_object (¶m_values[0]));
301 g_return_val_if_fail (ATK_IS_OBJECT (accessible), TRUE);
303 if (spi_cache_in (cache, G_OBJECT(accessible)))
306 if (recursion_check_and_set ())
307 g_warning ("AT-SPI: Recursive use of cache module");
309 g_debug ("AT-SPI: Tree update listener");
311 if (signal_hint->detail)
312 detail = g_quark_to_string (signal_hint->detail);
314 if (!strcmp (detail, "add"))
317 int index = g_value_get_uint (param_values + 1);
318 child = g_value_get_pointer (param_values + 2);
320 if (!ATK_IS_OBJECT (child))
322 child = atk_object_ref_accessible_child (accessible, index);
324 add_subtree (cache, child);
327 recursion_check_unset ();
331 g_static_rec_mutex_unlock (&cache_mutex);
336 /*---------------------------------------------------------------------------*/
339 toplevel_added_listener (AtkObject * accessible,
340 guint index, AtkObject * child)
342 SpiCache *cache = spi_global_cache;
344 g_static_rec_mutex_lock (&cache_mutex);
346 g_return_if_fail (ATK_IS_OBJECT (accessible));
348 if (spi_cache_in (cache, G_OBJECT(accessible)))
351 if (recursion_check_and_set ())
352 g_warning ("AT-SPI: Recursive use of registration module");
354 g_debug ("AT-SPI: Toplevel added listener");
356 if (!ATK_IS_OBJECT (child))
358 child = atk_object_ref_accessible_child (accessible, index);
360 add_subtree (cache, child);
362 recursion_check_unset ();
366 g_static_rec_mutex_unlock (&cache_mutex);
369 /*---------------------------------------------------------------------------*/
372 spi_cache_foreach (SpiCache * cache, GHFunc func, gpointer data)
374 g_hash_table_foreach (cache->objects, func, data);
378 spi_cache_in (SpiCache * cache, GObject * object)
380 if (g_hash_table_lookup_extended (cache->objects,
389 /*END------------------------------------------------------------------------*/