Don't translate g_warning messages
[platform/upstream/at-spi2-core.git] / atspi / atspi-accessible.c
index 2f42005..dfeed59 100644 (file)
@@ -4,6 +4,7 @@
  *
  * Copyright 2001, 2002 Sun Microsystems Inc.,
  * Copyright 2001, 2002 Ximian, Inc.
+ * Copyright 2010, 2011 Novell, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -44,6 +45,7 @@ atspi_document_interface_init (AtspiDocument *document)
 {
 }
 
+static void
 atspi_editable_text_interface_init (AtspiEditableText *editable_text)
 {
 }
@@ -91,17 +93,78 @@ G_DEFINE_TYPE_WITH_CODE (AtspiAccessible, atspi_accessible, ATSPI_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_TEXT, atspi_text_interface_init)
                          G_IMPLEMENT_INTERFACE (ATSPI_TYPE_VALUE, atspi_value_interface_init))
 
+#ifdef DEBUG_REF_COUNTS
+static gint accessible_count = 0;
+#endif
+
 static void
 atspi_accessible_init (AtspiAccessible *accessible)
 {
+#ifdef DEBUG_REF_COUNTS
+  accessible_count++;
+  printf("at-spi: init: %d objects\n", accessible_count);
+#endif
 }
 
 static void
-atspi_accessible_finalize (GObject *obj)
+atspi_accessible_dispose (GObject *object)
 {
-  /*AtspiAccessible *accessible = ATSPI_ACCESSIBLE (obj); */
+  AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
+  AtspiEvent e;
+  AtspiAccessible *parent;
+
+  /* TODO: Only fire if object not already marked defunct */
+  memset (&e, 0, sizeof (e));
+  e.type = "object:state-change:defunct";
+  e.source = accessible;
+  e.detail1 = 1;
+  e.detail2 = 0;
+  _atspi_send_event (&e);
+
+  if (accessible->states)
+  {
+    g_object_unref (accessible->states);
+    accessible->states = NULL;
+  }
+
+  parent = accessible->accessible_parent;
+  if (parent && parent->children)
+  {
+    GList*ls = g_list_find (parent->children, accessible);
+    if(ls)
+    {
+      gboolean replace = (ls == parent->children);
+      ls = g_list_remove (ls, accessible);
+      if (replace)
+        parent->children = ls;
+      g_object_unref (object);
+    }
+  }
 
-  /* TODO: Unref parent/children, etc. */
+  if (parent)
+  {
+    g_object_unref (parent);
+    accessible->accessible_parent = NULL;
+  }
+
+  G_OBJECT_CLASS (atspi_accessible_parent_class) ->dispose (object);
+}
+
+static void
+atspi_accessible_finalize (GObject *object)
+{
+  AtspiAccessible *accessible = ATSPI_ACCESSIBLE (object);
+
+    g_free (accessible->description);
+    g_free (accessible->name);
+
+#ifdef DEBUG_REF_COUNTS
+  accessible_count--;
+  printf("at-spi: finalize: %d objects\n", accessible_count);
+#endif
+
+  G_OBJECT_CLASS (atspi_accessible_parent_class)
+    ->finalize (object);
 }
 
 static void
@@ -109,6 +172,7 @@ atspi_accessible_class_init (AtspiAccessibleClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+  object_class->dispose = atspi_accessible_dispose;
   object_class->finalize = atspi_accessible_finalize;
 }
 
@@ -248,7 +312,7 @@ atspi_accessible_get_name (AtspiAccessible *obj, GError **error)
     if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error,
                                    "s", &obj->name))
       return g_strdup ("");
-    obj->cached_properties |= ATSPI_CACHE_NAME;
+    _atspi_accessible_add_cache (obj, ATSPI_CACHE_NAME);
   }
   return g_strdup (obj->name);
 }
@@ -273,7 +337,7 @@ atspi_accessible_get_description (AtspiAccessible *obj, GError **error)
                                    "Description", error, "s",
                                    &obj->description))
       return g_strdup ("");
-    obj->cached_properties |= ATSPI_CACHE_DESCRIPTION;
+    _atspi_accessible_add_cache (obj, ATSPI_CACHE_DESCRIPTION);
   }
   return g_strdup (obj->description);
 }
