]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkentry.c
entry: guard for != NULL before unreffing the pixbuf
[~andy/gtk] / gtk / gtkentry.c
index b21146c7235ad5e81fe48f719cc7761deedd16c9..db83f058d46e8f60d7e5bbbd2532f77fc5f32f8f 100644 (file)
@@ -16,9 +16,7 @@
  * 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 <math.h>
 #include <string.h>
 
-#include "gtkalignment.h"
 #include "gtkbindings.h"
 #include "gtkcelleditable.h"
 #include "gtkclipboard.h"
 #include "gtkdnd.h"
 #include "gtkentry.h"
 #include "gtkentrybuffer.h"
+#include "gtkiconhelperprivate.h"
 #include "gtkimagemenuitem.h"
 #include "gtkimcontextsimple.h"
 #include "gtkimmulticontext.h"
 #include "gtktooltip.h"
 #include "gtkiconfactory.h"
 #include "gtkicontheme.h"
+#include "gtkwidgetprivate.h"
+#include "gtkstylecontextprivate.h"
 
+#include "a11y/gtkentryaccessible.h"
 
 /**
  * SECTION:gtkentry
 #define DRAW_TIMEOUT     20
 #define COMPLETION_TIMEOUT 300
 #define PASSWORD_HINT_MAX 8
+#define PAGE_STEP 14
 
 #define MAX_ICONS 2
 
@@ -143,6 +145,8 @@ struct _GtkEntryPrivate
   GtkShadowType          shadow_type;
   GtkWidget             *popup_menu;
 
+  GdkDevice             *device;
+
   GdkDevice             *completion_device;
   GdkWindow             *text_area;
 
@@ -154,6 +158,8 @@ struct _GtkEntryPrivate
   gdouble       progress_pulse_fraction;
   gdouble       progress_pulse_current;
 
+  gchar        *placeholder_text;
+
   gfloat        xalign;
 
   gint          ascent;                     /* font ascent in pango units  */
@@ -163,7 +169,6 @@ struct _GtkEntryPrivate
   gint          drag_start_x;
   gint          drag_start_y;
   gint          focus_width;
-  gint          icon_margin;
   gint          insert_pos;
   gint          selection_bound;
   gint          scroll_offset;
@@ -222,11 +227,7 @@ struct _EntryIconInfo
   guint in_drag        : 1;
   guint pressed        : 1;
 
-  GtkImageType  storage_type;
-  GdkPixbuf    *pixbuf;
-  gchar        *stock_id;
-  gchar        *icon_name;
-  GIcon        *gicon;
+  GtkIconHelper *icon_helper;
 
   GtkTargetList *target_list;
   GdkDragAction actions;
@@ -305,7 +306,9 @@ enum {
   PROP_TOOLTIP_MARKUP_PRIMARY,
   PROP_TOOLTIP_MARKUP_SECONDARY,
   PROP_IM_MODULE,
-  PROP_EDITING_CANCELED
+  PROP_EDITING_CANCELED,
+  PROP_PLACEHOLDER_TEXT,
+  PROP_COMPLETION
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -514,7 +517,6 @@ static void         gtk_entry_draw_cursor              (GtkEntry       *entry,
 static PangoLayout *gtk_entry_ensure_layout            (GtkEntry       *entry,
                                                         gboolean        include_preedit);
 static void         gtk_entry_reset_layout             (GtkEntry       *entry);
-static void         gtk_entry_queue_draw               (GtkEntry       *entry);
 static void         gtk_entry_recompute                (GtkEntry       *entry);
 static gint         gtk_entry_find_position            (GtkEntry       *entry,
                                                        gint            x);
@@ -545,6 +547,8 @@ static void         gtk_entry_do_popup                 (GtkEntry       *entry,
                                                        GdkEventButton *event);
 static gboolean     gtk_entry_mnemonic_activate        (GtkWidget      *widget,
                                                        gboolean        group_cycling);
+static void         gtk_entry_grab_notify              (GtkWidget      *widget,
+                                                        gboolean        was_grabbed);
 static void         gtk_entry_check_cursor_blink       (GtkEntry       *entry);
 static void         gtk_entry_pend_cursor_blink        (GtkEntry       *entry);
 static void         gtk_entry_reset_blink_time         (GtkEntry       *entry);
@@ -559,14 +563,16 @@ static void         get_text_area_size                 (GtkEntry       *entry,
                                                        gint           *width,
                                                        gint           *height);
 static void         get_frame_size                     (GtkEntry       *entry,
+                                                        gboolean        relative_to_window,
                                                        gint           *x,
                                                        gint           *y,
                                                        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);
+static gboolean     get_middle_click_paste             (GtkEntry *entry);
 
 /* Completion */
 static gint         gtk_entry_completion_timeout       (gpointer            data);
@@ -596,7 +602,6 @@ static void         begin_change                       (GtkEntry *entry);
 static void         end_change                         (GtkEntry *entry);
 static void         emit_changed                       (GtkEntry *entry);
 
-
 static void         buffer_inserted_text               (GtkEntryBuffer *buffer, 
                                                         guint           position,
                                                         const gchar    *chars,
@@ -619,7 +624,6 @@ static void         buffer_connect_signals             (GtkEntry       *entry);
 static void         buffer_disconnect_signals          (GtkEntry       *entry);
 static GtkEntryBuffer *get_buffer                      (GtkEntry       *entry);
 
-
 G_DEFINE_TYPE_WITH_CODE (GtkEntry, gtk_entry, GTK_TYPE_WIDGET,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
                                                 gtk_entry_editable_init)
@@ -690,6 +694,7 @@ gtk_entry_class_init (GtkEntryClass *class)
   widget_class->state_flags_changed = gtk_entry_state_flags_changed;
   widget_class->screen_changed = gtk_entry_screen_changed;
   widget_class->mnemonic_activate = gtk_entry_mnemonic_activate;
+  widget_class->grab_notify = gtk_entry_grab_notify;
 
   widget_class->drag_drop = gtk_entry_drag_drop;
   widget_class->drag_motion = gtk_entry_drag_motion;
@@ -781,13 +786,23 @@ gtk_entry_class_init (GtkEntryClass *class)
                                                          TRUE,
                                                         GTK_PARAM_READWRITE));
 
+  /**
+   * GtkEntry:inner-border:
+   *
+   * Sets the text area's border between the text and the frame.
+   *
+   * Deprecated: 3.4: Use the standard border and padding CSS properties
+   *   (through objects like #GtkStyleContext and #GtkCssProvider); the value
+   *   of this style property is ignored.
+   */
   g_object_class_install_property (gobject_class,
                                    PROP_INNER_BORDER,
                                    g_param_spec_boxed ("inner-border",
                                                        P_("Inner Border"),
                                                        P_("Border between text and frame. Overrides the inner-border style property"),
                                                        GTK_TYPE_BORDER,
-                                                       GTK_PARAM_READWRITE));
+                                                       GTK_PARAM_READWRITE |
+                                                       G_PARAM_DEPRECATED));
 
   g_object_class_install_property (gobject_class,
                                    PROP_INVISIBLE_CHAR,
@@ -929,7 +944,7 @@ gtk_entry_class_init (GtkEntryClass *class)
                                                          GTK_PARAM_READWRITE));
 
   /**
-   * GtkEntry:caps-lock-warning
+   * GtkEntry:caps-lock-warning:
    *
    * Whether password entries will show a warning when Caps Lock is on.
    *
@@ -983,6 +998,22 @@ gtk_entry_class_init (GtkEntryClass *class)
                                                         GTK_PARAM_READWRITE));
 
   /**
+  * GtkEntry:placeholder-text:
+  *
+  * The text that will be displayed in the #GtkEntry when it is empty
+  * and unfocused.
+  *
+  * Since: 3.2
+  */
+ g_object_class_install_property (gobject_class,
+                                  PROP_PLACEHOLDER_TEXT,
+                                  g_param_spec_string ("placeholder-text",
+                                                       P_("Placeholder text"),
+                                                       P_("Show text in the entry when it's empty and unfocused"),
+                                                       NULL,
+                                                       GTK_PARAM_READWRITE));
+
+   /**
    * GtkEntry:primary-icon-pixbuf:
    *
    * A pixbuf to use as the primary icon for the entry.
@@ -1313,6 +1344,21 @@ gtk_entry_class_init (GtkEntryClass *class)
                                                         NULL,
                                                         GTK_PARAM_READWRITE));
 
+  /**
+   * GtkEntry:completion:
+   *
+   * The auxiliary completion object to use with the entry.
+   *
+   * Since: 3.2
+   */     
+  g_object_class_install_property (gobject_class,
+                                   PROP_COMPLETION,
+                                   g_param_spec_object ("completion",
+                                                        P_("Completion"),
+                                                        P_("The auxiliary completion object"),
+                                                        GTK_TYPE_ENTRY_COMPLETION,
+                                                        GTK_PARAM_READWRITE));
+
   /**
    * GtkEntry:icon-prelight:
    *
@@ -1334,20 +1380,25 @@ gtk_entry_class_init (GtkEntryClass *class)
    * The border around the progress bar in the entry.
    *
    * Since: 2.16
+   *
+   * Deprecated: 3.4: Use the standard margin CSS property (through objects
+   *   like #GtkStyleContext and #GtkCssProvider); the value of this style
+   *   property is ignored.
    */
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boxed ("progress-border",
                                                                P_("Progress Border"),
                                                                P_("Border around the progress bar"),
                                                                GTK_TYPE_BORDER,
-                                                               GTK_PARAM_READABLE));
+                                                               GTK_PARAM_READABLE |
+                                                               G_PARAM_DEPRECATED));
   
   /**
    * GtkEntry:invisible-char:
    *
    * The invisible character is used when masking entry contents (in
    * \"password mode\")"). When it is not explicitly set with the
-   * #GtkEntry::invisible-char property, GTK+ determines the character
+   * #GtkEntry:invisible-char property, GTK+ determines the character
    * to use from a list of possible candidates, depending on availability
    * in the current font.
    *
@@ -1390,12 +1441,13 @@ gtk_entry_class_init (GtkEntryClass *class)
    * GtkEntry::activate:
    * @entry: The entry on which the signal is emitted
    *
-   * A  <link linkend="keybinding-signals">keybinding signal</link>
-   * which gets emitted when the user activates the entry.
-   * 
-   * Applications should not connect to it, but may emit it with
-   * g_signal_emit_by_name() if they need to control activation 
-   * programmatically.
+   * The ::activate signal is emitted when the user hits
+   * the Enter key.
+   *
+   * While this signal is used as a
+   * <link linkend="keybinding-signals">keybinding signal</link>,
+   * it is also commonly used by applications to intercept
+   * activation of entries.
    *
    * The default bindings for this signal are all forms of the Enter key.
    */
