]> Pileus Git - ~andy/gtk/blobdiff - gtk/a11y/gtkentryaccessible.c
Another trivial sorting fix
[~andy/gtk] / gtk / a11y / gtkentryaccessible.c
index e0ff6dfc09b6f1c8c2e0c75e5bc13b139f8249c5..07fea6e07918649db695191667a994268abbc4bf 100644 (file)
@@ -1,4 +1,4 @@
-/* GAIL - The GNOME Accessibility Implementation Library
+/* GTK+ - accessibility implementations
  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
  *
  * This library is free software; you can redistribute it and/or
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #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,
@@ -33,7 +371,6 @@ static void     insert_text_cb             (GtkEditable        *editable,
 static void     delete_text_cb             (GtkEditable        *editable,
                                             gint                start,
                                             gint                end);
-static void     changed_cb                 (GtkEditable        *editable);
 
 static gboolean check_for_selection_change (GtkEntryAccessible *entry,
                                             GtkEntry           *gtk_entry);
@@ -44,7 +381,7 @@ static void atk_text_interface_init          (AtkTextIface         *iface);
 static void atk_action_interface_init        (AtkActionIface       *iface);
 
 
-G_DEFINE_TYPE_WITH_CODE (GtkEntryAccessible, _gtk_entry_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
+G_DEFINE_TYPE_WITH_CODE (GtkEntryAccessible, gtk_entry_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
                          G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
@@ -61,7 +398,7 @@ gtk_entry_accessible_ref_state_set (AtkObject *accessible)
   if (widget == NULL)
     return NULL;
 
-  state_set = ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->ref_state_set (accessible);
+  state_set = ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->ref_state_set (accessible);
 
   g_object_get (G_OBJECT (widget), "editable", &value, NULL);
   if (value)
@@ -79,7 +416,7 @@ gtk_entry_accessible_get_attributes (AtkObject *accessible)
   AtkAttribute *placeholder_text;
   const gchar *text;
 
-  attributes = ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_attributes (accessible);
+  attributes = ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->get_attributes (accessible);
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
   if (widget == NULL)
@@ -106,20 +443,18 @@ gtk_entry_accessible_initialize (AtkObject *obj,
   GtkEntryAccessible *gtk_entry_accessible;
   gint start_pos, end_pos;
 
-  ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->initialize (obj, data);
+  ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->initialize (obj, data);
 
   gtk_entry_accessible = GTK_ENTRY_ACCESSIBLE (obj);
 
   entry = GTK_ENTRY (data);
-  gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
-                                     &start_pos, &end_pos);
-  gtk_entry_accessible->cursor_position = end_pos;
-  gtk_entry_accessible->selection_bound = start_pos;
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start_pos, &end_pos);
+  gtk_entry_accessible->priv->cursor_position = end_pos;
+  gtk_entry_accessible->priv->selection_bound = start_pos;
 
   /* Set up signal callbacks */
   g_signal_connect (entry, "insert-text", G_CALLBACK (insert_text_cb), NULL);
   g_signal_connect (entry, "delete-text", G_CALLBACK (delete_text_cb), NULL);
-  g_signal_connect (entry, "changed", G_CALLBACK (changed_cb), NULL);
 
   if (gtk_entry_get_visibility (entry))
     obj->role = ATK_ROLE_TEXT;
@@ -135,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)
     {
@@ -149,7 +486,7 @@ gtk_entry_accessible_notify_gtk (GObject    *obj,
        * The entry cursor position has moved so generate the signal.
        */
       g_signal_emit_by_name (atk_obj, "text-caret-moved",
-                             entry->cursor_position);
+                             entry->priv->cursor_position);
     }
   else if (g_strcmp0 (pspec->name, "selection-bound") == 0)
     {
@@ -172,8 +509,123 @@ 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);
+          if (name)
+            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);
+          if (name)
+            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);
+    GTK_WIDGET_ACCESSIBLE_CLASS (gtk_entry_accessible_parent_class)->notify_gtk (obj, pspec);
 }
 
 static gint
@@ -187,30 +639,112 @@ gtk_entry_accessible_get_index_in_parent (AtkObject *accessible)
     if (GTK_IS_COMBO_BOX_ACCESSIBLE (accessible->accessible_parent))
       return 1;
 
-  return ATK_OBJECT_CLASS (_gtk_entry_accessible_parent_class)->get_index_in_parent (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_class_init (GtkEntryAccessibleClass *klass)
+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));
 }
 
 static void