@@ -296,7 +360,7 @@ atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
 {
   g_return_val_if_fail (obj != NULL, NULL);
 
-  if (!(obj->cached_properties & ATSPI_CACHE_PARENT))
+  if (obj->parent.app && !(obj->cached_properties & ATSPI_CACHE_PARENT))
   {
     DBusMessage *message, *reply;
     DBusMessageIter iter, iter_variant;
@@ -308,15 +372,19 @@ atspi_accessible_get_parent (AtspiAccessible *obj, GError **error)
     dbus_message_append_args (message, DBUS_TYPE_STRING, &atspi_interface_accessible,
                                DBUS_TYPE_STRING, &str_parent,
                               DBUS_TYPE_INVALID);
-    reply = _atspi_dbus_send_with_reply_and_block (message);
-    if (!reply ||
-       (strcmp (dbus_message_get_signature (reply), "v") != 0))
+    reply = _atspi_dbus_send_with_reply_and_block (message, error);
+    if (!reply)
+      return NULL;
+    if (strcmp (dbus_message_get_signature (reply), "v") != 0)
+    {
+      dbus_message_unref (reply);
       return NULL;
+    }
     dbus_message_iter_init (reply, &iter);
     dbus_message_iter_recurse (&iter, &iter_variant);
     obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant);
     dbus_message_unref (reply);
-    obj->cached_properties |= ATSPI_CACHE_PARENT;
+    _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT);
   }
   if (!obj->accessible_parent)
     return NULL;
@@ -446,7 +514,9 @@ atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
   g_return_val_if_fail (obj != NULL, NULL);
 
   reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetRelationSet", error, "");
-  _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", NULL);
+  if (!reply)
+    return NULL;
+  _ATSPI_DBUS_CHECK_SIG (reply, "a(ua(so))", error, NULL);
 
   ret = g_array_new (TRUE, TRUE, sizeof (AtspiRelation *));
   dbus_message_iter_init (reply, &iter);
@@ -461,6 +531,7 @@ atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error)
       ret = new_array;
     dbus_message_iter_next (&iter_array);
   }
+  dbus_message_unref (reply);
   return ret;
 }
 
@@ -483,10 +554,10 @@ atspi_accessible_get_role (AtspiAccessible *obj, GError **error)
   {
     dbus_uint32_t role;
     /* TODO: Make this a property */
-    if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", NULL, "=>u", &role))
+    if (_atspi_dbus_call (obj, atspi_interface_accessible, "GetRole", error, "=>u", &role))
     {
-      obj->cached_properties |= ATSPI_CACHE_ROLE;
       obj->role = role;
+    _atspi_accessible_add_cache (obj, ATSPI_CACHE_ROLE);
     }
   }
   return obj->role;
@@ -544,6 +615,14 @@ atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
   return retval;
 }
 
+static AtspiStateSet *
+defunct_set ()
+{
+  AtspiStateSet *set = atspi_state_set_new (NULL);
+  atspi_state_set_add (set, ATSPI_STATE_DEFUNCT);
+  return set;
+}
+
 /**
  * atspi_accessible_get_state_set:
  * @obj: a pointer to the #AtspiAccessible object on which to operate.
@@ -556,16 +635,21 @@ atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error)
 AtspiStateSet *
 atspi_accessible_get_state_set (AtspiAccessible *obj)
 {
+  if (!obj->parent.app || !obj->parent.app->bus)
+    return defunct_set ();
+
+
   if (!(obj->cached_properties & ATSPI_CACHE_STATES))
   {
     DBusMessage *reply;
     DBusMessageIter iter;
     reply = _atspi_dbus_call_partial (obj, atspi_interface_accessible,
                                       "GetState", NULL, "");
-    _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL);
+    _ATSPI_DBUS_CHECK_SIG (reply, "au", NULL, defunct_set ());
     dbus_message_iter_init (reply, &iter);
     _atspi_dbus_set_state (obj, &iter);
     dbus_message_unref (reply);
+    _atspi_accessible_add_cache (obj, ATSPI_CACHE_STATES);
   }
 
   return g_object_ref (obj->states);
@@ -631,12 +715,29 @@ atspi_accessible_get_application (AtspiAccessible *obj, GError **error)
 {
   AtspiAccessible *parent;
 
+  g_object_ref (obj);
   for (;;)
   {
     parent = atspi_accessible_get_parent (obj, NULL);
+    if (!parent && obj->parent.app &&
+        atspi_accessible_get_role (obj, NULL) != ATSPI_ROLE_APPLICATION)
+    {
+      AtspiAccessible *root = g_object_ref (obj->parent.app->root);
+      if (root)
+      {
+        g_object_unref (obj);
+        if (atspi_accessible_get_role (root, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
+        {
+          g_object_unref (root);
+          return NULL;
+        }
+        return root;
+      }
+    }
     if (!parent || parent == obj ||
         atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME)
-    return g_object_ref (obj);
+    return obj;
+    g_object_unref (obj);
     obj = parent;
   }
 }
@@ -686,8 +787,60 @@ atspi_accessible_get_toolkit_version (AtspiAccessible *obj, GError **error)
       return NULL;
   return g_strdup (ret);
 }
+/**
+ * atspi_accessible_get_toolkit_version:
+ * @obj: a pointer to the #AtspiAccessible object on which to operate.
+ *
+ * Get the application id for a #AtspiAccessible object.
+ * Only works on application root objects.
+ *
+ * Returns: a gint indicating the id for the #AtspiAccessible object.
+ * or -1 on exception
+ **/
+gint
+atspi_accessible_get_id (AtspiAccessible *obj, GError **error)
+{
+  gint ret = -1;
+
+  g_return_val_if_fail (obj != NULL, -1);
+
+  if (!_atspi_dbus_get_property (obj, atspi_interface_application, "Id", error, "i", &ret))
+      return -1;
+  return ret;
+}
+
+
 /* Interface query methods */
 
