]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkentry.c
entrycompletion: Don't reconnect signals all the time
[~andy/gtk] / gtk / gtkentry.c
index 2c8823befb65491c29b52150ad00f738026d8a64..6e34ab5be2018662af049d9cbe4dd5c287a65558 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/>.
  */
 
 /*
@@ -67,6 +65,7 @@
 #include "gtkicontheme.h"
 #include "gtkwidgetprivate.h"
 #include "gtkstylecontextprivate.h"
+#include "gtktexthandleprivate.h"
 
 #include "a11y/gtkentryaccessible.h"
 
 
 #define MIN_ENTRY_WIDTH  150
 #define DRAW_TIMEOUT     20
-#define COMPLETION_TIMEOUT 300
 #define PASSWORD_HINT_MAX 8
-#define PAGE_STEP 14
 
 #define MAX_ICONS 2
 
@@ -144,13 +141,14 @@ struct _GtkEntryPrivate
 
   GtkEntryBuffer        *buffer;
   GtkIMContext          *im_context;
-  GtkShadowType          shadow_type;
   GtkWidget             *popup_menu;
 
-  GdkDevice             *completion_device;
+  GdkDevice             *device;
+
   GdkWindow             *text_area;
 
   PangoLayout           *cached_layout;
+  PangoAttrList         *attrs;
 
   gchar        *im_module;
 
@@ -160,6 +158,8 @@ struct _GtkEntryPrivate
 
   gchar        *placeholder_text;
 
+  GtkTextHandle *text_handle;
+
   gfloat        xalign;
 
   gint          ascent;                     /* font ascent in pango units  */
@@ -169,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;
@@ -190,6 +189,7 @@ struct _GtkEntryPrivate
   guint16       preedit_length;              /* length of preedit string, in bytes */
   guint16      preedit_cursor;              /* offset of cursor within preedit string, in chars */
 
+  guint         shadow_type             : 4;
   guint         editable                : 1;
   guint         in_drag                 : 1;
   guint         overwrite_mode          : 1;
@@ -216,6 +216,8 @@ struct _GtkEntryPrivate
   guint         select_words            : 1;
   guint         select_lines            : 1;
   guint         truncate_multiline      : 1;
+  guint         cursor_handle_dragged   : 1;
+  guint         selection_handle_dragged : 1;
 };
 
 struct _EntryIconInfo
@@ -309,10 +311,14 @@ enum {
   PROP_IM_MODULE,
   PROP_EDITING_CANCELED,
   PROP_PLACEHOLDER_TEXT,
-  PROP_COMPLETION
+  PROP_COMPLETION,
+  PROP_INPUT_PURPOSE,
+  PROP_INPUT_HINTS,
+  PROP_ATTRIBUTES
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
+static gboolean test_touchscreen = FALSE;
 
 typedef enum {
   CURSOR_STANDARD,
@@ -548,6 +554,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);
@@ -556,6 +564,11 @@ static void         gtk_entry_get_text_area_size       (GtkEntry       *entry,
                                                        gint           *y,
                                                        gint           *width,
                                                        gint           *height);
+static void         gtk_entry_get_frame_size           (GtkEntry       *entry,
+                                                       gint           *x,
+                                                       gint           *y,
+                                                       gint           *width,
+                                                       gint           *height);
 static void         get_text_area_size                 (GtkEntry       *entry,
                                                        gint           *x,
                                                        gint           *y,
@@ -571,30 +584,14 @@ static void         gtk_entry_move_adjustments         (GtkEntry             *en
 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);
-static gboolean     gtk_entry_completion_key_press     (GtkWidget          *widget,
-                                                       GdkEventKey        *event,
-                                                       gpointer            user_data);
-static void         gtk_entry_completion_changed       (GtkWidget          *entry,
-                                                       gpointer            user_data);
-static gboolean     check_completion_callback          (GtkEntryCompletion *completion);
-static void         clear_completion_callback          (GtkEntry           *entry,
-                                                       GParamSpec         *pspec);
-static gboolean     accept_completion_callback         (GtkEntry           *entry);
-static void         completion_insert_text_callback    (GtkEntry           *entry,
-                                                       const gchar        *text,
-                                                       gint                length,
-                                                       gint                position,
-                                                       GtkEntryCompletion *completion);
-static void         completion_changed                 (GtkEntryCompletion *completion,
-                                                       GParamSpec         *pspec,
-                                                       gpointer            data);
-static void         disconnect_completion_signals      (GtkEntry           *entry,
-                                                       GtkEntryCompletion *completion);
-static void         connect_completion_signals         (GtkEntry           *entry,
-                                                       GtkEntryCompletion *completion);
+/* GtkTextHandle handlers */
+static void         gtk_entry_handle_dragged           (GtkTextHandle         *handle,
+                                                        GtkTextHandlePosition  pos,
+                                                        gint                   x,
+                                                        gint                   y,
+                                                        GtkEntry              *entry);
 
 static void         begin_change                       (GtkEntry *entry);
 static void         end_change                         (GtkEntry *entry);
@@ -692,6 +689,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;
@@ -712,6 +710,7 @@ gtk_entry_class_init (GtkEntryClass *class)
   class->toggle_overwrite = gtk_entry_toggle_overwrite;
   class->activate = gtk_entry_real_activate;
   class->get_text_area_size = gtk_entry_get_text_area_size;
+  class->get_frame_size = gtk_entry_get_frame_size;
   
   quark_inner_border = g_quark_from_static_string ("gtk-entry-inner-border");
   quark_password_hint = g_quark_from_static_string ("gtk-entry-password-hint");
@@ -783,13 +782,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,
@@ -931,7 +940,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.
    *
@@ -1346,6 +1355,63 @@ gtk_entry_class_init (GtkEntryClass *class)
                                                         GTK_TYPE_ENTRY_COMPLETION,
                                                         GTK_PARAM_READWRITE));
 
+  /**
+   * GtkEntry:input-purpose:
+   *
+   * The purpose of this text field.
+   *
+   * This property can be used by on-screen keyboards and other input
+   * methods to adjust their behaviour.
+   *
+   * Note that setting the purpose to %GTK_INPUT_PURPOSE_PASSWORD or
+   * %GTK_INPUT_PURPOSE_PIN is independent from setting
+   * #GtkEntry:visibility.
+   *
+   * Since: 3.6
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_INPUT_PURPOSE,
+                                   g_param_spec_enum ("input-purpose",
+                                                      P_("Purpose"),
+                                                      P_("Purpose of the text field"),
+                                                      GTK_TYPE_INPUT_PURPOSE,
+                                                      GTK_INPUT_PURPOSE_FREE_FORM,
+                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GtkEntry:input-hints:
+   *
+   * Additional hints (beyond #GtkEntry:input-purpose) that
+   * allow input methods to fine-tune their behaviour.
+   *
+   * Since: 3.6
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_INPUT_HINTS,
+                                   g_param_spec_flags ("input-hints",
+                                                       P_("hints"),
+                                                       P_("Hints for the text field behaviour"),
+                                                       GTK_TYPE_INPUT_HINTS,
+                                                       GTK_INPUT_HINT_NONE,
+                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GtkEntry:attributes:
+   *
+   * A list of Pango attributes to apply to the text of the entry.
+   *
+   * This is mainly useful to change the size or weight of the text.
+   *
+   * Since: 3.6
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ATTRIBUTES,
+                                   g_param_spec_boxed ("attributes",
+                                                       P_("Attributes"),
+                                                       P_("A list of style attributes to apply to the text of the label"),
+                                                       PANGO_TYPE_ATTR_LIST,
+                                                       GTK_PARAM_READWRITE));
+
   /**
    * GtkEntry:icon-prelight:
    *
@@ -1367,20 +1433,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.
    *
@@ -1423,12 +1494,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.
    */
@@ -1859,15 +1931,21 @@ 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));
+  test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN") != NULL;
 
   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE);
 }
