[gtk+] Add accessibles for GtkEntry icons



commit b7743430aa1471c9ec054606d2234700e2da3eda
Author: Mike Gorse <mgorse suse com>
Date:   Sun Nov 11 11:00:49 2012 -0600

    Add accessibles for GtkEntry icons
    
    Expose GtkEntry icons as child accessibles of a GtkEntry, and provide
    actions to simulate clicking them. Also, refactor the a11y children test
    slightly to add a test.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=686347

 gtk/a11y/gtkentryaccessible.c |  527 +++++++++++++++++++++++++++++++++++++++++
 tests/a11y/children.c         |  101 +++++++--
 tests/a11y/entries.txt        |   58 +++++-
 tests/a11y/entries.ui         |    8 +
 4 files changed, 676 insertions(+), 18 deletions(-)
---
diff --git a/gtk/a11y/gtkentryaccessible.c b/gtk/a11y/gtkentryaccessible.c
index bac2b02..5f113da 100644
--- a/gtk/a11y/gtkentryaccessible.c
+++ b/gtk/a11y/gtkentryaccessible.c
@@ -17,18 +17,351 @@
 
 #include "config.h"
 
+#include <string.h>
 #include <gtk/gtk.h>
 #include "gtkpango.h"
 #include "gtkentryaccessible.h"
 #include "gtkentryprivate.h"
 #include "gtkcomboboxaccessible.h"
 
+#define GTK_TYPE_ENTRY_ICON_ACCESSIBLE      (_gtk_entry_icon_accessible_get_type ())
+#define GTK_ENTRY_ICON_ACCESSIBLE(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ENTRY_ICON_ACCESSIBLE, GtkEntryIconAccessible))
+#define GTK_IS_ENTRY_ICON_ACCESSIBLE(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ENTRY_ICON_ACCESSIBLE))
+
 struct _GtkEntryAccessiblePrivate
 {
   gint cursor_position;
   gint selection_bound;
+  AtkObject *icons[2];
 };
 
