1 /* Copyright (c) 2007, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "modest-validating-entry.h"
31 #include <modest-ui-constants.h>
32 #include <string.h> /* For strlen(). */
34 /* Include config.h so that _() works: */
40 G_DEFINE_TYPE (ModestValidatingEntry, modest_validating_entry, GTK_TYPE_ENTRY);
42 G_DEFINE_TYPE (ModestValidatingEntry, modest_validating_entry, HILDON_TYPE_ENTRY);
45 #define VALIDATING_ENTRY_GET_PRIVATE(o) \
46 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_VALIDATING_ENTRY, ModestValidatingEntryPrivate))
48 typedef struct _ModestValidatingEntryPrivate ModestValidatingEntryPrivate;
50 struct _ModestValidatingEntryPrivate
52 /* A list of gunichar, rather than char*,
53 * because gunichar is easier to deal with internally,
54 * but gchar* is easier to supply from the external interface.
58 gboolean prevent_whitespace;
60 EasySetupValidatingEntryFunc func;
61 gpointer func_user_data;
63 EasySetupValidatingEntryMaxFunc max_func;
64 gpointer max_func_user_data;
68 modest_validating_entry_get_property (GObject *object, guint property_id,
69 GValue *value, GParamSpec *pspec)
71 switch (property_id) {
73 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
78 modest_validating_entry_set_property (GObject *object, guint property_id,
79 const GValue *value, GParamSpec *pspec)
81 switch (property_id) {
83 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
88 modest_validating_entry_dispose (GObject *object)
90 if (G_OBJECT_CLASS (modest_validating_entry_parent_class)->dispose)
91 G_OBJECT_CLASS (modest_validating_entry_parent_class)->dispose (object);
95 modest_validating_entry_finalize (GObject *object)
97 ModestValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (object);
99 /* Free the list and its items: */
100 if (priv->list_prevent) {
101 g_list_foreach (priv->list_prevent, (GFunc)&g_free, NULL);
102 g_list_free (priv->list_prevent);
105 G_OBJECT_CLASS (modest_validating_entry_parent_class)->finalize (object);
109 modest_validating_entry_class_init (ModestValidatingEntryClass *klass)
111 GObjectClass *object_class = G_OBJECT_CLASS (klass);
113 g_type_class_add_private (klass, sizeof (ModestValidatingEntryPrivate));
115 object_class->get_property = modest_validating_entry_get_property;
116 object_class->set_property = modest_validating_entry_set_property;
117 object_class->dispose = modest_validating_entry_dispose;
118 object_class->finalize = modest_validating_entry_finalize;
122 on_list_compare(gconstpointer a, gconstpointer b)
124 gunichar* unichar_a = (gunichar*)(a);
125 gunichar* unichar_b = (gunichar*)(b);
126 if(*unichar_a == *unichar_b)
129 return -1; /* Really, we should return > and <, but we don't use this for sorting. */
133 on_insert_text(GtkEditable *editable,
134 gchar *new_text, gint new_text_length,
138 ModestValidatingEntry *self = MODEST_VALIDATING_ENTRY (user_data);
139 ModestValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
144 /* Note: new_text_length is documented as the number of bytes, not characters. */
145 if(!g_utf8_validate (new_text, new_text_length, NULL))
148 /* Look at each UTF-8 character in the text (it could be several via a drop or a paste),
150 gboolean allow = TRUE;
151 gchar *iter = new_text; /* new_text seems to be NULL-terminated, though that is not documented. */
154 if(priv->list_prevent) {
155 /* If the character is in our prevent list,
156 * then do not allow this text to be entered.
158 * This prevents entry of all text, without removing the unwanted characters.
159 * It is debatable whether that is the best thing to do.
161 gunichar one_char = g_utf8_get_char (iter);
162 GList *found = g_list_find_custom(priv->list_prevent, &one_char, &on_list_compare);
167 priv->func(self, iter, priv->func_user_data);
173 if(priv->prevent_whitespace) {
174 /* Check for whitespace characters: */
175 gunichar one_char = g_utf8_get_char (iter);
176 if (g_unichar_isspace (one_char)) {
180 priv->func(self, NULL, priv->func_user_data);
186 /* Crashes. Don't know why: iter = g_utf8_next_char (iter);
187 * Maybe it doesn't check for null-termination. */
188 iter = g_utf8_find_next_char (iter, new_text + new_text_length);
191 /* Prevent more than the max characters.
192 * The regular GtkEntry does this already, but we also want to call a specified callback,
193 * so that the application can show a warning dialog. */
195 const gint max_num = gtk_entry_get_max_length (GTK_ENTRY (self));
197 const gchar *existing_text = gtk_entry_get_text (GTK_ENTRY(self));
198 const gint existing_length = existing_text ? g_utf8_strlen (existing_text, -1) : 0;
199 const gint new_length_chars = g_utf8_strlen (new_text, new_text_length);
201 if ((existing_length + new_length_chars) > max_num) {
202 priv->max_func (self, priv->max_func_user_data);
203 /* We shouldn't need to stop the signal because the underlying code will check too.
204 * Well, that would maybe be a performance optimization,
205 * but it's generally safer not to interfere too much. */
211 /* The signal documentation says: */
212 /* "by connecting to this signal and then stopping the signal with */
213 /* g_signal_stop_emission(), it is possible to modify the inserted text, */
214 g_signal_stop_emission_by_name (self, "insert-text");
220 modest_validating_entry_init (ModestValidatingEntry *self)
222 /* Connect to the GtkEditable::insert-text signal
223 * so we can filter out some characters:
224 * We connect _before_ so we can stop the default signal handler from running.
226 g_signal_connect (G_OBJECT (self), "insert-text", (GCallback)&on_insert_text, self);
229 ModestValidatingEntry*
230 modest_validating_entry_new (void)
232 ModestValidatingEntry *entry;
234 entry = g_object_new (MODEST_TYPE_VALIDATING_ENTRY, NULL);
236 #ifdef MODEST_TOOLKIT_HILDON2
237 hildon_gtk_widget_set_theme_size (GTK_WIDGET (entry), MODEST_EDITABLE_SIZE);
243 /** Specify characters that may not be entered into this GtkEntry.
245 * list: A list of gchar* strings. Each one identifies a UTF-8 character.
247 void modest_validating_entry_set_unallowed_characters (ModestValidatingEntry *self, GList *list)
249 ModestValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
251 /* Free the list and its items: */
252 if (priv->list_prevent) {
253 g_list_foreach (priv->list_prevent, (GFunc)&g_free, NULL);
254 g_list_free (priv->list_prevent);
257 /* Do a deep copy of the list, converting gchar* to gunichar: */
258 priv->list_prevent = NULL;
260 for (iter = list; iter != NULL; iter = iter->next) {
261 gunichar *one_char = g_new0 (gunichar, 1);
263 *one_char = g_utf8_get_char ((gchar*)iter->data);
267 priv->list_prevent = g_list_append (priv->list_prevent, one_char);
271 /** Specify that no whitespace characters may be entered into this GtkEntry.
274 void modest_validating_entry_set_unallowed_characters_whitespace (ModestValidatingEntry *self)
276 ModestValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
277 priv->prevent_whitespace = TRUE;
280 /** Set a callback to be called when the maximum number of characters have been entered.
281 * This may be used to show an informative dialog.
283 void modest_validating_entry_set_max_func (ModestValidatingEntry *self, EasySetupValidatingEntryMaxFunc func, gpointer user_data)
285 ModestValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
286 priv->max_func = func;
287 priv->max_func_user_data = user_data;
290 /** Set a callback to be called when a character was prevented so that a
291 * note can be shown by the application to inform the user. For whitespaces,
292 * character will be NULL
294 void modest_validating_entry_set_func (ModestValidatingEntry *self, EasySetupValidatingEntryFunc func, gpointer user_data)
296 ModestValidatingEntryPrivate *priv = VALIDATING_ENTRY_GET_PRIVATE (self);
298 priv->func_user_data = user_data;