@@ -1892,6 +1970,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,
@@ -1917,7 +2016,7 @@ gtk_entry_set_property (GObject         *object,
 
            if (!new_value)
              {
-               _gtk_entry_reset_im_context (entry);
+               gtk_entry_reset_im_context (entry);
                if (gtk_widget_has_focus (widget))
                  gtk_im_context_focus_out (priv->im_context);
 
@@ -1948,7 +2047,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:
@@ -2117,6 +2216,18 @@ gtk_entry_set_property (GObject         *object,
       gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (g_value_get_object (value)));
       break;
 
+    case PROP_INPUT_PURPOSE:
+      gtk_entry_set_input_purpose (entry, g_value_get_enum (value));
+      break;
+
+    case PROP_INPUT_HINTS:
+      gtk_entry_set_input_hints (entry, g_value_get_flags (value));
+      break;
+
+    case PROP_ATTRIBUTES:
+      gtk_entry_set_attributes (entry, g_value_get_boxed (value));
+      break;
+
     case PROP_SCROLL_OFFSET:
     case PROP_CURSOR_POSITION:
     default:
@@ -2165,7 +2276,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:
@@ -2341,6 +2452,18 @@ gtk_entry_get_property (GObject         *object,
       g_value_set_object (value, G_OBJECT (gtk_entry_get_completion (entry)));
       break;
 
+    case PROP_INPUT_PURPOSE:
+      g_value_set_enum (value, gtk_entry_get_input_purpose (entry));
+      break;
+
+    case PROP_INPUT_HINTS:
+      g_value_set_flags (value, gtk_entry_get_input_hints (entry));
+      break;
+
+    case PROP_ATTRIBUTES:
+      g_value_set_boxed (value, priv->attrs);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2448,6 +2571,10 @@ gtk_entry_init (GtkEntry *entry)
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
 
   gtk_entry_update_cached_style_values (entry);
+
+  priv->text_handle = _gtk_text_handle_new (GTK_WIDGET (entry));
+  g_signal_connect (priv->text_handle, "handle-dragged",
+                    G_CALLBACK (gtk_entry_handle_dragged), entry);
 }
 
 static void
@@ -2474,6 +2601,21 @@ gtk_entry_prepare_context_for_icon (GtkEntry             *entry,
 
   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
@@ -2483,6 +2625,7 @@ get_icon_width (GtkEntry             *entry,
   GtkEntryPrivate *priv = entry->priv;
   EntryIconInfo *icon_info = priv->icons[icon_pos];
   GtkStyleContext *context;
+  GtkBorder padding;
   gint width;
 
   if (!icon_info)
@@ -2490,10 +2633,15 @@ get_icon_width (GtkEntry             *entry,
 
   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_helper_get_size (icon_info->icon_helper, context,
                              &width, NULL);
   gtk_style_context_restore (context);
 
+  if (width > 0)
+    width += padding.left + padding.right;
+
   return width;
 }
 
@@ -2514,14 +2662,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)
     {
@@ -2587,7 +2731,7 @@ gtk_entry_destroy (GtkWidget *widget)
   GtkEntryPrivate *priv = entry->priv;
 
   priv->current_pos = priv->selection_bound = 0;
-  _gtk_entry_reset_im_context (entry);
+  gtk_entry_reset_im_context (entry);
   gtk_entry_reset_layout (entry);
 
   if (priv->blink_timeout)
@@ -2616,6 +2760,7 @@ gtk_entry_dispose (GObject *object)
   gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
   gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
   gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+  gtk_entry_set_completion (entry, NULL);
 
   if (priv->buffer)
     {
@@ -2655,8 +2800,6 @@ gtk_entry_finalize (GObject *object)
         }
     }
 
-  gtk_entry_set_completion (entry, NULL);
-
   if (priv->cached_layout)
     g_object_unref (priv->cached_layout);
 
@@ -2668,6 +2811,7 @@ gtk_entry_finalize (GObject *object)
   if (priv->recompute_idle)
     g_source_remove (priv->recompute_idle);
 
+  g_object_unref (priv->text_handle);
   g_free (priv->placeholder_text);
   g_free (priv->im_module);
 
@@ -2688,10 +2832,10 @@ gtk_entry_get_display_mode (GtkEntry *entry)
   return DISPLAY_INVISIBLE;
 }
 
-static gchar*
-gtk_entry_get_display_text (GtkEntry *entry,
-                            gint      start_pos,
-                            gint      end_pos)
+gchar*
+_gtk_entry_get_display_text (GtkEntry *entry,
+                             gint      start_pos,
+                             gint      end_pos)
 {
   GtkEntryPasswordHint *password_hint;
   GtkEntryPrivate *priv;
@@ -2829,7 +2973,7 @@ realize_icon_info (GtkWidget            *widget,
   icon_info->window = gdk_window_new (gtk_widget_get_window (widget),
                                       &attributes,
                                       attributes_mask);
-  gdk_window_set_user_data (icon_info->window, widget);
+  gtk_widget_register_window (widget, icon_info->window);
 
   gtk_widget_queue_resize (widget);
 }
@@ -2848,6 +2992,7 @@ construct_icon_info (GtkWidget            *widget,
   priv->icons[icon_pos] = icon_info;
 
   icon_info->icon_helper = _gtk_icon_helper_new ();
+  _gtk_icon_helper_set_force_scale_pixbuf (icon_info->icon_helper, TRUE);
 
   if (gtk_widget_get_realized (widget))
     realize_icon_info (widget, icon_pos);
@@ -2888,6 +3033,9 @@ gtk_entry_unmap (GtkWidget *widget)
   EntryIconInfo *icon_info = NULL;
   gint i;
 
+  _gtk_text_handle_set_mode (priv->text_handle,
+                             GTK_TEXT_HANDLE_MODE_NONE);
+
   for (i = 0; i < MAX_ICONS; i++)
     {
       if ((icon_info = priv->icons[i]) != NULL)
@@ -2952,7 +3100,7 @@ gtk_entry_realize (GtkWidget *widget)
                                     &attributes,
                                     attributes_mask);
 
-  gdk_window_set_user_data (priv->text_area, entry);
+  gtk_widget_register_window (widget, priv->text_area);
 
   if (attributes_mask & GDK_WA_CURSOR)
     g_object_unref (attributes.cursor);
@@ -2961,7 +3109,7 @@ gtk_entry_realize (GtkWidget *widget)
 
   gtk_entry_adjust_scroll (entry);
   gtk_entry_update_primary_selection (entry);
-
+  _gtk_text_handle_set_relative_to (priv->text_handle, priv->text_area);
 
   /* If the icon positions are already setup, create their windows.
    * Otherwise if they don't exist yet, then construct_icon_info()
@@ -2989,6 +3137,7 @@ gtk_entry_unrealize (GtkWidget *widget)
   gtk_entry_reset_layout (entry);
   
   gtk_im_context_set_client_window (priv->im_context, NULL);
+  _gtk_text_handle_set_relative_to (priv->text_handle, NULL);
 
   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY);
   if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry))
@@ -2996,7 +3145,7 @@ gtk_entry_unrealize (GtkWidget *widget)
   
   if (priv->text_area)
     {
-      gdk_window_set_user_data (priv->text_area, NULL);
+      gtk_widget_unregister_window (widget, priv->text_area);
       gdk_window_destroy (priv->text_area);
       priv->text_area = NULL;
     }
@@ -3015,6 +3164,7 @@ gtk_entry_unrealize (GtkWidget *widget)
         {
           if (icon_info->window != NULL)
             {
+              gtk_widget_unregister_window (widget, icon_info->window);
               gdk_window_destroy (icon_info->window);
               icon_info->window = NULL;
             }
@@ -3067,19 +3217,14 @@ gtk_entry_get_preferred_width (GtkWidget *widget,
   PangoFontMetrics *metrics;
   GtkBorder borders;
   PangoContext *context;
-  GtkStyleContext *style_context;
-  GtkStateFlags state;
   gint icon_widths = 0;
   gint icon_width, i;
   gint width;
 
   context = gtk_widget_get_pango_context (widget);
 
-  style_context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-
   metrics = pango_context_get_metrics (context,
-                                       gtk_style_context_get_font (style_context, state),
+                                       pango_context_get_font_description (context),
                                        pango_context_get_language (context));
 
   _gtk_entry_get_borders (entry, &borders);
@@ -3099,7 +3244,7 @@ gtk_entry_get_preferred_width (GtkWidget *widget,
     {
       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)
@@ -3120,28 +3265,25 @@ gtk_entry_get_preferred_height (GtkWidget *widget,
   GtkEntryPrivate *priv = entry->priv;
   PangoFontMetrics *metrics;
   GtkBorder borders;
-  GtkStyleContext *style_context;
-  GtkStateFlags state;
   PangoContext *context;
   gint height;
+  PangoLayout *layout;
 
+  layout = gtk_entry_ensure_layout (entry, TRUE);
   context = gtk_widget_get_pango_context (widget);
 
-  style_context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-
   metrics = pango_context_get_metrics (context,
-                                       gtk_style_context_get_font (style_context, state),
+                                       pango_context_get_font_description (context),
                                       pango_context_get_language (context));
 
   priv->ascent = pango_font_metrics_get_ascent (metrics);
   priv->descent = pango_font_metrics_get_descent (metrics);
+  pango_font_metrics_unref (metrics);
 
   _gtk_entry_get_borders (entry, &borders);
+  pango_layout_get_pixel_size (layout, NULL, &height);
 
-  height = PANGO_PIXELS (priv->ascent + priv->descent) + borders.top + borders.bottom;
-
-  pango_font_metrics_unref (metrics);
+  height += borders.top + borders.bottom;
 
   *minimum = height;
   *natural = height;
@@ -3252,12 +3394,11 @@ get_text_area_size (GtkEntry *entry,
 
 
 static void
-get_frame_size (GtkEntry *entry,
-                gboolean  relative_to_window,
-                gint     *x,
-                gint     *y,
-                gint     *width,
-                gint     *height)
+gtk_entry_get_frame_size (GtkEntry *entry,
+                          gint     *x,
+                          gint     *y,
+                          gint     *width,
+                          gint     *height)
 {
   GtkEntryPrivate *priv = entry->priv;
   GtkAllocation allocation;
@@ -3272,7 +3413,7 @@ get_frame_size (GtkEntry *entry,
   gtk_widget_get_allocation (widget, &allocation);
 
   if (x)
-    *x = relative_to_window ? allocation.x : 0;
+    *x = allocation.x;
 
   if (y)
     {
@@ -3281,8 +3422,7 @@ get_frame_size (GtkEntry *entry,
       else
         *y = (allocation.height - req_height) / 2;
 
-      if (relative_to_window)
-        *y += allocation.y;
+      *y += allocation.y;
     }
 
   if (width)
@@ -3297,6 +3437,36 @@ get_frame_size (GtkEntry *entry,
     }
 }
 
+static void
+get_frame_size (GtkEntry *entry,
+                gboolean  relative_to_window,
+                gint     *x,
+                gint     *y,
+                gint     *width,
+                gint     *height)
+{
+  GtkEntryClass *class;
+  GtkWidget *widget = GTK_WIDGET (entry);
+
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  class = GTK_ENTRY_GET_CLASS (entry);
+
+  if (class->get_frame_size)
+    class->get_frame_size (entry, x, y, width, height);
+
+  if (!relative_to_window)
+    {
+      GtkAllocation allocation;
+      gtk_widget_get_allocation (widget, &allocation);
+
+      if (x)
+        *x -= allocation.x;
+      if (y)
+        *y -= allocation.y;
+    }
+}
+
 static void
 gtk_entry_size_allocate (GtkWidget     *widget,
                         GtkAllocation *allocation)
@@ -3313,7 +3483,7 @@ gtk_entry_size_allocate (GtkWidget     *widget,
       gtk_entry_recompute (entry);
 
       completion = gtk_entry_get_completion (entry);
-      if (completion && gtk_widget_get_mapped (completion->priv->popup_window))
+      if (completion)
         _gtk_entry_completion_resize_popup (completion);
     }
 }
@@ -3352,6 +3522,7 @@ draw_icon (GtkWidget            *widget,
   EntryIconInfo *icon_info = priv->icons[icon_pos];
   gint x, y, width, height, pix_width, pix_height;
   GtkStyleContext *context;
+  GtkBorder padding;
 
   if (!icon_info)
     return;
@@ -3369,9 +3540,11 @@ draw_icon (GtkWidget            *widget,
 
   context = gtk_widget_get_style_context (widget);
   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_icon_helper_get_size (icon_info->icon_helper, context,
+                             &pix_width, &pix_height);
+  gtk_style_context_get_padding (context, 0, &padding);
 
-  x = MAX (0, (width  - pix_width) / 2);
+  x = MAX (0, padding.left);
   y = MAX (0, (height - pix_height) / 2);
 
   _gtk_icon_helper_draw (icon_info->icon_helper,
@@ -3444,6 +3617,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,
@@ -3453,29 +3638,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;
+
+  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);
 
-  get_text_area_size (entry, x, y, width, height);
+  *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)
     {
@@ -3514,19 +3721,14 @@ gtk_entry_draw_progress (GtkWidget       *widget,
                          cairo_t         *cr)
 {
   GtkEntry *entry = GTK_ENTRY (widget);
-  GtkEntryPrivate *private = entry->priv;
   gint x, y, width, height;
 
   get_progress_area (widget, &x, &y, &width, &height);
 
   if ((width <= 0) || (height <= 0))
     return;
-
-  gtk_style_context_save (context);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
-  if (private->progress_pulse_mode)
-    gtk_style_context_add_class (context, GTK_STYLE_CLASS_PULSE);
-
+  gtk_entry_prepare_context_for_progress (entry, context);
   gtk_render_activity (context, cr,
                        x, y, width, height);
 
@@ -3542,36 +3744,40 @@ gtk_entry_draw (GtkWidget *widget,
   GtkEntryPrivate *priv = entry->priv;
   int i;
 
-  context = gtk_widget_get_style_context (widget);
+  if (gtk_cairo_should_draw_window (cr,
+                                    gtk_widget_get_window (widget)))
+    {
+      context = gtk_widget_get_style_context (widget);
 
-  /* Draw entry_bg, shadow, progress and focus */
-  gtk_entry_draw_frame (widget, context, cr);
+      /* Draw entry_bg, shadow, progress and focus */
+      gtk_entry_draw_frame (widget, context, cr);
 
-  /* Draw text and cursor */
-  cairo_save (cr);
+      /* Draw text and cursor */
+      cairo_save (cr);
 
-  gtk_cairo_transform_to_window (cr, widget, priv->text_area);
+      gtk_cairo_transform_to_window (cr, widget, priv->text_area);
 
-  if (priv->dnd_position != -1)
-    gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
-  
-  gtk_entry_draw_text (GTK_ENTRY (widget), cr);
+      if (priv->dnd_position != -1)
+        gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
 
-  /* 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_entry_draw_text (GTK_ENTRY (widget), cr);
 
-  cairo_restore (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 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);
+      /* Draw icons */
+      for (i = 0; i < MAX_ICONS; i++)
+        {
+          EntryIconInfo *icon_info = priv->icons[i];
+
+          if (icon_info != NULL)
+            draw_icon (widget, cr, i);
+        }
     }
 
   return FALSE;
@@ -3698,7 +3904,108 @@ in_selection (GtkEntry *entry,
   g_free (ranges);
   return retval;
 }
-             
+
+static void
+gtk_entry_move_handle (GtkEntry              *entry,
+                       GtkTextHandlePosition  pos,
+                       gint                   x,
+                       gint                   y,
+                       gint                   height)
+{
+  GtkEntryPrivate *priv = entry->priv;
+
+  if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) &&
+      (x < 0 || x > gdk_window_get_width (priv->text_area)))
+    {
+      /* Hide the handle if it's not being manipulated
+       * and fell outside of the visible text area.
+       */
+      _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE);
+    }
+  else
+    {
+      GdkRectangle rect;
+
+      rect.x = CLAMP (x, 0, gdk_window_get_width (priv->text_area));
+      rect.y = y;
+      rect.width = 1;
+      rect.height = height;
+
+      _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE);
+      _gtk_text_handle_set_position (priv->text_handle, pos, &rect);
+    }
+}
+
+static gint
+gtk_entry_get_selection_bound_location (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv = entry->priv;
+  PangoLayout *layout;
+  PangoRectangle pos;
+  gint x;
+  const gchar *text;
+  gint index;
+
+  layout = gtk_entry_ensure_layout (entry, FALSE);
+  text = pango_layout_get_text (layout);
+  index = g_utf8_offset_to_pointer (text, priv->selection_bound) - text;
+  pango_layout_index_to_pos (layout, index, &pos);
+
+  if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL)
+    x = (pos.x + pos.width) / PANGO_SCALE;
+  else
+    x = pos.x / PANGO_SCALE;
+
+  return x;
+}
+
+static void
+gtk_entry_update_handles (GtkEntry          *entry,
+                          GtkTextHandleMode  mode)
+{
+  GtkEntryPrivate *priv = entry->priv;
+  gint strong_x, height;
+  gint cursor, bound;
+
+  _gtk_text_handle_set_mode (priv->text_handle, mode);
+
+  /* Wait for recomputation before repositioning */
+  if (priv->recompute_idle != 0)
+    return;
+
+  height = gdk_window_get_height (priv->text_area);
+
+  gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
+  cursor = strong_x - priv->scroll_offset;
+
+  if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
+    {
+      gint start, end;
+
+      bound = gtk_entry_get_selection_bound_location (entry) - priv->scroll_offset;
+
+      if (priv->selection_bound > priv->current_pos)
+        {
+          start = cursor;
+          end = bound;
+        }
+      else
+        {
+          start = bound;
+          end = cursor;
+        }
+
+      /* Update start selection bound */
+      gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_START,
+                             start, 0, height);
+      gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_END,
+                             end, 0, height);
+    }
+  else
+    gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_CURSOR,
+                           cursor, 0, height);
+}
+
 static gint
 gtk_entry_button_press (GtkWidget      *widget,
                        GdkEventButton *event)