+typedef struct _GtkEntryIconAccessible GtkEntryIconAccessible;
+typedef struct _GtkEntryIconAccessibleClass GtkEntryIconAccessibleClass;
+
+struct _GtkEntryIconAccessible
+{
+  AtkObject parent;
+
+  GtkEntryAccessible *entry;
+  GtkEntryIconPosition pos;
+};
+
+struct _GtkEntryIconAccessibleClass
+{
+  AtkObjectClass parent_class;
+};
+
+static void atk_action_interface_init (AtkActionIface *iface);
+
+static void icon_atk_action_interface_init (AtkActionIface *iface);
+static void icon_atk_component_interface_init (AtkComponentIface *iface);
+
+GType _gtk_entry_icon_accessible_get_type (void);
+
+G_DEFINE_TYPE_WITH_CODE (GtkEntryIconAccessible, _gtk_entry_icon_accessible, ATK_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, icon_atk_action_interface_init)
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, icon_atk_component_interface_init))
+
+static void
+gtk_entry_icon_accessible_remove_entry (gpointer data, GObject *obj)
+{
+  GtkEntryIconAccessible *icon = data;
+
+  if (icon->entry)
+    {
+      icon->entry = NULL;
+      g_object_notify (G_OBJECT (icon), "accessible-parent");
+      atk_object_notify_state_change (ATK_OBJECT (icon), ATK_STATE_DEFUNCT, TRUE);
+    }
+}
+
+static AtkObject *
+gtk_entry_icon_accessible_new (GtkEntryAccessible *entry,
+                               GtkEntryIconPosition pos)
+{
+  GtkEntryIconAccessible *icon;
+  AtkObject *accessible;
+
+  icon = g_object_new (_gtk_entry_icon_accessible_get_type (), NULL);
+  icon->entry = entry;
+  g_object_weak_ref (G_OBJECT (entry),
+                     gtk_entry_icon_accessible_remove_entry,
+                     icon);
+  icon->pos = pos;
+
+  accessible = ATK_OBJECT (icon);
+  atk_object_initialize (accessible, NULL);
+  return accessible;
+}
+
+static void
+_gtk_entry_icon_accessible_init (GtkEntryIconAccessible *icon)
+{
+}
+
+static void
+gtk_entry_icon_accessible_initialize (AtkObject *obj,
+                                      gpointer   data)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (obj);
+  GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  GtkEntry *gtk_entry = GTK_ENTRY (widget);
+  const gchar *name;
+  gchar *text;
+
+  ATK_OBJECT_CLASS (_gtk_entry_icon_accessible_parent_class)->initialize (obj, data);
+  atk_object_set_role (obj, ATK_ROLE_ICON);
+
+  name = gtk_entry_get_icon_name (gtk_entry, icon->pos);
+  if (name)
+    atk_object_set_name (obj, name);
+
+  text = gtk_entry_get_icon_tooltip_text (gtk_entry, icon->pos);
+  if (text)
+    {
+      atk_object_set_description (obj, text);
+      g_free (text);
+    }
+
+  atk_object_set_parent (obj, ATK_OBJECT (icon->entry));
+}
+
+static AtkObject *
+gtk_entry_icon_accessible_get_parent (AtkObject *accessible)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (accessible);
+
+  return ATK_OBJECT (icon->entry);
+}
+
+static AtkStateSet *
+gtk_entry_icon_accessible_ref_state_set (AtkObject *accessible)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (accessible);
+  AtkStateSet *set = atk_state_set_new ();
+  AtkStateSet *entry_set;
+  GtkWidget *widget;
+  GtkEntry *gtk_entry;
+
+  if (!icon->entry)
+    {
+      atk_state_set_add_state (set, ATK_STATE_DEFUNCT);
+      return set;
+    }
+
+  entry_set = atk_object_ref_state_set (ATK_OBJECT (icon->entry));
+  if (!entry_set || atk_state_set_contains_state (entry_set, ATK_STATE_DEFUNCT))
+    {
+      atk_state_set_add_state (set, ATK_STATE_DEFUNCT);
+    g_clear_object (&entry_set);
+      return set;
+    }
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  gtk_entry = GTK_ENTRY (widget);
+
+  if (atk_state_set_contains_state (entry_set, ATK_STATE_ENABLED))
+    atk_state_set_add_state (set, ATK_STATE_ENABLED);
+  if (atk_state_set_contains_state (entry_set, ATK_STATE_SENSITIVE))
+    atk_state_set_add_state (set, ATK_STATE_SENSITIVE);
+  if (atk_state_set_contains_state (entry_set, ATK_STATE_SHOWING))
+    atk_state_set_add_state (set, ATK_STATE_SHOWING);
+  if (atk_state_set_contains_state (entry_set, ATK_STATE_VISIBLE))
+    atk_state_set_add_state (set, ATK_STATE_VISIBLE);
+
+  if (!gtk_entry_get_icon_sensitive (gtk_entry, icon->pos))
+      atk_state_set_remove_state (set, ATK_STATE_SENSITIVE);
+  if (!gtk_entry_get_icon_activatable (gtk_entry, icon->pos))
+      atk_state_set_remove_state (set, ATK_STATE_ENABLED);
+
+  g_object_unref (entry_set);
+  return set;
+}
+
+static void
+gtk_entry_icon_accessible_invalidate (GtkEntryIconAccessible *icon)
+{
+  if (!icon->entry)
+    return;
+  g_object_weak_unref (G_OBJECT (icon->entry),
+                       gtk_entry_icon_accessible_remove_entry,
+                       icon);
+  gtk_entry_icon_accessible_remove_entry (icon, NULL);
+}
+
+static void
+gtk_entry_icon_accessible_finalize (GObject *object)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (object);
+
+  gtk_entry_icon_accessible_invalidate (icon);
+
+  G_OBJECT_CLASS (_gtk_entry_icon_accessible_parent_class)->finalize (object);
+}
+
+static void
+_gtk_entry_icon_accessible_class_init (GtkEntryIconAccessibleClass *klass)
+{
+  AtkObjectClass  *atk_class = ATK_OBJECT_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  atk_class->initialize = gtk_entry_icon_accessible_initialize;
+  atk_class->get_parent = gtk_entry_icon_accessible_get_parent;
+  atk_class->ref_state_set = gtk_entry_icon_accessible_ref_state_set;
+
+  gobject_class->finalize = gtk_entry_icon_accessible_finalize;
+}
+
+static gboolean
+gtk_entry_icon_accessible_do_action (AtkAction *action,
+                                     gint       i)
+{
+  GtkEntryIconAccessible *icon = (GtkEntryIconAccessible *)action;
+  GtkWidget *widget;
+  GtkEntry *gtk_entry;
+  GdkEvent event;
+  GdkRectangle icon_area;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  if (widget == NULL)
+    return FALSE;
+
+  if (i != 0)
+    return FALSE;
+
+  if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_visible (widget))
+    return FALSE;
+
+  gtk_entry = GTK_ENTRY (widget);
+
+  if (!gtk_entry_get_icon_sensitive (gtk_entry, icon->pos) ||
+      !gtk_entry_get_icon_activatable (gtk_entry, icon->pos))
+    return FALSE;
+
+  gtk_entry_get_icon_area (gtk_entry, icon->pos, &icon_area);
+  memset (&event, 0, sizeof (event));
+  event.button.type = GDK_BUTTON_PRESS;
+  event.button.window = gtk_widget_get_window (widget);
+  event.button.button = 1;
+  event.button.send_event = TRUE;
+  event.button.time = GDK_CURRENT_TIME;
+  event.button.x = icon_area.x;
+  event.button.y = icon_area.y;
+
+  g_signal_emit_by_name (widget, "icon-press", 0, icon->pos, &event);
+  return TRUE;
+}
+
+static gint
+gtk_entry_icon_accessible_get_n_actions (AtkAction *action)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (action);
+  GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  GtkEntry *gtk_entry = GTK_ENTRY (widget);
+
+  return (gtk_entry_get_icon_activatable (gtk_entry, icon->pos) ? 1 : 0);
+}
+
+static const gchar *
+gtk_entry_icon_accessible_get_name (AtkAction *action,
+                                    gint       i)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (action);
+  GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  GtkEntry *gtk_entry = GTK_ENTRY (widget);
+
+  if (i != 0)
+    return NULL;
+  if (!gtk_entry_get_icon_activatable (gtk_entry, icon->pos))
+    return NULL;
+
+  return "activate";
+}
+
+static void
+icon_atk_action_interface_init (AtkActionIface *iface)
+{
+  iface->do_action = gtk_entry_icon_accessible_do_action;
+  iface->get_n_actions = gtk_entry_icon_accessible_get_n_actions;
+  iface->get_name = gtk_entry_icon_accessible_get_name;
+}
+
+static void
+gtk_entry_icon_accessible_get_extents (AtkComponent   *component,
+                                       gint           *x,
+                                       gint           *y,
+                                       gint           *width,
+                                       gint           *height,
+                                       AtkCoordType    coord_type)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (component);
+  GdkRectangle icon_area;
+  GtkEntry *gtk_entry;
+  GtkWidget *widget;
+
+  *x = G_MININT;
+  atk_component_get_extents (ATK_COMPONENT (icon->entry), x, y, width, height,
+                             coord_type);
+  if (*x == G_MININT)
+    return;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  gtk_entry = GTK_ENTRY (widget);
+  gtk_entry_get_icon_area (gtk_entry, icon->pos, &icon_area);
+  *width = icon_area.width;
+  *height = icon_area.height;
+  *x += icon_area.x;
+  *y += icon_area.y;
+}
+
+static void
+gtk_entry_icon_accessible_get_position (AtkComponent   *component,
+                                        gint           *x,
+                                        gint           *y,
+                                        AtkCoordType    coord_type)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (component);
+  GdkRectangle icon_area;
+  GtkEntry *gtk_entry;
+  GtkWidget *widget;
+
+  *x = G_MININT;
+  atk_component_get_position (ATK_COMPONENT (icon->entry), x, y, coord_type);
+  if (*x == G_MININT)
+    return;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  gtk_entry = GTK_ENTRY (widget);
+  gtk_entry_get_icon_area (gtk_entry, icon->pos, &icon_area);
+  *x += icon_area.x;
+  *y += icon_area.y;
+}
+
+static void
+gtk_entry_icon_accessible_get_size (AtkComponent *component,
+                                gint         *width,
+                                gint         *height)
+{
+  GtkEntryIconAccessible *icon = GTK_ENTRY_ICON_ACCESSIBLE (component);
+  GdkRectangle icon_area;
+  GtkEntry *gtk_entry;
+  GtkWidget *widget;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (icon->entry));
+  gtk_entry = GTK_ENTRY (widget);
+  gtk_entry_get_icon_area (gtk_entry, icon->pos, &icon_area);
+  *width = icon_area.width;
+  *height = icon_area.height;
+}
+
+static void
+icon_atk_component_interface_init (AtkComponentIface *iface)
+{
+  iface->get_extents = gtk_entry_icon_accessible_get_extents;
+  iface->get_size = gtk_entry_icon_accessible_get_size;
+  iface->get_position = gtk_entry_icon_accessible_get_position;
+}
+
 /* Callbacks */
 
 static void     insert_text_cb             (GtkEditable        *editable,
@@ -137,11 +470,13 @@ gtk_entry_accessible_notify_gtk (GObject    *obj,
   AtkObject* atk_obj;
   GtkEntry* gtk_entry;
   GtkEntryAccessible* entry;
+  GtkEntryAccessiblePrivate *priv;
 
   widget = GTK_WIDGET (obj);
   atk_obj = gtk_widget_get_accessible (widget);
   gtk_entry = GTK_ENTRY (widget);
   entry = GTK_ENTRY_ACCESSIBLE (atk_obj);
+  priv = entry->priv;
 
   if (g_strcmp0 (pspec->name, "cursor-position") == 0)
     {
@@ -174,6 +509,119 @@ gtk_entry_accessible_notify_gtk (GObject    *obj,
       new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT;
       atk_object_set_role (atk_obj, new_role);
     }
+  else if (g_strcmp0 (pspec->name, "primary-icon-storage-type") == 0)
+    {
+      if (gtk_entry_get_icon_storage_type (gtk_entry, GTK_ENTRY_ICON_PRIMARY) != GTK_IMAGE_EMPTY && !priv->icons[GTK_ENTRY_ICON_PRIMARY])
+        {
+          priv->icons[GTK_ENTRY_ICON_PRIMARY] = gtk_entry_icon_accessible_new (entry, GTK_ENTRY_ICON_PRIMARY);
+          g_signal_emit_by_name (entry, "children-changed::add", 0,
+                                 priv->icons[GTK_ENTRY_ICON_PRIMARY], NULL);
+        }
+      else if (gtk_entry_get_icon_storage_type (gtk_entry, GTK_ENTRY_ICON_PRIMARY) == GTK_IMAGE_EMPTY && priv->icons[GTK_ENTRY_ICON_PRIMARY])
+        {
+          gtk_entry_icon_accessible_invalidate (GTK_ENTRY_ICON_ACCESSIBLE (priv->icons[GTK_ENTRY_ICON_PRIMARY]));
+          g_signal_emit_by_name (entry, "children-changed::remove", 0,
+                                 priv->icons[GTK_ENTRY_ICON_PRIMARY], NULL);
+          g_clear_object (&priv->icons[GTK_ENTRY_ICON_PRIMARY]);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "secondary-icon-storage-type") == 0)
+    {
+      gint index = (priv->icons[GTK_ENTRY_ICON_PRIMARY] ? 1 : 0);
+      if (gtk_entry_get_icon_storage_type (gtk_entry, GTK_ENTRY_ICON_SECONDARY) != GTK_IMAGE_EMPTY && !priv->icons[GTK_ENTRY_ICON_SECONDARY])
+        {
+          priv->icons[GTK_ENTRY_ICON_SECONDARY] = gtk_entry_icon_accessible_new (entry, GTK_ENTRY_ICON_SECONDARY);
+          g_signal_emit_by_name (entry, "children-changed::add", index,
+                                 priv->icons[GTK_ENTRY_ICON_SECONDARY], NULL);
+        }
+      else if (gtk_entry_get_icon_storage_type (gtk_entry, GTK_ENTRY_ICON_SECONDARY) == GTK_IMAGE_EMPTY && priv->icons[GTK_ENTRY_ICON_SECONDARY])
+        {
+          gtk_entry_icon_accessible_invalidate (GTK_ENTRY_ICON_ACCESSIBLE (priv->icons[GTK_ENTRY_ICON_SECONDARY]));
+          g_signal_emit_by_name (entry, "children-changed::remove", index,
+                                 priv->icons[GTK_ENTRY_ICON_SECONDARY], NULL);
+          g_clear_object (&priv->icons[GTK_ENTRY_ICON_SECONDARY]);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "primary-icon-name") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_PRIMARY])
+        {
+          const gchar *name;
+          name = gtk_entry_get_icon_name (gtk_entry,
+                                          GTK_ENTRY_ICON_PRIMARY);
+          atk_object_set_name (priv->icons[GTK_ENTRY_ICON_PRIMARY], name);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "secondary-icon-name") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_SECONDARY])
+        {
+          const gchar *name;
+          name = gtk_entry_get_icon_name (gtk_entry,
+                                          GTK_ENTRY_ICON_SECONDARY);
+          atk_object_set_name (priv->icons[GTK_ENTRY_ICON_SECONDARY], name);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "primary-icon-tooltip-text") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_PRIMARY])
+        {
+          gchar *text;
+          text = gtk_entry_get_icon_tooltip_text (gtk_entry,
+                                                    GTK_ENTRY_ICON_PRIMARY);
+          atk_object_set_description (priv->icons[GTK_ENTRY_ICON_PRIMARY],
+                                      text);
+          g_free (text);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "secondary-icon-tooltip-text") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_SECONDARY])
+        {
+          gchar *text;
+          text = gtk_entry_get_icon_tooltip_text (gtk_entry,
+                                                    GTK_ENTRY_ICON_SECONDARY);
+          atk_object_set_description (priv->icons[GTK_ENTRY_ICON_SECONDARY],
+                                      text);
+          g_free (text);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "primary-icon-activatable") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_PRIMARY])
+        {
+          gboolean on = gtk_entry_get_icon_activatable (gtk_entry, GTK_ENTRY_ICON_PRIMARY);
+          atk_object_notify_state_change (priv->icons[GTK_ENTRY_ICON_PRIMARY],
+                                          ATK_STATE_ENABLED, on);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "secondary-icon-activatable") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_SECONDARY])
+        {
+          gboolean on = gtk_entry_get_icon_activatable (gtk_entry, GTK_ENTRY_ICON_SECONDARY);
+          atk_object_notify_state_change (priv->icons[GTK_ENTRY_ICON_SECONDARY],
+                                          ATK_STATE_ENABLED, on);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "primary-icon-sensitive") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_PRIMARY])
+        {
+          gboolean on = gtk_entry_get_icon_sensitive (gtk_entry, GTK_ENTRY_ICON_PRIMARY);
+          atk_object_notify_state_change (priv->icons[GTK_ENTRY_ICON_PRIMARY],
+                                          ATK_STATE_SENSITIVE, on);
+        }
+    }
+  else if (g_strcmp0 (pspec->name, "secondary-icon-sensitive") == 0)
+    {
+      if (priv->icons[GTK_ENTRY_ICON_SECONDARY])
+        {
+          gboolean on = gtk_entry_get_icon_sensitive (gtk_entry, GTK_ENTRY_ICON_SECONDARY);
+          atk_object_notify_state_change (priv->icons[GTK_ENTRY_ICON_SECONDARY],
+                                          ATK_STATE_SENSITIVE, on);
+        }
+    }
   else
     GTK_WIDGET_ACCESSIBLE_CLASS (_gtk_entry_accessible_parent_class)->notify_gtk (obj, pspec);
 }