@@ -1603,7 +1655,7 @@ gtk_entry_class_init (GtkEntryClass *class)
    * GtkEntry::icon-press:
    * @entry: The entry on which the signal is emitted
    * @icon_pos: The position of the clicked icon
-   * @event: the button press event
+   * @event: (type Gdk.EventButton): the button press event
    *
    * The ::icon-press signal is emitted when an activatable icon
    * is clicked.
@@ -1625,7 +1677,7 @@ gtk_entry_class_init (GtkEntryClass *class)
    * GtkEntry::icon-release:
    * @entry: The entry on which the signal is emitted
    * @icon_pos: The position of the clicked icon
-   * @event: the button release event
+   * @event: (type Gdk.EventButton): the button release event
    *
    * The ::icon-release signal is emitted on the button release from a
    * mouse click over an activatable icon.
@@ -1826,15 +1878,22 @@ gtk_entry_class_init (GtkEntryClass *class)
    * Sets the text area's border between the text and the frame.
    *
    * Since: 2.10
+   *
+   * Deprecated: 3.4: Use the standard border and padding CSS properties
+   *   (through objects like #GtkStyleContext and #GtkCssProvider); the value
+   *   of this style property is ignored.
    */
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boxed ("inner-border",
                                                                P_("Inner Border"),
                                                                P_("Border between text and frame."),
                                                                GTK_TYPE_BORDER,
-                                                               GTK_PARAM_READABLE)); 
+                                                               GTK_PARAM_READABLE |
+                                                               G_PARAM_DEPRECATED));
 
   g_type_class_add_private (gobject_class, sizeof (GtkEntryPrivate));
+
+  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE);
 }
 
 static void
@@ -1857,6 +1916,27 @@ gtk_entry_cell_editable_init (GtkCellEditableIface *iface)
   iface->start_editing = gtk_entry_start_editing;
 }
 
+/* for deprecated properties */
+static void
+gtk_entry_do_set_inner_border (GtkEntry *entry,
+                               const GtkBorder *border)
+{
+  if (border)
+    g_object_set_qdata_full (G_OBJECT (entry), quark_inner_border,
+                             gtk_border_copy (border),
+                             (GDestroyNotify) gtk_border_free);
+  else
+    g_object_set_qdata (G_OBJECT (entry), quark_inner_border, NULL);
+
+  g_object_notify (G_OBJECT (entry), "inner-border");
+}
+
+static const GtkBorder *
+gtk_entry_do_get_inner_border (GtkEntry *entry)
+{
+  return g_object_get_qdata (G_OBJECT (entry), quark_inner_border);
+}
+
 static void
 gtk_entry_set_property (GObject         *object,
                         guint            prop_id,
@@ -1865,7 +1945,6 @@ gtk_entry_set_property (GObject         *object,
 {
   GtkEntry *entry = GTK_ENTRY (object);
   GtkEntryPrivate *priv = entry->priv;
-  GtkWidget *widget;
 
   switch (prop_id)
     {
@@ -1879,7 +1958,8 @@ gtk_entry_set_property (GObject         *object,
 
         if (new_value != priv->editable)
          {
-            widget = GTK_WIDGET (entry);
+            GtkWidget *widget = GTK_WIDGET (entry);
+
            if (!new_value)
              {
                _gtk_entry_reset_im_context (entry);
@@ -1895,7 +1975,7 @@ gtk_entry_set_property (GObject         *object,
            if (new_value && gtk_widget_has_focus (widget))
              gtk_im_context_focus_in (priv->im_context);
 
-           gtk_entry_queue_draw (entry);
+           gtk_widget_queue_draw (widget);
          }
       }
       break;
@@ -1913,7 +1993,7 @@ gtk_entry_set_property (GObject         *object,
       break;
 
     case PROP_INNER_BORDER:
-      gtk_entry_set_inner_border (entry, g_value_get_boxed (value));
+      gtk_entry_do_set_inner_border (entry, g_value_get_boxed (value));
       break;
 
     case PROP_INVISIBLE_CHAR:
@@ -1967,6 +2047,10 @@ gtk_entry_set_property (GObject         *object,
       gtk_entry_set_progress_pulse_step (entry, g_value_get_double (value));
       break;
 
+    case PROP_PLACEHOLDER_TEXT:
+      gtk_entry_set_placeholder_text (entry, g_value_get_string (value));
+      break;
+
     case PROP_PIXBUF_PRIMARY:
       gtk_entry_set_icon_from_pixbuf (entry,
                                       GTK_ENTRY_ICON_PRIMARY,
@@ -2074,6 +2158,10 @@ gtk_entry_set_property (GObject         *object,
       priv->editing_canceled = g_value_get_boolean (value);
       break;
 
+    case PROP_COMPLETION:
+      gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (g_value_get_object (value)));
+      break;
+
     case PROP_SCROLL_OFFSET:
     case PROP_CURSOR_POSITION:
     default:
@@ -2122,7 +2210,7 @@ gtk_entry_get_property (GObject         *object,
       break;
 
     case PROP_INNER_BORDER:
-      g_value_set_boxed (value, gtk_entry_get_inner_border (entry));
+      g_value_set_boxed (value, gtk_entry_do_get_inner_border (entry));
       break;
 
     case PROP_INVISIBLE_CHAR:
@@ -2185,6 +2273,10 @@ gtk_entry_get_property (GObject         *object,
       g_value_set_double (value, priv->progress_pulse_fraction);
       break;
 
+    case PROP_PLACEHOLDER_TEXT:
+      g_value_set_string (value, gtk_entry_get_placeholder_text (entry));
+      break;
+
     case PROP_PIXBUF_PRIMARY:
       g_value_set_object (value,
                           gtk_entry_get_icon_pixbuf (entry,
@@ -2290,6 +2382,10 @@ gtk_entry_get_property (GObject         *object,
                            priv->editing_canceled);
       break;
 
+    case PROP_COMPLETION:
+      g_value_set_object (value, G_OBJECT (gtk_entry_get_completion (entry)));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2360,7 +2456,6 @@ gtk_entry_init (GtkEntry *entry)
 
   priv->editable = TRUE;
   priv->visible = TRUE;
-  priv->invisible_char = find_invisible_char (GTK_WIDGET (entry));
   priv->dnd_position = -1;
   priv->width_chars = -1;
   priv->is_cell_renderer = FALSE;
@@ -2396,6 +2491,49 @@ gtk_entry_init (GtkEntry *entry)
 
   context = gtk_widget_get_style_context (GTK_WIDGET (entry));
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_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_widget_get_state_flags (widget);
+
+  state &= ~(GTK_STATE_FLAG_PRELIGHT);
+
+  if ((state & GTK_STATE_FLAG_INSENSITIVE) || 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);
+
+  if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL) 
+    {
+      if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
+        gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
+      else
+        gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
+    }
+  else
+    {
+      if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
+        gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
+      else
+        gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
+    }
 }
 
 static gint
@@ -2404,20 +2542,25 @@ get_icon_width (GtkEntry             *entry,
 {
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
-  GdkScreen *screen;
-  GtkSettings *settings;
-  gint menu_icon_width;
+  GtkStyleContext *context;
+  GtkBorder padding;
+  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);
+  context = gtk_widget_get_style_context (GTK_WIDGET (entry));
+  gtk_entry_prepare_context_for_icon (entry, context, icon_pos);
+  gtk_style_context_get_padding (context, 0, &padding);
 
-  gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
-                                     &menu_icon_width, NULL);
+  _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);
+  if (width > 0)
+    width += padding.left + padding.right;
+
+  return width;
 }
 
 static void
@@ -2437,14 +2580,10 @@ get_icon_allocations (GtkEntry      *entry,
   primary->y = y;
   primary->height = height;
   primary->width = get_icon_width (entry, GTK_ENTRY_ICON_PRIMARY);
-  if (primary->width > 0)
-    primary->width += 2 * priv->icon_margin;
 
   secondary->y = y;
   secondary->height = height;
   secondary->width = get_icon_width (entry, GTK_ENTRY_ICON_SECONDARY);
-  if (secondary->width > 0)
-    secondary->width += 2 * priv->icon_margin;
 
   if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
     {
@@ -2465,6 +2604,8 @@ begin_change (GtkEntry *entry)
   GtkEntryPrivate *priv = entry->priv;
 
   priv->change_count++;
+
+  g_object_freeze_notify (G_OBJECT (entry));
 }
 
 static void
@@ -2475,6 +2616,8 @@ end_change (GtkEntry *entry)
 
   g_return_if_fail (priv->change_count > 0);
 
+  g_object_thaw_notify (G_OBJECT (entry));
+
   priv->change_count--;
 
   if (priv->change_count == 0)
@@ -2529,6 +2672,7 @@ gtk_entry_dispose (GObject *object)
 {
   GtkEntry *entry = GTK_ENTRY (object);
   GtkEntryPrivate *priv = entry->priv;
+  GdkKeymap *keymap;
 
   gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
   gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
@@ -2542,6 +2686,9 @@ gtk_entry_dispose (GObject *object)
       priv->buffer = NULL;
     }
 
+  keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (object)));
+  g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
+  g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, entry);
   G_OBJECT_CLASS (gtk_entry_parent_class)->dispose (object);
 }
 
@@ -2563,6 +2710,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;
         }
@@ -2581,6 +2730,7 @@ gtk_entry_finalize (GObject *object)
   if (priv->recompute_idle)
     g_source_remove (priv->recompute_idle);
 
+  g_free (priv->placeholder_text);
   g_free (priv->im_module);
 
   G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object);
@@ -2667,7 +2817,6 @@ gtk_entry_get_display_text (GtkEntry *entry,
 
       return g_string_free (str, FALSE);
     }
-
 }
 
 static void
@@ -2684,7 +2833,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,
@@ -2759,6 +2909,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);
 
@@ -2781,7 +2933,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);
         }
     }
@@ -2801,7 +2954,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);
         }
     }
@@ -2846,7 +3000,7 @@ gtk_entry_realize (GtkWidget *widget)
 
   get_text_area_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height);
 
-  get_frame_size (entry, &frame_x, &frame_y, NULL, NULL);
+  get_frame_size (entry, TRUE, &frame_x, &frame_y, NULL, NULL);
   attributes.x += frame_x;
   attributes.y += frame_y;
 
