]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkentry.c
entry: simplify code
[~andy/gtk] / gtk / gtkentry.c
index e58b1071f3ae75e6bfe6540a528ca1e3eb959909..989b24aff3d56760cc80a1be56c235b20dd5ef9a 100644 (file)
@@ -39,6 +39,7 @@
 #include "gtkdnd.h"
 #include "gtkentry.h"
 #include "gtkentrybuffer.h"
+#include "gtkiconhelperprivate.h"
 #include "gtkimagemenuitem.h"
 #include "gtkimcontextsimple.h"
 #include "gtkimmulticontext.h"
@@ -227,13 +228,7 @@ struct _EntryIconInfo
   guint in_drag        : 1;
   guint pressed        : 1;
 
-  GtkImageType  storage_type;
-  GdkPixbuf    *pixbuf;
-  gchar        *stock_id;
-  gchar        *icon_name;
-  GIcon        *gicon;
-
-  GtkStateFlags last_state;
+  GtkIconHelper *icon_helper;
 
   GtkTargetList *target_list;
   GdkDragAction actions;
@@ -573,7 +568,7 @@ static void         get_frame_size                     (GtkEntry       *entry,
                                                        gint           *width,
                                                        gint           *height);
 static void         gtk_entry_move_adjustments         (GtkEntry             *entry);
-static void         gtk_entry_ensure_pixbuf            (GtkEntry             *entry,
+static GdkPixbuf *  gtk_entry_ensure_pixbuf            (GtkEntry             *entry,
                                                         GtkEntryIconPosition  icon_pos);
 static void         gtk_entry_update_cached_style_values(GtkEntry      *entry);
 
@@ -992,7 +987,8 @@ gtk_entry_class_init (GtkEntryClass *class)
   /**
   * GtkEntry:placeholder-text:
   *
-  * The text that will be displayed in the #GtkEntry when it is empty and unfocused.
+  * The text that will be displayed in the #GtkEntry when it is empty
+  * and unfocused.
   *
   * Since: 3.2
   */
@@ -2454,26 +2450,49 @@ gtk_entry_init (GtkEntry *entry)
   gtk_entry_update_cached_style_values (entry);
 }
 
+static void
+gtk_entry_prepare_context_for_icon (GtkEntry             *entry,
+                                    GtkStyleContext      *context,
+                                    GtkEntryIconPosition  icon_pos)
+{
+  GtkEntryPrivate *priv = entry->priv;
+  EntryIconInfo *icon_info = priv->icons[icon_pos];
+  GtkWidget *widget;
+  GtkStateFlags state;
+
+  widget = GTK_WIDGET (entry);
+  state = GTK_STATE_FLAG_NORMAL;
+
+  if (!gtk_widget_is_sensitive (widget) || icon_info->insensitive)
+    state |= GTK_STATE_FLAG_INSENSITIVE;
+  else if (icon_info->prelight)
+    state |= GTK_STATE_FLAG_PRELIGHT;
+
+  gtk_style_context_save (context);
+
+  gtk_style_context_set_state (context, state);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_IMAGE);
+}
+
 static gint
 get_icon_width (GtkEntry             *entry,
                 GtkEntryIconPosition  icon_pos)
 {
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
-  GdkScreen *screen;
-  GtkSettings *settings;
-  gint menu_icon_width;
+  GtkStyleContext *context;
+  gint width;
 
-  if (!icon_info || icon_info->pixbuf == NULL)
+  if (!icon_info)
     return 0;
 
-  screen = gtk_widget_get_screen (GTK_WIDGET (entry));
-  settings = gtk_settings_get_for_screen (screen);
-
-  gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
-                                     &menu_icon_width, NULL);
+  context = gtk_widget_get_style_context (GTK_WIDGET (entry));
+  gtk_entry_prepare_context_for_icon (entry, context, icon_pos);
+  _gtk_icon_helper_get_size (icon_info->icon_helper, context,
+                             &width, NULL);
+  gtk_style_context_restore (context);
 
-  return MAX (gdk_pixbuf_get_width (icon_info->pixbuf), menu_icon_width);
+  return width;
 }
 
 static void