@@ -192,19 +640,98 @@ gtk_entry_accessible_get_index_in_parent (AtkObject *accessible)
   return ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_index_in_parent (accessible);
 }
 
+static gint
+gtk_entry_accessible_get_n_children (AtkObject* obj)
+{
+  GtkWidget *widget;
+  GtkEntry *entry;
+  gint count = 0;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
+  if (widget == NULL)
+    return 0;
+
+  entry = GTK_ENTRY (widget);
+
+  if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_PRIMARY) != GTK_IMAGE_EMPTY)
+    count++;
+  if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_SECONDARY) != GTK_IMAGE_EMPTY)
+    count++;
+  return count;
+}
+
+static AtkObject *
+gtk_entry_accessible_ref_child (AtkObject *obj,
+                                gint i)
+{
+  GtkEntryAccessible *accessible = GTK_ENTRY_ACCESSIBLE (obj);
+  GtkEntryAccessiblePrivate *priv = accessible->priv;
+  GtkWidget *widget;
+  GtkEntry *entry;
+  GtkEntryIconPosition pos;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
+  if (widget == NULL)
+    return NULL;
+
+  entry = GTK_ENTRY (widget);
+
+  switch (i)
+    {
+    case 0:
+      if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_PRIMARY) != GTK_IMAGE_EMPTY)
+        pos = GTK_ENTRY_ICON_PRIMARY;
+      else if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_SECONDARY) != GTK_IMAGE_EMPTY)
+        pos = GTK_ENTRY_ICON_SECONDARY;
+      else
+        return NULL;
+      break;
+    case 1:
+      if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_PRIMARY) == GTK_IMAGE_EMPTY)
+        return NULL;
+      if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_SECONDARY) == GTK_IMAGE_EMPTY)
+        return NULL;
+      pos = GTK_ENTRY_ICON_SECONDARY;
+      break;
+    default:
+      return NULL;
+    }
+
+  if (!priv->icons[pos])
+    priv->icons[pos] = gtk_entry_icon_accessible_new (accessible, pos);
+  return g_object_ref (priv->icons[pos]);
+}
+
+static void
+gtk_entry_accessible_finalize (GObject *object)
+{
+  GtkEntryAccessible *entry = GTK_ENTRY_ACCESSIBLE (object);
+  GtkEntryAccessiblePrivate *priv = entry->priv;
+
+  g_clear_object (&priv->icons[GTK_ENTRY_ICON_PRIMARY]);
+  g_clear_object (&priv->icons[GTK_ENTRY_ICON_SECONDARY]);
+
+  G_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->finalize (object);
+}
+
 static void
 _gtk_entry_accessible_class_init (GtkEntryAccessibleClass *klass)
 {
   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
   GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   class->ref_state_set = gtk_entry_accessible_ref_state_set;
   class->get_index_in_parent = gtk_entry_accessible_get_index_in_parent;
   class->initialize = gtk_entry_accessible_initialize;
   class->get_attributes = gtk_entry_accessible_get_attributes;
+  class->get_n_children = gtk_entry_accessible_get_n_children;
+  class->ref_child = gtk_entry_accessible_ref_child;
 
   widget_class->notify_gtk = gtk_entry_accessible_notify_gtk;
 
+  gobject_class->finalize = gtk_entry_accessible_finalize;
+
   g_type_class_add_private (klass, sizeof (GtkEntryAccessiblePrivate));
 }
 