@@ -3744,7 +4051,8 @@ 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;
@@ -3758,12 +4066,19 @@ gtk_entry_button_press (GtkWidget      *widget,
     {
       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);
+      gboolean is_touchscreen;
+      GdkDevice *source;
+
+      source = gdk_event_get_source_device ((GdkEvent *) event);
+      is_touchscreen = test_touchscreen ||
+        gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN;
 
       priv->select_words = FALSE;
       priv->select_lines = FALSE;
@@ -3772,11 +4087,11 @@ gtk_entry_button_press (GtkWidget      *widget,
           gtk_widget_get_modifier_mask (widget,
                                         GDK_MODIFIER_INTENT_EXTEND_SELECTION))
        {
-         _gtk_entry_reset_im_context (entry);
+         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;
-         
+
          if (tmp_pos > sel_start && tmp_pos < sel_end)
            {
              /* Truncate current selection, but keep it as big as possible */
@@ -3842,7 +4157,12 @@ gtk_entry_button_press (GtkWidget      *widget,
              priv->drag_start_y = event->y;
            }
          else
-            gtk_editable_set_position (editable, tmp_pos);
+            {
+              gtk_editable_set_position (editable, tmp_pos);
+
+              if (is_touchscreen)
+                gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
+            }
          break;
  
        case GDK_2BUTTON_PRESS:
@@ -3853,6 +4173,9 @@ gtk_entry_button_press (GtkWidget      *widget,
          priv->in_drag = FALSE;
          priv->select_words = TRUE;
          gtk_entry_select_word (entry);
+
+          if (is_touchscreen)
+            gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION);
          break;
        
        case GDK_3BUTTON_PRESS:
@@ -3863,6 +4186,8 @@ gtk_entry_button_press (GtkWidget      *widget,
          priv->in_drag = FALSE;
          priv->select_lines = TRUE;
          gtk_entry_select_line (entry);
+          if (is_touchscreen)
+            gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION);
          break;
 
        default:
@@ -3871,7 +4196,9 @@ gtk_entry_button_press (GtkWidget      *widget,
 
       return TRUE;
     }
-  else if (event->button == GDK_BUTTON_MIDDLE && 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)
         {
@@ -3930,14 +4257,22 @@ gtk_entry_button_release (GtkWidget      *widget,
   if (priv->in_drag)
     {
       gint tmp_pos = gtk_entry_find_position (entry, priv->drag_start_x);
+      GdkDevice *source;
 
       gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos);
 
+      source = gdk_event_get_source_device ((GdkEvent *) event);
+
+      if (test_touchscreen ||
+          gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN)
+        gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR);
+
       priv->in_drag = 0;
     }
-  
+
   priv->button = 0;
-  
+  priv->device = NULL;
+
   gtk_entry_update_primary_selection (entry);
              
   return TRUE;
@@ -4046,19 +4381,29 @@ gtk_entry_motion_notify (GtkWidget      *widget,
 
           priv->in_drag = FALSE;
           priv->button = 0;
-         
+          priv->device = NULL;
+
           gtk_target_list_unref (target_list);
         }
     }
   else
     {
+      GdkInputSource input_source;
+      GdkDevice *source;
+      guint length;
+
+      length = gtk_entry_buffer_get_length (get_buffer (entry));
+
       if (event->y < 0)
        tmp_pos = 0;
       else if (event->y >= gdk_window_get_height (priv->text_area))
-       tmp_pos = gtk_entry_buffer_get_length (get_buffer (entry));
+       tmp_pos = length;
       else
        tmp_pos = gtk_entry_find_position (entry, event->x + priv->scroll_offset);
 
+      source = gdk_event_get_source_device ((GdkEvent *) event);
+      input_source = gdk_device_get_source (source);
+
       if (priv->select_words)
        {
          gint min, max;
@@ -4094,13 +4439,20 @@ gtk_entry_motion_notify (GtkWidget      *widget,
              if (priv->current_pos != max)
                pos = min;
            }
-       
+
          gtk_entry_set_positions (entry, pos, bound);
        }
       else
         gtk_entry_set_positions (entry, tmp_pos, -1);
+
+      /* Update touch handles' position */
+      if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN)
+        gtk_entry_update_handles (entry,
+                                  (priv->current_pos == priv->selection_bound) ?
+                                  GTK_TEXT_HANDLE_MODE_CURSOR :
+                                  GTK_TEXT_HANDLE_MODE_SELECTION);
     }
-      
+
   return TRUE;
 }
 
