Index: gtkentry.c
===================================================================
--- gtkentry.c	(revision 18083)
+++ gtkentry.c	(working copy)
@@ -85,12 +85,14 @@ struct _GtkEntryPrivate 
   guint blink_time;  /* time in msec the cursor has blinked since last user event */
   guint real_changed : 1;
   guint change_count : 8;
+  guint filter_match : 1;
 
   gint focus_width;
   gboolean interior_focus;
   GtkShadowType shadow_type;
 
   GtkAdjustment *cursor_hadjustment;
+  GRegex *filter_regex;
 };
 
 typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint;
@@ -114,6 +116,7 @@ enum {
   COPY_CLIPBOARD,
   PASTE_CLIPBOARD,
   TOGGLE_OVERWRITE,
+  MATCH_CHANGED,
   LAST_SIGNAL
 };
 
@@ -133,7 +136,8 @@ enum {
   PROP_TEXT,
   PROP_XALIGN,
   PROP_TRUNCATE_MULTILINE,
-  PROP_SHADOW_TYPE
+  PROP_SHADOW_TYPE,
+  PROP_FILTER_REGEX
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -143,6 +147,12 @@ typedef enum {
   CURSOR_DND
 } CursorType;
 
+typedef enum {
+  FILTER_MATCH_PARTIAL,
+  FILTER_MATCH_COMPLETE,
+  FILTER_NO_MATCH
+} FilterMatchType;
+
 /* GObject, GtkObject methods
  */
 static void   gtk_entry_editable_init        (GtkEditableClass     *iface);
@@ -625,6 +635,19 @@ gtk_entry_class_init (GtkEntryClass *cla
                                                       GTK_SHADOW_IN,
                                                       GTK_PARAM_READWRITE));
 
+  /**
+   * GtkEntry:filter-regex:
+   *
+   * Regular expression to filter the entry contents.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_FILTER_REGEX,
+                                   g_param_spec_boxed ("filter-regex",
+                                                       P_("Filter regex"),
+                                                       P_("Regular expression to filter the entry contents"),
+                                                       G_TYPE_REGEX,
+                                                       GTK_PARAM_READWRITE));
+
   signals[POPULATE_POPUP] =
     g_signal_new (I_("populate_popup"),
 		  G_OBJECT_CLASS_TYPE (gobject_class),
@@ -725,6 +748,15 @@ gtk_entry_class_init (GtkEntryClass *cla
 		  _gtk_marshal_VOID__VOID,
 		  G_TYPE_NONE, 0);
 
+  signals[MATCH_CHANGED] =
+    g_signal_new (I_("match_changed"),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (GtkEntryClass, match_changed),
+		  NULL, NULL,
+		  _gtk_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
   /*
    * Key bindings
    */
@@ -1017,6 +1049,10 @@ gtk_entry_set_property (GObject         
       priv->shadow_type = g_value_get_enum (value);
       break;
 
+    case PROP_FILTER_REGEX:
+      gtk_entry_set_filter_regex (entry, (GRegex *) g_value_get_boxed (value));
+      break;
+
     case PROP_SCROLL_OFFSET:
     case PROP_CURSOR_POSITION:
     default:
@@ -1081,6 +1117,9 @@ gtk_entry_get_property (GObject         
     case PROP_SHADOW_TYPE:
       g_value_set_enum (value, priv->shadow_type);
       break;
+    case PROP_FILTER_REGEX:
+      g_value_set_boxed (value, priv->filter_regex);
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1218,6 +1257,7 @@ static void
 gtk_entry_finalize (GObject *object)
 {
   GtkEntry *entry = GTK_ENTRY (object);
+  GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (object);
 
   gtk_entry_set_completion (entry, NULL);
 
@@ -1242,6 +1282,12 @@ gtk_entry_finalize (GObject *object)
       entry->text = NULL;
     }
 
+  if (priv->cursor_hadjustment)
+    g_object_unref (priv->cursor_hadjustment);
+
+  if (priv->filter_regex)
+    g_regex_unref (priv->filter_regex);
+
   G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object);
 }
 
