Added files from osso-modest-easysetup
[modest] / src / maemo / easysetup / modest-validating-entry.c
diff --git a/src/maemo/easysetup/modest-validating-entry.c b/src/maemo/easysetup/modest-validating-entry.c
new file mode 100644 (file)
index 0000000..79f2ea3
--- /dev/null
@@ -0,0 +1,205 @@
+/* Copyright (c) 2007, Nokia Corporation
+ * All rights reserved.
+ *
+ */
+
+#include "modest-validating-entry.h"
+#include <gtk/gtksignal.h> /* For the gtk_signal_stop_emit_by_name() convenience function. */
+#include <string.h> /* For strlen(). */
+
+G_DEFINE_TYPE (EasysetupValidatingEntry, easysetup_validating_entry, GTK_TYPE_ENTRY);
+
+#define VALIDATING_ENTRY_GET_PRIVATE(o) \
+       (G_TYPE_INSTANCE_GET_PRIVATE ((o), EASYSETUP_TYPE_VALIDATING_ENTRY, EasysetupValidatingEntryPrivate))
+
+typedef struct _EasysetupValidatingEntryPrivate EasysetupValidatingEntryPrivate;
+
+struct _EasysetupValidatingEntryPrivate
+{
+       /* A list of gunichar, rather than char*,
+        * because gunichar is easier to deal with internally,
+        * but gchar* is easier to supply from the external interface.
+        */
+       GList *list_prevent;
+       
+       gboolean prevent_whitespace;
+};
+
+static void
+easysetup_validating_entry_get_property (GObject *object, guint property_id,
+                                                                                                                       GValue *value, GParamSpec *pspec)
+{
+       switch (property_id) {
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+       }
+}
+
+static void
+easysetup_validating_entry_set_property (GObject *object, guint property_id,
+                                                                                                                       const GValue *value, GParamSpec *pspec)
+{
+       switch (property_id) {
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+       }
+}
+
+static void
+easysetup_validating_entry_dispose (GObject *object)
+{
+       if (G_OBJECT_CLASS (easysetup_validating_entry_parent_class)->dispose)
+               G_OBJECT_CLASS (easysetup_validating_entry_parent_class)->dispose (object);
+}
+
+static void
+easysetup_validating_entry_finalize (GObject *object)
+{
+       EasysetupValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (object);
+       
+       /* Free the list and its items: */
+       if (priv->list_prevent) {
+               g_list_foreach (priv->list_prevent, (GFunc)&g_free, NULL);
+               g_list_free (priv->list_prevent);
+       }
+       
+       G_OBJECT_CLASS (easysetup_validating_entry_parent_class)->finalize (object);
+}
+
+static void
+easysetup_validating_entry_class_init (EasysetupValidatingEntryClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       g_type_class_add_private (klass, sizeof (EasysetupValidatingEntryPrivate));
+
+       object_class->get_property = easysetup_validating_entry_get_property;
+       object_class->set_property = easysetup_validating_entry_set_property;
+       object_class->dispose = easysetup_validating_entry_dispose;
+       object_class->finalize = easysetup_validating_entry_finalize;
+}
+
+static gint
+on_list_compare(gconstpointer a, gconstpointer b)
+{
+       gunichar* unichar_a = (gunichar*)(a);
+       gunichar* unichar_b = (gunichar*)(b);
+       if(*unichar_a == *unichar_b)
+               return 0;
+       else
+               return -1; /* Really, we should return > and <, but we don't use this for sorting. */
+}
+                                             
+static void 
+on_insert_text(GtkEditable *editable,
+       gchar *new_text, gint new_text_length, 
+       gint *position,
+    gpointer user_data)
+{
+       EasysetupValidatingEntry *self = EASYSETUP_VALIDATING_ENTRY (user_data);
+       EasysetupValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
+       
+       if(!new_text_length)
+               return;
+               
+       /* Note: new_text_length is documented as the number of bytes, not characters. */
+       if(!g_utf8_validate (new_text, new_text_length, NULL))
+               return;
+       
+       /* Look at each UTF-8 character in the text (it could be several via a drop or a paste),
+        * and check them */
+       gboolean allow = TRUE;
+       gchar *iter = new_text; /* new_text seems to be NULL-terminated, though that is not documented. */
+       while (iter)
+       {
+               if(priv->list_prevent) {
+                       /* If the character is in our prevent list, 
+                        * then do not allow this text to be entered.
+                        * 
+                        * This prevents entry of all text, without removing the unwanted characters.
+                        * It is debatable whether that is the best thing to do.
+                        */
+                       gunichar one_char = g_utf8_get_char (iter);
+                       GList *found = g_list_find_custom(priv->list_prevent, &one_char, &on_list_compare);
+                       if(found) {
+                               allow = FALSE;
+                               break;
+                       }       
+               }
+               
+               if(priv->prevent_whitespace) {
+                       /* Check for whitespace characters: */
+                       gunichar one_char = g_utf8_get_char (iter);
+                       if (g_unichar_isspace (one_char)) {
+                               allow = FALSE;
+                               break;
+                       }
+               }
+
+               /* Crashes. Don't know why: iter = g_utf8_next_char (iter); 
+                * Maybe it doesn't check for null-termination. */      
+               iter = g_utf8_find_next_char (iter, new_text + new_text_length);
+       }
+       
+       if(!allow) {
+               /* The signal documentation says 
+                * "by connecting to this signal and then stopping the signal with 
+                * gtk_signal_emit_stop(), it is possible to modify the inserted text, 
+                * or prevent it from being inserted entirely."
+                */
+                gtk_signal_emit_stop_by_name (GTK_OBJECT (self), "insert-text");
+       }
+} 
+                                            
+static void
+easysetup_validating_entry_init (EasysetupValidatingEntry *self)
+{
+       /* Connect to the GtkEditable::insert-text signal 
+        * so we can filter out some characters:
+        * We connect _before_ so we can stop the default signal handler from running.
+        */
+       g_signal_connect (G_OBJECT (self), "insert-text", (GCallback)&on_insert_text, self);
+}
+
+EasysetupValidatingEntry*
+easysetup_validating_entry_new (void)
+{
+       return g_object_new (EASYSETUP_TYPE_VALIDATING_ENTRY, NULL);
+}
+
+/** Specify characters that may not be entered into this GtkEntry.
+ *  
+ * list: A list of gchar* strings. Each one identifies a UTF-8 character.
+ */
+void easysetup_validating_entry_set_unallowed_characters (EasysetupValidatingEntry *self, GList *list)
+{
+       EasysetupValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
+           
+       /* Free the list and its items: */      
+       if (priv->list_prevent) {
+               g_list_foreach (priv->list_prevent, (GFunc)&g_free, NULL);
+               g_list_free (priv->list_prevent);
+       }
+     
+    /* Do a deep copy of the list, converting gchar* to gunichar: */
+    priv->list_prevent = NULL;
+    GList *iter = NULL;               
+    for (iter = list; iter != NULL; iter = iter->next) {
+       gunichar *one_char = g_new0 (gunichar, 1);
+       if(iter->data)
+               *one_char = g_utf8_get_char ((gchar*)iter->data);
+       else
+               *one_char = 0;
+               
+       priv->list_prevent = g_list_append (priv->list_prevent, one_char);      
+    }
+}
+
+/** Specify that no whitespace characters may be entered into this GtkEntry.
+ *  
+ */
+void easysetup_validating_entry_set_unallowed_characters_whitespace (EasysetupValidatingEntry *self)
+{
+       EasysetupValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
+       priv->prevent_whitespace = TRUE;
+}