@@ -4140,6 +4492,8 @@ gtk_entry_key_press (GtkWidget   *widget,
 
   gtk_entry_reset_blink_time (entry);
   gtk_entry_pend_cursor_blink (entry);
+  _gtk_text_handle_set_mode (priv->text_handle,
+                             GTK_TEXT_HANDLE_MODE_NONE);
 
   if (priv->editable)
     {
@@ -4151,21 +4505,11 @@ gtk_entry_key_press (GtkWidget   *widget,
        }
     }
 
-  if (event->keyval == GDK_KEY_Return || 
-      event->keyval == GDK_KEY_KP_Enter || 
-      event->keyval == GDK_KEY_ISO_Enter || 
+  if (event->keyval == GDK_KEY_Return ||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_ISO_Enter ||
       event->keyval == GDK_KEY_Escape)
-    {
-      GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-
-      if (completion && completion->priv->completion_timeout)
-        {
-          g_source_remove (completion->priv->completion_timeout);
-          completion->priv->completion_timeout = 0;
-        }
-
-      _gtk_entry_reset_im_context (entry);
-    }
+    gtk_entry_reset_im_context (entry);
 
   if (GTK_WIDGET_CLASS (gtk_entry_parent_class)->key_press_event (widget, event))
     /* Activate key bindings
@@ -4244,6 +4588,9 @@ gtk_entry_focus_out (GtkWidget     *widget,
   GtkEntryCompletion *completion;
   GdkKeymap *keymap;
 
+  _gtk_text_handle_set_mode (priv->text_handle,
+                             GTK_TEXT_HANDLE_MODE_NONE);
+
   gtk_widget_queue_draw (widget);
 
   keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
@@ -4421,7 +4768,7 @@ gtk_entry_real_set_position (GtkEditable *editable,
   if (position != priv->current_pos ||
       position != priv->selection_bound)
     {
-      _gtk_entry_reset_im_context (entry);
+      gtk_entry_reset_im_context (entry);
       gtk_entry_set_positions (entry, position, position);
     }
 }
@@ -4448,8 +4795,8 @@ gtk_entry_set_selection_bounds (GtkEditable *editable,
     start = length;
   if (end < 0)
     end = length;
-  
-  _gtk_entry_reset_im_context (entry);
+
+  gtk_entry_reset_im_context (entry);
 
   gtk_entry_set_positions (entry,
                           MIN (end, length),
@@ -4488,17 +4835,6 @@ icon_theme_changed (GtkEntry *entry)
   gtk_widget_queue_draw (GTK_WIDGET (entry));
 }
 
-static void
-icon_margin_changed (GtkEntry *entry)
-{
-  GtkEntryPrivate *priv = entry->priv;
-  GtkBorder border;
-
-  _gtk_entry_get_borders (GTK_ENTRY (entry), &border);
-
-  priv->icon_margin = border.left;
-}
-
 static void
 gtk_entry_update_cached_style_values (GtkEntry *entry)
 {
@@ -4534,10 +4870,7 @@ gtk_entry_style_updated (GtkWidget *widget)
 
   gtk_entry_update_cached_style_values (entry);
 
-  gtk_entry_recompute (entry);
-
   icon_theme_changed (entry);
-  icon_margin_changed (entry);
 }
 
 /* GtkCellEditable method implementations
@@ -4838,7 +5171,7 @@ gtk_entry_move_cursor (GtkEntry       *entry,
   GtkEntryPrivate *priv = entry->priv;
   gint new_pos = priv->current_pos;
 
-  _gtk_entry_reset_im_context (entry);
+  gtk_entry_reset_im_context (entry);
 
   if (priv->current_pos != priv->selection_bound && !extend_selection)
     {
@@ -4955,8 +5288,7 @@ gtk_entry_insert_at_cursor (GtkEntry    *entry,
 
   if (priv->editable)
     {
-      _gtk_entry_reset_im_context (entry);
-
+      gtk_entry_reset_im_context (entry);
       gtk_editable_insert_text (editable, str, -1, &pos);
       gtk_editable_set_position (editable, pos);
     }
@@ -4972,8 +5304,8 @@ gtk_entry_delete_from_cursor (GtkEntry       *entry,
   gint start_pos = priv->current_pos;
   gint end_pos = priv->current_pos;
   gint old_n_bytes = gtk_entry_buffer_get_bytes (get_buffer (entry));
-  
-  _gtk_entry_reset_im_context (entry);
+
+  gtk_entry_reset_im_context (entry);
 
   if (!priv->editable)
     {
@@ -5050,7 +5382,7 @@ gtk_entry_backspace (GtkEntry *entry)
   GtkEditable *editable = GTK_EDITABLE (entry);
   gint prev_pos;
 
-  _gtk_entry_reset_im_context (entry);
+  gtk_entry_reset_im_context (entry);
 
   if (!priv->editable)
     {
@@ -5081,8 +5413,8 @@ gtk_entry_backspace (GtkEntry *entry)
          gchar *normalized_text;
           glong  len;
 
-         cluster_text = gtk_entry_get_display_text (entry, prev_pos,
-                                                    priv->current_pos);
+         cluster_text = _gtk_entry_get_display_text (entry, prev_pos,
+                                                      priv->current_pos);
          normalized_text = g_utf8_normalize (cluster_text,
                                              strlen (cluster_text),
                                              G_NORMALIZE_NFD);
@@ -5133,7 +5465,7 @@ gtk_entry_copy_clipboard (GtkEntry *entry)
           return;
         }
 
-      str = gtk_entry_get_display_text (entry, start, end);
+      str = _gtk_entry_get_display_text (entry, start, end);
       gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (entry),
                                                        GDK_SELECTION_CLIPBOARD),
                              str, -1);
@@ -5291,7 +5623,7 @@ gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
   gchar *text;
 
   /* XXXX ??? does this even make sense when text is not visible? Should we return FALSE? */
-  text = gtk_entry_get_display_text (entry, 0, -1);
+  text = _gtk_entry_get_display_text (entry, 0, -1);
   gtk_im_context_set_surrounding (context, text, strlen (text), /* Length in bytes */
                                  g_utf8_offset_to_pointer (text, priv->current_pos) - text);
   g_free (text);
@@ -5437,10 +5769,17 @@ recompute_idle_func (gpointer data)
 
   if (gtk_widget_has_screen (GTK_WIDGET (entry)))
     {
+      GtkTextHandleMode handle_mode;
+
       gtk_entry_adjust_scroll (entry);
       gtk_widget_queue_draw (GTK_WIDGET (entry));
 
       update_im_cursor_location (entry);
+
+      handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
+
+      if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
+        gtk_entry_update_handles (entry, handle_mode);
     }
 
   return FALSE;
@@ -5496,9 +5835,9 @@ gtk_entry_create_layout (GtkEntry *entry,
 {
   GtkEntryPrivate *priv = entry->priv;
   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);
+  PangoLayout *layout;
+  PangoAttrList *tmp_attrs;
+  gboolean placeholder_layout;
 
   gchar *preedit_string = NULL;
   gint preedit_length = 0;
@@ -5507,9 +5846,14 @@ gtk_entry_create_layout (GtkEntry *entry,
   gchar *display;
   guint n_bytes;
 
+  layout = gtk_widget_create_pango_layout (widget, NULL);
   pango_layout_set_single_paragraph_mode (layout, TRUE);
 
-  display = placeholder_layout ? g_strdup (priv->placeholder_text) : gtk_entry_get_display_text (entry, 0, -1);
+  tmp_attrs = priv->attrs ? pango_attr_list_ref (priv->attrs)
+                          : pango_attr_list_new ();
+
+  placeholder_layout = show_placeholder_text (entry);
+  display = placeholder_layout ? g_strdup (priv->placeholder_text) : _gtk_entry_get_display_text (entry, 0, -1);
   n_bytes = strlen (display);
 
   if (!placeholder_layout && include_preedit)
@@ -5536,12 +5880,9 @@ gtk_entry_create_layout (GtkEntry *entry,
       gint cursor_index = g_utf8_offset_to_pointer (display, priv->current_pos) - display;
 
       g_string_insert (tmp_string, cursor_index, preedit_string);
-      
       pango_layout_set_text (layout, tmp_string->str, tmp_string->len);
-      
       pango_attr_list_splice (tmp_attrs, preedit_attrs,
                              cursor_index, preedit_length);
-      
       g_string_free (tmp_string, TRUE);
     }
   else
@@ -5627,7 +5968,7 @@ get_layout_position (GtkEntry *entry,
   
   layout = gtk_entry_ensure_layout (entry, TRUE);
 
-  gtk_entry_get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
+  get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
   area_height = PANGO_SCALE * area_height;
 
   line = pango_layout_get_lines_readonly (layout)->data;
@@ -5738,8 +6079,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);
 
@@ -5816,12 +6156,14 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
   gboolean block_at_line_end;
   PangoLayout *layout;
   const char *text;
+  gint x, y;
 
   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;
@@ -5832,19 +6174,16 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
   if (!block)
     {
       gtk_render_insertion_cursor (context, cr,
-                                   - priv->scroll_offset, 0,
+                                   x, y,
                                    layout, cursor_index, priv->resolved_dir);
     }
   else /* overwrite_mode */
     {
       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);
@@ -5874,15 +6213,67 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
     }
 }
 
-void
-_gtk_entry_reset_im_context (GtkEntry *entry)
+static void
+gtk_entry_handle_dragged (GtkTextHandle         *handle,
+                          GtkTextHandlePosition  pos,
+                          gint                   x,
+                          gint                   y,
+                          GtkEntry              *entry)
 {
+  gint cursor_pos, selection_bound_pos, tmp_pos;
   GtkEntryPrivate *priv = entry->priv;
+  GtkTextHandleMode mode;
+  gint *min, *max;
 
-  if (priv->need_im_reset)
+  cursor_pos = priv->current_pos;
+  selection_bound_pos = priv->selection_bound;
+  mode = _gtk_text_handle_get_mode (handle);
+  tmp_pos = gtk_entry_find_position (entry, x + priv->scroll_offset);
+
+  if (mode == GTK_TEXT_HANDLE_MODE_CURSOR ||
+      cursor_pos >= selection_bound_pos)
     {
-      priv->need_im_reset = FALSE;
-      gtk_im_context_reset (priv->im_context);
+      max = &cursor_pos;
+      min = &selection_bound_pos;
+    }
+  else
+    {
+      max = &selection_bound_pos;
+      min = &cursor_pos;
+    }
+
+  if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END)
+    {
+      if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
+        {
+          gint min_pos;
+
+          min_pos = MAX (*min + 1, 0);
+          tmp_pos = MAX (tmp_pos, min_pos);
+        }
+
+      *max = tmp_pos;
+    }
+  else
+    {
+      if (mode == GTK_TEXT_HANDLE_MODE_SELECTION)
+        {
+          gint max_pos;
+
+          max_pos = *max - 1;
+          *min = MIN (tmp_pos, max_pos);
+        }
+    }
+
+  if (cursor_pos != priv->current_pos ||
+      selection_bound_pos != priv->selection_bound)
+    {
+      if (mode == GTK_TEXT_HANDLE_MODE_CURSOR)
+        gtk_entry_set_positions (entry, cursor_pos, cursor_pos);
+      else
+        gtk_entry_set_positions (entry, cursor_pos, selection_bound_pos);
+
+      gtk_entry_update_handles (entry, mode);
     }
 }
 
@@ -5900,9 +6291,15 @@ _gtk_entry_reset_im_context (GtkEntry *entry)
 void
 gtk_entry_reset_im_context (GtkEntry *entry)
 {
+  GtkEntryPrivate *priv = entry->priv;
+
   g_return_if_fail (GTK_IS_ENTRY (entry));
 
-  _gtk_entry_reset_im_context (entry);
+  if (priv->need_im_reset)
+    {
+      priv->need_im_reset = FALSE;
+      gtk_im_context_reset (priv->im_context);
+    }
 }
 
 /**
@@ -6036,19 +6433,36 @@ gtk_entry_get_cursor_locations (GtkEntry   *entry,
     }
 }
 
+static gboolean
+gtk_entry_get_is_selection_handle_dragged (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv = entry->priv;
+  GtkTextHandlePosition pos;
+
+  if (_gtk_text_handle_get_mode (priv->text_handle) != GTK_TEXT_HANDLE_MODE_SELECTION)
+    return FALSE;
+
+  if (priv->current_pos >= priv->selection_bound)
+    pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START;
+  else
+    pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END;
+
+  return _gtk_text_handle_get_is_dragged (priv->text_handle, pos);
+}
+
 static void
 gtk_entry_adjust_scroll (GtkEntry *entry)
 {
   GtkEntryPrivate *priv = entry->priv;
   gint min_offset, max_offset;
   gint text_area_width, text_width;
-  GtkBorder borders;
   gint strong_x, weak_x;
   gint strong_xoffset, weak_xoffset;
   gfloat xalign;
   PangoLayout *layout;
   PangoLayoutLine *line;
   PangoRectangle logical_rect;
+  GtkTextHandleMode handle_mode;
 
   if (!gtk_widget_get_realized (GTK_WIDGET (entry)))
     return;
@@ -6085,22 +6499,33 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
 
   priv->scroll_offset = CLAMP (priv->scroll_offset, min_offset, max_offset);
 
-  /* And make sure cursors are on screen. Note that the cursor is
-   * actually drawn one pixel into the INNER_BORDER space on
-   * the right, when the scroll is at the utmost right. This
-   * looks better to to me than confining the cursor inside the
-   * border entirely, though it means that the cursor gets one
-   * pixel closer to the edge of the widget on the right than
-   * on the left. This might need changing if one changed
-   * INNER_BORDER from 2 to 1, as one would do on a
-   * small-screen-real-estate display.
-   *
-   * We always make sure that the strong cursor is on screen, and
-   * put the weak cursor on screen if possible.
-   */
+  if (gtk_entry_get_is_selection_handle_dragged (entry))
+    {
+      /* The text handle corresponding to the selection bound is
+       * being dragged, ensure it stays onscreen even if we scroll
+       * cursors away, this is so both handles can cause content
+       * to scroll.
+       */
+      strong_x = weak_x = gtk_entry_get_selection_bound_location (entry);
+    }
+  else
+    {
+      /* And make sure cursors are on screen. Note that the cursor is
+       * actually drawn one pixel into the INNER_BORDER space on
+       * the right, when the scroll is at the utmost right. This
+       * looks better to to me than confining the cursor inside the
+       * border entirely, though it means that the cursor gets one
+       * pixel closer to the edge of the widget on the right than
+       * on the left. This might need changing if one changed
+       * INNER_BORDER from 2 to 1, as one would do on a
+       * small-screen-real-estate display.
+       *
+       * We always make sure that the strong cursor is on screen, and
+       * put the weak cursor on screen if possible.
+       */
+      gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
+    }
 
-  gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x);
-  
   strong_xoffset = strong_x - priv->scroll_offset;
 
   if (strong_xoffset < 0)