diff --git a/tests/a11y/children.c b/tests/a11y/children.c
index 19b1e60..dfd2894 100644
--- a/tests/a11y/children.c
+++ b/tests/a11y/children.c
@@ -23,6 +23,12 @@
 #include <gtk/gtk.h>
 #include <string.h>
 
+typedef struct
+{
+  GtkWidget *widget;
+  gpointer child[3];
+} STATE;
+
 static void
 test_scrolled_window_child_count (void)
 {
@@ -70,16 +76,38 @@ add_child (GtkWidget *container,
 }
 
 static void
-remove_child (GtkWidget *container,
-              GtkWidget *child)
+remove_child (STATE *state,
+              gint i)
 {
-  if (GTK_IS_SCROLLED_WINDOW (container))
+  GtkWidget *child;
+
+  if (GTK_IS_ENTRY (state->widget))
+    {
+      switch (i)
+        {
+        case 0:
+          gtk_entry_set_icon_from_gicon (GTK_ENTRY (state->widget),
+                                         GTK_ENTRY_ICON_PRIMARY,
+                                         NULL);
+        return;
+        case 1:
+          gtk_entry_set_icon_from_gicon (GTK_ENTRY (state->widget),
+                                         GTK_ENTRY_ICON_SECONDARY,
+                                         NULL);
+        return;
+        default:
+          return;
+        }
+    }
+
+  child = state->child [i];
+  if (GTK_IS_SCROLLED_WINDOW (state->widget))
     {
-      if (gtk_widget_get_parent (child) != container)
+      if (gtk_widget_get_parent (child) != state->widget)
         child = gtk_widget_get_parent (child);
     }
 
-  gtk_container_remove (GTK_CONTAINER (container), child);
+  gtk_container_remove (GTK_CONTAINER (state->widget), child);
 }
 
 static void
@@ -89,6 +117,34 @@ parent_notify (AtkObject *obj, GParamSpec *pspec, SignalData *data)
   data->parent = atk_object_get_parent (obj);
 }
 