@@ -2932,34 +3086,37 @@ gtk_entry_unrealize (GtkWidget *widget)
 
 void
 _gtk_entry_get_borders (GtkEntry *entry,
-                       gint     *xborder,
-                       gint     *yborder)
+                        GtkBorder *border_out)
 {
   GtkEntryPrivate *priv = entry->priv;
   GtkWidget *widget = GTK_WIDGET (entry);
+  GtkBorder tmp = { 0, 0, 0, 0 };
+  GtkStyleContext *context;
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_get_padding (context, 0, &tmp);
 
   if (priv->has_frame)
     {
-      GtkStyleContext *context;
-      GtkBorder padding;
-
-      context = gtk_widget_get_style_context (widget);
-      gtk_style_context_get_padding (context, 0, &padding);
+      GtkBorder border;
 
-      *xborder = padding.left;
-      *yborder = padding.top;
-    }
-  else
-    {
-      *xborder = 0;
-      *yborder = 0;
+      gtk_style_context_get_border (context, 0, &border);
+      tmp.top += border.top;
+      tmp.right += border.right;
+      tmp.bottom += border.bottom;
+      tmp.left += border.left;
     }
 
   if (!priv->interior_focus)
     {
-      *xborder += priv->focus_width;
-      *yborder += priv->focus_width;
+      tmp.top += priv->focus_width;
+      tmp.right += priv->focus_width;
+      tmp.bottom += priv->focus_width;
+      tmp.left += priv->focus_width;
     }
+
+  if (border_out != NULL)
+    *border_out = tmp;
 }
 
 static void