@@ -6127,6 +6552,11 @@ gtk_entry_adjust_scroll (GtkEntry *entry)
     }
 
   g_object_notify (G_OBJECT (entry), "scroll-offset");
+
+  handle_mode = _gtk_text_handle_get_mode (priv->text_handle);
+
+  if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE)
+    gtk_entry_update_handles (entry, handle_mode);
 }
 
 static void
@@ -6137,8 +6567,6 @@ gtk_entry_move_adjustments (GtkEntry *entry)
   GtkAdjustment *adjustment;
   PangoContext *context;
   PangoFontMetrics *metrics;
-  GtkStyleContext *style_context;
-  GtkStateFlags state;
   GtkBorder borders;
   gint x, layout_x;
   gint char_width;
@@ -6149,7 +6577,7 @@ gtk_entry_move_adjustments (GtkEntry *entry)
 
   gtk_widget_get_allocation (widget, &allocation);
 
-  /* Cursor position, layout offset, border width, and widget allocation */
+  /* Cursor/char 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, &borders);
@@ -6157,11 +6585,9 @@ gtk_entry_move_adjustments (GtkEntry *entry)
 
   /* Approximate width of a char, so user can see what is ahead/behind */
   context = gtk_widget_get_pango_context (widget);
-  style_context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
 
   metrics = pango_context_get_metrics (context,
-                                       gtk_style_context_get_font (style_context, state),
+                                       pango_context_get_font_description (context),
                                       pango_context_get_language (context));
   char_width = pango_font_metrics_get_approximate_char_width (metrics) / PANGO_SCALE;
 
