Index: gtk/gtk.symbols
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtk.symbols,v
retrieving revision 1.134
diff -u -r1.134 gtk.symbols
--- gtk/gtk.symbols	16 Nov 2006 12:56:29 -0000	1.134
+++ gtk/gtk.symbols	24 Nov 2006 00:53:14 -0000
@@ -1978,6 +1978,7 @@
 gtk_label_get
 gtk_label_parse_uline
 #endif
+gtk_label_get_editable
 gtk_label_get_angle
 gtk_label_get_attributes
 gtk_label_get_ellipsize
@@ -2001,6 +2002,7 @@
 gtk_label_new
 gtk_label_new_with_mnemonic
 gtk_label_select_region
+gtk_label_set_editable
 gtk_label_set_angle
 gtk_label_set_attributes
 gtk_label_set_ellipsize
Index: gtk/gtklabel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.c,v
retrieving revision 1.204
diff -u -r1.204 gtklabel.c
--- gtk/gtklabel.c	16 Nov 2006 12:56:29 -0000	1.204
+++ gtk/gtklabel.c	24 Nov 2006 00:53:19 -0000
@@ -44,6 +44,7 @@
 #include "gtkbindings.h"
 #include "gtkprivate.h"
 #include "gtkalias.h"
+#include "gtkimmulticontext.h"
 
 #define GTK_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_LABEL, GtkLabelPrivate))
 
@@ -51,6 +52,18 @@
 {
   gint width_chars;
   gint max_width_chars;
+
+  GtkIMContext *im_context;
+  guint overwrite_mode : 1;
+  gboolean need_im_reset;
+  int preedit_length;	/* length of preedit string, in bytes */
+  int preedit_cursor;	/* offset of cursor within preedit string, in chars */
+
+  int text_size; /* allocated size, in bytes */
+  int n_bytes;	 /* length in use (not including terminating zero), in bytes */
+  gboolean editable;
+  gboolean cursor_visible;
+  guint blink_timeout;
 }
 GtkLabelPrivate;
 
@@ -71,6 +84,10 @@
   MOVE_CURSOR,
   COPY_CLIPBOARD,
   POPULATE_POPUP,
+  DELETE_FROM_CURSOR,
+  CUT_CLIPBOARD,
+  PASTE_CLIPBOARD,
+  TOGGLE_OVERWRITE,
   LAST_SIGNAL
 };
 
@@ -93,11 +110,13 @@
   PROP_WIDTH_CHARS,
   PROP_SINGLE_LINE_MODE,
   PROP_ANGLE,
-  PROP_MAX_WIDTH_CHARS
+  PROP_MAX_WIDTH_CHARS,
+  PROP_EDITABLE
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+static void gtk_label_editable_init     (GtkEditableClass *iface);
 static void gtk_label_set_property      (GObject          *object,
 					 guint             prop_id,
 					 const GValue     *value,
@@ -134,6 +153,27 @@
 					     GdkEventMotion   *event);
 static void     gtk_label_grab_focus        (GtkWidget        *widget);
 
+static gint     gtk_label_key_press         (GtkWidget        *widget,
+					     GdkEventKey      *event);
+static gint     gtk_label_key_release       (GtkWidget        *widget,
+					     GdkEventKey      *event);
+static gboolean gtk_label_focus             (GtkWidget        *widget,
+					     GtkDirectionType  direction);
+static gint     gtk_label_focus_in          (GtkWidget        *widget,
+					     GdkEventFocus    *event);
+static gint     gtk_label_focus_out         (GtkWidget        *widget,
+					     GdkEventFocus    *event);
+static void     gtk_label_commit_cb               (GtkIMContext  *context,
+						   const gchar   *str,
+						   GtkLabel      *label);
+static void     gtk_label_preedit_changed_cb      (GtkIMContext  *context,
+						   GtkLabel      *label);
+static gboolean gtk_label_retrieve_surrounding_cb (GtkIMContext  *context,
+						   GtkLabel      *label);
+static gboolean gtk_label_delete_surrounding_cb   (GtkIMContext  *slave,
+						   gint           offset,
+						   gint           n_chars,
+						   GtkLabel      *label);
 
 static void gtk_label_set_text_internal          (GtkLabel      *label,
 						  gchar         *str);
@@ -161,7 +201,9 @@
 static void gtk_label_create_window       (GtkLabel *label);
 static void gtk_label_destroy_window      (GtkLabel *label);
 static void gtk_label_clear_layout        (GtkLabel *label);
-static void gtk_label_ensure_layout       (GtkLabel *label);
+static void gtk_label_recompute           (GtkLabel *label);
+static void gtk_label_ensure_layout       (GtkLabel *label,
+					   gboolean include_preedit);
 static void gtk_label_select_region_index (GtkLabel *label,
                                            gint      anchor_index,
                                            gint      end_index);
@@ -183,6 +225,11 @@
 					  gint             count,
 					  gboolean         extend_selection);
 static void gtk_label_copy_clipboard     (GtkLabel        *label);