@@ -2970,8 +3127,7 @@ gtk_entry_get_preferred_width (GtkWidget *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   PangoFontMetrics *metrics;
-  gint xborder, yborder;
-  GtkBorder inner_border;
+  GtkBorder borders;
   PangoContext *context;
   GtkStyleContext *style_context;
   GtkStateFlags state;
@@ -2988,25 +3144,24 @@ gtk_entry_get_preferred_width (GtkWidget *widget,
                                        gtk_style_context_get_font (style_context, state),
                                        pango_context_get_language (context));
 
-  _gtk_entry_get_borders (entry, &xborder, &yborder);
-  _gtk_entry_effective_inner_border (entry, &inner_border);
+  _gtk_entry_get_borders (entry, &borders);
 
   if (priv->width_chars < 0)
-    width = MIN_ENTRY_WIDTH + xborder * 2 + inner_border.left + inner_border.right;
+    width = MIN_ENTRY_WIDTH + borders.left + borders.right;
   else
     {
       gint char_width = pango_font_metrics_get_approximate_char_width (metrics);
       gint digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
       gint char_pixels = (MAX (char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
 
-      width = char_pixels * priv->width_chars + xborder * 2 + inner_border.left + inner_border.right;
+      width = char_pixels * priv->width_chars + borders.left + borders.right;
     }
 
   for (i = 0; i < MAX_ICONS; i++)
     {
       icon_width = get_icon_width (entry, i);
       if (icon_width > 0)
-        icon_widths += icon_width + 2 * priv->icon_margin;
+        icon_widths += icon_width;
     }
 
   if (icon_widths > width)
@@ -3026,8 +3181,7 @@ gtk_entry_get_preferred_height (GtkWidget *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   PangoFontMetrics *metrics;
-  gint xborder, yborder;
-  GtkBorder inner_border;
+  GtkBorder borders;
   GtkStyleContext *style_context;
   GtkStateFlags state;
   PangoContext *context;
@@ -3045,10 +3199,9 @@ gtk_entry_get_preferred_height (GtkWidget *widget,
   priv->ascent = pango_font_metrics_get_ascent (metrics);
   priv->descent = pango_font_metrics_get_descent (metrics);
 
-  _gtk_entry_get_borders (entry, &xborder, &yborder);
-  _gtk_entry_effective_inner_border (entry, &inner_border);
+  _gtk_entry_get_borders (entry, &borders);
 
-  height = PANGO_PIXELS (priv->ascent + priv->descent) + yborder * 2 + inner_border.top + inner_border.bottom;
+  height = PANGO_PIXELS (priv->ascent + priv->descent) + borders.top + borders.bottom;
 
   pango_font_metrics_unref (metrics);
 
@@ -3067,7 +3220,7 @@ place_windows (GtkEntry *entry)
   GtkAllocation secondary;
   EntryIconInfo *icon_info = NULL;
 
-  get_frame_size (entry, &frame_x, &frame_y, NULL, NULL);
+  get_frame_size (entry, TRUE, &frame_x, &frame_y, NULL, NULL);
   get_text_area_size (entry, &x, &y, &width, &height);
   get_icon_allocations (entry, &primary, &secondary);
 
@@ -3111,32 +3264,35 @@ 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;
+  GtkBorder borders;
 
   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);
+  _gtk_entry_get_borders (entry, &borders);
 
   if (gtk_widget_get_realized (widget))
-    get_frame_size (entry, NULL, NULL, NULL, &frame_height);
+    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;
 
   if (x)
-    *x = xborder;
+    *x = borders.left;
 
   if (y)
-    *y = frame_height / 2 - (requisition.height - yborder * 2) / 2;
+    *y = floor ((frame_height - req_height) / 2) + borders.top;
 
   if (width)
-    *width = allocation.width - xborder * 2;
+    *width = allocation.width - borders.left - borders.right;
 
   if (height)
-    *height = requisition.height - yborder * 2;
+    *height = req_height - borders.top - borders.bottom;
 }
 
 static void
@@ -3159,6 +3315,7 @@ get_text_area_size (GtkEntry *entry,
 
 static void
 get_frame_size (GtkEntry *entry,
+                gboolean  relative_to_window,
                 gint     *x,
                 gint     *y,
                 gint     *width,
@@ -3168,19 +3325,26 @@ 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)
-    *x = allocation.x;
+    *x = relative_to_window ? allocation.x : 0;
 
   if (y)
     {
       if (priv->is_cell_renderer)
-       *y = allocation.y;
+        *y = 0;
       else
-       *y = allocation.y + (allocation.height - requisition.height) / 2;
+        *y = (allocation.height - req_height) / 2;
+
+      if (relative_to_window)
+        *y += allocation.y;
     }
 
   if (width)
@@ -3189,36 +3353,10 @@ get_frame_size (GtkEntry *entry,
   if (height)
     {
       if (priv->is_cell_renderer)
-       *height = allocation.height;
+        *height = allocation.height;
       else
-       *height = requisition.height;
-    }
-}
-
-void
-_gtk_entry_effective_inner_border (GtkEntry  *entry,
-                                   GtkBorder *border)
-{
-  GtkBorder *tmp_border;
-
-  tmp_border = g_object_get_qdata (G_OBJECT (entry), quark_inner_border);
-
-  if (tmp_border)
-    {
-      *border = *tmp_border;
-      return;
-    }
-
-  gtk_widget_style_get (GTK_WIDGET (entry), "inner-border", &tmp_border, NULL);
-
-  if (tmp_border)
-    {
-      *border = *tmp_border;
-      gtk_border_free (tmp_border);
-      return;
+        *height = req_height;
     }
-
-  *border = default_inner_border;
 }
 
 static void
@@ -3274,20 +3412,13 @@ 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;
-  GtkIconSource *icon_source;
-  GtkStateFlags state;
+  GtkBorder padding;
 
   if (!icon_info)
     return;
 
-  gtk_entry_ensure_pixbuf (entry, icon_pos);
-
-  if (icon_info->pixbuf == NULL)
-    return;
-
   width = gdk_window_get_width (icon_info->window);
   height = gdk_window_get_height (icon_info->window);
 
@@ -3296,46 +3427,24 @@ draw_icon (GtkWidget            *widget,
   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;
-
-      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 = (width  - gdk_pixbuf_get_width (pixbuf)) / 2;
-  y = (height - gdk_pixbuf_get_height (pixbuf)) / 2;
-
-  icon_source = gtk_icon_source_new ();
-  gtk_icon_source_set_pixbuf (icon_source, pixbuf);
-  gtk_icon_source_set_state_wildcarded (icon_source, TRUE);
-
-  state = 0;
-  if (!gtk_widget_is_sensitive (widget) || icon_info->insensitive)
-    state |= GTK_STATE_FLAG_INSENSITIVE;
-  else if (icon_info->prelight)
-    state |= GTK_STATE_FLAG_PRELIGHT;
+  cairo_save (cr);
+  gtk_cairo_transform_to_window (cr, widget, icon_info->window);
 
   context = gtk_widget_get_style_context (widget);
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  pixbuf = gtk_render_icon_pixbuf (context, icon_source, (GtkIconSize)-1);
-  gtk_style_context_restore (context);
+  gtk_entry_prepare_context_for_icon (entry, context, icon_pos);
+  _gtk_icon_helper_get_size (icon_info->icon_helper, context,
+                             &pix_width, &pix_height);
+  gtk_style_context_get_padding (context, 0, &padding);
 
-  gtk_icon_source_free (icon_source);
+  x = MAX (0, padding.left);
+  y = MAX (0, (height - pix_height) / 2);
 
-  gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
-  cairo_paint (cr);
+  _gtk_icon_helper_draw (icon_info->icon_helper,
+                         context, cr,
+                         x, y);
 
-  g_object_unref (pixbuf);
+  gtk_style_context_restore (context);
+  cairo_restore (cr);
 }
 
 
@@ -3347,28 +3456,26 @@ gtk_entry_draw_frame (GtkWidget       *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   gint x = 0, y = 0, width, height;
-  GtkAllocation allocation;
   gint frame_x, frame_y;
 
   cairo_save (cr);
 
-  get_frame_size (GTK_ENTRY (widget), &frame_x, &frame_y, &width, &height);
-  gtk_widget_get_allocation (widget, &allocation);
+  get_frame_size (GTK_ENTRY (widget), FALSE, &frame_x, &frame_y, &width, &height);
 
-  cairo_translate (cr, frame_x - allocation.x, frame_y - allocation.y);
+  cairo_translate (cr, frame_x, frame_y);
 
   /* Fix a problem with some themes which assume that entry->text_area's
    * width equals widget->window's width
    * http://bugzilla.gnome.org/show_bug.cgi?id=466000 */
   if (GTK_IS_SPIN_BUTTON (widget))
     {
-      gint xborder, yborder;
+      GtkBorder borders;
 
       gtk_entry_get_text_area_size (GTK_ENTRY (widget), &x, NULL, &width, NULL);
-      _gtk_entry_get_borders (GTK_ENTRY (widget), &xborder, &yborder);
+      _gtk_entry_get_borders (GTK_ENTRY (widget), &borders);
 
-      x -= xborder;
-      width += xborder * 2;
+      x -= borders.left;
+      width += borders.left + borders.right;
     }
 
   if (gtk_widget_has_focus (widget) && !priv->interior_focus)
@@ -3388,7 +3495,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;
@@ -3402,6 +3509,18 @@ gtk_entry_draw_frame (GtkWidget       *widget,
   cairo_restore (cr);
 }
 
+static void
+gtk_entry_prepare_context_for_progress (GtkEntry *entry,
+                                        GtkStyleContext *context)
+{
+  GtkEntryPrivate *private = entry->priv;
+
+  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);
+}
+
 static void
 get_progress_area (GtkWidget *widget,
                    gint       *x,
@@ -3411,29 +3530,51 @@ get_progress_area (GtkWidget *widget,
 {
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *private = entry->priv;
-  GtkBorder *progress_border;
+  GtkStyleContext *context;
+  GtkBorder margin, border, entry_borders;
+  gint frame_width, text_area_width, text_area_height;
 
-  get_text_area_size (entry, x, y, width, height);
+  context = gtk_widget_get_style_context (widget);
+  _gtk_entry_get_borders (entry, &entry_borders);
+  get_text_area_size (entry,
+                      NULL, NULL,
+                      &text_area_width, &text_area_height);
+  get_frame_size (entry, FALSE,
+                  NULL, NULL,
+                  &frame_width, NULL);
+
+  *x = 0;
+  *y = 0;
+  *width = text_area_width + entry_borders.left + entry_borders.right;
+  *height = text_area_height + entry_borders.top + entry_borders.bottom;
 
-  if (!private->interior_focus)
+  /* if the text area got resized by a subclass, subtract the left/right
+   * border width, so that the progress bar won't extend over the resized
+   * text area.
+   */
+  if (frame_width > *width)
     {
-      *x -= private->focus_width;
-      *y -= private->focus_width;
-      *width += 2 * private->focus_width;
-      *height += 2 * private->focus_width;
+      gtk_style_context_get_border (context, 0, &border);
+      if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
+        {
+          *x = (frame_width - *width) + border.left;
+          *width -= border.left;
+        }
+      else
+        {
+          *width -= border.right;
+        }
     }
 
-  gtk_widget_style_get (widget, "progress-border", &progress_border, NULL);
+  gtk_entry_prepare_context_for_progress (entry, context);
+  gtk_style_context_get_margin (context, 0, &margin);
 
-  if (progress_border)
-    {
-      *x += progress_border->left;
-      *y += progress_border->top;
-      *width -= progress_border->left + progress_border->right;
-      *height -= progress_border->top + progress_border->bottom;
+  gtk_style_context_restore (context);
 
-      gtk_border_free (progress_border);
-    }
+  *x += margin.left;
+  *y += margin.top;
+  *width -= margin.left + margin.right;
+  *height -= margin.top + margin.bottom;
 
   if (private->progress_pulse_mode)
     {
@@ -3471,16 +3612,15 @@ gtk_entry_draw_progress (GtkWidget       *widget,
                          GtkStyleContext *context,
                          cairo_t         *cr)
 {
+  GtkEntry *entry = GTK_ENTRY (widget);
   gint x, y, width, height;
 
   get_progress_area (widget, &x, &y, &width, &height);
 
   if ((width <= 0) || (height <= 0))
     return;
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
-
+  gtk_entry_prepare_context_for_progress (entry, context);
   gtk_render_activity (context, cr,
                        x, y, width, height);
 
@@ -3493,59 +3633,41 @@ gtk_entry_draw (GtkWidget *widget,
 {
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkStyleContext *context;
-  GtkStateFlags state;
   GtkEntryPrivate *priv = entry->priv;
   int i;
 
   context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-
-  if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
-    {
-      /* Draw entry_bg, shadow, progress and focus */
-      gtk_entry_draw_frame (widget, context, cr);
-
-      /* Draw text and cursor */
-      cairo_save (cr);
 
-      gtk_cairo_transform_to_window (cr, widget, priv->text_area);
+  /* Draw entry_bg, shadow, progress and focus */
+  gtk_entry_draw_frame (widget, context, cr);
 
-      if (priv->dnd_position != -1)
-       gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
-      
-      gtk_entry_draw_text (GTK_ENTRY (widget), cr);
-
-      /* When no text is being displayed at all, don't show the cursor */
-      if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK &&
-         gtk_widget_has_focus (widget) &&
-         priv->selection_bound == priv->current_pos && priv->cursor_visible)
-        gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD);
+  /* Draw text and cursor */
+  cairo_save (cr);
 
-      cairo_restore (cr);
+  gtk_cairo_transform_to_window (cr, widget, priv->text_area);
 
-      /* Draw icons */
-      for (i = 0; i < MAX_ICONS; i++)
-        {
-          EntryIconInfo *icon_info = priv->icons[i];
+  if (priv->dnd_position != -1)
+    gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
+  
+  gtk_entry_draw_text (GTK_ENTRY (widget), cr);
 
-          if (icon_info != NULL)
-            {
-              cairo_save (cr);
+  /* When no text is being displayed at all, don't show the cursor */
+  if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK &&
+      gtk_widget_has_focus (widget) &&
+      priv->selection_bound == priv->current_pos && priv->cursor_visible)
+    gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD);
 
-              gtk_cairo_transform_to_window (cr, widget, icon_info->window);
+  cairo_restore (cr);
 
-              draw_icon (widget, cr, i);
+  /* Draw icons */
+  for (i = 0; i < MAX_ICONS; i++)
+    {
+      EntryIconInfo *icon_info = priv->icons[i];
 
-              cairo_restore (cr);
-            }
-        }
+      if (icon_info != NULL)
+        draw_icon (widget, cr, i);
     }
 
-  gtk_style_context_restore (context);
-
   return FALSE;
 }
 
@@ -3716,27 +3838,38 @@ gtk_entry_button_press (GtkWidget      *widget,
   gtk_entry_reset_blink_time (entry);
 
   priv->button = event->button;
-  
+  priv->device = gdk_event_get_device ((GdkEvent *) event);
+
   if (!gtk_widget_has_focus (widget))
     {
       priv->in_click = TRUE;
       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 */
+      priv->device = NULL;
+
+      return TRUE;
+    }
+  else if (event->button == GDK_BUTTON_PRIMARY)
     {
       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;
          
@@ -3834,7 +3967,9 @@ gtk_entry_button_press (GtkWidget      *widget,
 
       return TRUE;
     }
-  else if (event->button == 2 && event->type == GDK_BUTTON_PRESS)
+  else if (event->type == GDK_BUTTON_PRESS &&
+           event->button == GDK_BUTTON_MIDDLE &&
+           get_middle_click_paste (entry))
     {
       if (priv->editable)
         {
@@ -3847,13 +3982,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;
 }
@@ -3905,9 +4033,10 @@ gtk_entry_button_release (GtkWidget      *widget,
 
       priv->in_drag = 0;
     }
-  
+
   priv->button = 0;
-  
+  priv->device = NULL;
+
   gtk_entry_update_primary_selection (entry);
              
   return TRUE;
@@ -3933,7 +4062,6 @@ gtk_entry_motion_notify (GtkWidget      *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = NULL;
-  GdkDragContext *context;
   gint tmp_pos;
   gint i;
 
@@ -3956,11 +4084,11 @@ gtk_entry_motion_notify (GtkWidget      *widget,
             {
               icon_info->in_drag = TRUE;
               icon_info->pressed = FALSE;
-              context = gtk_drag_begin (widget,
-                                        icon_info->target_list,
-                                        icon_info->actions,
-                                        1,
-                                        (GdkEvent*)event);
+              gtk_drag_begin (widget,
+                              icon_info->target_list,
+                              icon_info->actions,
+                              1,
+                              (GdkEvent*)event);
             }
 
           return TRUE;
@@ -3970,14 +4098,14 @@ gtk_entry_motion_notify (GtkWidget      *widget,
   if (priv->mouse_cursor_obscured)
     {
       GdkCursor *cursor;
-      
+
       cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_XTERM);
       gdk_window_set_cursor (priv->text_area, cursor);
       g_object_unref (cursor);
       priv->mouse_cursor_obscured = FALSE;
     }
 
-  if (event->window != priv->text_area || priv->button != 1)
+  if (event->window != priv->text_area || priv->button != GDK_BUTTON_PRIMARY)
     return FALSE;
 
   if (priv->select_lines)
@@ -4017,7 +4145,8 @@ gtk_entry_motion_notify (GtkWidget      *widget,
 
           priv->in_drag = FALSE;
           priv->button = 0;
-         
+          priv->device = NULL;
+
           gtk_target_list_unref (target_list);
         }
     }
@@ -4192,8 +4321,16 @@ gtk_entry_focus_in (GtkWidget     *widget,
   g_signal_connect (keymap, "direction-changed",
                    G_CALLBACK (keymap_direction_changed), entry);
 
-  gtk_entry_reset_blink_time (entry);
-  gtk_entry_check_cursor_blink (entry);
+  if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
+      priv->placeholder_text != NULL)
+    {
+      gtk_entry_recompute (entry);
+    }
+  else
+    {
+      gtk_entry_reset_blink_time (entry);
+      gtk_entry_check_cursor_blink (entry);
+    }
 
   return FALSE;
 }
@@ -4218,25 +4355,33 @@ gtk_entry_focus_out (GtkWidget     *widget,
       remove_capslock_feedback (entry);
     }
 
-  gtk_entry_check_cursor_blink (entry);
-  
+  if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
+      priv->placeholder_text != NULL)
+    {
+      gtk_entry_recompute (entry);
+    }
+  else
+    {
+      gtk_entry_check_cursor_blink (entry);
+    }
+
   g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
   g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, entry);
 
   completion = gtk_entry_get_completion (entry);
   if (completion)
     _gtk_entry_completion_popdown (completion);
-  
+
   return FALSE;
 }
 
 static void
-gtk_entry_grab_focus (GtkWidget        *widget)
+gtk_entry_grab_focus (GtkWidget *widget)
 {
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   gboolean select_on_focus;
-  
+
   GTK_WIDGET_CLASS (gtk_entry_parent_class)->grab_focus (widget);
 
   if (priv->editable && !priv->in_click)
@@ -4245,20 +4390,20 @@ gtk_entry_grab_focus (GtkWidget        *widget)
                     "gtk-entry-select-on-focus",
                     &select_on_focus,
                     NULL);
-  
+
       if (select_on_focus)
         gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
     }
 }
 
-static void 
+static void
 gtk_entry_direction_changed (GtkWidget        *widget,
-                            GtkTextDirection  previous_dir)
+                             GtkTextDirection  previous_dir)
 {
   GtkEntry *entry = GTK_ENTRY (widget);
 
   gtk_entry_recompute (entry);
-      
+
   GTK_WIDGET_CLASS (gtk_entry_parent_class)->direction_changed (widget, previous_dir);
 }
 
@@ -4269,12 +4414,12 @@ gtk_entry_state_flags_changed (GtkWidget     *widget,
   GtkEntry *entry = GTK_ENTRY (widget);
   GtkEntryPrivate *priv = entry->priv;
   GdkCursor *cursor;
-  
+
   if (gtk_widget_get_realized (widget))
     {
       if (gtk_widget_is_sensitive (widget))
         cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_XTERM);
-      else 
+      else
         cursor = NULL;
 
       gdk_window_set_cursor (priv->text_area, cursor);
@@ -4292,8 +4437,8 @@ gtk_entry_state_flags_changed (GtkWidget     *widget,
       /* Clear any selection */
       gtk_editable_select_region (GTK_EDITABLE (entry), priv->current_pos, priv->current_pos);
     }
-  
-  gtk_widget_queue_draw (widget);
+
+  gtk_entry_update_cached_style_values (entry);
 }
 
 static void
@@ -4437,53 +4582,50 @@ 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));
 }
 
 static void
-icon_margin_changed (GtkEntry *entry)
+gtk_entry_update_cached_style_values (GtkEntry *entry)
 {
   GtkEntryPrivate *priv = entry->priv;
-  GtkBorder border;
+  gint focus_width;
+  gboolean interior_focus;
+
+  gtk_widget_style_get (GTK_WIDGET (entry),
+                       "focus-line-width", &focus_width,
+                       "interior-focus", &interior_focus,
+                       NULL);
+  priv->focus_width = focus_width;
+  priv->interior_focus = interior_focus;
 
-  _gtk_entry_effective_inner_border (GTK_ENTRY (entry), &border);
+  if (!priv->invisible_char_set)
+    {
+      gunichar ch = find_invisible_char (GTK_WIDGET (entry));
 
-  priv->icon_margin = border.left;
+      if (priv->invisible_char != ch)
+        {
+          priv->invisible_char = ch;
+          g_object_notify (G_OBJECT (entry), "invisible-char");
+        }
+    }
 }
 
 static void 
 gtk_entry_style_updated (GtkWidget *widget)
 {
   GtkEntry *entry = GTK_ENTRY (widget);
-  GtkEntryPrivate *priv = entry->priv;
-  gint focus_width;
-  gboolean interior_focus;
-
-  gtk_widget_style_get (widget,
-                       "focus-line-width", &focus_width,
-                       "interior-focus", &interior_focus,
-                       NULL);
 
-  priv->focus_width = focus_width;
-  priv->interior_focus = interior_focus;
+  GTK_WIDGET_CLASS (gtk_entry_parent_class)->style_updated (widget);
 
-  if (!priv->invisible_char_set)
-    priv->invisible_char = find_invisible_char (GTK_WIDGET (entry));
+  gtk_entry_update_cached_style_values (entry);
 
   gtk_entry_recompute (entry);
 
   icon_theme_changed (entry);
-  icon_margin_changed (entry);
 }
 
 /* GtkCellEditable method implementations
@@ -4577,8 +4719,12 @@ gtk_entry_real_insert_text (GtkEditable *editable,
    * following signal handlers: buffer_inserted_text(), buffer_notify_display_text(),
    * buffer_notify_text(), buffer_notify_length()
    */
+  begin_change (GTK_ENTRY (editable));
+
   n_inserted = gtk_entry_buffer_insert_text (get_buffer (GTK_ENTRY (editable)), *position, new_text, n_chars);
 
+  end_change (GTK_ENTRY (editable));
+
   if (n_inserted != n_chars)
       gtk_widget_error_bell (GTK_WIDGET (editable));
 
@@ -4596,7 +4742,11 @@ gtk_entry_real_delete_text (GtkEditable *editable,
    * buffer_notify_text(), buffer_notify_length()
    */
 
+  begin_change (GTK_ENTRY (editable));
+
   gtk_entry_buffer_delete_text (get_buffer (GTK_ENTRY (editable)), start_pos, end_pos - start_pos);
+
+  end_change (GTK_ENTRY (editable));
 }
 
 /* GtkEntryBuffer signal handlers
@@ -4610,12 +4760,18 @@ buffer_inserted_text (GtkEntryBuffer *buffer,
 {
   GtkEntryPrivate *priv = entry->priv;
   guint password_hint_timeout;
+  guint current_pos;
+  gint selection_bound;
+
+  current_pos = priv->current_pos;
+  if (current_pos > position)
+    current_pos += n_chars;
 
-  if (priv->current_pos > position)
-    priv->current_pos += n_chars;
+  selection_bound = priv->selection_bound;
+  if (selection_bound > position)
+    selection_bound += n_chars;
 
-  if (priv->selection_bound > position)
-    priv->selection_bound += n_chars;
+  gtk_entry_set_positions (entry, current_pos, selection_bound);
 
   /* Calculate the password hint if it needs to be displayed. */
   if (n_chars == 1 && !priv->visible)
@@ -5105,7 +5261,7 @@ gtk_entry_paste_clipboard (GtkEntry *entry)
   GtkEntryPrivate *priv = entry->priv;
 
   if (priv->editable)
-    gtk_entry_paste (entry, GDK_NONE);
+    gtk_entry_paste (entry, GDK_SELECTION_CLIPBOARD);
   else
     gtk_widget_error_bell (GTK_WIDGET (entry));
 }
