diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 07a1f4e..9490141 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -31,6 +31,8 @@
 #include "gtklabel.h"
 #include "gtkaccellabel.h"
 #include "gtkdnd.h"
+#include "gtkeditable.h"
+#include "gtkimmulticontext.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtkwindow.h"
@@ -51,16 +53,19 @@
 #include "gtksizerequest.h"
 #include "gtkprivate.h"
 
+typedef struct _GtkLabelEditableInfo GtkLabelEditableInfo;
 
 struct _GtkLabelPriv
 {
   GtkLabelSelectionInfo *select_info;
+  GtkLabelEditableInfo *edit_info;
   GtkWidget *mnemonic_widget;
   GtkWindow *mnemonic_window;
 
   PangoAttrList *attrs;
   PangoAttrList *effective_attrs;
   PangoLayout   *layout;
+  gboolean       layout_includes_preedit;
 
   gboolean mnemonics_visible;
 
@@ -84,6 +89,12 @@ struct _GtkLabelPriv
   gint     wrap_width;
   gint     width_chars;
   gint     max_width_chars;
+  gint     n_bytes;
+  gint     text_size;
+
+  guint    blink_time;
+  guint    blink_timeout;
+  gboolean cursor_visible;
 };
 
 /* Notes about the handling of links:
@@ -126,6 +137,20 @@ typedef struct
   gint end;
 } GtkLabelLink;
 
+struct _GtkLabelEditableInfo {
+  guint old_selectable : 1;
+  guint editable       : 1;
+  guint overwrite_mode : 1;
+
+  gint preedit_length;
+  gint preedit_cursor;
+
+  GtkIMContext *im_context;
+  guint         need_im_reset : 1;
+
+  gulong direction_handler_id;
+};
+
 struct _GtkLabelSelectionInfo
 {
   GdkWindow *window;
@@ -145,10 +170,15 @@ struct _GtkLabelSelectionInfo
   guint link_clicked : 1;
 };
 
+
 enum {
   MOVE_CURSOR,
+  DELETE_FROM_CURSOR,
   COPY_CLIPBOARD,
+  CUT_CLIPBOARD,
+  PASTE_CLIPBOARD,
   POPULATE_POPUP,
+  TOGGLE_OVERWRITE,
   ACTIVATE_LINK,
   ACTIVATE_CURRENT_LINK,
   LAST_SIGNAL
@@ -174,7 +204,8 @@ enum {
   PROP_SINGLE_LINE_MODE,
   PROP_ANGLE,
   PROP_MAX_WIDTH_CHARS,
-  PROP_TRACK_VISITED_LINKS
+  PROP_TRACK_VISITED_LINKS,
+  PROP_EDITABLE,
 };
 
 /* When rotating ellipsizable text we want the natural size to request 
@@ -215,6 +246,17 @@ static void gtk_label_unrealize         (GtkWidget        *widget);
 static void gtk_label_map               (GtkWidget        *widget);
 static void gtk_label_unmap             (GtkWidget        *widget);
 
+static gboolean gtk_label_key_press     (GtkWidget        *widget,
+					 GdkEventKey      *event);
+static gboolean gtk_label_key_release   (GtkWidget        *widget,
+					 GdkEventKey      *event);
+static gint     gtk_label_focus_in      (GtkWidget        *widget,
+					 GdkEventFocus    *event);
+static gint     gtk_label_focus_out     (GtkWidget     *widget,
+					 GdkEventFocus *event);
+static void     gtk_label_check_cursor_blink (GtkLabel *label);
+static void     gtk_label_reset_blink_time   (GtkLabel *label);
+
 static gboolean gtk_label_button_press      (GtkWidget        *widget,
 					     GdkEventButton   *event);
 static gboolean gtk_label_button_release    (GtkWidget        *widget,
@@ -232,6 +274,24 @@ static gboolean gtk_label_query_tooltip     (GtkWidget        *widget,
                                              gboolean          keyboard_tip,
                                              GtkTooltip       *tooltip);
 
+static void gtk_label_editable_init              (GtkEditableClass *iface);
+static void gtk_label_delete_from_cursor         (GtkLabel         *label,
+				                  GtkDeleteType     type,
+				                  gint              count);
+static void gtk_label_cut_clipboard              (GtkLabel         *label);
+static void gtk_label_paste_clipboard            (GtkLabel         *label);
+static void gtk_label_paste                      (GtkLabel         *label,
+						  GdkAtom           selection);
+static void gtk_label_toggle_overwrite           (GtkLabel         *label);
+
+
+static void gtk_label_insert_text                (GtkLabel      *label,
+						  const gchar   *new_text,
+						  gint           new_text_length,
+						  gint          *index);
+static void gtk_label_delete_text                (GtkLabel      *label,
+						  int            start_pos,
+						  int            end_pos);
 static void gtk_label_set_text_internal          (GtkLabel      *label,
 						  gchar         *str);
 static void gtk_label_set_label_internal         (GtkLabel      *label,
@@ -261,9 +321,12 @@ static void gtk_label_create_window       (GtkLabel *label);
 static void gtk_label_destroy_window      (GtkLabel *label);
 static void gtk_label_ensure_select_info  (GtkLabel *label);
 static void gtk_label_clear_select_info   (GtkLabel *label);
+static void gtk_label_clear_edit_info     (GtkLabel *label);
 static void gtk_label_update_cursor       (GtkLabel *label);
 static void gtk_label_clear_layout        (GtkLabel *label);
-static void gtk_label_ensure_layout       (GtkLabel *label, gboolean guess_wrap_width);
+static void gtk_label_ensure_layout       (GtkLabel *label,
+					   gboolean  guess_wrap_width,
+					   gboolean  include_preedit);
 static void gtk_label_invalidate_wrap_width (GtkLabel *label);
 static void gtk_label_select_region_index (GtkLabel *label,
                                            gint      anchor_index,
@@ -353,7 +416,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_MISC,
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
 						gtk_label_buildable_interface_init)
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST,
-                                                gtk_label_size_request_init));
+                                                gtk_label_size_request_init)
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
+						gtk_label_editable_init));
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -415,10 +480,18 @@ gtk_label_class_init (GtkLabelClass *class)
   widget_class->grab_focus = gtk_label_grab_focus;
   widget_class->popup_menu = gtk_label_popup_menu;
   widget_class->focus = gtk_label_focus;
+  widget_class->key_press_event = gtk_label_key_press;
+  widget_class->key_release_event = gtk_label_key_release;
+  widget_class->focus_in_event = gtk_label_focus_in;
+  widget_class->focus_out_event = gtk_label_focus_out;
 
   class->move_cursor = gtk_label_move_cursor;
+  class->delete_from_cursor = gtk_label_delete_from_cursor;
   class->copy_clipboard = gtk_label_copy_clipboard;
+  class->cut_clipboard = gtk_label_cut_clipboard;
+  class->paste_clipboard = gtk_label_paste_clipboard;
   class->activate_link = gtk_label_activate_link;
+  class->toggle_overwrite = gtk_label_toggle_overwrite;
 
   /**
    * GtkLabel::move-cursor:
@@ -459,6 +532,17 @@ gtk_label_class_init (GtkLabelClass *class)
 		  G_TYPE_INT,
 		  G_TYPE_BOOLEAN);
 
+  signals[DELETE_FROM_CURSOR] = 
+    g_signal_new (I_("delete_from_cursor"),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, delete_from_cursor),
+		  NULL, NULL, 
+		  _gtk_marshal_VOID__ENUM_INT,
+		  G_TYPE_NONE, 2,
+		  GTK_TYPE_DELETE_TYPE,
+		  G_TYPE_INT);
+
    /**
    * GtkLabel::copy-clipboard:
    * @label: the object which received the signal
@@ -477,6 +561,24 @@ gtk_label_class_init (GtkLabelClass *class)
 		  NULL, NULL,
 		  _gtk_marshal_VOID__VOID,
 		  G_TYPE_NONE, 0);
+
+  signals[CUT_CLIPBOARD] =
+    g_signal_new (I_("cut_clipboard"),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, cut_clipboard),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
+  signals[PASTE_CLIPBOARD] =
+    g_signal_new (I_("paste_clipboard"),
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, paste_clipboard),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
   
   /**
    * GtkLabel::populate-popup:
@@ -500,6 +602,15 @@ gtk_label_class_init (GtkLabelClass *class)
 		  G_TYPE_NONE, 1,
 		  GTK_TYPE_MENU);
 
+  signals[TOGGLE_OVERWRITE] =
+    g_signal_new (I_("toggle_overwrite"),
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, toggle_overwrite),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
     /**
      * GtkLabel::activate-current-link:
      * @label: The label on which the signal was emitted
@@ -623,6 +734,13 @@ gtk_label_class_init (GtkLabelClass *class)
                                                         FALSE,
                                                         GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
+                                   PROP_EDITABLE,
+                                   g_param_spec_boolean ("editable",
+                                                        P_("Editable"),
+                                                        P_("Whether the label text can be edited"),
+                                                        FALSE,
+                                                        GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
                                    PROP_MNEMONIC_KEYVAL,
                                    g_param_spec_uint ("mnemonic-keyval",
 						      P_("Mnemonic key"),
@@ -860,6 +978,49 @@ gtk_label_class_init (GtkLabelClass *class)
 				G_TYPE_INT, 0,
 				G_TYPE_BOOLEAN, FALSE);
 
+  /* deleting text */
+  gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_CHARS,
+				G_TYPE_INT, 1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_CHARS,
+				G_TYPE_INT, 1);
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_CHARS,
+				G_TYPE_INT, -1);
+
+  /* Make this do the same as Backspace, to help with mis-typing */
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_SHIFT_MASK,
+                                "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_CHARS,
+                                G_TYPE_INT, -1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+				G_TYPE_INT, 1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+				G_TYPE_INT, 1);
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+				G_TYPE_INT, -1);
+
+  /* Overwrite */
+  gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
+				"toggle_overwrite", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, 0,
+				"toggle_overwrite", 0);
+
   add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
 		    GTK_MOVEMENT_WORDS, 1);
 