@@ -2623,6 +2642,8 @@ gtk_entry_finalize (GObject *object)
               icon_info->target_list = NULL;
             }
 
+          g_clear_object (&icon_info->icon_helper);
+
           g_slice_free (EntryIconInfo, icon_info);
           priv->icons[i] = NULL;
         }
@@ -2744,7 +2765,8 @@ update_cursors (GtkWidget *widget)
     {
       if ((icon_info = priv->icons[i]) != NULL)
         {
-          if (icon_info->pixbuf != NULL && icon_info->window != NULL)
+          if (!_gtk_icon_helper_get_is_empty (icon_info->icon_helper) && 
+              icon_info->window != NULL)
             gdk_window_show_unraised (icon_info->window);
 
           /* The icon windows are not children of the visible entry window,
@@ -2819,6 +2841,8 @@ construct_icon_info (GtkWidget            *widget,
   icon_info = g_slice_new0 (EntryIconInfo);
   priv->icons[icon_pos] = icon_info;
 
+  icon_info->icon_helper = _gtk_icon_helper_new ();
+
   if (gtk_widget_get_realized (widget))
     realize_icon_info (widget, icon_pos);
 
@@ -2841,7 +2865,8 @@ gtk_entry_map (GtkWidget *widget)
     {
       if ((icon_info = priv->icons[i]) != NULL)
         {
-          if (icon_info->pixbuf != NULL && icon_info->window != NULL)
+          if (!_gtk_icon_helper_get_is_empty (icon_info->icon_helper) &&
+              icon_info->window != NULL)
             gdk_window_show (icon_info->window);
         }
     }
@@ -2861,7 +2886,8 @@ gtk_entry_unmap (GtkWidget *widget)
     {
       if ((icon_info = priv->icons[i]) != NULL)
         {
-          if (icon_info->pixbuf != NULL && icon_info->window != NULL)
+          if (!_gtk_icon_helper_get_is_empty (icon_info->icon_helper) && 
+              icon_info->window != NULL)
             gdk_window_hide (icon_info->window);
         }
     }
@@ -3171,17 +3197,20 @@ gtk_entry_get_text_area_size (GtkEntry *entry,
   GtkWidget *widget = GTK_WIDGET (entry);
   GtkAllocation allocation;
   GtkRequisition requisition;
+  gint req_height;
   gint frame_height;
   gint xborder, yborder;
 
   gtk_widget_get_preferred_size (widget, &requisition, NULL);
+  req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom (widget);
+
   gtk_widget_get_allocation (widget, &allocation);
   _gtk_entry_get_borders (entry, &xborder, &yborder);
 
   if (gtk_widget_get_realized (widget))
     get_frame_size (entry, TRUE, NULL, NULL, NULL, &frame_height);
   else
-    frame_height = requisition.height;
+    frame_height = req_height;
 
   if (gtk_widget_has_focus (widget) && !priv->interior_focus)
     frame_height -= 2 * priv->focus_width;
@@ -3190,13 +3219,13 @@ gtk_entry_get_text_area_size (GtkEntry *entry,
     *x = xborder;
 
   if (y)
-    *y = frame_height / 2 - (requisition.height - yborder * 2) / 2;
+    *y = frame_height / 2 - (req_height - yborder * 2) / 2;
 
   if (width)
     *width = allocation.width - xborder * 2;
 
   if (height)
-    *height = requisition.height - yborder * 2;
+    *height = req_height - yborder * 2;
 }
 
 static void
@@ -3229,8 +3258,12 @@ get_frame_size (GtkEntry *entry,
   GtkAllocation allocation;
   GtkRequisition requisition;
   GtkWidget *widget = GTK_WIDGET (entry);
+  gint req_height;
 
   gtk_widget_get_preferred_size (widget, &requisition, NULL);
+
+  req_height = requisition.height - gtk_widget_get_margin_top (widget) - gtk_widget_get_margin_bottom (widget);
+
   gtk_widget_get_allocation (widget, &allocation);
 
   if (x)
@@ -3239,9 +3272,9 @@ get_frame_size (GtkEntry *entry,
   if (y)
     {
       if (priv->is_cell_renderer)
-       *y = 0;
+        *y = 0;
       else
-       *y = (allocation.height - requisition.height) / 2;
+        *y = (allocation.height - req_height) / 2;
 
       if (relative_to_window)
         *y += allocation.y;
@@ -3253,9 +3286,9 @@ get_frame_size (GtkEntry *entry,
   if (height)
     {
       if (priv->is_cell_renderer)
-       *height = allocation.height;
+        *height = allocation.height;
       else
-       *height = requisition.height;
+        *height = req_height;
     }
 }
 
@@ -3338,49 +3371,37 @@ draw_icon (GtkWidget            *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
-  GdkPixbuf *pixbuf;
-  gint x, y, width, height;
+  gint x, y, width, height, pix_width, pix_height;
   GtkStyleContext *context;
 
+  context = gtk_widget_get_style_context (widget);
+
   if (!icon_info)
     return;
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
-
-  if (icon_info->pixbuf == NULL)
-    return;
+  cairo_save (cr);
+  gtk_cairo_transform_to_window (cr, widget, icon_info->window);
 
   width = gdk_window_get_width (icon_info->window);
   height = gdk_window_get_height (icon_info->window);
 
-  context = gtk_widget_get_style_context (widget);
-
   /* size_allocate hasn't been called yet. These are the default values.
    */
   if (width == 1 || height == 1)
     return;
 
-  pixbuf = icon_info->pixbuf;
-  g_object_ref (pixbuf);
-
-  if (gdk_pixbuf_get_height (pixbuf) > height)
-    {
-      GdkPixbuf *temp_pixbuf;
-      gint scale;
+  gtk_entry_prepare_context_for_icon (entry, context, icon_pos);
+  _gtk_icon_helper_get_size (icon_info->icon_helper, context, &pix_width, &pix_height);
 
-      scale = height - 2 * priv->icon_margin;
-      temp_pixbuf = gdk_pixbuf_scale_simple (pixbuf, scale, scale,
-                                             GDK_INTERP_BILINEAR);
-      g_object_unref (pixbuf);
-      pixbuf = temp_pixbuf;
-    }
+  x = MAX (0, (width  - pix_width) / 2);
+  y = MAX (0, (height - pix_height) / 2);
 
-  x = (width  - gdk_pixbuf_get_width (pixbuf)) / 2;
-  y = (height - gdk_pixbuf_get_height (pixbuf)) / 2;
+  _gtk_icon_helper_draw (icon_info->icon_helper,
+                         context, cr,
+                         x, y);
 
-  gtk_render_icon (context, cr, pixbuf, x, y);
-
-  g_object_unref (pixbuf);
+  gtk_style_context_restore (context);
+  cairo_restore (cr);
 }
 
 
@@ -3431,7 +3452,7 @@ gtk_entry_draw_frame (GtkWidget       *widget,
 
   gtk_entry_draw_progress (widget, context, cr);
 
-  if (gtk_widget_has_focus (widget) && !priv->interior_focus)
+  if (gtk_widget_has_visible_focus (widget) && !priv->interior_focus)
     {
       x -= priv->focus_width;
       y -= priv->focus_width;
@@ -3514,6 +3535,8 @@ gtk_entry_draw_progress (GtkWidget       *widget,
                          GtkStyleContext *context,
                          cairo_t         *cr)
 {
+  GtkEntry *entry = GTK_ENTRY (widget);
+  GtkEntryPrivate *private = entry->priv;
   gint x, y, width, height;
 
   get_progress_area (widget, &x, &y, &width, &height);
@@ -3523,6 +3546,8 @@ gtk_entry_draw_progress (GtkWidget       *widget,
 
   gtk_style_context_save (context);
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
+  if (private->progress_pulse_mode)
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_PULSE);
 
   gtk_render_activity (context, cr,
                        x, y, width, height);
@@ -3573,15 +3598,7 @@ gtk_entry_draw (GtkWidget *widget,
       EntryIconInfo *icon_info = priv->icons[i];
 
       if (icon_info != NULL)
-        {
-          cairo_save (cr);
-
-          gtk_cairo_transform_to_window (cr, widget, icon_info->window);
-
-          draw_icon (widget, cr, i);
-
-          cairo_restore (cr);
-        }
+        draw_icon (widget, cr, i);
     }
 
   gtk_style_context_restore (context);
@@ -3763,20 +3780,29 @@ gtk_entry_button_press (GtkWidget      *widget,
       gtk_widget_grab_focus (widget);
       priv->in_click = FALSE;
     }
-  
+
   tmp_pos = gtk_entry_find_position (entry, event->x + priv->scroll_offset);
-    
-  if (event->button == 1)
+
+  if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+    {
+      gtk_entry_do_popup (entry, event);
+      priv->button = 0; /* Don't wait for release, since the menu will gtk_grab_add */
+
+      return TRUE;
+    }
+  else if (event->button == 1)
     {
       gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end);
-      
+
       priv->select_words = FALSE;
       priv->select_lines = FALSE;
 
-      if (event->state & GDK_SHIFT_MASK)
+      if (event->state &
+          gtk_widget_get_modifier_mask (widget,
+                                        GDK_MODIFIER_INTENT_EXTEND_SELECTION))
        {
          _gtk_entry_reset_im_context (entry);
-         
+
          if (!have_selection) /* select from the current position to the clicked position */
            sel_start = sel_end = priv->current_pos;
          
@@ -3887,13 +3913,6 @@ gtk_entry_button_press (GtkWidget      *widget,
           gtk_widget_error_bell (widget);
         }
     }
-  else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
-    {
-      gtk_entry_do_popup (entry, event);
-      priv->button = 0;        /* Don't wait for release, since the menu will gtk_grab_add */
-
-      return TRUE;
-    }
 
   return FALSE;
 }
@@ -4492,14 +4511,7 @@ icon_theme_changed (GtkEntry *entry)
     {
       EntryIconInfo *icon_info = priv->icons[i];
       if (icon_info != NULL) 
-        {
-          if (icon_info->storage_type == GTK_IMAGE_ICON_NAME)
-            gtk_entry_set_icon_from_icon_name (entry, i, icon_info->icon_name);
-          else if (icon_info->storage_type == GTK_IMAGE_STOCK)
-            gtk_entry_set_icon_from_stock (entry, i, icon_info->stock_id);
-          else if (icon_info->storage_type == GTK_IMAGE_GICON)
-            gtk_entry_set_icon_from_gicon (entry, i, icon_info->gicon);
-        }
+        _gtk_icon_helper_invalidate (icon_info->icon_helper);
     }
 
   gtk_widget_queue_draw (GTK_WIDGET (entry));
@@ -6642,8 +6654,9 @@ gtk_entry_clear (GtkEntry             *entry,
 {
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
+  GtkImageType storage_type;
 
-  if (!icon_info || icon_info->storage_type == GTK_IMAGE_EMPTY)
+  if (icon_info && _gtk_icon_helper_get_is_empty (icon_info->icon_helper))
     return;
 
   g_object_freeze_notify (G_OBJECT (entry));
@@ -6654,13 +6667,9 @@ gtk_entry_clear (GtkEntry             *entry,
   if (GDK_IS_WINDOW (icon_info->window))
     gdk_window_hide (icon_info->window);
 
-  if (icon_info->pixbuf)
-    {
-      g_object_unref (icon_info->pixbuf);
-      icon_info->pixbuf = NULL;
-    }
+  storage_type = _gtk_icon_helper_get_storage_type (icon_info->icon_helper);
 
-  switch (icon_info->storage_type)
+  switch (storage_type)
     {
     case GTK_IMAGE_PIXBUF:
       g_object_notify (G_OBJECT (entry),
@@ -6668,25 +6677,16 @@ gtk_entry_clear (GtkEntry             *entry,
       break;
 
     case GTK_IMAGE_STOCK:
-      g_free (icon_info->stock_id);
-      icon_info->stock_id = NULL;
       g_object_notify (G_OBJECT (entry),
                        icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-stock" : "secondary-icon-stock");
       break;
 
     case GTK_IMAGE_ICON_NAME:
-      g_free (icon_info->icon_name);
-      icon_info->icon_name = NULL;
       g_object_notify (G_OBJECT (entry),
                        icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-name" : "secondary-icon-name");
       break;
 
     case GTK_IMAGE_GICON:
-      if (icon_info->gicon)
-        {
-          g_object_unref (icon_info->gicon);
-          icon_info->gicon = NULL;
-        }
       g_object_notify (G_OBJECT (entry),
                        icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-gicon" : "secondary-icon-gicon");
       break;
@@ -6696,7 +6696,8 @@ gtk_entry_clear (GtkEntry             *entry,
       break;
     }
 
-  icon_info->storage_type = GTK_IMAGE_EMPTY;
+  _gtk_icon_helper_clear (icon_info->icon_helper);
+
   g_object_notify (G_OBJECT (entry),
                    icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-storage-type" : "secondary-icon-storage-type");
 
@@ -6704,179 +6705,25 @@ gtk_entry_clear (GtkEntry             *entry,
 }
 
 static GdkPixbuf *
-create_normal_pixbuf (GtkStyleContext *context,
-                      const gchar     *stock_id,
-                      GtkIconSize      icon_size)
-{
-  GtkIconSet *icon_set;
-  GdkPixbuf *pixbuf;
-
-  gtk_style_context_save (context);
-
-  /* Unset any state */
-  gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
-
-  icon_set = gtk_style_context_lookup_icon_set (context, stock_id);
-  pixbuf = gtk_icon_set_render_icon_pixbuf (icon_set, context, icon_size);
-
-  gtk_style_context_restore (context);
-
-  return pixbuf;
-}
-
-static GdkPixbuf *
-ensure_stated_icon_from_info (GtkStyleContext *context,
-                              GtkIconInfo *info)
-{
-  GdkPixbuf *retval = NULL;
-  gboolean symbolic = FALSE;
-
-  if (info != NULL)
-    {
-      retval =
-        gtk_icon_info_load_symbolic_for_context (info,
-                                                 context,
-                                                 &symbolic,
-                                                 NULL);
-    }
-
-  if (retval == NULL)
-    {
-      retval =
-        create_normal_pixbuf (context,
-                              GTK_STOCK_MISSING_IMAGE,
-                              GTK_ICON_SIZE_MENU);
-    }
-  else if (!symbolic)
-    {
-      GdkPixbuf *temp_pixbuf;
-      GtkIconSource *icon_source;
-
-      icon_source = gtk_icon_source_new ();
-      gtk_icon_source_set_pixbuf (icon_source, retval);
-      gtk_icon_source_set_state_wildcarded (icon_source, TRUE);
-
-      temp_pixbuf = gtk_render_icon_pixbuf (context, icon_source, (GtkIconSize)-1);
-
-      gtk_icon_source_free (icon_source);
-
-      g_object_unref (retval);
-      retval = temp_pixbuf;
-    }
-
-  return retval;
-}
-
-static void
 gtk_entry_ensure_pixbuf (GtkEntry             *entry,
                          GtkEntryIconPosition  icon_pos)
 {
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
   GtkStyleContext *context;
-  GtkIconInfo *info;
-  GtkIconTheme *icon_theme;
-  GtkSettings *settings;
-  GtkWidget *widget;
-  GdkScreen *screen;
-  gint width, height;
-  GtkStateFlags state;
-
-  widget = GTK_WIDGET (entry);
-  context = gtk_widget_get_style_context (widget);
+  GdkPixbuf *pix;
 
-  state = GTK_STATE_FLAG_NORMAL;
-
-  if (!gtk_widget_is_sensitive (widget) || icon_info->insensitive)
-    state |= GTK_STATE_FLAG_INSENSITIVE;
-  else if (icon_info->prelight)
-    state |= GTK_STATE_FLAG_PRELIGHT;
-
-  if ((icon_info == NULL) ||
-      ((icon_info->pixbuf != NULL) && icon_info->last_state == state))
-    return;
-
-  icon_info->last_state = state;
-
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_IMAGE);
-
-  switch (icon_info->storage_type)
-    {
-    case GTK_IMAGE_EMPTY:
-    case GTK_IMAGE_PIXBUF:
-      break;
-    case GTK_IMAGE_STOCK:
-      icon_info->pixbuf = create_normal_pixbuf (context, icon_info->stock_id,
-                                                GTK_ICON_SIZE_MENU);
-
-      if (!icon_info->pixbuf)
-        icon_info->pixbuf = create_normal_pixbuf (context,
-                                                  GTK_STOCK_MISSING_IMAGE,
-                                                  GTK_ICON_SIZE_MENU);
-      break;
-
-    case GTK_IMAGE_ICON_NAME:
-      screen = gtk_widget_get_screen (widget);
-      if (screen)
-        {
-          icon_theme = gtk_icon_theme_get_for_screen (screen);
-          settings = gtk_settings_get_for_screen (screen);
-
-          gtk_icon_size_lookup_for_settings (settings,
-                                             GTK_ICON_SIZE_MENU,
-                                             &width, &height);
-
-          info = gtk_icon_theme_lookup_icon (icon_theme,
-                                             icon_info->icon_name,
-                                             MIN (width, height), 
-                                             0);
-
-          icon_info->pixbuf =
-            ensure_stated_icon_from_info (context, info);
-
-          if (info)
-            gtk_icon_info_free (info);
-        }
-      break;
-
-    case GTK_IMAGE_GICON:
-      screen = gtk_widget_get_screen (widget);
-      if (screen)
-        {
-          icon_theme = gtk_icon_theme_get_for_screen (screen);
-          settings = gtk_settings_get_for_screen (screen);
-
-          gtk_icon_size_lookup_for_settings (settings,
-                                             GTK_ICON_SIZE_MENU,
-                                             &width, &height);
-
-          info = gtk_icon_theme_lookup_by_gicon (icon_theme,
-                                                 icon_info->gicon,
-                                                 MIN (width, height), 
-                                                 GTK_ICON_LOOKUP_USE_BUILTIN);
-
-          icon_info->pixbuf =
-            ensure_stated_icon_from_info (context, info);
-
-          if (info)
-            gtk_icon_info_free (info);
-        }
-      break;
+  context = gtk_widget_get_style_context (GTK_WIDGET (entry));
+  gtk_entry_prepare_context_for_icon (entry, context, icon_pos);
 
-    default:
-      g_assert_not_reached ();
-      break;
-    }
+  pix = _gtk_icon_helper_ensure_pixbuf (icon_info->icon_helper,
+                                        context);
 
   gtk_style_context_restore (context);
 
-  if (icon_info->pixbuf != NULL && icon_info->window != NULL)
-    gdk_window_show_unraised (icon_info->window);
+  return pix;
 }
 
-
 /* Public API
  */
 
@@ -7810,8 +7657,7 @@ gtk_entry_set_icon_from_pixbuf (GtkEntry             *entry,
 
   if (pixbuf)
     {
-      icon_info->storage_type = GTK_IMAGE_PIXBUF;
-      icon_info->pixbuf = pixbuf;
+      _gtk_icon_helper_set_pixbuf (icon_info->icon_helper, pixbuf);
 
       if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
         {
@@ -7826,9 +7672,9 @@ gtk_entry_set_icon_from_pixbuf (GtkEntry             *entry,
 
       if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
           gdk_window_show_unraised (icon_info->window);
-    }
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
+      g_object_unref (pixbuf);
+    }
   
   if (gtk_widget_get_visible (GTK_WIDGET (entry)))
     gtk_widget_queue_resize (GTK_WIDGET (entry));
@@ -7875,8 +7721,7 @@ gtk_entry_set_icon_from_stock (GtkEntry             *entry,
 
   if (new_id != NULL)
     {
-      icon_info->storage_type = GTK_IMAGE_STOCK;
-      icon_info->stock_id = new_id;
+      _gtk_icon_helper_set_stock_id (icon_info->icon_helper, new_id, GTK_ICON_SIZE_MENU);
 
       if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
         {
@@ -7891,9 +7736,9 @@ gtk_entry_set_icon_from_stock (GtkEntry             *entry,
 
       if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
           gdk_window_show_unraised (icon_info->window);
-    }
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
+      g_free (new_id);
+    }
 
   if (gtk_widget_get_visible (GTK_WIDGET (entry)))
     gtk_widget_queue_resize (GTK_WIDGET (entry));
@@ -7943,8 +7788,7 @@ gtk_entry_set_icon_from_icon_name (GtkEntry             *entry,
 
   if (new_name != NULL)
     {
-      icon_info->storage_type = GTK_IMAGE_ICON_NAME;
-      icon_info->icon_name = new_name;
+      _gtk_icon_helper_set_icon_name (icon_info->icon_helper, new_name, GTK_ICON_SIZE_MENU);
 
       if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
         {
@@ -7959,9 +7803,9 @@ gtk_entry_set_icon_from_icon_name (GtkEntry             *entry,
 
       if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
           gdk_window_show_unraised (icon_info->window);
-    }
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
+      g_free (new_name);
+    }
 
   if (gtk_widget_get_visible (GTK_WIDGET (entry)))
     gtk_widget_queue_resize (GTK_WIDGET (entry));
@@ -8010,8 +7854,7 @@ gtk_entry_set_icon_from_gicon (GtkEntry             *entry,
 
   if (icon)
     {
-      icon_info->storage_type = GTK_IMAGE_GICON;
-      icon_info->gicon = icon;
+      _gtk_icon_helper_set_gicon (icon_info->icon_helper, icon, GTK_ICON_SIZE_MENU);
 
       if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
         {
@@ -8026,9 +7869,9 @@ gtk_entry_set_icon_from_gicon (GtkEntry             *entry,
 
       if (gtk_widget_get_mapped (GTK_WIDGET (entry)))
           gdk_window_show_unraised (icon_info->window);
-    }
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
+      g_object_unref (icon);
+    }
 
   if (gtk_widget_get_visible (GTK_WIDGET (entry)))
     gtk_widget_queue_resize (GTK_WIDGET (entry));
@@ -8125,6 +7968,7 @@ gtk_entry_get_icon_pixbuf (GtkEntry             *entry,
 {
   GtkEntryPrivate *priv;
   EntryIconInfo *icon_info;
+  GdkPixbuf *pixbuf;
 
   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
   g_return_val_if_fail (IS_VALID_ICON_POSITION (icon_pos), NULL);
@@ -8136,9 +7980,13 @@ gtk_entry_get_icon_pixbuf (GtkEntry             *entry,
   if (!icon_info)
     return NULL;
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
+  /* HACK: unfortunately this is transfer none, so we need to return
+   * the icon helper's cache ref directly.
+   */
+  pixbuf = gtk_entry_ensure_pixbuf (entry, icon_pos);
+  g_object_unref (pixbuf);
 
-  return icon_info->pixbuf;
+  return pixbuf;
 }
 
 /**
@@ -8171,7 +8019,7 @@ gtk_entry_get_icon_gicon (GtkEntry             *entry,
   if (!icon_info)
     return NULL;
 
-  return icon_info->storage_type == GTK_IMAGE_GICON ? icon_info->gicon : NULL;
+  return _gtk_icon_helper_peek_gicon (icon_info->icon_helper);
 }
 
 /**
@@ -8204,7 +8052,7 @@ gtk_entry_get_icon_stock (GtkEntry             *entry,
   if (!icon_info)
     return NULL;
 
-  return icon_info->storage_type == GTK_IMAGE_STOCK ? icon_info->stock_id : NULL;
+  return _gtk_icon_helper_get_stock_id (icon_info->icon_helper);
 }
 
 /**
@@ -8237,7 +8085,7 @@ gtk_entry_get_icon_name (GtkEntry             *entry,
   if (!icon_info)
     return NULL;
 
-  return icon_info->storage_type == GTK_IMAGE_ICON_NAME ? icon_info->icon_name : NULL;
+  return _gtk_icon_helper_get_icon_name (icon_info->icon_helper);
 }
 
 /**
@@ -8343,7 +8191,7 @@ gtk_entry_get_icon_storage_type (GtkEntry             *entry,
   if (!icon_info)
     return GTK_IMAGE_EMPTY;
 
-  return icon_info->storage_type;
+  return _gtk_icon_helper_get_storage_type (icon_info->icon_helper);
 }
 
 /**
@@ -8872,6 +8720,7 @@ typedef struct
   GtkEntry *entry;
   gint button;
   guint time;
+  GdkDevice *device;
 } PopupInfo;
 
 static void
@@ -8979,15 +8828,15 @@ popup_targets_received (GtkClipboard     *clipboard,
                     info_entry_priv->popup_menu);
 
 
-      if (info->button)
-       gtk_menu_popup (GTK_MENU (info_entry_priv->popup_menu), NULL, NULL,
-                       NULL, NULL,
+      if (info->device)
+       gtk_menu_popup_for_device (GTK_MENU (info_entry_priv->popup_menu),
+                        info->device, NULL, NULL, NULL, NULL, NULL,
                        info->button, info->time);
       else
        {
          gtk_menu_popup (GTK_MENU (info_entry_priv->popup_menu), NULL, NULL,
                          popup_position_func, entry,
-                         info->button, info->time);
+                         0, gtk_get_current_event_time ());
          gtk_menu_shell_select_first (GTK_MENU_SHELL (info_entry_priv->popup_menu), FALSE);
        }
     }
@@ -9012,11 +8861,13 @@ gtk_entry_do_popup (GtkEntry       *entry,
     {
       info->button = event->button;
       info->time = event->time;
+      info->device = event->device;
     }
   else
     {
       info->button = 0;
       info->time = gtk_get_current_event_time ();
+      info->device = NULL;
     }
 
   gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD),
@@ -9048,24 +8899,14 @@ gtk_entry_drag_begin (GtkWidget      *widget,
         {
           if (icon_info->in_drag) 
             {
-              switch (icon_info->storage_type)
-                {
-                case GTK_IMAGE_STOCK:
-                  gtk_drag_set_icon_stock (context, icon_info->stock_id, -2, -2);
-                  break;
-
-                case GTK_IMAGE_ICON_NAME:
-                  gtk_drag_set_icon_name (context, icon_info->icon_name, -2, -2);
-                  break;
-
-                  /* FIXME: No GIcon support for dnd icons */
-                case GTK_IMAGE_GICON:
-                case GTK_IMAGE_PIXBUF:
-                  gtk_drag_set_icon_pixbuf (context, icon_info->pixbuf, -2, -2);
-                  break;
-                default:
-                  g_assert_not_reached ();
-                }
+              GdkPixbuf *pix;
+
+              pix = _gtk_icon_helper_ensure_pixbuf
+                (icon_info->icon_helper,
+                 gtk_widget_get_style_context (GTK_WIDGET (entry)));
+              gtk_drag_set_icon_pixbuf (context, pix, -2, -2);
+
+              g_object_unref (pix);
             }
         }
     }
@@ -10337,9 +10178,15 @@ gtk_entry_progress_pulse (GtkEntry *entry)
  * @entry: a #GtkEntry
  * @text: a string to be displayed when @entry is empty an unfocused, or %NULL
  *
- * Sets text to be displayed in @entry when
- * it is empty and unfocused. This can be used to give a visual hint
- * of the expected contents of the #GtkEntry.
+ * Sets text to be displayed in @entry when it is empty and unfocused.
+ * This can be used to give a visual hint of the expected contents of
+ * the #GtkEntry.
+ *
+ * Note that since the placeholder text gets removed when the entry
+ * received focus, using this feature is a bit problematic if the entry
+ * is given the initial focus in a window. Sometimes this can be
+ * worked around by delaying the initial focus setting until the
+ * first key event arrives.
  *
  * Since: 3.2
  **/