@@ -2425,6 +2471,42 @@ gtk_entry_password_hint_free (GtkEntryPa
   g_free (password_hint);
 }
 
+static FilterMatchType
+gtk_entry_get_filter_regex_match (GtkEntry    *entry,
+				  const gchar *text)
+{
+  GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
+  FilterMatchType match_type = FILTER_NO_MATCH;
+  GMatchInfo *match_info;
+  gboolean match;
+
+  priv = GTK_ENTRY_GET_PRIVATE (entry);
+
+  if (!priv->filter_regex)
+    return FILTER_MATCH_COMPLETE;
+
+  /* small optimization for empty strings */
+  if (!text || !text[0])
+    return FILTER_MATCH_PARTIAL;
+
+  match = g_regex_match (priv->filter_regex, text,
+			 G_REGEX_MATCH_ANCHORED | G_REGEX_MATCH_PARTIAL,
+			 &match_info);
+
+  if (match)
+    match_type = FILTER_MATCH_COMPLETE;
+  else
+    {
+      if (match_info && g_match_info_is_partial_match (match_info))
+	match_type = FILTER_MATCH_PARTIAL;
+    }
+
+  if (match_info)
+    g_match_info_free (match_info);
+
+  return match_type;
+}
+
 /* Default signal handlers
  */
 static void