@@ -890,10 +1051,21 @@ gtk_label_class_init (GtkLabelClass *class)
   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
 		    GTK_MOVEMENT_BUFFER_ENDS, 1);
 
-  /* copy */
+  /* copy and paste */
+  gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
+				"cut_clipboard", 0);
   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
-				"copy-clipboard", 0);
-
+				"copy_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
+				"paste_clipboard", 0);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_SHIFT_MASK,
+				"cut_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_CONTROL_MASK,
+				"copy_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK,
+				"paste_clipboard", 0);
+  
   gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
 				"activate-current-link", 0);
   gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0,
@@ -946,7 +1118,10 @@ gtk_label_set_property (GObject      *object,
       break;	  
     case PROP_SELECTABLE:
       gtk_label_set_selectable (label, g_value_get_boolean (value));
-      break;	  
+      break;
+    case PROP_EDITABLE:
+      gtk_label_set_editable (label, g_value_get_boolean (value));
+      break;
     case PROP_MNEMONIC_WIDGET:
       gtk_label_set_mnemonic_widget (label, (GtkWidget*) g_value_get_object (value));
       break;
@@ -1009,6 +1184,9 @@ gtk_label_get_property (GObject     *object,
     case PROP_SELECTABLE:
       g_value_set_boolean (value, gtk_label_get_selectable (label));
       break;
+    case PROP_EDITABLE:
+      g_value_set_boolean (value, gtk_label_get_editable (label));
+      break;
     case PROP_MNEMONIC_KEYVAL:
       g_value_set_uint (value, priv->mnemonic_keyval);
       break;
@@ -1074,6 +1252,8 @@ gtk_label_init (GtkLabel *label)
   priv->width_chars = -1;
   priv->max_width_chars = -1;
   priv->wrap_width = -1;
+  priv->text_size = 1;
+  priv->n_bytes = 0;
   priv->label = NULL;
 
   priv->jtype = GTK_JUSTIFY_LEFT;
@@ -1088,7 +1268,7 @@ gtk_label_init (GtkLabel *label)
 
   priv->mnemonic_keyval = GDK_VoidSymbol;
   priv->layout = NULL;
-  priv->text = NULL;
+  priv->text = g_malloc (priv->text_size);
   priv->attrs = NULL;
 
   priv->mnemonic_widget = NULL;
@@ -1816,11 +1996,13 @@ static void
 gtk_label_set_text_internal (GtkLabel *label,
 			     gchar    *str)
 {
-  GtkLabelPriv *priv = label->priv;
+  int tmp_pos = 0;
 
-  g_free (priv->text);
+  if (g_strcmp0 (label->priv->text, str) == 0)
+    return;
 
-  priv->text = str;
+  gtk_label_delete_text (label, 0, -1);
+  gtk_label_insert_text (label, str, strlen (str), &tmp_pos);
 
   gtk_label_select_region_index (label, 0, 0);
 }
@@ -1956,6 +2138,7 @@ gtk_label_recalculate (GtkLabel *label)
 
   gtk_label_clear_layout (label);
   gtk_label_clear_select_info (label);
+  gtk_label_clear_edit_info (label);
   gtk_widget_queue_resize (GTK_WIDGET (label));
 }
 
@@ -2981,6 +3164,13 @@ gtk_label_finalize (GObject *object)
   gtk_label_clear_links (label);
   g_free (priv->select_info);
 
+  if (priv->edit_info != NULL)
+    {
+      gtk_im_context_set_client_window (priv->edit_info->im_context, NULL);
+      g_object_unref (priv->edit_info->im_context);
+      g_free (priv->edit_info);
+    }
+
   G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
 }
 
@@ -3164,13 +3354,21 @@ get_label_wrap_width (GtkLabel *label)
 }
 
 static void
-gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
+gtk_label_ensure_layout (GtkLabel *label,
+			 gboolean guess_wrap_width,
+			 gboolean include_preedit)
 {
   GtkLabelPriv *priv = label->priv;
   GtkWidget *widget;
   PangoRectangle logical_rect;
   gboolean rtl;
 
+  include_preedit = include_preedit != 0;
+
+  if (priv->edit_info && priv->edit_info->preedit_length > 0 &&
+      include_preedit != priv->layout_includes_preedit)
+    gtk_label_clear_layout (label);
+
   widget = GTK_WIDGET (label);
 
   rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
@@ -3179,8 +3377,19 @@ gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
     {
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
       gdouble angle = gtk_label_get_angle (label);
+      gchar *preedit_string = 0;
+      gint preedit_length = 0;
+      PangoAttrList *preedit_attrs = NULL;
+      PangoAttrList *tmp_attrs = pango_attr_list_new ();
 
-      if (angle != 0.0 && !priv->select_info)
+      if (include_preedit && priv->edit_info)
+	{
+	  gtk_im_context_get_preedit_string (priv->edit_info->im_context,
+					     &preedit_string, &preedit_attrs, NULL);
+	  preedit_length = priv->edit_info->preedit_length;
+	}
+
+      if (angle != 0.0 && !priv->select_info && !priv->edit_info)
 	{
           PangoMatrix matrix = PANGO_MATRIX_INIT;
 
@@ -3202,11 +3411,38 @@ gtk_label_ensure_layout (GtkLabel *label, gboolean guess_wrap_width)
 	  priv->have_transform = FALSE;
 	}
 
-      priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
+      if (preedit_length)
+	{
+	  GString *tmp_string = g_string_new (NULL);
+	  
+	  g_string_prepend_len (tmp_string, priv->text, priv->n_bytes);
+	  g_string_insert (tmp_string,
+			   priv->select_info->selection_anchor,
+			   preedit_string);
+
+	  priv->layout = gtk_widget_create_pango_layout (widget, tmp_string->str);
+	  pango_attr_list_splice (tmp_attrs, preedit_attrs,
+				  priv->select_info->selection_anchor,
+				  preedit_length);
+
+	  g_string_free (tmp_string, TRUE);
+	}
+      else
+	{
+	  priv->layout = gtk_widget_create_pango_layout (widget, priv->text);
+	}
+
+      priv->layout_includes_preedit = include_preedit;
 
+      pango_layout_set_attributes (priv->layout, tmp_attrs);
       if (priv->effective_attrs)
 	pango_layout_set_attributes (priv->layout, priv->effective_attrs);
 
+      pango_attr_list_unref (tmp_attrs);
+      g_free (preedit_string);
+      if (preedit_attrs)
+	pango_attr_list_unref (preedit_attrs);
+
       gtk_label_rescan_links (label);
 
       switch (priv->jtype)
@@ -3380,7 +3616,7 @@ get_size_for_allocation (GtkLabel        *label,
   gint aux_size;
   gint text_height;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
   layout = pango_layout_copy (priv->layout);
 
   if (aux_info)
@@ -3436,7 +3672,7 @@ gtk_label_get_size (GtkSizeRequest *widget,
    * that stuff here instead of inside gtk_label_ensure_layout() */
   if (priv->wrap)
     gtk_label_clear_layout (label);
-  gtk_label_ensure_layout (label, TRUE);
+  gtk_label_ensure_layout (label, TRUE, TRUE);
 
   angle = gtk_label_get_angle (label);
 
@@ -3554,7 +3790,7 @@ gtk_label_get_size (GtkSizeRequest *widget,
    */
   if (priv->wrap)
     gtk_label_clear_layout (label);
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
 }
 
@@ -3592,7 +3828,10 @@ gtk_label_get_width_for_height (GtkSizeRequest *widget,
       gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
 
       if (priv->wrap)
-        gtk_label_clear_layout (label);
+	{
+	  gtk_label_check_cursor_blink (label);
+	  gtk_label_clear_layout (label);
+	}
 
       get_size_for_allocation (label, GTK_ORIENTATION_VERTICAL,
                                MAX (1, height - (ypad * 2)),
@@ -3656,7 +3895,7 @@ gtk_label_size_allocate (GtkWidget     *widget,
   if (priv->wrap)
     gtk_label_clear_layout (label);
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
   if (priv->ellipsize)
     {
@@ -3806,6 +4045,7 @@ gtk_label_style_set (GtkWidget *widget,
   GtkLabel *label = GTK_LABEL (widget);
 
   /* We have to clear the layout, fonts etc. may have changed */
+  gtk_label_check_cursor_blink (label);
   gtk_label_clear_layout (label);
   gtk_label_invalidate_wrap_width (label);
 }
@@ -3948,7 +4188,7 @@ get_cursor_direction (GtkLabel *label)
 
   g_assert (priv->select_info);
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
   for (l = pango_layout_get_lines_readonly (priv->layout); l; l = l->next)
     {
@@ -3994,7 +4234,7 @@ gtk_label_draw_cursor (GtkLabel  *label, gint xoffset, gint yoffset)
       keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
       cursor_direction = get_cursor_direction (label);
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label, FALSE, TRUE);
       
       pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
 				   &strong_pos, &weak_pos);
@@ -4080,7 +4320,7 @@ gtk_label_expose (GtkWidget      *widget,
   GtkLabelSelectionInfo *info = priv->select_info;
   gint x, y;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
   if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
       priv->text && (*priv->text != '\0'))
@@ -4153,7 +4393,8 @@ gtk_label_expose (GtkWidget      *widget,
           GdkColor *link_color;
           GdkColor *visited_link_color;
 
-          if (info->selectable && gtk_widget_has_focus (widget))
+          if (info->selectable && gtk_widget_has_focus (widget) &&
+	      priv->cursor_visible)
 	    gtk_label_draw_cursor (label, x, y);
 
           focus_link = gtk_label_get_focus_link (label);
@@ -4461,7 +4702,7 @@ get_layout_index (GtkLabel *label,
 
   *index = 0;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
   window_to_layout_coords (label, &x, &y);
 
@@ -4649,6 +4890,282 @@ out:
   return FALSE;
 }
 
+static void
+gtk_label_keymap_direction_changed (GdkKeymap *keymap,
+				    GtkLabel  *label)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+#define CURSOR_ON_MULTIPLIER 2
+#define CURSOR_OFF_MULTIPLIER 1
+#define CURSOR_PEND_MULTIPLIER 1.0
+#define CURSOR_DIVIDER 3
+
+static gboolean
+cursor_blinks (GtkLabel *label)
+{
+  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (label));
+  gboolean blink;
+
+  if (gtk_widget_has_focus (GTK_WIDGET (label)) && label->priv->select_info != NULL)
+    {
+      g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
+      return blink;
+    }
+  else
+    return FALSE;
+}
+
+static gint
+get_cursor_time (GtkLabel *label)
+{
+  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (label));
+  gint time;
+
+  g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
+
+  return time;
+}
+
+static gint
+get_cursor_blink_timeout (GtkLabel *label)
+{
+  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (label));
+  gint timeout;
+
+  g_object_get (settings, "gtk-cursor-blink-timeout", &timeout, NULL);
+
+  return timeout;
+}
+
+static void
+show_cursor (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (!priv->cursor_visible)
+    {
+      priv->cursor_visible = TRUE;
+
+      if (gtk_widget_has_focus (GTK_WIDGET (label)) && label->priv->select_info != NULL)
+	gtk_widget_queue_draw (GTK_WIDGET (label));
+    }
+}
+
+static void
+hide_cursor (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->cursor_visible)
+    {
+      priv->cursor_visible = FALSE;
+
+      if (gtk_widget_has_focus (GTK_WIDGET (label)) && label->priv->select_info != NULL)
+	gtk_widget_queue_draw (GTK_WIDGET (label));
+    }
+}
+
+/*
+ * Blink!
+ */
+static gint
+blink_cb (gpointer data)
+{
+  GtkLabel *label = data;
+  GtkLabelPriv *priv = label->priv;
+  gint blink_timeout;
+
+  priv = label->priv;
+
+  if (!gtk_widget_has_focus (GTK_WIDGET (label)))
+    {
+      g_warning ("GtkLabel - did not receive focus-out-event. If you\n"
+		 "connect a handler to this signal, it must return\n"
+		 "FALSE so the entry gets the event as well");
+
+      gtk_label_check_cursor_blink (label);
+
+      return FALSE;
+    }
+
+  g_assert (priv->select_info != NULL);
+
+  blink_timeout = get_cursor_blink_timeout (label);
+  if (priv->blink_time > 1000 * blink_timeout &&
+      blink_timeout < G_MAXINT/1000)
+    {
+      /* we've blinked enough without the user doing anything, stop blinking */
+      show_cursor (label);
+      priv->blink_timeout = 0;
+    }
+  if (priv->cursor_visible)
+    {
+      hide_cursor (label);
+      priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (label) *
+						     CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
+                                                     blink_cb, label);
+    }
+  else
+    {
+      show_cursor (label);
+      priv->blink_time += get_cursor_time (label);
+      priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (label) *
+						     CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
+                                                     blink_cb, label);
+    }
+
+  /* Remove ourselves */
+  return FALSE;
+}
+
+
+static void
+gtk_label_pend_cursor_blink (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (cursor_blinks (label))
+    {
+      if (priv->blink_timeout != 0)
+	g_source_remove (priv->blink_timeout);
+
+      priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (label) *
+						     CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
+                                                     blink_cb, label);
+      show_cursor (label);
+    }
+}
+
+static void
+gtk_label_check_cursor_blink (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (cursor_blinks (label))
+    {
+      if (!priv->blink_timeout)
+	{
+       	  show_cursor (label);
+	  priv->blink_timeout = gdk_threads_add_timeout (get_cursor_time (label) *
+							 CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
+						         blink_cb, label);
+	}
+    }
+  else
+    {
+      if (priv->blink_timeout)  
+	{ 
+	  g_source_remove (priv->blink_timeout);
+	  priv->blink_timeout = 0;
+	}
+
+      priv->cursor_visible = TRUE;
+    }
+}
+
+static void
+gtk_label_reset_blink_time (GtkLabel *label)
+{
+  label->priv->blink_time = 0;
+}
+
+static gint
+gtk_label_focus_in (GtkWidget     *widget,
+		    GdkEventFocus *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info != NULL &&
+      priv->edit_info->editable)
+    {
+      gtk_widget_queue_draw (widget);
+
+      priv->edit_info->need_im_reset = TRUE;
+      gtk_im_context_focus_in (priv->edit_info->im_context);
+
+      priv->edit_info->direction_handler_id =
+	g_signal_connect (gdk_keymap_get_default (),
+			  "direction_changed",
+			  G_CALLBACK (gtk_label_keymap_direction_changed), label);
+
+      gtk_label_reset_blink_time (label);
+      gtk_label_check_cursor_blink (label);
+    }
+
+  return FALSE;
+}
+
+static gint
+gtk_label_focus_out (GtkWidget     *widget,
+		     GdkEventFocus *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info != NULL &&
+      priv->edit_info->editable)
+    {
+      gtk_widget_queue_draw (widget);
+
+      priv->edit_info->need_im_reset = TRUE;
+      gtk_im_context_focus_out (priv->edit_info->im_context);
+
+      gtk_label_check_cursor_blink (label);
+      g_signal_handler_disconnect (gdk_keymap_get_default (),
+				   priv->edit_info->direction_handler_id);
+    }
+
+  return FALSE;
+}
+
+static gint
+gtk_label_key_press (GtkWidget   *widget,
+		     GdkEventKey *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info != NULL)
+    {
+      gtk_label_reset_blink_time (label);
+      gtk_label_pend_cursor_blink (label);
+
+      if (gtk_im_context_filter_keypress (priv->edit_info->im_context, event))
+	{
+	  priv->edit_info->need_im_reset = TRUE;
+	  return TRUE;
+	}
+    }
+
+  if (GTK_WIDGET_CLASS (gtk_label_parent_class)->key_press_event (widget, event))
+    /* activate key bindings */
+    return TRUE;
+
+  return FALSE;
+}
+
+static gint
+gtk_label_key_release (GtkWidget   *widget,
+		       GdkEventKey *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info != NULL)
+    {
+      if (gtk_im_context_filter_keypress (priv->edit_info->im_context, event))
+	{
+	  priv->edit_info->need_im_reset = TRUE;
+	  return TRUE;
+	}
+    }
+
+  return GTK_WIDGET_CLASS (gtk_label_parent_class)->key_release_event (widget, event);
+}
+
 static gboolean
 gtk_label_button_press (GtkWidget      *widget,
                         GdkEventButton *event)