+static void gtk_label_cut_clipboard      (GtkLabel        *label);
+static void gtk_label_paste              (GtkLabel        *label,
+					  GdkAtom          selection);
+static void gtk_label_paste_clipboard    (GtkLabel        *label);
+static void gtk_label_toggle_overwrite   (GtkLabel        *label);
 static void gtk_label_select_all         (GtkLabel        *label);
 static void gtk_label_do_popup           (GtkLabel        *label,
 					  GdkEventButton  *event);
@@ -191,10 +238,57 @@
 					  gint             start);
 static gint gtk_label_move_backward_word (GtkLabel        *label,
 					  gint             start);
+static void gtk_label_check_cursor_blink (GtkLabel        *label);
+static void gtk_label_pend_cursor_blink  (GtkLabel        *label);
+static void gtk_label_delete_from_cursor (GtkLabel        *label,
+					  GtkDeleteType    type,
+					  gint             count);
+static void gtk_label_reset_im_context   (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);
+
+
+/* Editable implementation: */
+static void     editable_insert_text_emit     (GtkEditable *editable,
+					       const gchar *new_text,
+					       gint         new_text_length,
+					       gint        *position);
+static void     editable_delete_text_emit     (GtkEditable *editable,
+					       gint         start_pos,
+					       gint         end_pos);
+static void     editable_insert_text          (GtkEditable *editable,
+					       const gchar *new_text,
+					       gint         new_text_length,
+					       gint        *position);
+static void     editable_delete_text          (GtkEditable *editable,
+					       gint         start_pos,
+					       gint         end_pos);
+static gchar *  editable_get_chars            (GtkEditable *editable,
+					       gint         start_pos,
+					       gint         end_pos);
+static void     editable_set_selection_bounds (GtkEditable *editable,
+					       gint         start,
+					       gint         end);
+static gboolean editable_get_selection_bounds (GtkEditable *editable,
+					       gint        *start,
+					       gint        *end);
+static void     editable_real_set_position    (GtkEditable *editable,
+					       gint         position);
+static gint     editable_get_position         (GtkEditable *editable);
 
 static GQuark quark_angle = 0;
 
-G_DEFINE_TYPE (GtkLabel, gtk_label, GTK_TYPE_MISC)
+G_DEFINE_TYPE_EXTENDED (GtkLabel,
+                        gtk_label,
+                        GTK_TYPE_MISC,
+                        0,
+                        G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
+                                              gtk_label_editable_init));
 
 static void
 add_move_binding (GtkBindingSet  *binding_set,
@@ -253,10 +347,19 @@
   widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
   widget_class->drag_data_get = gtk_label_drag_data_get;
   widget_class->grab_focus = gtk_label_grab_focus;
-
+  widget_class->key_press_event = gtk_label_key_press;
+  widget_class->key_release_event = gtk_label_key_release;
+  widget_class->focus = gtk_label_focus;
+  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->copy_clipboard = gtk_label_copy_clipboard;
-  
+  class->delete_from_cursor = gtk_label_delete_from_cursor;
+  class->cut_clipboard = gtk_label_cut_clipboard;
+  class->paste_clipboard = gtk_label_paste_clipboard;
+  class->toggle_overwrite = gtk_label_toggle_overwrite;
+
   signals[MOVE_CURSOR] = 
     g_signal_new (I_("move_cursor"),
 		  G_OBJECT_CLASS_TYPE (gobject_class),
@@ -288,6 +391,42 @@
 		  G_TYPE_NONE, 1,
 		  GTK_TYPE_MENU);
 
+  signals[DELETE_FROM_CURSOR] = 
+    g_signal_new ("delete_from_cursor",
+		  G_TYPE_FROM_CLASS (object_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);
+  
+  signals[CUT_CLIPBOARD] =
+    g_signal_new ("cut_clipboard",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, cut_clipboard),
+		  NULL, NULL, 
+		  _gtk_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
+  signals[PASTE_CLIPBOARD] =
+    g_signal_new ("paste_clipboard",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, paste_clipboard),
+		  NULL, NULL, 
+		  _gtk_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
+  signals[TOGGLE_OVERWRITE] =
+    g_signal_new ("toggle_overwrite",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkLabelClass, toggle_overwrite),
+		  NULL, NULL, 
+		  _gtk_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
   g_object_class_install_property (gobject_class,
                                    PROP_LABEL,
                                    g_param_spec_string ("label",
@@ -507,6 +646,23 @@
                                                      G_MAXINT,
                                                      -1,
                                                      GTK_PARAM_READWRITE));
+
+  /**
+   * GtkLabel:editable:
+   *
+   * If true, allow the user to edit the text
+   *
+   * Since: 2.12
+   **/
+  
+  g_object_class_install_property (gobject_class,
+                                   PROP_EDITABLE,
+                                   g_param_spec_boolean ("editable",
+                                                        P_("Editable"),
+                                                        P_("Whether the label is editable"),
+                                                        FALSE,
+                                                        GTK_PARAM_READWRITE));
+
   /*
    * Key bindings
    */
@@ -612,10 +768,75 @@
   add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
 		    GTK_MOVEMENT_BUFFER_ENDS, 1);
 
-  /* copy */
+  add_move_binding (binding_set, GDK_Up, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, -1);
+
+  add_move_binding (binding_set, GDK_KP_Up, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, -1);
+  
+  add_move_binding (binding_set, GDK_Down, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, 1);
+
+  add_move_binding (binding_set, GDK_KP_Down, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, 1);
+  
+  /* 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);
+
+  /* clipboard */
+  gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
+				"cut_clipboard", 0);
   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
 				"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);