@@ -6477,7 +6903,7 @@ primary_get_cb (GtkClipboard     *clipboard,
   
   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
     {
-      gchar *str = gtk_entry_get_display_text (entry, start, end);
+      gchar *str = _gtk_entry_get_display_text (entry, start, end);
       gtk_selection_data_set_text (selection_data, str, -1);
       g_free (str);
     }
@@ -6818,14 +7244,19 @@ gtk_entry_set_text (GtkEntry    *entry,
  * @visible: %TRUE if the contents of the entry are displayed
  *           as plaintext
  *
- * Sets whether the contents of the entry are visible or not. 
- * When visibility is set to %FALSE, characters are displayed 
- * as the invisible char, and will also appear that way when 
+ * Sets whether the contents of the entry are visible or not.
+ * When visibility is set to %FALSE, characters are displayed
+ * as the invisible char, and will also appear that way when
  * the text in the entry widget is copied elsewhere.
  *
  * By default, GTK+ picks the best invisible character available
  * in the current font, but it can be changed with
  * gtk_entry_set_invisible_char().
+ *
+ * Note that you probably want to set #GtkEntry:input-purpose
+ * to %GTK_INPUT_PURPOSE_PASSWORD or %GTK_INPUT_PURPOSE_PIN to
+ * inform input methods about the purpose of this entry,
+ * in addition to setting visibility to %FALSE.
  */
 void
 gtk_entry_set_visibility (GtkEntry *entry,
@@ -7112,7 +7543,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,
@@ -7252,6 +7683,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,
@@ -7259,16 +7694,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);
 }
 
 /**
@@ -7281,13 +7707,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.
  **/
 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);
 }
 
 /**
@@ -7536,6 +7966,8 @@ gtk_entry_set_icon_from_pixbuf (GtkEntry             *entry,
   if (pixbuf)
     {
       _gtk_icon_helper_set_pixbuf (icon_info->icon_helper, pixbuf);
+      _gtk_icon_helper_set_icon_size (icon_info->icon_helper,
+                                      GTK_ICON_SIZE_MENU);
 
       if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
         {
@@ -7862,7 +8294,8 @@ gtk_entry_get_icon_pixbuf (GtkEntry             *entry,
    * the icon helper's cache ref directly.
    */
   pixbuf = gtk_entry_ensure_pixbuf (entry, icon_pos);
-  g_object_unref (pixbuf);
+  if (pixbuf)
+    g_object_unref (pixbuf);
 
   return pixbuf;
 }
@@ -8118,7 +8551,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
@@ -8363,6 +8796,9 @@ gtk_entry_set_icon_tooltip_text (GtkEntry             *entry,
   icon_info->tooltip = tooltip ? g_markup_escape_text (tooltip, -1) : NULL;
 
   ensure_has_tooltip (entry);
+
+  g_object_notify (G_OBJECT (entry),
+                   icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-tooltip-text" : "secondary-icon-tooltip-text");
 }
 
 /**
@@ -8411,7 +8847,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
  */
@@ -8504,6 +8940,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,
@@ -8994,7 +9450,7 @@ gtk_entry_drag_data_get (GtkWidget        *widget,
 
   if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end))
     {
-      gchar *str = gtk_entry_get_display_text (GTK_ENTRY (widget), sel_start, sel_end);
+      gchar *str = _gtk_entry_get_display_text (GTK_ENTRY (widget), sel_start, sel_end);
 
       gtk_selection_data_set_text (selection_data, str, -1);
       
@@ -9062,6 +9518,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)
 {
@@ -9222,509 +9690,6 @@ gtk_entry_reset_blink_time (GtkEntry *entry)
   priv->blink_time = 0;
 }
 