-_gtk_entry_accessible_init (GtkEntryAccessible *entry)
+gtk_entry_accessible_init (GtkEntryAccessible *entry)
 {
-  entry->length_insert = 0;
-  entry->length_delete = 0;
-  entry->cursor_position = 0;
-  entry->selection_bound = 0;
+  entry->priv = G_TYPE_INSTANCE_GET_PRIVATE (entry,
+                                             GTK_TYPE_ENTRY_ACCESSIBLE,
+                                             GtkEntryAccessiblePrivate);
+  entry->priv->cursor_position = 0;
+  entry->priv->selection_bound = 0;
 }
 
 static gchar *
@@ -219,22 +753,12 @@ gtk_entry_accessible_get_text (AtkText *atk_text,
                                gint     end_pos)
 {
   GtkWidget *widget;
-  const gchar *text;
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
   if (widget == NULL)
     return NULL;
 
-  /* FIXME: is this acceptable ? */
-  if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
-    return g_strdup ("");
-
-  text = gtk_entry_get_text (GTK_ENTRY (widget));
-
-  if (text)
-    return g_utf8_substring (text, start_pos, end_pos > -1 ? end_pos : g_utf8_strlen (text, -1));
-
-  return NULL;
+  return _gtk_entry_get_display_text (GTK_ENTRY (widget), start_pos, end_pos);
 }
 
 static gchar *