+  /* 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);
+  
   gtk_settings_install_property (g_param_spec_boolean ("gtk-label-select-on-focus",
 						       P_("Select on focus"),
 						       P_("Whether to select the contents of a selectable label when it is focused"),
@@ -626,6 +847,20 @@
   g_type_class_add_private (class, sizeof (GtkLabelPrivate));
 }
 
+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;
+}
+
 static void 
 gtk_label_set_property (GObject      *object,
 			guint         prop_id,
@@ -683,6 +918,9 @@
     case PROP_MAX_WIDTH_CHARS:
       gtk_label_set_max_width_chars (label, g_value_get_int (value));
       break;
+    case PROP_EDITABLE:
+      gtk_label_set_editable (label, g_value_get_boolean (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -766,6 +1004,9 @@
     case PROP_MAX_WIDTH_CHARS:
       g_value_set_int (value, gtk_label_get_max_width_chars (label));
       break;
+    case PROP_EDITABLE:
+      g_value_set_boolean (value, gtk_label_get_editable (label));
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -783,6 +1024,9 @@
   priv = GTK_LABEL_GET_PRIVATE (label);
   priv->width_chars = -1;
   priv->max_width_chars = -1;
+  priv->text_size = 1;
+  priv->n_bytes = 0;
+  
   label->label = NULL;
 
   label->jtype = GTK_JUSTIFY_LEFT;
@@ -794,14 +1038,25 @@
   label->use_markup = FALSE;
   
   label->mnemonic_keyval = GDK_VoidSymbol;
+
   label->layout = NULL;
-  label->text = NULL;
+  label->text = g_malloc (priv->text_size);
+  label->text[0] = '\0';
   label->attrs = NULL;
-
+ 
   label->mnemonic_widget = NULL;
   label->mnemonic_window = NULL;
-  
-  gtk_label_set_text (label, "");
+
+  priv->im_context = gtk_im_multicontext_new ();
+
+  g_signal_connect (G_OBJECT (priv->im_context), "commit",
+		    G_CALLBACK (gtk_label_commit_cb), label);
+  g_signal_connect (G_OBJECT (priv->im_context), "preedit_changed",
+		    G_CALLBACK (gtk_label_preedit_changed_cb), label);
+  g_signal_connect (G_OBJECT (priv->im_context), "retrieve_surrounding",
+		    G_CALLBACK (gtk_label_retrieve_surrounding_cb), label);
+  g_signal_connect (G_OBJECT (priv->im_context), "delete_surrounding",
+		    G_CALLBACK (gtk_label_delete_surrounding_cb), label);
 }
 
 /**
@@ -1068,10 +1323,14 @@
 gtk_label_set_text_internal (GtkLabel *label,
 			     gchar    *str)
 {
-  g_free (label->text);
-  
-  label->text = str;
+  int tmp_pos;
+
+  if (strcmp (label->text, str) == 0)
+    return;
 
+  gtk_label_delete_text (label, 0, -1);
+  tmp_pos = 0;
+  gtk_label_insert_text (label, str, strlen (str), &tmp_pos);
   gtk_label_select_region_index (label, 0, 0);
 }
 
@@ -1777,8 +2036,22 @@
 gtk_label_destroy (GtkObject *object)
 {
   GtkLabel *label = GTK_LABEL (object);
+  GtkLabelPrivate *priv;
 
   gtk_label_set_mnemonic_widget (label, NULL);
+  
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  if (priv->im_context)
+    {
+      g_object_unref (priv->im_context);
+      priv->im_context = NULL;
+    }
+
+  if (priv->blink_timeout)
+    {
+      g_source_remove (priv->blink_timeout);
+      priv->blink_timeout = 0;
+    }
 
   GTK_OBJECT_CLASS (gtk_label_parent_class)->destroy (object);
 }
@@ -1819,6 +2092,13 @@
     }
 }
 
+static void
+gtk_label_recompute (GtkLabel *label)
+{
+  gtk_label_clear_layout (label);
+  gtk_label_check_cursor_blink (label);
+}
+
 typedef struct _LabelWrapWidth LabelWrapWidth;
 struct _LabelWrapWidth
 {
@@ -1871,13 +2151,16 @@
 }
 
 static void
-gtk_label_ensure_layout (GtkLabel *label)
+gtk_label_ensure_layout (GtkLabel *label,
+			 gboolean include_preedit)
 {
+  GtkLabelPrivate *priv;
   GtkWidget *widget;
   PangoRectangle logical_rect;
   gboolean rtl;
 
   widget = GTK_WIDGET (label);
+  priv = GTK_LABEL_GET_PRIVATE (widget);
 
   rtl = gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL;
 
@@ -1885,6 +2168,37 @@
     {
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
       gdouble angle = gtk_label_get_angle (label);
+      gchar *preedit_string = NULL;
+      gint preedit_length = 0;
+      PangoAttrList *preedit_attrs = NULL;
+      PangoAttrList *tmp_attrs = pango_attr_list_new ();
+  
+
+      if (include_preedit)
+	{
+	  gtk_im_context_get_preedit_string (priv->im_context,
+					     &preedit_string, &preedit_attrs, NULL);
+	  preedit_length = priv->preedit_length;
+	}
+      
+      if (preedit_length)
+	{
+	  GString *tmp_string = g_string_new (NULL);
+	  
+	  g_string_prepend_len (tmp_string, label->text, priv->n_bytes);
+	  g_string_insert (tmp_string, label->select_info->selection_anchor, preedit_string);
+      
+	  label->layout = gtk_widget_create_pango_layout (widget, tmp_string->str);
+      
+	  pango_attr_list_splice (tmp_attrs, preedit_attrs,
+				  label->select_info->selection_anchor, preedit_length);
+	  
+	  g_string_free (tmp_string, TRUE);
+	}
+      else
+	{
+	  label->layout = gtk_widget_create_pango_layout (widget, label->text);
+	}
 
       if (angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
 	{
@@ -2011,6 +2325,7 @@
 	}
       else /* !label->wrap */
 	pango_layout_set_width (label->layout, -1);