@@ -4683,6 +5200,8 @@ gtk_label_button_press (GtkWidget      *widget,
   info->in_drag = FALSE;
   info->select_words = FALSE;
 
+  gtk_label_reset_blink_time (label);
+
   if (event->button == 1)
     {
       if (!gtk_widget_has_focus (widget))
@@ -5060,7 +5579,10 @@ gtk_label_create_window (GtkLabel *label)
     GDK_BUTTON_PRESS_MASK        |
     GDK_BUTTON_RELEASE_MASK      |
     GDK_LEAVE_NOTIFY_MASK        |
+    GDK_ENTER_NOTIFY_MASK        |
     GDK_BUTTON_MOTION_MASK       |
+    GDK_BUTTON1_MOTION_MASK      |
+    GDK_BUTTON3_MOTION_MASK      |
     GDK_POINTER_MOTION_MASK      |
     GDK_POINTER_MOTION_HINT_MASK;
   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
@@ -5096,50 +5618,233 @@ gtk_label_destroy_window (GtkLabel *label)
 }
 
 static void
-gtk_label_ensure_select_info (GtkLabel *label)
+gtk_label_reset_im_context (GtkLabel *label)
 {
   GtkLabelPriv *priv = label->priv;
 
-  if (priv->select_info == NULL)
+  if (priv->edit_info != NULL &&
+      priv->edit_info->need_im_reset)
     {
-      priv->select_info = g_new0 (GtkLabelSelectionInfo, 1);
-
-      gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
-
-      if (gtk_widget_get_realized (GTK_WIDGET (label)))
-	gtk_label_create_window (label);
-
-      if (gtk_widget_get_mapped (GTK_WIDGET (label)))
-        gdk_window_show (priv->select_info->window);
+      priv->edit_info->need_im_reset = 0;
+      gtk_im_context_reset (priv->edit_info->im_context);
     }
 }
 
-static void
-gtk_label_clear_select_info (GtkLabel *label)
+static gboolean
+im_context_retrieve_surrounding_cb (GtkIMContext *context,
+				    GtkLabel  *label)
 {
   GtkLabelPriv *priv = label->priv;
+  gtk_im_context_set_surrounding (context,
+				  priv->text,
+				  strlen (priv->text) + 1,
+				  priv->select_info->selection_end);
 
-  if (priv->select_info == NULL)
-    return;
+  return TRUE;
+}
 
-  if (!priv->select_info->selectable && !priv->select_info->links)
-    {
-      gtk_label_destroy_window (label);
+static gboolean
+im_context_delete_surrounding_cb (GtkIMContext *context,
+				  gint          offset,
+				  gint          n_chars,
+				  GtkLabel     *label)
+{
+  GtkLabelPriv *priv = label->priv;
+  gint current_pos;
 
-      g_free (priv->select_info);
-      priv->select_info = NULL;
+  current_pos = g_utf8_pointer_to_offset (priv->text,
+					  priv->text + priv->select_info->selection_anchor);
+  gtk_editable_delete_text (GTK_EDITABLE (label), 
+			    current_pos + offset, 
+			    current_pos + offset + n_chars);
 
-      gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
-    }
+  return TRUE;
 }
 
-/**
- * gtk_label_set_selectable:
- * @label: a #GtkLabel
- * @setting: %TRUE to allow selecting text in the label
- *
- * Selectable labels allow the user to select text from the label, for
- * copy-and-paste.
+static void 
+im_context_preedit_changed_cb (GtkIMContext *context,
+			       GtkLabel     *label)
+{
+  GtkLabelPriv *priv = label->priv;
+  gchar *preedit_string;
+  gint cursor_pos;
+
+  gtk_im_context_get_preedit_string (context,
+				     &preedit_string, NULL,
+				     &cursor_pos);
+  priv->edit_info->preedit_length = strlen (preedit_string);
+  cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
+  priv->edit_info->preedit_cursor = cursor_pos;
+  g_free (preedit_string);
+
+  gtk_label_check_cursor_blink (label);
+  gtk_label_clear_layout (label);
+  gtk_widget_queue_resize (GTK_WIDGET (label));
+}
+
+static void
+gtk_label_enter_text (GtkLabel *label,
+		      const gchar    *str)
+{
+  GtkLabelPriv *priv = label->priv;
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint tmp_pos;
+  gboolean old_need_im_reset;
+
+  /* Never reset the im while commiting, as that resets possible im state */
+  old_need_im_reset = priv->edit_info->need_im_reset;
+  priv->edit_info->need_im_reset = FALSE;
+
+  if (priv->select_info->selection_end != priv->select_info->selection_anchor)
+    gtk_editable_delete_selection (editable);
+  else
+    {
+      if (priv->edit_info->overwrite_mode)
+        gtk_label_delete_from_cursor (label, GTK_DELETE_CHARS, 1);
+    }
+  
+  tmp_pos = g_utf8_pointer_to_offset (priv->text,
+                                      priv->text + priv->select_info->selection_anchor);
+  gtk_editable_insert_text (GTK_EDITABLE (label), str, strlen (str), &tmp_pos);
+  tmp_pos = g_utf8_offset_to_pointer (priv->text, tmp_pos) - priv->text;
+  gtk_label_select_region_index (label, tmp_pos, tmp_pos);
+
+  priv->edit_info->need_im_reset = old_need_im_reset;
+}
+
+static void
+im_context_commit_cb (GtkIMContext *context,
+		      const gchar  *str,
+		      GtkLabel     *label)
+{
+  gtk_label_enter_text (label, str);
+}
+
+static void
+gtk_label_ensure_edit_info (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  gtk_label_ensure_select_info (label);
+
+  if (priv->edit_info == NULL)
+    {
+      GtkIMContext *im_context;
+      
+      priv->edit_info = g_new0 (GtkLabelEditableInfo, 1);
+      priv->edit_info->im_context = im_context = gtk_im_multicontext_new ();
+      priv->edit_info->need_im_reset = FALSE;
+
+      g_signal_connect (G_OBJECT (im_context), "commit",
+			G_CALLBACK (im_context_commit_cb), label);
+      g_signal_connect (G_OBJECT (im_context), "preedit_changed",
+			G_CALLBACK (im_context_preedit_changed_cb), label);
+      g_signal_connect (G_OBJECT (im_context), "retrieve_surrounding",
+			G_CALLBACK (im_context_retrieve_surrounding_cb), label);
+      g_signal_connect (G_OBJECT (im_context), "delete_surrounding",
+			G_CALLBACK (im_context_delete_surrounding_cb), label);
+
+      gtk_im_context_set_client_window (im_context,
+					priv->select_info->window);
+    }
+}
+
+static void
+gtk_label_clear_edit_info (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info == NULL)
+    return;
+
+  if (!priv->edit_info->editable)
+    {
+      gtk_im_context_set_client_window (priv->edit_info->im_context, NULL);
+      g_object_unref (priv->edit_info->im_context);
+      g_free (priv->edit_info);
+      priv->edit_info = NULL;
+    }
+}
+
+static void
+gtk_label_ensure_select_info (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->select_info == NULL)
+    {
+      priv->select_info = g_new0 (GtkLabelSelectionInfo, 1);
+
+      gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
+
+      if (gtk_widget_get_realized (GTK_WIDGET (label)))
+	gtk_label_create_window (label);
+
+      if (gtk_widget_get_mapped (GTK_WIDGET (label)))
+        gdk_window_show (priv->select_info->window);
+    }
+}
+
+static void
+gtk_label_clear_select_info (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->select_info == NULL)
+    return;
+
+  if (!priv->select_info->selectable && !priv->select_info->links)
+    {
+      gtk_label_destroy_window (label);
+
+      g_free (priv->select_info);
+      priv->select_info = NULL;
+
+      gtk_widget_set_can_focus (GTK_WIDGET (label), FALSE);
+    }
+}
+
+void
+gtk_label_set_editable (GtkLabel *label,
+			gboolean setting)
+{
+  GtkLabelPriv *priv = label->priv;
+  gboolean old_setting, old_selectable;
+
+  setting = setting != FALSE;
+  old_setting = priv->edit_info && priv->edit_info->editable;
+  old_selectable = gtk_label_get_selectable (label);
+
+  if (setting)
+    {
+      gtk_label_ensure_edit_info (label);
+      priv->edit_info->editable = TRUE;
+      priv->edit_info->old_selectable = old_selectable;
+    }
+  else
+    {
+      old_selectable = priv->edit_info->old_selectable;
+      priv->edit_info->editable = FALSE;
+      gtk_label_clear_edit_info (label);
+    }
+
+  if (setting != old_setting)
+    {
+      g_object_freeze_notify (G_OBJECT (label));
+      g_object_notify (G_OBJECT (label), "editable");
+      gtk_label_set_selectable (label, setting ? setting : old_selectable);
+      g_object_thaw_notify (G_OBJECT (label));
+      gtk_widget_queue_draw (GTK_WIDGET (label));
+    }
+}
+
+/**
+ * gtk_label_set_selectable:
+ * @label: a #GtkLabel
+ * @setting: %TRUE to allow selecting text in the label
+ *
+ * Selectable labels allow the user to select text from the label, for
+ * copy-and-paste.
  **/
 void
 gtk_label_set_selectable (GtkLabel *label,
@@ -5204,6 +5909,18 @@ gtk_label_get_selectable (GtkLabel *label)
   return priv->select_info && priv->select_info->selectable;
 }
 