@@ -250,10 +774,6 @@ gtk_entry_accessible_get_text_before_offset (AtkText         *text,
   if (widget == NULL)
     return NULL;
 
-  /* FIXME: is this acceptable ? */
-  if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
-    return g_strdup ("");
-
   return _gtk_pango_get_text_before (gtk_entry_get_layout (GTK_ENTRY (widget)),
                                      boundary_type, offset,
                                      start_offset, end_offset);
@@ -272,10 +792,6 @@ gtk_entry_accessible_get_text_at_offset (AtkText         *text,
   if (widget == NULL)
     return NULL;
 
-  /* FIXME: is this acceptable ? */
-  if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
-    return g_strdup ("");
-
   return _gtk_pango_get_text_at (gtk_entry_get_layout (GTK_ENTRY (widget)),
                                  boundary_type, offset,
                                  start_offset, end_offset);
@@ -294,10 +810,6 @@ gtk_entry_accessible_get_text_after_offset (AtkText         *text,
   if (widget == NULL)
     return NULL;
 
-  /* FIXME: is this acceptable ? */
-  if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
-    return g_strdup ("");
-
   return _gtk_pango_get_text_after (gtk_entry_get_layout (GTK_ENTRY (widget)),
                                     boundary_type, offset,
                                     start_offset, end_offset);
@@ -307,18 +819,23 @@ static gint
 gtk_entry_accessible_get_character_count (AtkText *atk_text)
 {
   GtkWidget *widget;
-  const gchar *text;
+  gchar *text;
+  glong char_count;
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
   if (widget == NULL)
     return 0;
 
-  text = gtk_entry_get_text (GTK_ENTRY (widget));
+  text = _gtk_entry_get_display_text (GTK_ENTRY (widget), 0, -1);
 
+  char_count = 0;
   if (text)
-    return g_utf8_strlen (text, -1);
+    {
+      char_count = g_utf8_strlen (text, -1);
+      g_free (text);
+    }
 
-  return 0;
+  return char_count;
 }
 
 static gint
@@ -421,7 +938,7 @@ gtk_entry_accessible_get_character_extents (AtkText      *text,
   GtkWidget *widget;
   GtkEntry *entry;
   PangoRectangle char_rect;
-  const gchar *entry_text;
+  gchar *entry_text;
   gint index, x_layout, y_layout;
   GdkWindow *window;
   gint x_window, y_window;
@@ -433,8 +950,10 @@ gtk_entry_accessible_get_character_extents (AtkText      *text,
   entry = GTK_ENTRY (widget);
 
   gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
-  entry_text = gtk_entry_get_text (entry);
+  entry_text = _gtk_entry_get_display_text (entry, 0, -1);
   index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text;
+  g_free (entry_text);
+
   pango_layout_index_to_pos (gtk_entry_get_layout (entry), index, &char_rect);
   pango_extents_to_pixels (&char_rect, NULL);
 
@@ -464,11 +983,12 @@ gtk_entry_accessible_get_offset_at_point (AtkText      *atk_text,
 {
   GtkWidget *widget;
   GtkEntry *entry;
-  const gchar *text;
+  gchar *text;
   gint index, x_layout, y_layout;
   gint x_window, y_window;
   gint x_local, y_local;
   GdkWindow *window;
+  glong offset;
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
   if (widget == NULL)
@@ -503,13 +1023,15 @@ gtk_entry_accessible_get_offset_at_point (AtkText      *atk_text,
         index = -1;
     }
 
+  offset = -1;
   if (index != -1)
     {
-      text = gtk_entry_get_text (entry);
-      return g_utf8_pointer_to_offset (text, text + index);
+      text = _gtk_entry_get_display_text (entry, 0, -1);
+      offset = g_utf8_pointer_to_offset (text, text + index);
+      g_free (text);
     }
 
-  return -1;
+  return offset;
 }
 
 static gint
@@ -626,23 +1148,28 @@ gtk_entry_accessible_get_character_at_offset (AtkText *atk_text,
                                               gint     offset)
 {
   GtkWidget *widget;
-  const gchar *text;
+  gchar *text;
   gchar *index;
+  gunichar result;
+
+  result = '\0';
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
   if (widget == NULL)
-    return '\0';
+    return result;
 
   if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
-    return '\0';
+    return result;
 
-  text = gtk_entry_get_text (GTK_ENTRY (widget));
-  if (offset >= g_utf8_strlen (text, -1))
-    return '\0';
-
-  index = g_utf8_offset_to_pointer (text, offset);
+  text = _gtk_entry_get_display_text (GTK_ENTRY (widget), 0, -1);
+  if (offset < g_utf8_strlen (text, -1))
+    {
+      index = g_utf8_offset_to_pointer (text, offset);
+      result = g_utf8_get_char (index);
+      g_free (text);
+    }
 
-  return g_utf8_get_char (index);
+  return result;
 }
 
 static void
@@ -853,11 +1380,11 @@ insert_text_cb (GtkEditable *editable,
     return;
 
   accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
-  if (accessible->length_insert == 0)
-    {
-      accessible->position_insert = *position;
-      accessible->length_insert = g_utf8_strlen (new_text, new_text_length);
-    }
+
+  g_signal_emit_by_name (accessible,
+                         "text-changed::insert",
+                         *position,
+                          g_utf8_strlen (new_text, new_text_length));
 }
 
 /* We connect to GtkEditable::delete-text, since it carries
@@ -872,53 +1399,24 @@ delete_text_cb (GtkEditable *editable,
 {
   GtkEntryAccessible *accessible;
 
+  accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
+
   if (end < 0)
     {
-      const gchar *text;
+      gchar *text;
 
-      text = gtk_entry_get_text (GTK_ENTRY (editable));
+      text = _gtk_entry_get_display_text (GTK_ENTRY (editable), 0, -1);
       end = g_utf8_strlen (text, -1);
+      g_free (text);
     }
 
   if (end == start)
     return;
 
-  accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
-  if (accessible->length_delete == 0)
-    {
-      accessible->position_delete = start;
-      accessible->length_delete = end - start;
-    }
-}
-
-/* Note the assumption here: A single ::changed emission
- * will only collect a single deletion/insertion, and there
- * won't be multiple insertions or deletions in a single
- * change.
- */
-static void
-changed_cb (GtkEditable *editable)
-{
-  GtkEntryAccessible *accessible;
-
-  accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
-
-  if (accessible->length_delete > 0)
-    {
-      g_signal_emit_by_name (accessible,
-                             "text-changed::delete",
-                             accessible->position_delete,
-                             accessible->length_delete);
-      accessible->length_delete = 0;
-    }
-  if (accessible->length_insert > 0)
-    {
-      g_signal_emit_by_name (accessible,
-                             "text-changed::insert",
-                             accessible->position_insert,
-                             accessible->length_insert);
-      accessible->length_insert = 0;
-    }
+  g_signal_emit_by_name (accessible,
+                         "text-changed::delete",
+                         start,
+                         end);
 }
 
 static gboolean
@@ -930,8 +1428,8 @@ check_for_selection_change (GtkEntryAccessible *accessible,
 
   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
     {
-      if (end != accessible->cursor_position ||
-          start != accessible->selection_bound)
+      if (end != accessible->priv->cursor_position ||
+          start != accessible->priv->selection_bound)
         /*
          * This check is here as this function can be called
          * for notification of selection_bound and current_pos.
@@ -944,11 +1442,11 @@ check_for_selection_change (GtkEntryAccessible *accessible,
   else
     {
       /* We had a selection */
-      ret_val = (accessible->cursor_position != accessible->selection_bound);
+      ret_val = (accessible->priv->cursor_position != accessible->priv->selection_bound);
     }
 
-  accessible->cursor_position = end;
-  accessible->selection_bound = start;
+  accessible->priv->cursor_position = end;
+  accessible->priv->selection_bound = start;
 
   return ret_val;
 }