-
-/* completion */
-static gint
-gtk_entry_completion_timeout (gpointer data)
-{
-  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
-  GtkEntryPrivate *completion_entry_priv = GTK_ENTRY (completion->priv->entry)->priv;
-
-  completion->priv->completion_timeout = 0;
-
-  if (completion->priv->filter_model &&
-      g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)), -1)
-      >= completion->priv->minimum_key_length)
-    {
-      gint matches;
-      gint actions;
-      GtkTreeSelection *s;
-      gboolean popup_single;
-
-      gtk_entry_completion_complete (completion);
-      matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
-
-      gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
-
-      s = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
-
-      gtk_tree_selection_unselect_all (s);
-
-      actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
-
-      g_object_get (completion, "popup-single-match", &popup_single, NULL);
-      if ((matches > (popup_single ? 0: 1)) || actions > 0)
-       { 
-         if (gtk_widget_get_visible (completion->priv->popup_window))
-           _gtk_entry_completion_resize_popup (completion);
-          else
-           _gtk_entry_completion_popup (completion, completion_entry_priv->completion_device);
-       }
-      else 
-       _gtk_entry_completion_popdown (completion);
-    }
-  else if (gtk_widget_get_visible (completion->priv->popup_window))
-    _gtk_entry_completion_popdown (completion);
-
-  return FALSE;
-}
-
-static inline gboolean
-keyval_is_cursor_move (guint keyval)
-{
-  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up)
-    return TRUE;
-
-  if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down)
-    return TRUE;
-
-  if (keyval == GDK_KEY_Page_Up)
-    return TRUE;
-
-  if (keyval == GDK_KEY_Page_Down)
-    return TRUE;
-
-  return FALSE;
-}
-
-static gboolean
-gtk_entry_completion_key_press (GtkWidget   *widget,
-                                GdkEventKey *event,
-                                gpointer     user_data)
-{
-  gint matches, actions = 0;
-  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
-
-  if (!gtk_widget_get_mapped (completion->priv->popup_window))
-    return FALSE;
-
-  matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL);
-
-  if (completion->priv->actions)
-    actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL);
-
-  if (keyval_is_cursor_move (event->keyval))
-    {
-      GtkTreePath *path = NULL;
-      
-      if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
-        {
-         if (completion->priv->current_selected < 0)
-           completion->priv->current_selected = matches + actions - 1;
-         else
-           completion->priv->current_selected--;
-        }
-      else if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
-        {
-          if (completion->priv->current_selected < matches + actions - 1)
-           completion->priv->current_selected++;
-         else
-            completion->priv->current_selected = -1;
-        }
-      else if (event->keyval == GDK_KEY_Page_Up)
-       {
-         if (completion->priv->current_selected < 0)
-           completion->priv->current_selected = matches + actions - 1;
-         else if (completion->priv->current_selected == 0)
-           completion->priv->current_selected = -1;
-         else if (completion->priv->current_selected < matches) 
-           {
-             completion->priv->current_selected -= PAGE_STEP;
-             if (completion->priv->current_selected < 0)
-               completion->priv->current_selected = 0;
-           }
-         else 
-           {
-             completion->priv->current_selected -= PAGE_STEP;
-             if (completion->priv->current_selected < matches - 1)
-               completion->priv->current_selected = matches - 1;
-           }
-       }
-      else if (event->keyval == GDK_KEY_Page_Down)
-       {
-         if (completion->priv->current_selected < 0)
-           completion->priv->current_selected = 0;
-         else if (completion->priv->current_selected < matches - 1)
-           {
-             completion->priv->current_selected += PAGE_STEP;
-             if (completion->priv->current_selected > matches - 1)
-               completion->priv->current_selected = matches - 1;
-           }
-         else if (completion->priv->current_selected == matches + actions - 1)
-           {
-             completion->priv->current_selected = -1;
-           }
-         else
-           {
-             completion->priv->current_selected += PAGE_STEP;
-             if (completion->priv->current_selected > matches + actions - 1)
-               completion->priv->current_selected = matches + actions - 1;
-           }
-       }
-
-      if (completion->priv->current_selected < 0)
-        {
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
-
-          if (completion->priv->inline_selection &&
-              completion->priv->completion_prefix)
-            {
-              gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), 
-                                  completion->priv->completion_prefix);
-              gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-            }
-        }
-      else if (completion->priv->current_selected < matches)
-        {
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)));
-
-          path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1);
-          gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view),
-                                    path, NULL, FALSE);
-
-          if (completion->priv->inline_selection)
-            {
-
-              GtkTreeIter iter;
-              GtkTreeIter child_iter;
-              GtkTreeModel *model = NULL;
-              GtkTreeSelection *sel;
-              gboolean entry_set;
-
-              sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
-              if (!gtk_tree_selection_get_selected (sel, &model, &iter))
-                return FALSE;
-
-              gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
-              model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
-              
-              if (completion->priv->completion_prefix == NULL)
-                completion->priv->completion_prefix = g_strdup (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)));
-
-              g_signal_emit_by_name (completion, "cursor-on-match", model,
-                                     &child_iter, &entry_set);
-            }
-        }
-      else if (completion->priv->current_selected - matches >= 0)
-        {
-          gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)));
-
-          path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
-          gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view),
-                                    path, NULL, FALSE);
-
-          if (completion->priv->inline_selection &&
-              completion->priv->completion_prefix)
-            {
-              gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), 
-                                  completion->priv->completion_prefix);
-              gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-            }
-        }
-
-      gtk_tree_path_free (path);
-
-      return TRUE;
-    }
-  else if (event->keyval == GDK_KEY_Escape ||
-           event->keyval == GDK_KEY_Left ||
-           event->keyval == GDK_KEY_KP_Left ||
-           event->keyval == GDK_KEY_Right ||
-           event->keyval == GDK_KEY_KP_Right) 
-    {
-      gboolean retval = TRUE;
-
-      _gtk_entry_reset_im_context (GTK_ENTRY (widget));
-      _gtk_entry_completion_popdown (completion);
-
-      if (completion->priv->current_selected < 0)
-        {
-          retval = FALSE;
-          goto keypress_completion_out;
-        }
-      else if (completion->priv->inline_selection)
-        {
-          /* Escape rejects the tentative completion */
-          if (event->keyval == GDK_KEY_Escape)
-            {
-              if (completion->priv->completion_prefix)
-                gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), 
-                                    completion->priv->completion_prefix);
-              else 
-                gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), "");
-            }
-
-          /* Move the cursor to the end for Right/Esc, to the
-             beginning for Left */
-          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);
-          else
-            gtk_editable_set_position (GTK_EDITABLE (widget), 0);
-        }
-
-keypress_completion_out:
-      if (completion->priv->inline_selection)
-        {
-          g_free (completion->priv->completion_prefix);
-          completion->priv->completion_prefix = NULL;
-        }
-
-      return retval;
-    }
-  else if (event->keyval == GDK_KEY_Tab || 
-          event->keyval == GDK_KEY_KP_Tab ||
-          event->keyval == GDK_KEY_ISO_Left_Tab) 
-    {
-      _gtk_entry_reset_im_context (GTK_ENTRY (widget));
-      _gtk_entry_completion_popdown (completion);
-
-      g_free (completion->priv->completion_prefix);
-      completion->priv->completion_prefix = NULL;
-
-      return FALSE;
-    }
-  else if (event->keyval == GDK_KEY_ISO_Enter ||
-           event->keyval == GDK_KEY_KP_Enter ||
-          event->keyval == GDK_KEY_Return)
-    {
-      GtkTreeIter iter;
-      GtkTreeModel *model = NULL;
-      GtkTreeModel *child_model;
-      GtkTreeIter child_iter;
-      GtkTreeSelection *sel;
-      gboolean retval = TRUE;
-
-      _gtk_entry_reset_im_context (GTK_ENTRY (widget));
-      _gtk_entry_completion_popdown (completion);
-
-      if (completion->priv->current_selected < matches)
-        {
-          gboolean entry_set;
-
-          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",
-                                     child_model, &child_iter, &entry_set);
-              g_signal_handler_unblock (widget, completion->priv->changed_id);
-
-              if (!entry_set)
-                {
-                  gchar *str = NULL;
-
-                  gtk_tree_model_get (model, &iter,
-                                      completion->priv->text_column, &str,
-                                      -1);
-
-                  gtk_entry_set_text (GTK_ENTRY (widget), str);
-
-                  /* move the cursor to the end */
-                  gtk_editable_set_position (GTK_EDITABLE (widget), -1);
-
-                  g_free (str);
-                }
-            }
-          else
-            retval = FALSE;
-        }
-      else if (completion->priv->current_selected - matches >= 0)
-        {
-          sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view));
-          if (gtk_tree_selection_get_selected (sel, &model, &iter))
-            {
-              GtkTreePath *path;
-
-              path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1);
-              g_signal_emit_by_name (completion, "action-activated",
-                                     gtk_tree_path_get_indices (path)[0]);
-              gtk_tree_path_free (path);
-            }
-          else
-            retval = FALSE;
-        }
-
-      g_free (completion->priv->completion_prefix);
-      completion->priv->completion_prefix = NULL;
-
-      return retval;
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_entry_completion_changed (GtkWidget *widget,
-                              gpointer   user_data)
-{
-  GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
-  GtkEntry *entry = GTK_ENTRY (widget);
-  GtkEntryPrivate *priv = entry->priv;
-  GdkDevice *device;
-
-  /* (re)install completion timeout */
-  if (completion->priv->completion_timeout)
-    g_source_remove (completion->priv->completion_timeout);
-
-  if (!gtk_entry_get_text (entry))
-    return;
-
-  /* no need to normalize for this test */
-  if (completion->priv->minimum_key_length > 0 &&
-      strcmp ("", gtk_entry_get_text (entry)) == 0)
-    {
-      if (gtk_widget_get_visible (completion->priv->popup_window))
-        _gtk_entry_completion_popdown (completion);
-      return;
-    }
-
-  device = gtk_get_current_event_device ();
-
-  if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
-    device = gdk_device_get_associated_device (device);
-
-  if (device)
-    priv->completion_device = device;
-
-  completion->priv->completion_timeout =
-    gdk_threads_add_timeout (COMPLETION_TIMEOUT,
-                   gtk_entry_completion_timeout,
-                   completion);
-}
-
-static gboolean
-check_completion_callback (GtkEntryCompletion *completion)
-{
-  completion->priv->check_completion_idle = NULL;
-  
-  gtk_entry_completion_complete (completion);
-  gtk_entry_completion_insert_prefix (completion);
-
-  return FALSE;
-}
-
-static void
-clear_completion_callback (GtkEntry   *entry,
-                          GParamSpec *pspec)
-{
-  if (pspec->name == I_("cursor-position") ||
-      pspec->name == I_("selection-bound"))
-    {
-      GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-      
-      completion->priv->has_completion = FALSE;
-    }
-}
-
-static gboolean
-accept_completion_callback (GtkEntry *entry)
-{
-  GtkEntryCompletion *completion = gtk_entry_get_completion (entry);
-
-  if (completion->priv->has_completion)
-    gtk_editable_set_position (GTK_EDITABLE (entry),
-                              gtk_entry_buffer_get_length (get_buffer (entry)));
-
-  return FALSE;
-}
-
-static void
-completion_insert_text_callback (GtkEntry           *entry,
-                                const gchar        *text,
-                                gint                length,
-                                gint                position,
-                                GtkEntryCompletion *completion)
-{
-  /* idle to update the selection based on the file list */
-  if (completion->priv->check_completion_idle == NULL)
-    {
-      completion->priv->check_completion_idle = g_idle_source_new ();
-      g_source_set_priority (completion->priv->check_completion_idle, G_PRIORITY_HIGH);
-      g_source_set_closure (completion->priv->check_completion_idle,
-                           g_cclosure_new_object (G_CALLBACK (check_completion_callback),
-                                                  G_OBJECT (completion)));
-      g_source_attach (completion->priv->check_completion_idle, NULL);
-    }
-}
-
-static void
-completion_changed (GtkEntryCompletion *completion,
-                   GParamSpec         *pspec,
-                   gpointer            data)
-{
-  GtkEntry *entry = GTK_ENTRY (data);
-
-  if (pspec->name == I_("popup-completion") ||
-      pspec->name == I_("inline-completion"))
-    {
-      disconnect_completion_signals (entry, completion);
-      connect_completion_signals (entry, completion);
-    }
-}
-
-static void
-disconnect_completion_signals (GtkEntry           *entry,
-                              GtkEntryCompletion *completion)
-{
-  g_signal_handlers_disconnect_by_func (completion, 
-                                      G_CALLBACK (completion_changed), entry);
-  if (completion->priv->changed_id > 0 &&
-      g_signal_handler_is_connected (entry, completion->priv->changed_id))
-    {
-      g_signal_handler_disconnect (entry, completion->priv->changed_id);
-      completion->priv->changed_id = 0;
-    }
-  g_signal_handlers_disconnect_by_func (entry, 
-                                       G_CALLBACK (gtk_entry_completion_key_press), completion);
-  if (completion->priv->insert_text_id > 0 &&
-      g_signal_handler_is_connected (entry, completion->priv->insert_text_id))
-    {
-      g_signal_handler_disconnect (entry, completion->priv->insert_text_id);
-      completion->priv->insert_text_id = 0;
-    }
-  g_signal_handlers_disconnect_by_func (entry, 
-                                       G_CALLBACK (completion_insert_text_callback), completion);
-  g_signal_handlers_disconnect_by_func (entry, 
-                                       G_CALLBACK (clear_completion_callback), completion);
-  g_signal_handlers_disconnect_by_func (entry, 
-                                       G_CALLBACK (accept_completion_callback), completion);
-}
-
-static void
-connect_completion_signals (GtkEntry           *entry,
-                           GtkEntryCompletion *completion)
-{
-  if (completion->priv->popup_completion)
-    {
-      completion->priv->changed_id =
-       g_signal_connect (entry, "changed",
-                         G_CALLBACK (gtk_entry_completion_changed), completion);
-      g_signal_connect (entry, "key-press-event",
-                       G_CALLBACK (gtk_entry_completion_key_press), completion);
-    }
-  if (completion->priv->inline_completion)
-    {
-      completion->priv->insert_text_id =
-       g_signal_connect (entry, "insert-text",
-                         G_CALLBACK (completion_insert_text_callback), completion);
-      g_signal_connect (entry, "notify",
-                       G_CALLBACK (clear_completion_callback), completion);
-      g_signal_connect (entry, "activate",
-                       G_CALLBACK (accept_completion_callback), completion);
-      g_signal_connect (entry, "focus-out-event",
-                       G_CALLBACK (accept_completion_callback), completion);
-    }
-
-  g_signal_connect (completion, "notify",
-                   G_CALLBACK (completion_changed), entry);
-}
-
 /**
  * gtk_entry_set_completion:
  * @entry: A #GtkEntry
@@ -9753,24 +9718,7 @@ gtk_entry_set_completion (GtkEntry           *entry,
   
   if (old)
     {
-      if (old->priv->completion_timeout)
-        {
-          g_source_remove (old->priv->completion_timeout);
-          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);
-
-      disconnect_completion_signals (entry, old);
-      old->priv->entry = NULL;
-
+      _gtk_entry_completion_disconnect (old);
       g_object_unref (old);
     }
 
@@ -9783,8 +9731,8 @@ gtk_entry_set_completion (GtkEntry           *entry,
   /* hook into the entry */
   g_object_ref (completion);
 
