Add missing introspection-loader files.
[platform/core/uifw/at-spi2-atk.git] / libspi / tree.c
index 7fb2ceb..fe26fbe 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#include <string.h>
+
 #include "accessible.h"
+#include "introspect-loader.h"
 
 #define get_object(message) spi_dbus_get_object(dbus_message_get_path(message))
 
 static dbus_bool_t
-spi_dbus_append_tree_helper (DBusMessageIter * iter_array, AtkObject * obj,
-                            DRouteData * data)
+append_update (DBusMessageIter * iter_array, AtkObject * obj,
+                            dbus_bool_t include_children, DRouteData * data)
 {
   DBusMessageIter iter_struct, iter_sub_array;
   char *path = NULL;
   char *path_parent;
   const char *name, *desc;
-  dbus_uint16_t updating = 1;
   int i;
   dbus_uint32_t role;
 
@@ -43,7 +45,6 @@ spi_dbus_append_tree_helper (DBusMessageIter * iter_array, AtkObject * obj,
 
   dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL,
                                    &iter_struct);
-  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT16, &updating);
   path = spi_dbus_get_path (obj);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &path);
   path_parent = spi_dbus_get_path (atk_object_get_parent(obj));
@@ -73,7 +74,7 @@ spi_dbus_append_tree_helper (DBusMessageIter * iter_array, AtkObject * obj,
   for (l = data->interfaces; l; l = g_slist_next (l))
     {
       DRouteInterface *iface_def = (DRouteInterface *) l->data;
-      void *datum;
+      void *datum = NULL;
       if (iface_def->get_datum)
        {
          datum = (*iface_def->get_datum) (path, data->user_data);
@@ -99,13 +100,14 @@ spi_dbus_append_tree_helper (DBusMessageIter * iter_array, AtkObject * obj,
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &desc);
   if (!dbus_message_iter_close_container (iter_array, &iter_struct))
     goto oom;
+  if (!include_children) childcount = 0;
   for (i = 0; i < childcount; i++)
     {
       AtkObject *child = atk_object_ref_accessible_child (obj, i);
       dbus_bool_t result;
       if (!child)
        continue;
-      result = spi_dbus_append_tree_helper (iter_array, child, data);
+      result = append_update (iter_array, child, TRUE, data);
       g_object_unref (child);
       if (!result)
        goto oom;
@@ -125,43 +127,265 @@ spi_dbus_append_tree (DBusMessage * message, AtkObject * obj,
   dbus_bool_t result;
 
   dbus_message_iter_init_append (message, &iter);
-  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(qooaoassus)",
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
                                    &iter_array);
-  result = spi_dbus_append_tree_helper (&iter_array, obj, data);
+  result = append_update (&iter_array, obj, TRUE, data);
   if (result)
     result = dbus_message_iter_close_container (&iter, &iter_array);
   return result;
 }
 
 static DBusMessage *
-impl_getTree (DBusConnection * bus, DBusMessage * message, void *user_data)
+impl_getRoot (DBusConnection * bus, DBusMessage * message, void *user_data)
 {
+  AtkObject *root = atk_get_root();
+  char *path;
   DBusMessage *reply;
-  AtkObject *root;
-  gchar *path;
 
-  root = atk_get_root ();
-  if (root)
-    path = spi_dbus_get_path (root);
+  if (root) path = spi_dbus_get_path(root);
   if (!root || !path)
     return spi_dbus_general_error (message);
   reply = dbus_message_new_method_return (message);
   dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &path,
                            DBUS_TYPE_INVALID);
   g_free (path);
+  return reply;
+}
+
+static DBusMessage *
+impl_getTree (DBusConnection * bus, DBusMessage * message, void *user_data)
+{
+  DBusMessage *reply;
+  AtkObject *root = atk_get_root();
+
+  if (!root) return spi_dbus_general_error(message);
+  reply = dbus_message_new_method_return (message);
   spi_dbus_append_tree (reply, root, (DRouteData *) user_data);
   return reply;
 }
 
-static DRouteMethod methods[] = {
-  {DROUTE_METHOD, impl_getTree, "getTree", "o,root,o:a(qooaoassus),tree,o",
-   TRUE},
-  {0, NULL, NULL, NULL}
+static DBusMessage *
+impl_introspect (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+  const char *path;
+  GString *output;
+  char *final;
+
+  DBusMessage *reply;
+
+  path = dbus_message_get_path(message);
+
+  output = g_string_new(spi_introspection_header);
+  
+  g_string_append_printf(output, spi_introspection_node_element, path);
+
+  spi_append_interface(output, "org.freedesktop.atspi.Tree");
+
+  g_string_append(output, spi_introspection_footer);
+  final = g_string_free(output, FALSE);
+
+  reply = dbus_message_new_method_return (message);
+  g_assert(reply != NULL);
+  dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
+                          DBUS_TYPE_INVALID);
+
+  g_free(final);
+  return reply;
+}
+
+static DBusHandlerResult
+message_handler (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+  const char *iface = dbus_message_get_interface (message);
+  const char *member = dbus_message_get_member (message);
+
+  DBusMessage *reply = NULL;
+  
+  if (!strcmp(iface, "org.freedesktop.atspi.Tree"))
+    {
+      if (!strcmp(member, "getRoot"))
+       {
+         reply = impl_getRoot(bus, message, user_data);
+       }
+
+      if (!strcmp(member, "getTree"))
+       {
+         reply = impl_getTree(bus, message, user_data);
+       }
+    }
+
+  if (!strcmp(iface, "org.freedesktop.DBus.Introspectable"))
+    {
+      if (!strcmp(member, "Introspect"))
+       {
+         reply = impl_introspect(bus, message, user_data);
+       }
+    }
+
+  if (reply)
+    {
+      dbus_connection_send (bus, reply, NULL);
+      dbus_message_unref (reply);
+    }
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusObjectPathVTable tree_vtable =
+{
+  NULL,
+  &message_handler,
+  NULL, NULL, NULL, NULL
 };
 
 void
-spi_initialize_tree (DRouteData * data)
+spi_register_tree_object(DBusConnection *bus,
+                        const char *path)
 {
-  droute_add_interface (data, "org.freedesktop.atspi.AccessibleTree",
-                       methods, NULL, NULL, NULL);
-};
+  dbus_bool_t mem = FALSE;
+  mem = dbus_connection_register_object_path(bus, path, &tree_vtable, NULL);
+  g_assert(mem == TRUE);
+}
+
+static GHashTable *cache_list;
+
+#define UPDATE_NEW     1
+#define UPDATE_REFRESH 2
+#define UPDATE_REMOVE 3
+
+static int update_pending = 0;
+static gint update_pending_id;
+
+typedef struct
+{
+  DBusMessageIter iter;
+  DRouteData *droute;
+  gboolean removing;
+} CacheIterData;
+
+static void handle_cache_item(char *path, guint action, CacheIterData *d)
+{
+  AtkObject *obj;
+
+  switch (action)
+  {
+  case UPDATE_NEW:
+  case UPDATE_REFRESH:
+  default:
+    if (d->removing) return;
+    obj = spi_dbus_get_object(path);
+//printf("update %s\n", path);
+    append_update(&d->iter, obj, FALSE, d->droute);
+    break;
+  case UPDATE_REMOVE:
+//printf("remove: %s\n", path);
+    if (!d->removing) return;
+    dbus_message_iter_append_basic(&d->iter, DBUS_TYPE_OBJECT_PATH, &path);
+    break;
+  }
+  g_hash_table_remove(cache_list, path);
+  }
+
+gboolean spi_dbus_update_cache(DRouteData *data)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  CacheIterData d;
+
+  if (update_pending == 0) return FALSE;
+//printf("Sending cache\n");
+  message = dbus_message_new_signal("/org/freedesktop/atspi/tree", "org.freedesktop.atspi.Tree", "UpdateTree");
+  if (!message) goto done;
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ooaoassus)",
+                                   &d.iter);
+  d.droute = data;
+  d.removing = FALSE;
+  do
+  {
+    /* This loop is needed because appending an item may cause new children
+     * to be registered and consequently added to the hash, so they, too,
+     * will need to be sent with the update */
+    update_pending = 0;
+    g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
+  } while (update_pending);
+  dbus_message_iter_close_container(&iter, &d.iter);
+  dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "o", &d.iter);
+  d.removing = TRUE;
+  g_hash_table_foreach(cache_list, (GHFunc)handle_cache_item, &d);
+  dbus_message_iter_close_container(&iter, &d.iter);
+  dbus_connection_send(data->bus, message, NULL);
+done:
+  return FALSE;
+}
+
+void spi_dbus_notify_change(AtkObject *obj, gboolean new, DRouteData *data)
+{
+  guint action = (new? UPDATE_NEW: UPDATE_REFRESH);
+  char *path = spi_dbus_get_path(obj);
+
+  if (!cache_list)
+  {
+    cache_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+    if (!cache_list)
+    {
+      g_free(path);
+      return;
+    }
+  }
+  if (g_hash_table_lookup(cache_list, path))
+  {
+    g_free(path);
+    return;
+  }
+//printf("change: %s\n", path);
+  g_hash_table_insert(cache_list, path, (gpointer)action);
+  if (update_pending != 2 && data)
+  {
+    update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
+    update_pending = 2;
+  }
+  else if (!update_pending) update_pending = 1;
+}
+
+void spi_dbus_notify_remove(AtkObject *obj, DRouteData *data)
+{
+  guint action = UPDATE_REMOVE;
+  guint cur_action;
+  gchar *path = spi_dbus_get_path(obj);
+
+//printf("notify remove: %s\n", path);
+  if (!cache_list)
+  {
+    g_free(path);
+    return;
+  }
+  cur_action = (guint)g_hash_table_lookup(cache_list, path);
+  if (cur_action == UPDATE_NEW)
+  {
+    /* No one knew that this object ever existed, so just remove it */
+//printf("Removing object from send queue\n");
+    g_hash_table_remove(cache_list, path);
+    g_free(path);
+  }
+  else
+  {
+    g_hash_table_insert(cache_list, path, (gpointer)action);
+    if (update_pending != 2 && data)
+    {
+      update_pending_id = g_idle_add((GSourceFunc)spi_dbus_update_cache, data);
+      update_pending = 2;
+    }
+    else if (!update_pending) update_pending = 1;
+  }
+}
+
+gboolean spi_dbus_object_is_known(AtkObject *obj)
+{
+  guint cur_action;
+  char *path = spi_dbus_get_path(obj);
+  if (!path) return FALSE;
+  cur_action = (guint)g_hash_table_lookup(cache_list, path);
+  g_free(path);
+  return (cur_action != UPDATE_NEW);
+}