+gboolean
+gtk_label_get_editable (GtkLabel *label)
+{
+  GtkLabelPriv *priv;
+
+  g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
+
+  priv = label->priv;
+
+  return priv->edit_info && priv->edit_info->editable;
+}
+
 static void
 free_angle (gpointer angle)
 {
@@ -5542,7 +6259,7 @@ gtk_label_get_layout (GtkLabel *label)
 
   priv = label->priv;
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
   return priv->layout;
 }
@@ -5569,7 +6286,7 @@ gtk_label_get_layout_offsets (GtkLabel *label,
 {
   g_return_if_fail (GTK_IS_LABEL (label));
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
 
   get_layout_location (label, x, y);
 }
@@ -5719,7 +6436,7 @@ get_better_cursor (GtkLabel *label,
 		"gtk-split-cursor", &split_cursor,
 		NULL);
 
-  gtk_label_ensure_layout (label, FALSE);
+  gtk_label_ensure_layout (label, FALSE, TRUE);
   
   pango_layout_get_cursor_pos (priv->layout, index,
 			       &strong_pos, &weak_pos);
@@ -5760,7 +6477,7 @@ gtk_label_move_logically (GtkLabel *label,
       gint n_attrs;
       gint length;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label, FALSE, FALSE);
       
       length = g_utf8_strlen (priv->text, -1);
 
@@ -5805,7 +6522,7 @@ gtk_label_move_visually (GtkLabel *label,
       gboolean split_cursor;
       gboolean strong;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label, FALSE, FALSE);
 
       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
 		    "gtk-split-cursor", &split_cursor,
@@ -5859,7 +6576,7 @@ gtk_label_move_forward_word (GtkLabel *label,
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label, FALSE, FALSE);
 
       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
 
@@ -5888,7 +6605,7 @@ gtk_label_move_backward_word (GtkLabel *label,
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label, FALSE);
+      gtk_label_ensure_layout (label, FALSE, FALSE);
 
       pango_layout_get_log_attrs (priv->layout, &log_attrs, &n_attrs);
 
@@ -6187,15 +6904,24 @@ gtk_label_popup_menu (GtkWidget *widget)
   return TRUE;
 }
 