+gboolean
+do_create_child (STATE *state, gint i)
+{
+  if (GTK_IS_ENTRY (state->widget))
+    {
+      switch (i)
+        {
+        case 0:
+          gtk_entry_set_icon_from_stock (GTK_ENTRY (state->widget),
+                                         GTK_ENTRY_ICON_PRIMARY,
+                                         GTK_STOCK_CAPS_LOCK_WARNING);
+        return TRUE;
+        case 1:
+          gtk_entry_set_icon_from_stock (GTK_ENTRY (state->widget),
+                                         GTK_ENTRY_ICON_SECONDARY,
+                                         GTK_STOCK_CLEAR);
+        return TRUE;
+        default:
+          return FALSE;
+        }
+    }
+  else if (gtk_container_child_type (GTK_CONTAINER (state->widget)) == G_TYPE_NONE)
+    return FALSE;
+
+  state->child[i] = gtk_label_new ("bla");
+  return TRUE;
+}
+
 static void
 test_add_remove (GtkWidget *widget)
 {
@@ -97,10 +153,11 @@ test_add_remove (GtkWidget *widget)
   SignalData add_data;
   SignalData remove_data;
   SignalData parent_data[3];
-  GtkWidget *child[3];
+  STATE state;
   gint i, j;
   gint step_children;
 
+  state.widget = widget;
   accessible = gtk_widget_get_accessible (widget);
 
   add_data.count = 0;
@@ -114,35 +171,44 @@ test_add_remove (GtkWidget *widget)
 
   for (i = 0; i < 3; i++)
     {
-      if (gtk_container_child_type (GTK_CONTAINER (widget)) == G_TYPE_NONE)
+      if (!do_create_child (&state, i))
         break;
-
-      child[i] = gtk_label_new ("bla");
-      parent_data[i].count = 0;
-      child_accessible = gtk_widget_get_accessible (child[i]);
-      g_signal_connect (child_accessible, "notify::accessible-parent",
-                        G_CALLBACK (parent_notify), &(parent_data[i]));
-      add_child (widget, child[i]);
+      if (!GTK_IS_ENTRY (widget))
+        {
+          parent_data[i].count = 0;
+          child_accessible = gtk_widget_get_accessible (state.child[i]);
+          g_signal_connect (child_accessible, "notify::accessible-parent",
+                            G_CALLBACK (parent_notify), &(parent_data[i]));
+          add_child (widget, state.child[i]);
+        }
+      else
+        child_accessible = atk_object_ref_accessible_child (accessible, i);
 
       g_assert_cmpint (add_data.count, ==, i + 1);
       g_assert_cmpint (add_data.n_children, ==, step_children + i + 1);
       g_assert_cmpint (remove_data.count, ==, 0);
-      g_assert_cmpint (parent_data[i].count, ==, 1);
+      if (!GTK_IS_ENTRY (widget))
+        g_assert_cmpint (parent_data[i].count, ==, 1);
       if (GTK_IS_SCROLLED_WINDOW (widget) ||
           GTK_IS_NOTEBOOK (widget))
         g_assert (atk_object_get_parent (ATK_OBJECT (parent_data[i].parent)) == accessible);
+      else if (GTK_IS_ENTRY (widget))
+        g_assert (atk_object_get_parent (child_accessible) == accessible);
       else
         g_assert (parent_data[i].parent == accessible);
+
+      if (GTK_IS_ENTRY (widget))
+        g_object_unref (child_accessible);
     }
   for (j = 0 ; j < i; j++)
     {
-      remove_child (widget, child[j]);
+      remove_child (&state, j);
       g_assert_cmpint (add_data.count, ==, i);
       g_assert_cmpint (remove_data.count, ==, j + 1);
       g_assert_cmpint (remove_data.n_children, ==, step_children + i - j - 1);
       if (parent_data[j].count == 2)
         g_assert (parent_data[j].parent == NULL);
-      else
+      else if (!GTK_IS_ENTRY (widget))
         {
           AtkStateSet *set;
           set = atk_object_ref_state_set (ATK_OBJECT (parent_data[j].parent));
@@ -205,6 +271,7 @@ main (int argc, char *argv[])
   add_child_tests (gtk_statusbar_new ());
 #endif
   add_child_tests (gtk_notebook_new ());
+  add_child_tests (gtk_entry_new ());
 
   return g_test_run ();
 }
diff --git a/tests/a11y/entries.txt b/tests/a11y/entries.txt
index cf15be1..8819516 100644
--- a/tests/a11y/entries.txt
+++ b/tests/a11y/entries.txt
@@ -219,10 +219,66 @@ window1
       <AtkAction>
       action 0 name: activate
       action 0 keybinding: <Alt>p
+    entry3
+      "text"
+      parent: box1
+      index: 5
+      state: editable enabled focusable sensitive single-line visible
+      toolkit: gtk
+      <AtkComponent>
+      layer: widget
+      alpha: 1
+      <AtkText>
+      text: icons
+      character count: 5
+      caret offset: 0
+      default attributes: bg-color: <omitted>
+                          bg-full-height: 0
+                          direction: <omitted>
+                          editable: false
+                          family-name: <omitted>
+                          fg-color: <omitted>
+                          indent: 0
+                          invisible: false
+                          justification: left
+                          language: <omitted>
+                          left-margin: 0
+                          pixels-above-lines: 0
+                          pixels-below-lines: 0
+                          pixels-inside-wrap: 0
+                          right-margin: 0
+                          rise: 0
+                          scale: 1
+                          size: <omitted>
+                          stretch: <omitted>
+                          strikethrough: false
+                          style: <omitted>
+                          underline: none
+                          variant: <omitted>
+                          weight: <omitted>
+                          wrap-mode: word
+      <AtkAction>
+      action 0 name: activate
+      unnamed-GtkEntryIconAccessible-0
+        "icon"
+        state: enabled sensitive visible
+        <AtkComponent>
+        layer: widget
+        alpha: 1
+        <AtkAction>
+        action 0 name: activate
+      unnamed-GtkEntryIconAccessible-1
+        "icon"
+        state: enabled sensitive visible
+        <AtkComponent>
+        layer: widget
+        alpha: 1
+        <AtkAction>
+        action 0 name: activate
     spinbutton1
       "spin button"
       parent: box1
-      index: 5
+      index: 6
       labelled-by: label3
       state: editable enabled focusable horizontal sensitive single-line visible
       toolkit: gtk
diff --git a/tests/a11y/entries.ui b/tests/a11y/entries.ui
index 0243dd7..280d3bc 100644
--- a/tests/a11y/entries.ui
+++ b/tests/a11y/entries.ui
@@ -54,6 +54,14 @@
           </object>
         </child>
         <child>
+          <object class="GtkEntry" id="entry3">
+            <property name="text" translatable="yes">icons</property>
+            <property name="visible">True</property>
+            <property name="primary-icon-stock">stock1</property>
+            <property name="secondary-icon-stock">stock2</property>
+          </object>
+        </child>
+        <child>
           <object class="GtkSpinButton" id="spinbutton1">
             <property name="adjustment">adjustment1</property>
             <property name="visible">True</property>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]