+static gboolean
+_atspi_accessible_is_a (AtspiAccessible *accessible,
+                     const char *interface_name)
+{
+  int n;
+
+  if (accessible == NULL)
+    {
+      return FALSE;
+    }
+
+  if (!(accessible->cached_properties & ATSPI_CACHE_INTERFACES))
+  {
+    DBusMessage *reply;
+    DBusMessageIter iter;
+    reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
+                                      "GetInterfaces", NULL, "");
+    _ATSPI_DBUS_CHECK_SIG (reply, "as", NULL, FALSE);
+    dbus_message_iter_init (reply, &iter);
+    _atspi_dbus_set_interfaces (accessible, &iter);
+    dbus_message_unref (reply);
+    _atspi_accessible_add_cache (accessible, ATSPI_CACHE_INTERFACES);
+  }
+
+  n = _atspi_get_iface_num (interface_name);
+  if (n == -1) return FALSE;
+  return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
+}
+
 /**
  * atspi_accessible_is_action:
  * @obj: a pointer to the #AtspiAccessible instance to query.
@@ -730,13 +883,8 @@ atspi_accessible_is_application (AtspiAccessible *obj)
 gboolean
 atspi_accessible_is_collection (AtspiAccessible *obj)
 {
-#if 0
-     g_warning ("Collections not implemented");
      return _atspi_accessible_is_a (obj,
                              atspi_interface_collection);
-#else
-     return FALSE;
-#endif
 }
 
 /**
@@ -804,6 +952,22 @@ atspi_accessible_is_hypertext (AtspiAccessible *obj)
 }
 
 /**
+ * atspi_accessible_is_hyperlink:
+ * @obj: a pointer to the #AtspiAccessible instance to query.
+ *
+ * Query whether the specified #AtspiAccessible implements #AtspiHyperlink.
+ *
+ * Returns: #TRUE if @obj implements the #AtspiHypertext interface,
+ *          #FALSE otherwise.
+ **/
+gboolean
+atspi_accessible_is_hyperlink (AtspiAccessible *obj)
+{
+  return _atspi_accessible_is_a (obj,
+                             atspi_interface_hyperlink);
+}
+
+/**
  * atspi_accessible_is_image:
  * @obj: a pointer to the #AtspiAccessible instance to query.
  *
@@ -1116,34 +1280,6 @@ atspi_accessible_get_value (AtspiAccessible *accessible)
           g_object_ref (ATSPI_VALUE (accessible)) : NULL);  
 }
 
-gboolean
-_atspi_accessible_is_a (AtspiAccessible *accessible,
-                     const char *interface_name)
-{
-  int n;
-
-  if (accessible == NULL)
-    {
-      return FALSE;
-    }
-
-  if (!(accessible->cached_properties & ATSPI_CACHE_INTERFACES))
-  {
-    DBusMessage *reply;
-    DBusMessageIter iter;
-    reply = _atspi_dbus_call_partial (accessible, atspi_interface_accessible,
-                                      "GetInterfaces", NULL, "");
-    _ATSPI_DBUS_CHECK_SIG (reply, "as", FALSE);
-    dbus_message_iter_init (reply, &iter);
-    _atspi_dbus_set_interfaces (accessible, &iter);
-    dbus_message_unref (reply);
-  }
-
-  n = _atspi_get_iface_num (interface_name);
-  if (n == -1) return FALSE;
-  return (gboolean) ((accessible->interfaces & (1 << n))? TRUE: FALSE);
-}
-
 static void
 append_const_val (GArray *array, const gchar *val)
 {
@@ -1172,6 +1308,7 @@ atspi_accessible_get_interfaces (AtspiAccessible *obj)
 
   g_return_val_if_fail (obj != NULL, NULL);
 
+  append_const_val (ret, "Accessible");
   if (atspi_accessible_is_action (obj))
     append_const_val (ret, "Action");
   if (atspi_accessible_is_collection (obj))
@@ -1184,6 +1321,8 @@ atspi_accessible_get_interfaces (AtspiAccessible *obj)
     append_const_val (ret, "EditableText");
   if (atspi_accessible_is_hypertext (obj))
     append_const_val (ret, "Hypertext");
+  if (atspi_accessible_is_hyperlink (obj))
+    append_const_val (ret, "Hyperlink");
   if (atspi_accessible_is_image (obj))
     append_const_val (ret, "Image");
   if (atspi_accessible_is_selection (obj))
@@ -1198,31 +1337,6 @@ atspi_accessible_get_interfaces (AtspiAccessible *obj)
   return ret;
 }
 
-/* TODO: Move to a finalizer */
-static void
-cspi_object_destroyed (AtspiAccessible *accessible)
-{
-  gboolean cached;
-  AtspiEvent e;
-
-  /* TODO: Only fire if object not already marked defunct */
-  memset (&e, 0, sizeof (e));
-  e.type.klass = "object";
-  e.type.major = "state-changed";
-  e.type.minor = "defunct";
-  e.source = accessible;
-  e.detail1 = 1;
-  e.detail2 = 0;
-  _atspi_send_event (&e);
-
-    g_free (accessible->parent.path);
-
-    if (accessible->states)
-      g_object_unref (accessible->states);
-    g_free (accessible->description);
-    g_free (accessible->name);
-}
-
 AtspiAccessible *
 atspi_accessible_new (AtspiApplication *app, const gchar *path)
 {
@@ -1236,3 +1350,49 @@ atspi_accessible_new (AtspiApplication *app, const gchar *path)
 
   return accessible;
 }