@@ -5370,8 +5526,8 @@ recompute_idle_func (gpointer data)
   if (gtk_widget_has_screen (GTK_WIDGET (entry)))
     {
       gtk_entry_adjust_scroll (entry);
-      gtk_entry_queue_draw (entry);
-      
+      gtk_widget_queue_draw (GTK_WIDGET (entry));
+
       update_im_cursor_location (entry);
     }
 
@@ -5393,6 +5549,35 @@ gtk_entry_recompute (GtkEntry *entry)
     }
 }
 
+static void
+gtk_entry_get_placeholder_text_color (GtkEntry   *entry,
+                                      PangoColor *color)
+{
+  GtkWidget *widget = GTK_WIDGET (entry);
+  GtkStyleContext *context;
+  GdkRGBA fg = { 0.5, 0.5, 0.5 };
+
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_lookup_color (context, "placeholder_text_color", &fg);
+
+  color->red = CLAMP (fg.red * 65535. + 0.5, 0, 65535);
+  color->green = CLAMP (fg.green * 65535. + 0.5, 0, 65535);
+  color->blue = CLAMP (fg.blue * 65535. + 0.5, 0, 65535);
+}
+
+static inline gboolean
+show_placeholder_text (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv = entry->priv;
+
+  if (!gtk_widget_has_focus (GTK_WIDGET (entry)) &&
+      gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
+      priv->placeholder_text != NULL)
+    return TRUE;
+
+  return FALSE;
+}
+
 static PangoLayout *
 gtk_entry_create_layout (GtkEntry *entry,
                         gboolean  include_preedit)
@@ -5401,6 +5586,7 @@ gtk_entry_create_layout (GtkEntry *entry,
   GtkWidget *widget = GTK_WIDGET (entry);
   PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL);
   PangoAttrList *tmp_attrs = pango_attr_list_new ();
+  gboolean placeholder_layout = show_placeholder_text (entry);
 
   gchar *preedit_string = NULL;
   gint preedit_length = 0;