+      
     }
 }
 
@@ -2093,9 +2408,9 @@
    */
 
   if (label->wrap)
-    gtk_label_clear_layout (label);
+    gtk_label_recompute (label);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, TRUE);
 
   width = label->misc.xpad * 2;
   height = label->misc.ypad * 2;
@@ -2325,7 +2640,7 @@
 
   g_assert (label->select_info);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, TRUE);
 
   for (l = pango_layout_get_lines (label->layout); l; l = l->next)
     {
@@ -2368,7 +2683,7 @@
       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);
+      gtk_label_ensure_layout (label, FALSE);
       
       pango_layout_get_cursor_pos (label->layout, label->select_info->selection_end,
 				   &strong_pos, &weak_pos);
@@ -2427,14 +2742,16 @@
 		  GdkEventExpose *event)
 {
   GtkLabel *label;
+  GtkLabelPrivate *priv;
   gint x, y;
   
   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
   
   label = GTK_LABEL (widget);
-
-  gtk_label_ensure_layout (label);
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  gtk_label_ensure_layout (label, TRUE);
   
   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
       label->text && (*label->text != '\0'))
@@ -2496,7 +2813,7 @@
           gdk_gc_set_clip_region (widget->style->black_gc, NULL);
           gdk_region_destroy (clip);
         }
-      else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget))
+      else if (label->select_info && GTK_WIDGET_HAS_FOCUS (widget) && priv->cursor_visible)
 	gtk_label_draw_cursor (label, x, y);
     }
 
@@ -2775,7 +3092,7 @@
 
   *index = 0;
   
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, TRUE);
   
   window_to_layout_coords (label, &x, &y);
 
@@ -3276,6 +3593,57 @@
     return 0.0;
 }
 
+/**
+ * gtk_label_set_editable:
+ * @label: a #GtkLabel
+ * @setting: %TRUE to allow editing of the text in the label
+ *
+ * Editable labels allow the user to edit the text in the label.
+ * The label is automatically made selectable if it wasn't.
+ * 
+ * Since: 2.12
+ **/
+void
+gtk_label_set_editable (GtkLabel *label,
+			gboolean  editable)
+{
+  GtkLabelPrivate *priv;
+
+  g_return_if_fail (GTK_IS_LABEL (label));
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  if (editable && !label->select_info)
+    {
+      gtk_label_set_selectable (label, TRUE);
+    }
+
+  priv->editable = editable;
+    
+}
+
+/**
+ * gtk_label_get_editable:
+ * @label: a #GtkLabel
+ * 
+ * Gets the value set by gtk_label_set_editable().
+ * 
+ * Return value: %TRUE if the user can edit the text in the label
+ *
+ * Since: 2.12
+ **/
+gboolean
+gtk_label_get_editable (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_LABEL (label), FALSE);
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  return priv->editable;
+}
+
 static void
 gtk_label_set_selection_text (GtkLabel         *label,
 			      GtkSelectionData *selection_data)
@@ -3511,7 +3879,7 @@
 {
   g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, TRUE);
 
   return label->layout;
 }
@@ -3684,7 +4052,7 @@
 		"gtk-split-cursor", &split_cursor,
 		NULL);
 
-  gtk_label_ensure_layout (label);
+  gtk_label_ensure_layout (label, TRUE);
   
   pango_layout_get_cursor_pos (label->layout, index,
 			       &strong_pos, &weak_pos);
@@ -3724,7 +4092,7 @@
       gint n_attrs;
       gint length;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       length = g_utf8_strlen (label->text, -1);
 