+typedef struct
+{
+  GtkLabel *label;
+  GdkEventButton *event;
+} PopupInfo;
+
 static void
-gtk_label_do_popup (GtkLabel       *label,
-                    GdkEventButton *event)
+popup_targets_received (GtkClipboard     *clipboard,
+			GtkSelectionData *data,
+                        PopupInfo        *info)
 {
+  GtkLabel *label = info->label;
+  GdkEventButton *event = info->event;
   GtkLabelPriv *priv = label->priv;
   GtkWidget *menuitem;
   GtkWidget *menu;
   GtkWidget *image;
-  gboolean have_selection;
+  gboolean have_selection, clipboard_contains_text, can_edit;
   GtkLabelLink *link;
 
   if (!priv->select_info)
@@ -6210,6 +6936,8 @@ gtk_label_do_popup (GtkLabel       *label,
 
   have_selection =
     priv->select_info->selection_anchor != priv->select_info->selection_end;
+  clipboard_contains_text = gtk_selection_data_targets_include_text (data);
+  can_edit = priv->edit_info != NULL && priv->edit_info->editable;
 
   if (event)
     {
@@ -6249,14 +6977,19 @@ gtk_label_do_popup (GtkLabel       *label,
     }
   else
     {
-      append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard", FALSE);
-      append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard", have_selection);
-      append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard", FALSE);
-  
+      append_action_signal (label, menu, GTK_STOCK_CUT, "cut-clipboard",
+			    have_selection && can_edit);
+      append_action_signal (label, menu, GTK_STOCK_COPY, "copy-clipboard",
+			    have_selection);
+      append_action_signal (label, menu, GTK_STOCK_PASTE, "paste-clipboard",
+			    clipboard_contains_text && can_edit);
+
       menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
-      gtk_widget_set_sensitive (menuitem, FALSE);
+      gtk_widget_set_sensitive (menuitem, have_selection);
       gtk_widget_show (menuitem);
       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+      g_signal_connect_swapped (menuitem, "activate",
+				G_CALLBACK (gtk_editable_delete_selection), label);
 
       menuitem = gtk_separator_menu_item_new ();
       gtk_widget_show (menuitem);
@@ -6267,6 +7000,25 @@ gtk_label_do_popup (GtkLabel       *label,
 			        G_CALLBACK (gtk_label_select_all), label);
       gtk_widget_show (menuitem);
       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+      if (can_edit)
+	{
+	  GtkWidget *submenu;
+
+	  menuitem = gtk_separator_menu_item_new ();
+	  gtk_widget_show (menuitem);
+	  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+	  menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
+	  gtk_widget_show (menuitem);
+	  submenu = gtk_menu_new ();
+	  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
+
+	  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+	  gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (priv->edit_info->im_context),
+						GTK_MENU_SHELL (submenu));
+	}
     }
 
   g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
@@ -6285,6 +7037,24 @@ gtk_label_do_popup (GtkLabel       *label,
 }
 
 static void
+gtk_label_do_popup (GtkLabel *label,
+		    GdkEventButton *event)
+{
+  PopupInfo *info = g_slice_new0 (PopupInfo);
+
+  info->label = g_object_ref (label);
+
+  if (event)
+    info->event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
+
+  gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (label),
+							    GDK_SELECTION_CLIPBOARD),
+				  gdk_atom_intern ("TARGETS", FALSE),
+				  (GtkClipboardReceivedFunc) popup_targets_received,
+				  info);
+}
+		    
+static void
 gtk_label_clear_links (GtkLabel *label)
 {
   GtkLabelPriv *priv = label->priv;
@@ -6567,3 +7337,483 @@ gtk_label_query_tooltip (GtkWidget  *widget,
                                                                    keyboard_tip,
                                                                    tooltip);
 }