@@ -5411,15 +5597,26 @@ gtk_entry_create_layout (GtkEntry *entry,
 
   pango_layout_set_single_paragraph_mode (layout, TRUE);
 
-  display = gtk_entry_get_display_text (entry, 0, -1);
+  display = placeholder_layout ? g_strdup (priv->placeholder_text) : gtk_entry_get_display_text (entry, 0, -1);
   n_bytes = strlen (display);
 
-  if (include_preedit)
+  if (!placeholder_layout && include_preedit)
     {
       gtk_im_context_get_preedit_string (priv->im_context,
                                         &preedit_string, &preedit_attrs, NULL);
       preedit_length = priv->preedit_length;
     }
+  else if (placeholder_layout)
+    {
+      PangoColor color;
+      PangoAttribute *attr;
+
+      gtk_entry_get_placeholder_text_color (entry, &color);
+      attr = pango_attr_foreground_new (color.red, color.green, color.blue);
+      attr->start_index = 0;
+      attr->end_index = G_MAXINT;
+      pango_attr_list_insert (tmp_attrs, attr);
+    }
 
   if (preedit_length)
     {
@@ -5513,16 +5710,13 @@ get_layout_position (GtkEntry *entry,
   PangoLayout *layout;
   PangoRectangle logical_rect;
   gint area_width, area_height;
-  GtkBorder inner_border;
   gint y_pos;
   PangoLayoutLine *line;
   
   layout = gtk_entry_ensure_layout (entry, TRUE);
 
   gtk_entry_get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
-  _gtk_entry_effective_inner_border (entry, &inner_border);
-
-  area_height = PANGO_SCALE * (area_height - inner_border.top - inner_border.bottom);
+  area_height = PANGO_SCALE * area_height;
 
   line = pango_layout_get_lines_readonly (layout)->data;
   pango_layout_line_get_extents (line, NULL, &logical_rect);
@@ -5539,10 +5733,10 @@ get_layout_position (GtkEntry *entry,
   else if (y_pos + logical_rect.height > area_height)
     y_pos = area_height - logical_rect.height;
   
-  y_pos = inner_border.top + y_pos / PANGO_SCALE;
+  y_pos = y_pos / PANGO_SCALE;
 
   if (x)
-    *x = inner_border.left - priv->scroll_offset;
+    *x = - priv->scroll_offset;
 
   if (y)
     *y = y_pos;
@@ -5575,7 +5769,6 @@ draw_text_with_color (GtkEntry *entry,
       gint n_ranges, i;
       PangoRectangle logical_rect;
       GdkRGBA selection_color, text_color;
-      GtkBorder inner_border;
       GtkStyleContext *context;
       GtkStateFlags state;
 
@@ -5583,19 +5776,15 @@ draw_text_with_color (GtkEntry *entry,
       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
       gtk_entry_get_pixel_ranges (entry, &ranges, &n_ranges);
 
-      state = GTK_STATE_FLAG_SELECTED;
-
-      if (gtk_widget_has_focus (widget))
-        state |= GTK_STATE_FLAG_FOCUSED;
+      state = gtk_widget_get_state_flags (widget);
+      state |= GTK_STATE_FLAG_SELECTED;
 
       gtk_style_context_get_background_color (context, state, &selection_color);
       gtk_style_context_get_color (context, state, &text_color);
 
-      _gtk_entry_effective_inner_border (entry, &inner_border);
-
       for (i = 0; i < n_ranges; ++i)
         cairo_rectangle (cr,
-                         inner_border.left - priv->scroll_offset + ranges[2 * i],
+                         - priv->scroll_offset + ranges[2 * i],
                         y,
                         ranges[2 * i + 1],
                         logical_rect.height);
@@ -5623,7 +5812,6 @@ gtk_entry_draw_text (GtkEntry *entry,
   GtkStateFlags state = 0;
   GdkRGBA text_color, bar_text_color;
   GtkStyleContext *context;
-  gint pos_x, pos_y;
   gint width, height;
   gint progress_x, progress_y, progress_width, progress_height;
   gint clip_width, clip_height;
@@ -5638,8 +5826,7 @@ gtk_entry_draw_text (GtkEntry *entry,
   gtk_style_context_get_color (context, state, &text_color);
 
   /* Get foreground color for progressbars */
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
+  gtk_entry_prepare_context_for_progress (entry, context);
   gtk_style_context_get_color (context, state, &bar_text_color);
   gtk_style_context_restore (context);
 
@@ -5647,6 +5834,8 @@ gtk_entry_draw_text (GtkEntry *entry,
                      &progress_x, &progress_y,
                      &progress_width, &progress_height);
 
+  cairo_save (cr);
+
   clip_width = gdk_window_get_width (priv->text_area);
   clip_height = gdk_window_get_height (priv->text_area);
   cairo_rectangle (cr, 0, 0, clip_width, clip_height);
@@ -5661,21 +5850,22 @@ gtk_entry_draw_text (GtkEntry *entry,
     }
   else
     {
+      int frame_x, frame_y, area_x, area_y;
+
       width = gdk_window_get_width (priv->text_area);
       height = gdk_window_get_height (priv->text_area);
 
       cairo_save (cr);
 
-      cairo_rectangle (cr, 0, 0, width, height);
-      cairo_clip (cr);
-      cairo_save (cr);
-
       cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
       cairo_rectangle (cr, 0, 0, width, height);
 
-      gdk_window_get_position (priv->text_area, &pos_x, &pos_y);
-      progress_x -= pos_x;
-      progress_y -= pos_y;
+      /* progres area is frame-relative, we need it text-area window
+       * relative */
+      get_frame_size (entry, TRUE, &frame_x, &frame_y, NULL, NULL);
+      gdk_window_get_position (priv->text_area, &area_x, &area_y);
+      progress_x += frame_x - area_x;
+      progress_y += frame_y - area_y;
 
       cairo_rectangle (cr, progress_x, progress_y,
                        progress_width, progress_height);
@@ -5685,6 +5875,8 @@ gtk_entry_draw_text (GtkEntry *entry,
       draw_text_with_color (entry, cr, &text_color);
       cairo_restore (cr);
 
+      cairo_save (cr);
+
       cairo_rectangle (cr, progress_x, progress_y,
                        progress_width, progress_height);
       cairo_clip (cr);
@@ -5693,27 +5885,8 @@ gtk_entry_draw_text (GtkEntry *entry,
 
       cairo_restore (cr);
     }
-}
 
-static void
-draw_insertion_cursor (GtkEntry      *entry,
-                       cairo_t       *cr,
-                      GdkRectangle  *cursor_location,
-                      gboolean       is_primary,
-                      PangoDirection direction,
-                      gboolean       draw_arrow)
-{
-  GtkWidget *widget = GTK_WIDGET (entry);
-  GtkTextDirection text_dir;
-
-  if (direction == PANGO_DIRECTION_LTR)
-    text_dir = GTK_TEXT_DIR_LTR;
-  else
-    text_dir = GTK_TEXT_DIR_RTL;
-
-  gtk_draw_insertion_cursor (widget, cr,
-                            cursor_location,
-                            is_primary, text_dir, draw_arrow);
+  cairo_restore (cr);
 }
 
 static void
@@ -5723,29 +5896,22 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
 {
   GtkEntryPrivate *priv = entry->priv;
   GtkWidget *widget = GTK_WIDGET (entry);
-  GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (entry)));
-  PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
-  GdkRectangle cursor_location;
-  gboolean split_cursor;
+  GtkStyleContext *context;
   PangoRectangle cursor_rect;
-  GtkBorder inner_border;
-  gint xoffset;
-  gint text_area_height;
   gint cursor_index;
   gboolean block;
   gboolean block_at_line_end;
   PangoLayout *layout;
   const char *text;
+  gint x, y;
 
-  _gtk_entry_effective_inner_border (entry, &inner_border);
-
-  xoffset = inner_border.left - priv->scroll_offset;
-
-  text_area_height = gdk_window_get_height (priv->text_area);
+  context = gtk_widget_get_style_context (widget);
 
   layout = gtk_entry_ensure_layout (entry, TRUE);
   text = pango_layout_get_text (layout);
   cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos + priv->preedit_cursor) - text;
+  get_layout_position (entry, &x, &y);
+
   if (!priv->overwrite_mode)
     block = FALSE;
   else
@@ -5754,82 +5920,32 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
 
   if (!block)
     {
-      gint strong_x, weak_x;
-      PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
-      PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
-      gint x1 = 0;
-      gint x2 = 0;
-
-      gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
-
-      g_object_get (gtk_widget_get_settings (widget),
-                    "gtk-split-cursor", &split_cursor,
-                    NULL);
-
-      dir1 = priv->resolved_dir;
-  
-      if (split_cursor)
-        {
-          x1 = strong_x;
-
-          if (weak_x != strong_x)
-            {
-              dir2 = (priv->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
-              x2 = weak_x;
-            }
-        }
-      else
-        {
-          if (keymap_direction == priv->resolved_dir)
-            x1 = strong_x;
-          else
-            x1 = weak_x;
-        }
-
-      cursor_location.x = xoffset + x1;
-      cursor_location.y = inner_border.top;
-      cursor_location.width = 0;
-      cursor_location.height = text_area_height - inner_border.top - inner_border.bottom;
-
-      draw_insertion_cursor (entry, cr,
-                             &cursor_location, TRUE, dir1,
-                             dir2 != PANGO_DIRECTION_NEUTRAL);
-  
-      if (dir2 != PANGO_DIRECTION_NEUTRAL)
-        {
-          cursor_location.x = xoffset + x2;
-          draw_insertion_cursor (entry, cr,
-                                 &cursor_location, FALSE, dir2,
-                                 TRUE);
-        }
+      gtk_render_insertion_cursor (context, cr,
+                                   x, y,
+                                   layout, cursor_index, priv->resolved_dir);
     }
   else /* overwrite_mode */
     {
-      GdkColor cursor_color;
+      GdkRGBA cursor_color;
       GdkRectangle rect;
-      gint x, y;
 
       cairo_save (cr);
 
-      get_layout_position (entry, &x, &y);
-
       rect.x = PANGO_PIXELS (cursor_rect.x) + x;
       rect.y = PANGO_PIXELS (cursor_rect.y) + y;
       rect.width = PANGO_PIXELS (cursor_rect.width);
       rect.height = PANGO_PIXELS (cursor_rect.height);
 
-      _gtk_widget_get_cursor_color (widget, &cursor_color);
-      gdk_cairo_set_source_color (cr, &cursor_color);
+      _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
+      gdk_cairo_set_source_rgba (cr, &cursor_color);
       gdk_cairo_rectangle (cr, &rect);
       cairo_fill (cr);
 
       if (!block_at_line_end)
         {
-          GtkStyleContext *context;
           GtkStateFlags state;
           GdkRGBA color;
 
-          context = gtk_widget_get_style_context (widget);
           state = gtk_widget_get_state_flags (widget);
           gtk_style_context_get_background_color (context, state, &color);
 
@@ -5844,14 +5960,6 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
     }
 }
 
-static void
-gtk_entry_queue_draw (GtkEntry *entry)
-{
-  if (gtk_widget_is_drawable (GTK_WIDGET (entry)))
-    gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (entry)),
-                                NULL, FALSE);
-}
-
 void
 _gtk_entry_reset_im_context (GtkEntry *entry)
 {
@@ -5886,7 +5994,7 @@ gtk_entry_reset_im_context (GtkEntry *entry)
 /**
  * gtk_entry_im_context_filter_keypress:
  * @entry: a #GtkEntry
- * @event: the key event
+ * @event: (type Gdk.EventKey): the key event
  *
  * Allow the #GtkEntry input method to internally handle key press
  * and release events. If this function returns %TRUE, then no further
@@ -6020,7 +6128,6 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
   GtkEntryPrivate *priv = entry->priv;
   gint min_offset, max_offset;
   gint text_area_width, text_width;
-  GtkBorder inner_border;
   gint strong_x, weak_x;
   gint strong_xoffset, weak_xoffset;
   gfloat xalign;
@@ -6031,10 +6138,8 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
   if (!gtk_widget_get_realized (GTK_WIDGET (entry)))
     return;
 
-  _gtk_entry_effective_inner_border (entry, &inner_border);
-
   text_area_width = gdk_window_get_width (priv->text_area);
-  text_area_width -= inner_border.left + inner_border.right;
+
   if (text_area_width < 0)
     text_area_width = 0;
 
@@ -6119,7 +6224,8 @@ gtk_entry_move_adjustments (GtkEntry *entry)
   PangoFontMetrics *metrics;
   GtkStyleContext *style_context;
   GtkStateFlags state;
-  gint x, layout_x, border_x, border_y;
+  GtkBorder borders;
+  gint x, layout_x;
   gint char_width;
 
   adjustment = g_object_get_qdata (G_OBJECT (entry), quark_cursor_hadjustment);
@@ -6131,8 +6237,8 @@ gtk_entry_move_adjustments (GtkEntry *entry)
   /* Cursor position, layout offset, border width, and widget allocation */
   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &x, NULL);
   get_layout_position (entry, &layout_x, NULL);
-  _gtk_entry_get_borders (entry, &border_x, &border_y);
-  x += allocation.x + layout_x + border_x;
+  _gtk_entry_get_borders (entry, &borders);
+  x += allocation.x + layout_x + borders.left;
 
   /* Approximate width of a char, so user can see what is ahead/behind */
   context = gtk_widget_get_pango_context (widget);
@@ -6385,7 +6491,7 @@ paste_received (GtkClipboard *clipboard,
   GtkEditable *editable = GTK_EDITABLE (entry);
   GtkEntryPrivate *priv = entry->priv;
 
-  if (priv->button == 2)
+  if (priv->button == GDK_BUTTON_MIDDLE)
     {
       gint pos, start, end;
       pos = priv->insert_pos;
@@ -6420,14 +6526,12 @@ paste_received (GtkClipboard *clipboard,
        }
 
       begin_change (entry);
-      g_object_freeze_notify (G_OBJECT (entry));
       if (gtk_editable_get_selection_bounds (editable, &start, &end))
         gtk_editable_delete_text (editable, start, end);
 
       pos = priv->current_pos;
       gtk_editable_insert_text (editable, text, length, &pos);
       gtk_editable_set_position (editable, pos);
-      g_object_thaw_notify (G_OBJECT (entry));
       end_change (entry);
 
       if (completion &&
@@ -6515,8 +6619,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));
@@ -6527,13 +6632,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),
@@ -6541,25 +6642,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;
@@ -6569,121 +6661,34 @@ 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");
 
   g_object_thaw_notify (G_OBJECT (entry));
 }
 
-static void
+static GdkPixbuf *
 gtk_entry_ensure_pixbuf (GtkEntry             *entry,
                          GtkEntryIconPosition  icon_pos)
 {
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
-  GtkIconInfo *info;
-  GtkIconTheme *icon_theme;
-  GtkSettings *settings;
-  GtkStateFlags state;
-  GtkWidget *widget;
-  GdkScreen *screen;
-  gint width, height;
-
-  if (!icon_info || icon_info->pixbuf)
-    return;
-
-  widget = GTK_WIDGET (entry);
-
-  switch (icon_info->storage_type)
-    {
-    case GTK_IMAGE_EMPTY:
-    case GTK_IMAGE_PIXBUF:
-      break;
-    case GTK_IMAGE_STOCK:
-      state = gtk_widget_get_state_flags (widget);
-      gtk_widget_set_state_flags (widget, 0, TRUE);
-      icon_info->pixbuf = gtk_widget_render_icon_pixbuf (widget,
-                                                         icon_info->stock_id,
-                                                         GTK_ICON_SIZE_MENU);
-      if (!icon_info->pixbuf)
-        icon_info->pixbuf = gtk_widget_render_icon_pixbuf (widget,
-                                                           GTK_STOCK_MISSING_IMAGE,
-                                                           GTK_ICON_SIZE_MENU);
-      gtk_widget_set_state_flags (widget, state, TRUE);
-      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);
-
-          icon_info->pixbuf = gtk_icon_theme_load_icon (icon_theme,
-                                                        icon_info->icon_name,
-                                                        MIN (width, height),
-                                                        0, NULL);
+  GtkStyleContext *context;
+  GdkPixbuf *pix;
 
-          if (icon_info->pixbuf == NULL)
-            {
-              state = gtk_widget_get_state_flags (widget);
-              gtk_widget_set_state_flags (widget, 0, TRUE);
-              icon_info->pixbuf = gtk_widget_render_icon_pixbuf (widget,
-                                                                 GTK_STOCK_MISSING_IMAGE,
-                                                                 GTK_ICON_SIZE_MENU);
-              gtk_widget_set_state_flags (widget, state, TRUE);
-            }
-        }
-      break;
+  context = gtk_widget_get_style_context (GTK_WIDGET (entry));
+  gtk_entry_prepare_context_for_icon (entry, context, icon_pos);
 
-    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);
-          if (info)
-            {
-              icon_info->pixbuf = gtk_icon_info_load_icon (info, NULL);
-              gtk_icon_info_free (info);
-            }
+  pix = _gtk_icon_helper_ensure_pixbuf (icon_info->icon_helper,
+                                        context);
 
-          if (icon_info->pixbuf == NULL)
-            {
-              state = gtk_widget_get_state_flags (widget);
-              gtk_widget_set_state_flags (widget, 0, TRUE);
-              icon_info->pixbuf = gtk_widget_render_icon_pixbuf (widget,
-                                                                 GTK_STOCK_MISSING_IMAGE,
-                                                                 GTK_ICON_SIZE_MENU);
-              gtk_widget_set_state_flags (widget, state, TRUE);
-            }
-        }
-      break;
+  gtk_style_context_restore (context);
 
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-    
-  if (icon_info->pixbuf != NULL && icon_info->window != NULL)
-    gdk_window_show_unraised (icon_info->window);
+  return pix;
 }
 
-
 /* Public API
  */
 
@@ -6883,11 +6888,9 @@ gtk_entry_set_text (GtkEntry    *entry,
     g_signal_handler_block (entry, completion->priv->changed_id);
 
   begin_change (entry);
-  g_object_freeze_notify (G_OBJECT (entry));
   gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
   tmp_pos = 0;
   gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos);
-  g_object_thaw_notify (G_OBJECT (entry));
   end_change (entry);
 
   if (completion && completion->priv->changed_id > 0)
@@ -7099,7 +7102,7 @@ gtk_entry_get_overwrite_mode (GtkEntry *entry)
  *      storage in the widget and must not be freed, modified or
  *      stored.
  **/
-G_CONST_RETURN gchar*
+const gchar*
 gtk_entry_get_text (GtkEntry *entry)
 {
   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
@@ -7194,7 +7197,7 @@ gtk_entry_get_text_length (GtkEntry *entry)
  *
  * (For experts: if @setting is %TRUE, the entry calls
  * gtk_window_activate_default() on the window containing the entry, in
- * the default handler for the #GtkWidget::activate signal.)
+ * the default handler for the #GtkEntry::activate signal.)
  **/
 void
 gtk_entry_set_activates_default (GtkEntry *entry,
@@ -7334,6 +7337,10 @@ gtk_entry_get_has_frame (GtkEntry *entry)
  * pixel-exact positioning of the entry is important.
  *
  * Since: 2.10
+ *
+ * Deprecated: 3.4: Use the standard border and padding CSS properties (through
+ *   objects like #GtkStyleContext and #GtkCssProvider); the value set with
+ *   this function is ignored by #GtkEntry.
  **/
 void
 gtk_entry_set_inner_border (GtkEntry        *entry,
@@ -7341,16 +7348,7 @@ gtk_entry_set_inner_border (GtkEntry        *entry,
 {
   g_return_if_fail (GTK_IS_ENTRY (entry));
 
-  gtk_widget_queue_resize (GTK_WIDGET (entry));
-
-  if (border)
-    g_object_set_qdata_full (G_OBJECT (entry), quark_inner_border,
-                             gtk_border_copy (border),
-                             (GDestroyNotify) gtk_border_free);
-  else
-    g_object_set_qdata (G_OBJECT (entry), quark_inner_border, NULL);
-
-  g_object_notify (G_OBJECT (entry), "inner-border");
+  gtk_entry_do_set_inner_border (entry, border);
 }
 
 /**
@@ -7363,13 +7361,17 @@ gtk_entry_set_inner_border (GtkEntry        *entry,
  * Return value: (transfer none): the entry's #GtkBorder, or %NULL if none was set.
  *
  * Since: 2.10
+ *
+ * Deprecated: 3.4: Use the standard border and padding CSS properties (through
+ *   objects like #GtkStyleContext and #GtkCssProvider); the value returned by
+ *   this function is ignored by #GtkEntry.
  **/
-G_CONST_RETURN GtkBorder *
+const GtkBorder *
 gtk_entry_get_inner_border (GtkEntry *entry)
 {
   g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
 
-  return g_object_get_qdata (G_OBJECT (entry), quark_inner_border);
+  return gtk_entry_do_get_inner_border (entry);
 }
 
 /**
@@ -7617,8 +7619,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)
         {
@@ -7633,9 +7634,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));
@@ -7682,8 +7683,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)
         {
@@ -7698,9 +7698,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));
@@ -7750,8 +7750,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)
         {
@@ -7766,9 +7765,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));
@@ -7817,8 +7816,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)
         {
@@ -7833,9 +7831,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));
@@ -7932,6 +7930,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);
@@ -7943,9 +7942,14 @@ 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);
+  if (pixbuf)
+    g_object_unref (pixbuf);
 
-  return icon_info->pixbuf;
+  return pixbuf;
 }
 
 /**
@@ -7978,7 +7982,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);
 }
 
 /**
@@ -8011,7 +8015,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);
 }
 
 /**
@@ -8044,7 +8048,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);
 }
 
 /**
@@ -8150,7 +8154,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);
 }
 
 /**
@@ -8159,7 +8163,8 @@ gtk_entry_get_icon_storage_type (GtkEntry             *entry,
  * @x: the x coordinate of the position to find
  * @y: the y coordinate of the position to find
  *
- * Finds the icon at the given position and return its index.
+ * Finds the icon at the given position and return its index. The
+ * position's coordinates are relative to the @entry's top left corner.
  * If @x, @y doesn't lie inside an icon, -1 is returned.
  * This function is intended for use in a #GtkWidget::query-tooltip
  * signal handler.
@@ -8179,7 +8184,7 @@ gtk_entry_get_icon_at_pos (GtkEntry *entry,
 
   g_return_val_if_fail (GTK_IS_ENTRY (entry), -1);
 
-  get_frame_size (entry, &frame_x, &frame_y, NULL, NULL);
+  get_frame_size (entry, FALSE, &frame_x, &frame_y, NULL, NULL);
   x -= frame_x;
   y -= frame_y;
 
@@ -8198,7 +8203,7 @@ gtk_entry_get_icon_at_pos (GtkEntry *entry,
 
 /**
  * gtk_entry_set_icon_drag_source:
- * @entry: a #GtkIconEntry
+ * @entry: a #GtkEntry
  * @icon_pos: icon position
  * @target_list: the targets (data formats) in which the data can be provided
  * @actions: a bitmask of the allowed drag actions
@@ -8491,7 +8496,7 @@ gtk_entry_get_icon_tooltip_markup (GtkEntry             *entry,
  * Use %NULL for @tooltip to remove an existing tooltip.
  *
  * See also gtk_widget_set_tooltip_markup() and 
- * gtk_enty_set_icon_tooltip_text().
+ * gtk_entry_set_icon_tooltip_text().
  *
  * Since: 2.16
  */
@@ -8584,6 +8589,26 @@ gtk_entry_mnemonic_activate (GtkWidget *widget,
   return TRUE;
 }
 
+static void
+gtk_entry_grab_notify (GtkWidget *widget,
+                       gboolean   was_grabbed)
+{
+  GtkEntryPrivate *priv;
+
+  priv = GTK_ENTRY (widget)->priv;
+
+  if (priv->device &&
+      gtk_widget_device_is_shadowed (widget, priv->device))
+    {
+      /* Unset button so we don't expect
+       * a button release anymore
+       */
+      priv->button = 0;
+      priv->device = NULL;
+      priv->in_drag = FALSE;
+    }
+}
+
 static void
 append_action_signal (GtkEntry     *entry,
                      GtkWidget    *menu,
@@ -8626,7 +8651,7 @@ popup_position_func (GtkMenu   *menu,
   GdkScreen *screen;
   GtkRequisition menu_req;
   GdkRectangle monitor;
-  GtkBorder inner_border;
+  GtkBorder borders;
   gint monitor_num, strong_x, height;
  
   g_return_if_fail (gtk_widget_get_realized (widget));
@@ -8639,14 +8664,14 @@ popup_position_func (GtkMenu   *menu,
     monitor_num = 0;
   gtk_menu_set_monitor (menu, monitor_num);
 
-  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+  gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
   gtk_widget_get_preferred_size (priv->popup_menu,
                                  &menu_req, NULL);
   height = gdk_window_get_height (priv->text_area);
   gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
-  _gtk_entry_effective_inner_border (entry, &inner_border);
+  _gtk_entry_get_borders (entry, &borders);
 
-  *x += inner_border.left + strong_x - priv->scroll_offset;
+  *x += borders.left + strong_x - priv->scroll_offset;
   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
     *x -= menu_req.width;
 
@@ -8678,6 +8703,7 @@ typedef struct
   GtkEntry *entry;
   gint button;
   guint time;
+  GdkDevice *device;
 } PopupInfo;
 
 static void
@@ -8732,6 +8758,7 @@ popup_targets_received (GtkClipboard     *clipboard,
       gtk_menu_shell_append (GTK_MENU_SHELL (info_entry_priv->popup_menu), menuitem);
       
       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
+      gtk_widget_set_sensitive (menuitem, gtk_entry_buffer_get_length (info_entry_priv->buffer) > 0);
       g_signal_connect_swapped (menuitem, "activate",
                                G_CALLBACK (gtk_entry_select_all), entry);
       gtk_widget_show (menuitem);
@@ -8784,15 +8811,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);
        }
     }
@@ -8817,11 +8844,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),
@@ -8853,24 +8882,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);
             }
         }
     }
@@ -9037,10 +9056,8 @@ gtk_entry_drag_data_received (GtkWidget        *widget,
        {
          /* Replacing selection */
           begin_change (entry);
-          g_object_freeze_notify (G_OBJECT (entry));
          gtk_editable_delete_text (editable, sel1, sel2);
          gtk_editable_insert_text (editable, str, length, &sel1);
-          g_object_thaw_notify (G_OBJECT (entry));
           end_change (entry);
        }
       
@@ -9150,6 +9167,18 @@ cursor_blinks (GtkEntry *entry)
     return FALSE;
 }
 
+static gboolean
+get_middle_click_paste (GtkEntry *entry)
+{
+  GtkSettings *settings;
+  gboolean paste;
+
+  settings = gtk_widget_get_settings (GTK_WIDGET (entry));
+  g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL);
+
+  return paste;
+}
+
 static gint
 get_cursor_time (GtkEntry *entry)
 {
@@ -9417,13 +9446,13 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
            completion->priv->current_selected = -1;
          else if (completion->priv->current_selected < matches) 
            {
-             completion->priv->current_selected -= 14;
+             completion->priv->current_selected -= PAGE_STEP;
              if (completion->priv->current_selected < 0)
                completion->priv->current_selected = 0;
            }
          else 
            {
-             completion->priv->current_selected -= 14;
+             completion->priv->current_selected -= PAGE_STEP;
              if (completion->priv->current_selected < matches - 1)
                completion->priv->current_selected = matches - 1;
            }
@@ -9434,7 +9463,7 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
            completion->priv->current_selected = 0;
          else if (completion->priv->current_selected < matches - 1)
            {
-             completion->priv->current_selected += 14;
+             completion->priv->current_selected += PAGE_STEP;
              if (completion->priv->current_selected > matches - 1)
                completion->priv->current_selected = matches - 1;
            }
@@ -9444,7 +9473,7 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
            }
          else
            {
-             completion->priv->current_selected += 14;
+             completion->priv->current_selected += PAGE_STEP;
              if (completion->priv->current_selected > matches + actions - 1)
                completion->priv->current_selected = matches + actions - 1;
            }
@@ -9543,14 +9572,15 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
                 gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), "");
             }
 