@@ -3768,7 +4136,7 @@
       gboolean split_cursor;
       gboolean strong;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
 
       g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
 		    "gtk-split-cursor", &split_cursor,
@@ -3821,7 +4189,7 @@
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
       
@@ -3849,7 +4217,7 @@
       PangoLogAttr *log_attrs;
       gint n_attrs;
 
-      gtk_label_ensure_layout (label);
+      gtk_label_ensure_layout (label, FALSE);
       
       pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
       
@@ -4022,6 +4390,86 @@
 }
 
 static void
+gtk_label_cut_clipboard (GtkLabel *label)
+{
+  if (label->text)
+    {
+      gint start, end;
+      gint len;
+      
+      start = MIN (label->select_info->selection_anchor,
+                   label->select_info->selection_end);
+      end = MAX (label->select_info->selection_anchor,
+                 label->select_info->selection_end);
+
+      len = strlen (label->text);
+
+      if (end > len)
+        end = len;
+
+      if (start > len)
+        start = len;
+
+      if (start != end)
+	{
+	  gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+				  label->text + start, end - start);
+          start = g_utf8_pointer_to_offset (label->text, label->text + start);
+          end = g_utf8_pointer_to_offset (label->text, label->text + end);
+	  gtk_editable_delete_text (GTK_EDITABLE (label), start, end);
+	}
+    }
+}
+
+static void
+paste_received (GtkClipboard *clipboard,
+		const gchar  *text,
+		gpointer      data)
+{
+  GtkLabel *label = GTK_LABEL (data);
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint tmp_pos;
+      
+  if (text)
+    {
+      if (label->select_info->selection_end != label->select_info->selection_anchor)
+	gtk_editable_delete_selection (editable);
+
+      tmp_pos = g_utf8_pointer_to_offset (label->text,
+                                      label->text + label->select_info->selection_anchor);
+      gtk_editable_insert_text (GTK_EDITABLE (label), text, strlen (text), &tmp_pos);
+      tmp_pos = g_utf8_offset_to_pointer (label->text, tmp_pos) - label->text;
+      gtk_label_select_region_index (label, tmp_pos, tmp_pos);
+    }
+
+  g_object_unref (G_OBJECT (label));
+}
+
+static void
+gtk_label_paste (GtkLabel *label,
+			  GdkAtom   selection)
+{
+  g_object_ref (G_OBJECT (label));
+  gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (label), selection),
+			      paste_received, label);
+}
+
+static void
+gtk_label_paste_clipboard (GtkLabel *label)
+{
+  gtk_label_paste (label, GDK_NONE);
+}
+
+static void
+gtk_label_toggle_overwrite (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  priv->overwrite_mode = !priv->overwrite_mode;
+}
+
+static void
 gtk_label_select_all (GtkLabel *label)
 {
   gtk_label_select_region_index (label, 0, strlen (label->text));
@@ -4158,6 +4606,789 @@
     gtk_menu_popup (GTK_MENU (label->select_info->popup_menu), NULL, NULL,
                     popup_position_func, label,
                     0, gtk_get_current_event_time ());
+}
+
+
+/* START */
+
+/* We display the cursor when
+ *
+ *  - the selection is empty, AND
+ *  - the widget has focus
+ */
+
+#define CURSOR_ON_MULTIPLIER 0.66
+#define CURSOR_OFF_MULTIPLIER 0.34
+#define CURSOR_PEND_MULTIPLIER 1.0
+
+static gboolean
+cursor_blinks (GtkLabel *label)
+{
+  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (label));
+  gboolean blink;
+
+  if (GTK_WIDGET_HAS_FOCUS (label) &&
+      gtk_label_get_editable(label) &&
+      label->select_info)
+    {
+      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 void
+show_cursor (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  if (!priv->cursor_visible)
+    {
+      priv->cursor_visible = TRUE;
+
+      if (GTK_WIDGET_HAS_FOCUS (label) && label->select_info)
+	gtk_widget_queue_draw (GTK_WIDGET (label));
+    }
+}
+
+static void
+hide_cursor (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  if (priv->cursor_visible)
+    {
+      priv->cursor_visible = FALSE;
+
+      if (GTK_WIDGET_HAS_FOCUS (label) && label->select_info)
+	gtk_widget_queue_draw (GTK_WIDGET (label));
+    }
+}
+
+/*
+ * Blink!
+ */
+static gint
+blink_cb (gpointer data)
+{
+  GtkLabel *label;
+  GtkLabelPrivate *priv;
+
+  GDK_THREADS_ENTER ();
+
+  label = GTK_LABEL (data);
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!GTK_WIDGET_HAS_FOCUS (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");
+    }
+  
+  g_assert (GTK_WIDGET_HAS_FOCUS (label));
+  g_assert (label->select_info);
+
+  if (priv->cursor_visible)
+    {
+      hide_cursor (label);
+      priv->blink_timeout = g_timeout_add (get_cursor_time (label) * CURSOR_OFF_MULTIPLIER,
+					    blink_cb,
+					    label);
+    }
+  else
+    {
+      show_cursor (label);
+      priv->blink_timeout = g_timeout_add (get_cursor_time (label) * CURSOR_ON_MULTIPLIER,
+					    blink_cb,
+					    label);
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  /* Remove ourselves */
+  return FALSE;
+}
+
+static void
+gtk_label_pend_cursor_blink (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  if (cursor_blinks (label))
+    {
+      if (priv->blink_timeout != 0)
+	g_source_remove (priv->blink_timeout);
+      
+      priv->blink_timeout = g_timeout_add (get_cursor_time (label) * CURSOR_PEND_MULTIPLIER,
+					    blink_cb,
+					    label);
+      show_cursor (label);
+    }
+}
+
+static void
+gtk_label_check_cursor_blink (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  if (cursor_blinks (label))
+    {
+      if (!priv->blink_timeout)
+	{
+	  priv->blink_timeout = g_timeout_add (get_cursor_time (label) * CURSOR_ON_MULTIPLIER,
+						blink_cb,
+						label);
+	  show_cursor (label);
+	}
+    }
+  else
+    {
+      if (priv->blink_timeout)  
+	{ 
+	  g_source_remove (priv->blink_timeout);
+	  priv->blink_timeout = 0;
+	}
+      
+      priv->cursor_visible = TRUE;
+    }
+
+}
+
+static gint
+gtk_label_key_press (GtkWidget   *widget,
+		     GdkEventKey *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (widget);
+
+  gtk_label_pend_cursor_blink (label);
+
+  if (gtk_im_context_filter_keypress (priv->im_context, event))
+    {
+      /*TODO gtk_label_obscure_mouse_cursor (label);*/
+      priv->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)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (widget);
+
+  if (gtk_im_context_filter_keypress (priv->im_context, event))
+    {
+      priv->need_im_reset = TRUE;
+      return TRUE;
+    }
+
+  return GTK_WIDGET_CLASS (gtk_label_parent_class)->key_release_event (widget, event);
+}
+
+static gboolean
+gtk_label_focus (GtkWidget         *widget,
+		 GtkDirectionType   direction)
+{
+  /* We never want to be in the tab chain */
+  return FALSE;
+}
+
+static void
+gtk_label_keymap_direction_changed (GdkKeymap *keymap,
+				    GtkLabel  *label)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+static gint
+gtk_label_focus_in (GtkWidget     *widget,
+		    GdkEventFocus *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (widget);
+  
+  gtk_widget_queue_draw (widget);
+  
+  priv->need_im_reset = TRUE;
+  gtk_im_context_focus_in (priv->im_context);
+
+  g_signal_connect (gdk_keymap_get_default (),
+		    "direction_changed",
+		    G_CALLBACK (gtk_label_keymap_direction_changed), label);
+
+  gtk_label_check_cursor_blink (label);
+  
+  return FALSE;
+}
+
+static gint
+gtk_label_focus_out (GtkWidget     *widget,
+		     GdkEventFocus *event)
+{
+  GtkLabel *label = GTK_LABEL (widget);
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (widget);
+  
+  gtk_widget_queue_draw (widget);
+
+  priv->need_im_reset = TRUE;
+  gtk_im_context_focus_out (priv->im_context);
+
+  gtk_label_check_cursor_blink (label);
+  
+  g_signal_handlers_disconnect_by_func (gdk_keymap_get_default (),
+                                        (gpointer) gtk_label_keymap_direction_changed,
+                                        label);
+  
+  return FALSE;
+}
+
+
+/* IM Context Callbacks
+ */
+
+static void
+gtk_label_commit_cb (GtkIMContext *context,
+		     const gchar  *str,
+		     GtkLabel  *label)
+{
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint tmp_pos;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+
+  if (label->select_info->selection_end != label->select_info->selection_anchor)
+    gtk_editable_delete_selection (editable);
+  else
+    {
+      if (priv->overwrite_mode)
+        gtk_label_delete_from_cursor (label, GTK_DELETE_CHARS, 1);
+    }
+  
+  tmp_pos = g_utf8_pointer_to_offset (label->text,
+                                      label->text + label->select_info->selection_anchor);
+  gtk_editable_insert_text (GTK_EDITABLE (label), str, strlen (str), &tmp_pos);
+  tmp_pos = g_utf8_offset_to_pointer (label->text, tmp_pos) - label->text;
+  gtk_label_select_region_index (label, tmp_pos, tmp_pos);
+}
+
+static void 
+gtk_label_preedit_changed_cb (GtkIMContext *context,
+			      GtkLabel  *label)
+{
+  gchar *preedit_string;
+  gint cursor_pos;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  gtk_im_context_get_preedit_string (priv->im_context,
+				     &preedit_string, NULL,
+				     &cursor_pos);
+  priv->preedit_length = strlen (preedit_string);
+  cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
+  priv->preedit_cursor = cursor_pos;
+  g_free (preedit_string);
+
+  gtk_label_recompute (label);  
+  gtk_widget_queue_resize (GTK_WIDGET (label));
+}
+
+static gboolean
+gtk_label_retrieve_surrounding_cb (GtkIMContext *context,
+				   GtkLabel  *label)
+{
+  gtk_im_context_set_surrounding (context,
+				  label->text,
+				  strlen (label->text) + 1,
+				  label->select_info->selection_end);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_label_delete_surrounding_cb (GtkIMContext *slave,
+				 gint          offset,
+				 gint          n_chars,
+				 GtkLabel     *label)
+{
+  GtkLabelPrivate *priv;
+  gint current_pos;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+
+  current_pos = g_utf8_pointer_to_offset (label->text,
+					  label->text + label->select_info->selection_anchor);
+  gtk_editable_delete_text (GTK_EDITABLE (label), 
+			    current_pos + offset, 
+			    current_pos + offset + n_chars);
+
+  return TRUE;
+}
+
+static void
+gtk_label_reset_im_context (GtkLabel  *label)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  if (priv->need_im_reset)
+    {
+      priv->need_im_reset = 0;
+      gtk_im_context_reset (priv->im_context);
+    }
+}
+
+static void
+gtk_label_delete_from_cursor (GtkLabel      *label,
+			      GtkDeleteType  type,
+			      gint           count)
+{
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint start_pos = label->select_info->selection_anchor;
+  gint end_pos = label->select_info->selection_anchor;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+  
+  gtk_label_reset_im_context (label);
+
+  if (label->select_info->selection_anchor != label->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 (label->text, label->text + start_pos);
+      end_pos = g_utf8_pointer_to_offset (label->text, label->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 (label->text, label->text + start_pos);
+      end_pos = g_utf8_pointer_to_offset (label->text, label->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 (label->text, label->text + label->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 gtk_label_delete_whitespace (label); */
+      break;
+    }
+  
+  gtk_label_pend_cursor_blink (label);
+}
+
+static void
+gtk_label_delete_text (GtkLabel *label,
+		       int start_pos,
+		       int end_pos)
+{
+  int anchor, end;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  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 (label->text + start_pos, label->text + end_pos, priv->n_bytes + 1 - end_pos);
+      priv->n_bytes -= (end_pos - start_pos);
+
+      anchor = label->select_info->selection_anchor;
+      if (anchor > start_pos)
+	anchor -= MIN (anchor, end_pos) - start_pos;
+
+      end = label->select_info->selection_end;
+      if (end > start_pos)
+	end -= MIN (end, end_pos) - start_pos;
+      
+      /* We might have changed the select_info->selection */
+      gtk_label_select_region_index (label, anchor, end);
+      
+      gtk_label_recompute (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
+gtk_label_insert_text (GtkLabel    *label,
+		       const gchar *new_text,
+		       gint         new_text_length,
+		       gint        *index)
+{
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+  
+  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;
+	}
+
+      label->text = g_realloc (label->text, priv->text_size);
+    }
+
+  g_object_freeze_notify (G_OBJECT (label));
+
+  g_memmove (label->text + *index + new_text_length,
+	     label->text + *index,
+	     priv->n_bytes - *index);
+  memcpy (label->text + *index, new_text, new_text_length);
+  
+  priv->n_bytes += new_text_length;
+
+  /* NUL terminate for safety and convenience */
+  label->text[priv->n_bytes] = '\0';
+
+  g_object_notify (G_OBJECT (label), "label");
+
+  if (label->select_info)
+    {
+      if (label->select_info->selection_anchor > *index)
+	{
+	  g_object_notify (G_OBJECT (label), "cursor_position");
+	  g_object_notify (G_OBJECT (label), "selection_bound");
+	  label->select_info->selection_anchor += new_text_length;
+	}
+  
+      if (label->select_info->selection_end > *index)
+	{
+	  label->select_info->selection_end += new_text_length;
+	  g_object_notify (G_OBJECT (label), "selection_bound");
+	}
+    }
+
+  *index += new_text_length;
+
+  gtk_label_recompute (label);  
+  gtk_widget_queue_resize (GTK_WIDGET (label));
+
+  g_object_thaw_notify (G_OBJECT (label));
+  g_signal_emit_by_name (GTK_EDITABLE (label), "changed");
+}
+
+/************ Editable implementation ****************/
+
+static void
+editable_insert_text_emit  (GtkEditable *editable,
+			    const gchar *new_text,
+			    gint         new_text_length,
+			    gint        *position)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  gchar buf[64];
+  gchar *text;
+  int text_length;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+  
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (*position < 0 || *position > text_length)
+    *position = text_length;
+  
+  g_object_ref (G_OBJECT (editable));
+  
+  if (new_text_length <= 63)
+    text = buf;
+  else
+    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);
+
+  if (new_text_length > 63)
+    g_free (text);
+
+  g_object_unref (G_OBJECT (editable));
+}
+
+static void
+editable_delete_text_emit (GtkEditable *editable,
+			   gint         start_pos,
+			   gint         end_pos)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  int text_length;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+
+  text_length = g_utf8_strlen (label->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_object_ref (G_OBJECT (editable));
+
+  g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
+
+  g_object_unref (G_OBJECT (editable));
+}
+
+static void
+editable_insert_text (GtkEditable *editable,
+		      const gchar *new_text,
+		      gint         new_text_length,
+		      gint        *position)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  gint index;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+
+  if (new_text_length < 0)
+    new_text_length = strlen (new_text);
+
+  index = g_utf8_offset_to_pointer (label->text, *position) - label->text;
+
+  gtk_label_insert_text (label,
+			 new_text,
+			 new_text_length,
+			 &index);
+  
+  *position = g_utf8_pointer_to_offset (label->text, label->text + index);
+}
+
+static void
+editable_delete_text (GtkEditable *editable,
+		      gint         start_pos,
+		      gint         end_pos)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  int text_length;
+  gint start_index, end_index;
+  GtkLabelPrivate *priv;
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  if (!priv->editable)
+    return;
+
+  text_length = g_utf8_strlen (label->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 (label->text, start_pos) - label->text;
+  end_index = g_utf8_offset_to_pointer (label->text, end_pos) - label->text;
+  
+  gtk_label_delete_text (label, start_index, end_index);
+}
+
+static gchar *    
+editable_get_chars (GtkEditable   *editable,
+		    gint           start_pos,
+		    gint           end_pos)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  int text_length;
+  gint start_index, end_index;
+
+  text_length = g_utf8_strlen (label->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 (label->text, start_pos) - label->text;
+  end_index = g_utf8_offset_to_pointer (label->text, end_pos) - label->text;
+
+  return g_strndup (label->text + start_index, end_index - start_index);
+}
+
+static void
+editable_set_selection_bounds (GtkEditable *editable,
+			       gint         start,
+			       gint         end)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  int text_length;
+  gint start_index, end_index;
+  
+  if (!label->select_info)
+    return;
+  
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (end < 0 || end > text_length)
+    end = text_length;
+  if (start < 0)
+    start = text_length;
+  if (start > text_length)
+    start = text_length;
+  
+  gtk_label_reset_im_context (label);
+
+  start_index = g_utf8_offset_to_pointer (label->text, start) - label->text;
+  end_index = g_utf8_offset_to_pointer (label->text, end) - label->text;
+
+  gtk_label_select_region_index (label, start_index, end_index);
+}
+
+static gboolean
+editable_get_selection_bounds (GtkEditable *editable,
+			       gint        *start,
+			       gint        *end)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+
+  *start = g_utf8_pointer_to_offset (label->text, label->text + label->select_info->selection_anchor);
+  *end = g_utf8_pointer_to_offset (label->text, label->text + label->select_info->selection_end);
+
+  return (label->select_info->selection_anchor != label->select_info->selection_end);
+}
+
+static void
+editable_real_set_position (GtkEditable *editable,
+			    gint         position)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  int text_length;
+  int index;
+  
+  text_length = g_utf8_strlen (label->text, -1);
+  
+  if (position < 0 || position > text_length)
+    position = text_length;
+
+  index = g_utf8_offset_to_pointer (label->text, position) - label->text;
+  
+  if (index != label->select_info->selection_anchor ||
+      index != label->select_info->selection_end)
+    {
+      gtk_label_select_region_index (label, index, index);
+    }
+}
+
+static gint
+editable_get_position (GtkEditable *editable)
+{
+  GtkLabel *label = GTK_LABEL (editable);
+  
+  return g_utf8_pointer_to_offset (label->text, label->text + label->select_info->selection_anchor);
 }
 
 #define __GTK_LABEL_C__