+
+static void
+gtk_label_toggle_overwrite (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info == NULL)
+    return;
+
+  priv->edit_info->overwrite_mode = !priv->edit_info->overwrite_mode;
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+static void
+paste_received (GtkClipboard *clipboard,
+		const gchar  *text,
+		gpointer      data)
+{
+  GtkLabel *label = GTK_LABEL (data);
+  GtkLabelPriv *priv = label->priv;
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint tmp_pos;
+
+  if (text)
+    {
+      if (priv->select_info->selection_end != priv->select_info->selection_anchor)
+	gtk_editable_delete_selection (editable);
+
+      tmp_pos = g_utf8_pointer_to_offset (priv->text,
+					  priv->text + priv->select_info->selection_anchor);
+      gtk_editable_insert_text (GTK_EDITABLE (label), text, strlen (text), &tmp_pos);
+      tmp_pos = g_utf8_offset_to_pointer (priv->text, tmp_pos) - priv->text;
+      gtk_label_select_region_index (label, tmp_pos, tmp_pos);
+    }
+
+  /* unref what we did in gtk_label_paste() */
+  g_object_unref (label);
+}
+
+static void
+gtk_label_paste (GtkLabel *label,
+		 GdkAtom   selection)
+{
+  g_object_ref (label);
+  gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (label), selection),
+			      paste_received, label);
+}
+
+static void
+gtk_label_paste_clipboard (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->edit_info != NULL)
+    gtk_label_paste (label, GDK_NONE);
+}
+
+static void
+gtk_label_cut_clipboard (GtkLabel *label)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (priv->text && priv->edit_info && priv->select_info)
+    {
+      gint start, end;
+      gint len;
+
+      start = MIN (priv->select_info->selection_anchor,
+                   priv->select_info->selection_end);
+      end = MAX (priv->select_info->selection_anchor,
+                 priv->select_info->selection_end);
+
+      len = strlen (priv->text);
+
+      if (end > len)
+        end = len;
+
+      if (start > len)
+        start = len;
+
+      if (start != end)
+	{
+	  gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+				  priv->text + start, end - start);
+          start = g_utf8_pointer_to_offset (priv->text, priv->text + start);
+          end = g_utf8_pointer_to_offset (priv->text, priv->text + end);
+	  gtk_editable_delete_text (GTK_EDITABLE (label), start, end);
+	}
+    }
+}
+
+static void
+gtk_label_delete_from_cursor (GtkLabel      *label,
+			      GtkDeleteType  type,
+			      gint           count)
+{
+  GtkLabelPriv *priv = label->priv;
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint start_pos = priv->select_info->selection_anchor;
+  gint end_pos = priv->select_info->selection_anchor;
+
+  if (priv->edit_info == NULL)
+    return;
+
+  gtk_label_reset_im_context (label);
+
+  if (priv->select_info->selection_anchor != priv->select_info->selection_end)
+    {
+      gtk_editable_delete_selection (editable);
+      return;
+    }
+  
+  switch (type)
+    {
+    case GTK_DELETE_CHARS:
+      end_pos = gtk_label_move_logically (label, start_pos, count);
+      start_pos = g_utf8_pointer_to_offset (priv->text, priv->text + start_pos);
+      end_pos = g_utf8_pointer_to_offset (priv->text, priv->text + end_pos);
+      gtk_editable_delete_text (GTK_EDITABLE (label),
+				MIN (start_pos, end_pos),
+				MAX (start_pos, end_pos));
+      break;
+    case GTK_DELETE_WORDS:
+      if (count < 0)
+	{
+	  /* Move to end of current word, or if not on a word, end of previous word */
+	  end_pos = gtk_label_move_backward_word (label, end_pos);
+	  end_pos = gtk_label_move_forward_word (label, end_pos);
+	}
+      else if (count > 0)
+	{
+	  /* Move to beginning of current word, or if not on a word, begining of next word */
+	  start_pos = gtk_label_move_forward_word (label, start_pos);
+	  start_pos = gtk_label_move_backward_word (label, start_pos);
+	}
+
+      /* Fall through */
+    case GTK_DELETE_WORD_ENDS:
+      while (count < 0)
+	{
+	  start_pos = gtk_label_move_backward_word (label, start_pos);
+	  count++;
+	}
+      while (count > 0)
+	{
+	  end_pos = gtk_label_move_forward_word (label, end_pos);
+	  count--;
+	}
+      start_pos = g_utf8_pointer_to_offset (priv->text, priv->text + start_pos);
+      end_pos = g_utf8_pointer_to_offset (priv->text, priv->text + end_pos);
+ 
+      gtk_editable_delete_text (GTK_EDITABLE (label), start_pos, end_pos);
+      break;
+    case GTK_DELETE_DISPLAY_LINE_ENDS:
+    case GTK_DELETE_PARAGRAPH_ENDS:
+      end_pos = g_utf8_pointer_to_offset (priv->text,
+					  priv->text + priv->select_info->selection_anchor);
+      if (count < 0)
+	gtk_editable_delete_text (GTK_EDITABLE (label), 0, end_pos);
+      else
+	gtk_editable_delete_text (GTK_EDITABLE (label), end_pos, -1);
+      break;
+    case GTK_DELETE_DISPLAY_LINES:
+    case GTK_DELETE_PARAGRAPHS:
+      gtk_editable_delete_text (GTK_EDITABLE (label), 0, -1);  
+      break;
+    case GTK_DELETE_WHITESPACE:
+      /* TODO eel_editable_label_delete_whitespace (label); */
+      break;
+    }
+
+  gtk_label_pend_cursor_blink (label);
+}
+
+static void
+editable_real_set_position (GtkEditable *editable,
+			    gint         position)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  int text_length;
+  int index;
+
+  text_length = g_utf8_strlen (priv->text, -1);
+  
+  if (position < 0 || position > text_length)
+    position = text_length;
+
+  index = g_utf8_offset_to_pointer (priv->text, position) - priv->text;
+  
+  if (index != priv->select_info->selection_anchor ||
+      index != priv->select_info->selection_end)
+    {
+      gtk_label_select_region_index (label, index, index);
+    }
+}
+
+static gint
+editable_get_position (GtkEditable *editable)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+
+  return g_utf8_pointer_to_offset (priv->text,
+				   priv->text + priv->select_info->selection_anchor);
+}
+
+static void
+editable_set_selection_bounds (GtkEditable *editable,
+			       gint         start,
+			       gint         end)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  int text_length;
+  gint start_index, end_index;
+
+  text_length = g_utf8_strlen (priv->text, -1);
+
+  if (end < 0 || end > text_length)
+    end = text_length;
+  if (start < 0)
+    start = text_length;
+  if (start > text_length)
+    start = text_length;
+
+  start_index = g_utf8_offset_to_pointer (priv->text, start) - priv->text;
+  end_index = g_utf8_offset_to_pointer (priv->text, end) - priv->text;
+
+  gtk_label_select_region_index (label, start_index, end_index);
+}
+
+static gboolean
+editable_get_selection_bounds (GtkEditable *editable,
+			       gint *start,
+			       gint *end)
+{
+  return gtk_label_get_selection_bounds (GTK_LABEL (editable),
+					 start, end);
+}
+
+static gchar *
+editable_get_chars (GtkEditable   *editable,
+		    gint           start_pos,
+		    gint           end_pos)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  int text_length;
+  gint start_index, end_index;
+
+  text_length = g_utf8_strlen (priv->text, -1);
+
+  if (end_pos < 0 || end_pos > text_length)
+    end_pos = text_length;
+  if (start_pos < 0)
+    start_pos = 0;
+  if (start_pos > end_pos)
+    start_pos = end_pos;
+
+  start_index = g_utf8_offset_to_pointer (priv->text, start_pos) - priv->text;
+  end_index = g_utf8_offset_to_pointer (priv->text, end_pos) - priv->text;
+
+  return g_strndup (priv->text + start_index, end_index - start_index);
+}
+
+static void
+gtk_label_delete_text (GtkLabel *label,
+		       int start_pos,
+		       int end_pos)
+{
+  GtkLabelPriv *priv = label->priv;
+  int anchor, end;
+
+  if (priv->select_info != NULL)
+    {
+      anchor = priv->select_info->selection_anchor;
+      end = priv->select_info->selection_end;
+    }
+  else
+    {
+      anchor = 0;
+      end = 0;
+    }
+
+  if (start_pos < 0)
+    start_pos = 0;
+  if (end_pos < 0 || end_pos > priv->n_bytes)
+    end_pos = priv->n_bytes;
+  
+  if (start_pos < end_pos)
+    {
+      g_memmove (priv->text + start_pos, priv->text + end_pos, priv->n_bytes + 1 - end_pos);
+      priv->n_bytes -= (end_pos - start_pos);
+
+      if (anchor > start_pos)
+	anchor -= MIN (anchor, end_pos) - start_pos;
+
+      if (end > start_pos)
+	end -= MIN (end, end_pos) - start_pos;
+
+      /* We might have changed the selection */
+      gtk_label_select_region_index (label, anchor, end);
+      
+      gtk_label_clear_layout (label);
+      gtk_label_check_cursor_blink (label);
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+
+      g_object_notify (G_OBJECT (label), "label");
+      g_signal_emit_by_name (GTK_EDITABLE (label), "changed");
+    }
+}
+
+static void
+editable_delete_text (GtkEditable *editable,
+		      gint         start_pos,
+		      gint         end_pos)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  int text_length;
+  gint start_index, end_index;
+
+  text_length = g_utf8_strlen (priv->text, -1);
+
+  if (end_pos < 0 || end_pos > text_length)
+    end_pos = text_length;
+  if (start_pos < 0)
+    start_pos = 0;
+  if (start_pos > end_pos)
+    start_pos = end_pos;
+
+  start_index = g_utf8_offset_to_pointer (priv->text, start_pos) - priv->text;
+  end_index = g_utf8_offset_to_pointer (priv->text, end_pos) - priv->text;
+
+  gtk_label_delete_text (label, start_index, end_index);
+}
+
+static void
+gtk_label_insert_text (GtkLabel *label,
+		       const gchar *new_text,
+		       gint         new_text_length,
+		       gint        *index)
+{
+  GtkLabelPriv *priv = label->priv;
+
+  if (new_text_length + priv->n_bytes + 1 > priv->text_size)
+    {
+      while (new_text_length + priv->n_bytes + 1 > priv->text_size)
+	{
+	  if (priv->text_size == 0)
+	    priv->text_size = 16;
+	  else
+	    priv->text_size *= 2;
+	}
+
+      priv->text = g_realloc (priv->text, priv->text_size);
+    }
+
+  g_object_freeze_notify (G_OBJECT (label));
+
+  g_memmove (priv->text + *index + new_text_length,
+	     priv->text + *index,
+	     priv->n_bytes - *index);
+  memcpy (priv->text + *index, new_text, new_text_length);
+
+  priv->n_bytes += new_text_length;
+
+  /* NUL terminate for safety and convenience */
+  priv->text[priv->n_bytes] = '\0';
+
+  g_object_notify (G_OBJECT (label), "label");
+
+  if (priv->select_info != NULL)
+    {
+      if (priv->select_info->selection_anchor > *index)
+	{
+	  g_object_notify (G_OBJECT (label), "cursor_position");
+	  g_object_notify (G_OBJECT (label), "selection_bound");
+	  priv->select_info->selection_anchor += new_text_length;
+	}
+  
+      if (priv->select_info->selection_end > *index)
+	{
+	  priv->select_info->selection_end += new_text_length;
+	  g_object_notify (G_OBJECT (label), "selection_bound");
+	}
+    }
+
+  *index += new_text_length;
+
+  gtk_label_clear_layout (label);
+  gtk_label_check_cursor_blink (label);
+  gtk_widget_queue_resize (GTK_WIDGET (label));
+
+  g_object_thaw_notify (G_OBJECT (label));
+  g_signal_emit_by_name (label, "changed");
+}
+
+static void
+editable_insert_text (GtkEditable *editable,
+		      const gchar *new_text,
+		      gint         new_text_length,
+		      gint        *position)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  gint index;
+
+  if (new_text_length < 0)
+    new_text_length = strlen (new_text);
+
+  index = g_utf8_offset_to_pointer (priv->text, *position) - priv->text;
+
+  gtk_label_insert_text (label,
+			 new_text,
+			 new_text_length,
+			 &index);
+  
+  *position = g_utf8_pointer_to_offset (priv->text, priv->text + index);
+}
+
+static void
+editable_delete_text_emit (GtkEditable *editable,
+			   gint         start_pos,
+			   gint         end_pos)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  int text_length;
+
+  text_length = g_utf8_strlen (priv->text, -1);
+
+  if (end_pos < 0 || end_pos > text_length)
+    end_pos = text_length;
+  if (start_pos < 0)
+    start_pos = 0;
+  if (start_pos > end_pos)
+    start_pos = end_pos;
+
+  g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
+}
+
+static void
+editable_insert_text_emit  (GtkEditable *editable,
+			    const gchar *new_text,
+			    gint         new_text_length,
+			    gint        *position)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  GtkLabelPriv *priv = label->priv;
+  gchar *text;
+  int text_length;
+
+  text_length = g_utf8_strlen (priv->text, -1);
+
+  if (*position < 0 || *position > text_length)
+    *position = text_length;
+
+  text = g_new (gchar, new_text_length + 1);
+  text[new_text_length] = '\0';
+  strncpy (text, new_text, new_text_length);
+
+  g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position);
+}
+
+static void
+gtk_label_editable_init (GtkEditableClass *iface)
+{
+  iface->do_insert_text = editable_insert_text_emit;
+  iface->do_delete_text = editable_delete_text_emit;
+  iface->insert_text = editable_insert_text;
+  iface->delete_text = editable_delete_text;
+  iface->get_chars = editable_get_chars;
+  iface->set_selection_bounds = editable_set_selection_bounds;
+  iface->get_selection_bounds = editable_get_selection_bounds;
+  iface->set_position = editable_real_set_position;
+  iface->get_position = editable_get_position;
+}
+
diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h
index 3fa3b65..bd1dff9 100644
--- a/gtk/gtklabel.h
+++ b/gtk/gtklabel.h
@@ -64,11 +64,20 @@ struct _GtkLabelClass
 {
   GtkMiscClass parent_class;
 
-  void (* move_cursor)     (GtkLabel       *label,
-			    GtkMovementStep step,
-			    gint            count,
-			    gboolean        extend_selection);
-  void (* copy_clipboard)  (GtkLabel       *label);
+  void (* move_cursor)        (GtkLabel       *label,
+			       GtkMovementStep step,
+			       gint            count,
+			       gboolean        extend_selection);
+  void (* insert_at_cursor)   (GtkLabel       *label,
+			       const gchar    *str);
+  void (* delete_from_cursor) (GtkLabel       *label,
+			       GtkDeleteType   type,
+			       gint            count);
+
+  void (* cut_clipboard)      (GtkLabel  *label);
+  void (* copy_clipboard)     (GtkLabel  *label);
+  void (* paste_clipboard)    (GtkLabel  *label);
+  void (* toggle_overwrite)   (GtkLabel  *label);
 
   /* Hook to customize right-click popup for selectable labels */
   void (* populate_popup)   (GtkLabel       *label,
@@ -135,6 +144,9 @@ PangoWrapMode gtk_label_get_line_wrap_mode        (GtkLabel         *label);
 void     gtk_label_set_selectable                 (GtkLabel         *label,
 						   gboolean          setting);
 gboolean gtk_label_get_selectable                 (GtkLabel         *label);
+void     gtk_label_set_editable                   (GtkLabel         *label,
+						   gboolean          setting);
+gboolean gtk_label_get_editable                   (GtkLabel         *label);
 void     gtk_label_set_angle                      (GtkLabel         *label,
 						   gdouble           angle);
 gdouble  gtk_label_get_angle                      (GtkLabel         *label);
diff --git a/tests/testgtk.c b/tests/testgtk.c
index 977164d..a46f1e8 100644
--- a/tests/testgtk.c
+++ b/tests/testgtk.c
@@ -2461,6 +2461,59 @@ create_selectable_control (GtkWidget *widget)
 }
 
 static void
+set_editable_recursive (GtkWidget *widget,
+			gboolean   setting)
+{
+  if (GTK_IS_CONTAINER (widget))
+    {
+      GList *children;
+      GList *tmp;
+      
+      children = gtk_container_get_children (GTK_CONTAINER (widget));
+      tmp = children;
+      while (tmp)
+        {
+          set_editable_recursive (tmp->data, setting);
+
+          tmp = tmp->next;
+        }
+      g_list_free (children);
+    }
+  else if (GTK_IS_LABEL (widget))
+    {
+      gtk_label_set_editable (GTK_LABEL (widget), setting);
+    }
+}
+
+static void
+editable_toggled (GtkWidget *toggle,
+                    GtkWidget *widget)
+{
+  set_editable_recursive (widget,
+			  GTK_TOGGLE_BUTTON (toggle)->active);
+}
+
+static GtkWidget*
+create_editable_control (GtkWidget *widget)
+{
+  GtkWidget *button;
+
+  button = gtk_toggle_button_new_with_label ("Editable");  
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+                                FALSE);
+  
+  g_signal_connect (button,
+                    "toggled",
+                    G_CALLBACK (editable_toggled),
+                    widget);
+  
+  gtk_widget_show_all (button);
+
+  return button;
+}
+
+static void
 dialog_response (GtkWidget *dialog, gint response_id, GtkLabel *label)
 {
   const gchar *text;
@@ -2535,6 +2588,10 @@ void create_labels (GtkWidget *widget)
       button = create_selectable_control (hbox);
 
       gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+      button = create_editable_control (hbox);
+
+      gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
       
       vbox = gtk_vbox_new (FALSE, 5);
       