@@ -2434,8 +2516,10 @@ gtk_entry_real_insert_text (GtkEditable 
 			    gint        *position)
 {
   GtkEntry *entry = GTK_ENTRY (editable);
+  GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
   gint index;
   gint n_chars;
+  FilterMatchType match_type = FILTER_NO_MATCH;
 
   if (new_text_length < 0)
     new_text_length = strlen (new_text);
@@ -2448,6 +2532,34 @@ gtk_entry_real_insert_text (GtkEditable 
       new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text;
     }
 
+  index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
+
+  if (priv->filter_regex)
+    {
+      gchar *text;
+      gint length;
+
+      length = new_text_length + entry->n_bytes + 1;
+      text = g_malloc (length);
+      memcpy (text, entry->text, index);
+      memcpy (text + index, new_text, new_text_length);
+      memcpy (text + index + new_text_length, entry->text + index, entry->n_bytes - index);
+      text [entry->n_bytes + new_text_length] = '\0';
+
+      match_type = gtk_entry_get_filter_regex_match (entry, text);
+
+      if (!entry->visible)
+	trash_area (text, length);
+
+      g_free (text);
+
+      if (match_type == FILTER_NO_MATCH)
+	{
+	  gtk_widget_error_bell (GTK_WIDGET (entry));
+	  return;
+	}
+    }
+
   if (new_text_length + entry->n_bytes + 1 > entry->text_size)
     {
       gsize prev_size = entry->text_size;
@@ -2488,8 +2600,6 @@ gtk_entry_real_insert_text (GtkEditable 
 	}
     }
 
-  index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text;
-
   g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
   memcpy (entry->text + index, new_text, new_text_length);
 
@@ -2498,7 +2608,7 @@ gtk_entry_real_insert_text (GtkEditable 
 
   /* NUL terminate for safety and convenience */
   entry->text[entry->n_bytes] = '\0';
-  
+
   if (entry->current_pos > *position)
     entry->current_pos += n_chars;
   
@@ -2543,6 +2653,12 @@ gtk_entry_real_insert_text (GtkEditable 
 
   emit_changed (entry);
   g_object_notify (G_OBJECT (editable), "text");
+
+  if (priv->filter_match != (match_type == FILTER_MATCH_COMPLETE))
+    {
+      priv->filter_match = (match_type == FILTER_MATCH_COMPLETE);
+      g_signal_emit (entry, signals[MATCH_CHANGED], 0);
+    }
 }
 
 static void
@@ -2551,6 +2667,8 @@ gtk_entry_real_delete_text (GtkEditable 
 			    gint         end_pos)
 {
   GtkEntry *entry = GTK_ENTRY (editable);
+  GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
+  FilterMatchType match_type = FILTER_NO_MATCH;
 
   if (start_pos < 0)
     start_pos = 0;
@@ -2564,6 +2682,31 @@ gtk_entry_real_delete_text (GtkEditable 
       gint current_pos;
       gint selection_bound;
 
+      if (priv->filter_regex)
+	{
+	  gint length;
+	  gchar *text;
+
+	  length = entry->text_length - (end_pos - start_pos);
+	  text = g_malloc (entry->text_length);
+	  memcpy (text, entry->text, start_index);
+	  memcpy (text + start_index, entry->text + end_index, entry->text_length - end_index);
+	  text [length] = '\0';
+
+	  match_type = gtk_entry_get_filter_regex_match (entry, text);
+
+	  if (!entry->visible)
+	    trash_area (text, length);
+
+	  g_free (text);
+
+	  if (match_type == FILTER_NO_MATCH)
+	    {
+	      gtk_widget_error_bell (GTK_WIDGET (entry));
+	      return;
+	    }
+	}
+
       g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index);
       entry->text_length -= (end_pos - start_pos);
       entry->n_bytes -= (end_index - start_index);
@@ -2593,6 +2736,12 @@ gtk_entry_real_delete_text (GtkEditable 
 
       emit_changed (entry);
       g_object_notify (G_OBJECT (editable), "text");
+
+      if (priv->filter_match != (match_type == FILTER_MATCH_COMPLETE))
+	{
+	  priv->filter_match = (match_type == FILTER_MATCH_COMPLETE);
+	  g_signal_emit (entry, signals[MATCH_CHANGED], 0);
+	}
     }
 }
 
@@ -6243,5 +6392,52 @@ gtk_entry_get_cursor_hadjustment (GtkEnt
   return priv->cursor_hadjustment;
 }
 
+void
+gtk_entry_set_filter_regex (GtkEntry *entry,
+			    GRegex   *filter_regex)
+{
+  GtkEntryPrivate *priv;
+
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+
+  priv = GTK_ENTRY_GET_PRIVATE (entry);
+
+  if (priv->filter_regex)
+    g_regex_unref (priv->filter_regex);
+
+  if (filter_regex)
+    g_regex_ref (filter_regex);
+
+  priv->filter_regex = filter_regex;
+}
+
+GRegex *
+gtk_entry_get_filter_regex (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+  priv = GTK_ENTRY_GET_PRIVATE (entry);
+
+  return priv->filter_regex;
+}
+
+gboolean
+gtk_entry_text_matches_filter (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_ENTRY (entry), FALSE);
+
+  priv = GTK_ENTRY_GET_PRIVATE (entry);
+
+  /* no filter, any text is valid */
+  if (!priv->filter_regex)
+    return TRUE;
+
+  return priv->filter_match;
+}
+
 #define __GTK_ENTRY_C__
 #include "gtkaliasdef.c"
Index: gtkentry.h
===================================================================
--- gtkentry.h	(revision 18083)
+++ gtkentry.h	(working copy)
@@ -34,6 +34,7 @@
 #include <gtk/gtkmenu.h>
 #include <gtk/gtkentrycompletion.h>
 #include <pango/pango.h>
+#include <glib/gregex.h>
 
 G_BEGIN_DECLS
 
@@ -134,11 +135,11 @@ struct _GtkEntryClass
   void (* copy_clipboard)     (GtkEntry       *entry);
   void (* paste_clipboard)    (GtkEntry       *entry);
   void (* toggle_overwrite)   (GtkEntry       *entry);
+  void (* match_changed)      (GtkEntry       *entry);
 
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
-  void (*_gtk_reserved3) (void);
 };
 
 GType      gtk_entry_get_type       		(void) G_GNUC_CONST;
@@ -197,6 +198,12 @@ void           gtk_entry_set_cursor_hadj
                                                  GtkAdjustment *adjustment);
 GtkAdjustment* gtk_entry_get_cursor_hadjustment (GtkEntry      *entry);
 
+/* Filter regular expressions */
+void           gtk_entry_set_filter_regex       (GtkEntry      *entry,
+						 GRegex        *filter_regex);
+GRegex*        gtk_entry_get_filter_regex       (GtkEntry      *entry);
+gboolean       gtk_entry_text_matches_filter    (GtkEntry      *entry);
+
 /* Deprecated compatibility functions
  */
 