Index: gtk/gtklabel.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtklabel.h,v
retrieving revision 1.47
diff -u -r1.47 gtklabel.h
--- gtk/gtklabel.h	19 Jun 2006 14:27:50 -0000	1.47
+++ gtk/gtklabel.h	24 Nov 2006 00:53:19 -0000
@@ -91,6 +91,15 @@
   void (* populate_popup)   (GtkLabel       *label,
                              GtkMenu        *menu);
 
+  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 (* paste_clipboard)    (GtkLabel     *label);
+  void (* toggle_overwrite)   (GtkLabel     *label);
+  
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
@@ -168,6 +177,10 @@
 void         gtk_label_set_single_line_mode  (GtkLabel *label,
                                               gboolean single_line_mode);
 gboolean     gtk_label_get_single_line_mode  (GtkLabel *label);
+
+void         gtk_label_set_editable (GtkLabel *label,
+				     gboolean  setting);
+gboolean     gtk_label_get_editable (GtkLabel *label);
 
 #ifndef GTK_DISABLE_DEPRECATED
 
Index: tests/testgtk.c
===================================================================
RCS file: /cvs/gnome/gtk+/tests/testgtk.c,v
retrieving revision 1.361
diff -u -r1.361 testgtk.c
--- tests/testgtk.c	19 Sep 2006 15:51:19 -0000	1.361
+++ tests/testgtk.c	24 Nov 2006 00:53:29 -0000
@@ -2801,6 +2801,59 @@
                     "toggled",
                     G_CALLBACK (selectable_toggled),
                     widget);
+
+  gtk_widget_show_all (button);
+
+  return button;
+}
+
+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);
 
@@ -2844,6 +2897,10 @@
 
       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);
       
       gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