-          /* Move the cursor to the end for Right/Esc, to the
-             beginning for Left */
+          /* Move the cursor to the end for Right/Esc */
           if (event->keyval == GDK_KEY_Right ||
               event->keyval == GDK_KEY_KP_Right ||
               event->keyval == GDK_KEY_Escape)
             gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+          /* Let the default keybindings run for Left, i.e. either move to the
+           * previous character or select word if a modifier is used */
           else
-            gtk_editable_set_position (GTK_EDITABLE (widget), 0);
+            retval = FALSE;
         }
 
 keypress_completion_out:
@@ -9566,18 +9596,13 @@ keypress_completion_out:
           event->keyval == GDK_KEY_KP_Tab ||
           event->keyval == GDK_KEY_ISO_Left_Tab) 
     {
-      GtkDirectionType dir = event->keyval == GDK_KEY_ISO_Left_Tab ? 
-       GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD;
-      
       _gtk_entry_reset_im_context (GTK_ENTRY (widget));
       _gtk_entry_completion_popdown (completion);
 
       g_free (completion->priv->completion_prefix);
       completion->priv->completion_prefix = NULL;
 
-      gtk_widget_child_focus (gtk_widget_get_toplevel (widget), dir);
-
-      return TRUE;
+      return FALSE;
     }
   else if (event->keyval == GDK_KEY_ISO_Enter ||
            event->keyval == GDK_KEY_KP_Enter ||
@@ -9585,6 +9610,8 @@ keypress_completion_out:
     {
       GtkTreeIter iter;
       GtkTreeModel *model = NULL;
+      GtkTreeModel *child_model;
+      GtkTreeIter child_iter;
       GtkTreeSelection *sel;
       gboolean retval = TRUE;
 
@@ -9598,9 +9625,11 @@ keypress_completion_out:
           sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
           if (gtk_tree_selection_get_selected (sel, &model, &iter))
             {
+              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
+              child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
               g_signal_handler_block (widget, completion->priv->changed_id);
               g_signal_emit_by_name (completion, "match-selected",
-                                     model, &iter, &entry_set);
+                                     child_model, &child_iter, &entry_set);
               g_signal_handler_unblock (widget, completion->priv->changed_id);
 
               if (!entry_set)
@@ -9848,6 +9877,12 @@ gtk_entry_set_completion (GtkEntry           *entry,
           old->priv->completion_timeout = 0;
         }
 
+      if (old->priv->check_completion_idle)
+        {
+          g_source_destroy (old->priv->check_completion_idle);
+          old->priv->check_completion_idle = NULL;
+        }
+
       if (gtk_widget_get_mapped (old->priv->popup_window))
         _gtk_entry_completion_popdown (old);
 
@@ -9869,6 +9904,8 @@ gtk_entry_set_completion (GtkEntry           *entry,
   connect_completion_signals (entry, completion);    
   completion->priv->entry = GTK_WIDGET (entry);
   g_object_set_data (G_OBJECT (entry), I_(GTK_ENTRY_COMPLETION_KEY), completion);
+
+  g_object_notify (G_OBJECT (entry), "completion");
 }
 
 /**
@@ -10125,6 +10162,67 @@ gtk_entry_progress_pulse (GtkEntry *entry)
   gtk_widget_queue_draw (GTK_WIDGET (entry));
 }
 
+/**
+ * gtk_entry_set_placeholder_text:
+ * @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.
+ *
+ * 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
+ **/
+void
+gtk_entry_set_placeholder_text (GtkEntry    *entry,
+                                const gchar *text)
+{
+  GtkEntryPrivate *priv;
+
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  priv = entry->priv;
+
+  if (g_strcmp0 (priv->placeholder_text, text) == 0)
+    return;
+
+  g_free (priv->placeholder_text);
+  priv->placeholder_text = g_strdup (text);
+
+  gtk_entry_recompute (entry);
+
+  g_object_notify (G_OBJECT (entry), "placeholder-text");
+}
+
+/**
+ * gtk_entry_get_placeholder_text:
+ * @entry: a #GtkEntry
+ *
+ * Retrieves the text that will be displayed when @entry is empty and unfocused
+ *
+ * Returns: a pointer to the placeholder text as a string. This string points to internally allocated
+ * storage in the widget and must not be freed, modified or stored.
+ *
+ * Since: 3.2
+ **/
+const gchar *
+gtk_entry_get_placeholder_text (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+  priv = entry->priv;
+
+  return priv->placeholder_text;
+}
+
 /* Caps Lock warning for password entries */
 
 static void
@@ -10167,12 +10265,7 @@ keymap_state_changed (GdkKeymap *keymap,
 
   if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL && priv->caps_lock_warning)
     { 
-      if (gdk_keymap_get_num_lock_state (keymap)
-          && gdk_keymap_get_caps_lock_state (keymap))
-        text = _("Caps Lock and Num Lock are on");
-      else if (gdk_keymap_get_num_lock_state (keymap))
-        text = _("Num Lock is on");
-      else if (gdk_keymap_get_caps_lock_state (keymap))
+      if (gdk_keymap_get_caps_lock_state (keymap))
         text = _("Caps Lock is on");
     }
 
@@ -10190,7 +10283,7 @@ keymap_state_changed (GdkKeymap *keymap,
  * This is a helper function for GtkComboBox. A GtkEntry in a GtkComboBox
  * is supposed to behave like a GtkCellEditable when placed in a combo box.
  *
- * I.e take up it's allocation and get GtkEntry->is_cell_renderer = TRUE.
+ * I.e take up its allocation and get GtkEntry->is_cell_renderer = TRUE.
  *
  */
 void