-  connect_completion_signals (entry, completion);    
-  completion->priv->entry = GTK_WIDGET (entry);
+  _gtk_entry_completion_connect (completion, entry);
+
   g_object_set_data (G_OBJECT (entry), I_(GTK_ENTRY_COMPLETION_KEY), completion);
 
   g_object_notify (G_OBJECT (entry), "completion");
@@ -10147,12 +10095,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");
     }
 
@@ -10179,3 +10122,153 @@ _gtk_entry_set_is_cell_renderer (GtkEntry *entry,
 {
   entry->priv->is_cell_renderer = is_cell_renderer;
 }
+
+/**
+ * gtk_entry_set_input_purpose:
+ * @entry: a #GtkEntry
+ * @purpose: the purpose
+ *
+ * Sets the #GtkEntry:input-purpose property which
+ * can be used by on-screen keyboards and other input
+ * methods to adjust their behaviour.
+ *
+ * Since: 3.6
+ */
+void
+gtk_entry_set_input_purpose (GtkEntry        *entry,
+                             GtkInputPurpose  purpose)
+
+{
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  if (gtk_entry_get_input_purpose (entry) != purpose)
+    {
+      g_object_set (G_OBJECT (entry->priv->im_context),
+                    "input-purpose", purpose,
+                    NULL);
+
+      g_object_notify (G_OBJECT (entry), "input-purpose");
+    }
+}
+
+/**
+ * gtk_entry_get_input_purpose:
+ * @entry: a #GtkEntry
+ *
+ * Gets the value of the #GtkEntry:input-purpose property.
+ *
+ * Since: 3.6
+ */
+GtkInputPurpose
+gtk_entry_get_input_purpose (GtkEntry *entry)
+{
+  GtkInputPurpose purpose;
+
+  g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_INPUT_PURPOSE_FREE_FORM);
+
+  g_object_get (G_OBJECT (entry->priv->im_context),
+                "input-purpose", &purpose,
+                NULL);
+
+  return purpose;
+}
+
+/**
+ * gtk_entry_set_input_hints:
+ * @entry: a #GtkEntry
+ * @hints: the hints
+ *
+ * Sets the #GtkEntry:input-hints property, which
+ * allows input methods to fine-tune their behaviour.
+ *
+ * Since: 3.6
+ */
+void
+gtk_entry_set_input_hints (GtkEntry      *entry,
+                           GtkInputHints  hints)
+
+{
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  if (gtk_entry_get_input_hints (entry) != hints)
+    {
+      g_object_set (G_OBJECT (entry->priv->im_context),
+                    "input-hints", hints,
+                    NULL);
+
+      g_object_notify (G_OBJECT (entry), "input-hints");
+    }
+}
+
+/**
+ * gtk_entry_get_input_hints:
+ * @entry: a #GtkEntry
+ *
+ * Gets the value of the #GtkEntry:input-hints property.
+ *
+ * Since: 3.6
+ */
+GtkInputHints
+gtk_entry_get_input_hints (GtkEntry *entry)
+{
+  GtkInputHints hints;
+
+  g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_INPUT_HINT_NONE);
+
+  g_object_get (G_OBJECT (entry->priv->im_context),
+                "input-hints", &hints,
+                NULL);
+
+  return hints;
+}
+
+/**
+ * gtk_entry_set_attributes:
+ * @entry: a #GtkEntry
+ * @attrs: a #PangoAttrList
+ *
+ * Sets a #PangoAttrList; the attributes in the list are applied to the
+ * entry text.
+ *
+ * Since: 3.6
+ */
+void
+gtk_entry_set_attributes (GtkEntry      *entry,
+                          PangoAttrList *attrs)
+{
+  GtkEntryPrivate *priv = entry->priv;
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  if (attrs)
+    pango_attr_list_ref (attrs);
+
+  if (priv->attrs)
+    pango_attr_list_unref (priv->attrs);
+  priv->attrs = attrs;
+
+  g_object_notify (G_OBJECT (entry), "attributes");
+
+  gtk_entry_recompute (entry);
+  gtk_widget_queue_resize (GTK_WIDGET (entry));
+}
+
+/**
+ * gtk_entry_get_attributes:
+ * @entry: a #GtkEntry
+ *
+ * Gets the attribute list that was set on the entry using
+ * gtk_entry_set_attributes(), if any.
+ *
+ * Return value: (transfer none): the attribute list, or %NULL
+ *     if none was set.
+ *
+ * Since: 3.6
+ */
+PangoAttrList *
+gtk_entry_get_attributes (GtkEntry *entry)
+{
+  g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+  return entry->priv->attrs;
+}
+