+
+/**
+ * atspi_accessible_set_cache_mask:
+ *
+ * @accessible: The #AtspiAccessible to operate on.  Must be the desktop or
+ *             the root of an application.
+ * @mask: An #AtspiCache specifying a bit mask of the types of data to cache.
+ *
+ * Sets the type of data to cache for accessibles.
+ * If this is not set for an application or is reset to ATSPI_CACHE_UNDEFINED,
+ * then the desktop's cache flag will be used.
+ * If the desktop's cache flag is also undefined, then all possible data will
+ * be cached.
+ * This function is intended to work around bugs in toolkits where the proper
+ * events are not raised / to aid in testing for such bugs.
+ *
+ * Note: This function has no effect on data that has already been cached.
+ **/
+void
+atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask)
+{
+  g_return_if_fail (accessible != NULL);
+  g_return_if_fail (accessible->parent.app != NULL);
+  g_return_if_fail (accessible == accessible->parent.app->root);
+  accessible->parent.app->cache = mask;
+}
+
+void
+_atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag)
+{
+  AtspiCache mask = accessible->parent.app->cache;
+
+  if (mask == ATSPI_CACHE_UNDEFINED &&
+      accessible->parent.app->root &&
+      accessible->parent.app->root->accessible_parent)
+  {
+    AtspiAccessible *desktop = atspi_get_desktop (0);
+    mask = desktop->parent.app->cache;
+    g_object_unref (desktop);
+  }
+
+  if (mask == ATSPI_CACHE_UNDEFINED)
+    mask = ATSPI_CACHE_ALL;
+
+  accessible->cached_properties |= flag & mask;
+}