1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301, USA.
19  */
20 
21 #include <glib/gi18n-lib.h>
22 #include <gdk/gdkkeysyms.h>
23 #include <gdk/gdk.h>
24 #include <string.h>
25 
26 #include "gdaui-entry.h"
27 
28 struct _GdauiEntryPrivate {
29 	gchar   *prefix;
30 	gint     prefix_len;
31 	gint     prefix_clen; /* UTF8 len */
32 	gchar   *suffix;
33 	gint     suffix_len;
34 	gint     suffix_clen; /* UTF8 len */
35 	gint     maxlen; /* UTF8 len */
36 	gboolean isnull;
37 	guchar   internal_changes;
38 };
39 
40 #define ENTER_INTERNAL_CHANGES(entry) (entry)->priv->internal_changes ++
41 #define LEAVE_INTERNAL_CHANGES(entry) (entry)->priv->internal_changes --
42 
43 static void gdaui_entry_class_init   (GdauiEntryClass *klass);
44 static void gdaui_entry_init         (GdauiEntry *entry);
45 static void gdaui_entry_finalize     (GObject *object);
46 static void gdaui_entry_set_property (GObject *object,
47 				      guint param_id,
48 				      const GValue *value,
49 				      GParamSpec *pspec);
50 static void gdaui_entry_get_property (GObject *object,
51 				      guint param_id,
52 				      GValue *value,
53 				      GParamSpec *pspec);
54 
55 void
_gdaui_entry_block_changes(GdauiEntry * entry)56 _gdaui_entry_block_changes (GdauiEntry *entry)
57 {
58 	ENTER_INTERNAL_CHANGES(entry);
59 }
60 
61 void
_gdaui_entry_unblock_changes(GdauiEntry * entry)62 _gdaui_entry_unblock_changes (GdauiEntry *entry)
63 {
64 	LEAVE_INTERNAL_CHANGES(entry);
65 }
66 
67 
68 static gchar *truncate_utf8_string (gchar *text, gint pos);
69 static void adjust_display (GdauiEntry *entry, gchar *existing_text);
70 
71 /* properties */
72 enum
73 {
74         PROP_0,
75 	PROP_PREFIX,
76 	PROP_SUFFIX,
77 	PROP_MAXLEN
78 };
79 
80 static void signal_handlers_block (GdauiEntry *entry);
81 static void signal_handlers_unblock (GdauiEntry *entry);
82 
83 static void changed_cb (GtkEditable *editable, gpointer data);
84 static void delete_text_cb (GtkEditable *editable, gint start_pos, gint end_pos, gpointer data);
85 static void insert_text_cb (GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data);
86 
87 
88 static GObjectClass *parent_class = NULL;
89 
90 GType
gdaui_entry_get_type(void)91 gdaui_entry_get_type (void)
92 {
93 	static GType type = 0;
94 
95 	if (G_UNLIKELY (type == 0)) {
96 		static const GTypeInfo type_info = {
97 			sizeof (GdauiEntryClass),
98 			NULL,		/* base_init */
99 			NULL,		/* base_finalize */
100 			(GClassInitFunc) gdaui_entry_class_init,
101 			NULL,		/* class_finalize */
102 			NULL,		/* class_data */
103 			sizeof (GdauiEntry),
104 			0,		/* n_preallocs */
105 			(GInstanceInitFunc) gdaui_entry_init,
106 			0
107 		};
108 
109 		type = g_type_register_static (GTK_TYPE_ENTRY, "GdauiEntry", &type_info, 0);
110 	}
111 
112 	return type;
113 }
114 
115 static void
gdaui_entry_class_init(GdauiEntryClass * klass)116 gdaui_entry_class_init (GdauiEntryClass *klass)
117 {
118 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
119 
120 	parent_class = g_type_class_peek_parent (klass);
121 
122 	object_class->finalize = gdaui_entry_finalize;
123 	klass->assume_insert = NULL;
124 	klass->assume_delete = NULL;
125 	klass->get_empty_text = NULL;
126 
127 	/* Properties */
128         object_class->set_property = gdaui_entry_set_property;
129         object_class->get_property = gdaui_entry_get_property;
130 
131         g_object_class_install_property (object_class, PROP_PREFIX,
132                                          g_param_spec_string ("prefix", NULL, NULL, NULL,
133 							      G_PARAM_READABLE | G_PARAM_WRITABLE));
134         g_object_class_install_property (object_class, PROP_SUFFIX,
135                                          g_param_spec_string ("suffix", NULL, NULL, NULL,
136 							      G_PARAM_READABLE | G_PARAM_WRITABLE));
137 	g_object_class_override_property (object_class, PROP_MAXLEN, "max-length");
138 }
139 
140 static void
gdaui_entry_init(GdauiEntry * entry)141 gdaui_entry_init (GdauiEntry *entry)
142 {
143 	entry->priv = g_new0 (GdauiEntryPrivate, 1);
144 	entry->priv->prefix = NULL;
145 	entry->priv->suffix = NULL;
146 	entry->priv->maxlen = 65535; /* eg. unlimited for GtkEntry */
147 	entry->priv->isnull = TRUE;
148 	entry->priv->internal_changes = 0;
149 
150 	g_signal_connect (G_OBJECT (entry), "delete-text",
151 			  G_CALLBACK (delete_text_cb), NULL);
152 
153 	g_signal_connect (G_OBJECT (entry), "insert-text",
154 			  G_CALLBACK (insert_text_cb), NULL);
155 
156 	g_signal_connect (G_OBJECT (entry), "changed",
157 			  G_CALLBACK (changed_cb), NULL);
158 }
159 
160 static void
gdaui_entry_finalize(GObject * object)161 gdaui_entry_finalize (GObject *object)
162 {
163 	GdauiEntry *entry;
164 
165         g_return_if_fail (object != NULL);
166         g_return_if_fail (GDAUI_IS_ENTRY (object));
167 
168         entry = GDAUI_ENTRY (object);
169         if (entry->priv) {
170 		g_free (entry->priv->prefix);
171 		g_free (entry->priv->suffix);
172                 g_free (entry->priv);
173                 entry->priv = NULL;
174         }
175 
176         /* parent class */
177         parent_class->finalize (object);
178 }
179 
180 static void
gdaui_entry_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)181 gdaui_entry_set_property (GObject *object,
182 				    guint param_id,
183 				    const GValue *value,
184 				    GParamSpec *pspec)
185 {
186 	GdauiEntry *entry;
187 	const gchar *str;
188 	gchar *otext;
189 
190         entry = GDAUI_ENTRY (object);
191         if (entry->priv) {
192                 switch (param_id) {
193                 case PROP_PREFIX:
194 			otext = gdaui_entry_get_text (entry);
195 			g_free (entry->priv->prefix);
196 			entry->priv->prefix = NULL;
197 			entry->priv->prefix_len = 0;
198 
199 			str = g_value_get_string (value);
200 			if (str) {
201 				if (! g_utf8_validate (str, -1, NULL))
202 					g_warning (_("Invalid UTF-8 format!"));
203 				else {
204 					entry->priv->prefix = g_strdup (str);
205 					entry->priv->prefix_len = strlen (str);
206 					entry->priv->prefix_clen = g_utf8_strlen (str, -1);
207 				}
208 			}
209 			adjust_display (entry, otext);
210 			g_free (otext);
211                         break;
212                 case PROP_SUFFIX:
213 			otext = gdaui_entry_get_text (entry);
214 			g_free (entry->priv->suffix);
215 			entry->priv->suffix = NULL;
216 			entry->priv->suffix_len = 0;
217 
218 			str = g_value_get_string (value);
219 			if (str) {
220 				if (! g_utf8_validate (str, -1, NULL))
221 					g_warning (_("Invalid UTF-8 format!"));
222 				else {
223 					entry->priv->suffix = g_strdup (str);
224 					entry->priv->suffix_len = strlen (str);
225 					entry->priv->suffix_clen = g_utf8_strlen (str, -1);
226 				}
227 			}
228 			adjust_display (entry, otext);
229 			g_free (otext);
230                         break;
231 		case PROP_MAXLEN:
232 			entry->priv->maxlen = g_value_get_int (value);
233 			otext = gdaui_entry_get_text (entry);
234 			adjust_display (entry, otext);
235 			g_free (otext);
236 			break;
237                 default:
238                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
239                         break;
240                 }
241         }
242 }
243 
244 static void
gdaui_entry_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)245 gdaui_entry_get_property (GObject *object,
246 			     guint param_id,
247 			     GValue *value,
248 			     GParamSpec *pspec)
249 {
250 	GdauiEntry *entry;
251 
252         entry = GDAUI_ENTRY (object);
253         if (entry->priv) {
254                 switch (param_id) {
255                 case PROP_PREFIX:
256 			g_value_set_string (value, entry->priv->prefix);
257                         break;
258                 case PROP_SUFFIX:
259 			g_value_set_string (value, entry->priv->suffix);
260                         break;
261 		case PROP_MAXLEN:
262 			g_value_set_int (value, entry->priv->maxlen);
263 			break;
264                 default:
265                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
266                         break;
267                 }
268         }
269 }
270 
271 static void
signal_handlers_block(GdauiEntry * entry)272 signal_handlers_block (GdauiEntry *entry)
273 {
274 	ENTER_INTERNAL_CHANGES (entry);
275 	g_signal_handlers_block_by_func (entry, G_CALLBACK (insert_text_cb), NULL);
276 	g_signal_handlers_block_by_func (entry, G_CALLBACK (delete_text_cb), NULL);
277 }
278 
279 static void
signal_handlers_unblock(GdauiEntry * entry)280 signal_handlers_unblock (GdauiEntry *entry)
281 {
282 	g_signal_handlers_unblock_by_func (entry, G_CALLBACK (insert_text_cb), NULL);
283 	g_signal_handlers_unblock_by_func (entry, G_CALLBACK (delete_text_cb), NULL);
284 	LEAVE_INTERNAL_CHANGES (entry);
285 }
286 
287 /*
288  * truncate_utf8_string
289  * @text: a string, not %NULL
290  * @pos: the position where the string wil be truncated <=> text[pos]=0 for ASCII
291  *
292  * Returns: @text
293  */
294 static gchar *
truncate_utf8_string(gchar * text,gint pos)295 truncate_utf8_string (gchar *text, gint pos)
296 {
297 	gchar *ptr;
298 	gint i;
299 	for (ptr = text, i = 0; (i < pos) && ptr && *ptr; ptr = g_utf8_next_char (ptr), i++);
300 	if (i == pos)
301 		*ptr = 0;
302 	return text;
303 }
304 
305 /*
306  * Computes new new display
307  *
308  * WARNING: @existing_text may be modified!!!
309  */
310 static void
adjust_display(GdauiEntry * entry,gchar * existing_text)311 adjust_display (GdauiEntry *entry, gchar *existing_text)
312 {
313 	gchar *tmp;
314 
315 	if (!entry->priv->isnull) {
316 		signal_handlers_block (entry);
317 		if (g_utf8_strlen (existing_text, -1) > entry->priv->maxlen)
318 			truncate_utf8_string (existing_text, entry->priv->maxlen);
319 		tmp = g_strdup_printf ("%s%s%s",
320 				       entry->priv->prefix ? entry->priv->prefix : "",
321 				       existing_text ? existing_text : "",
322 				       entry->priv->suffix ? entry->priv->suffix : "");
323 
324 		gtk_entry_set_text (GTK_ENTRY (entry), tmp); /* emits a "changed" signal */
325 		g_free (tmp);
326 		signal_handlers_unblock (entry);
327 	}
328 }
329 
330 /**
331  * gdaui_entry_new:
332  * @prefix: (allow-none): a prefix (not modifiable) string, or %NULL
333  * @suffix: (allow-none): a suffix (not modifiable) string, or %NULL
334  *
335  * Creates a new #GdauiEntry widget.
336  *
337  * Returns: (transfer full): the newly created #GdauiEntry widget.
338  */
339 GtkWidget*
gdaui_entry_new(const gchar * prefix,const gchar * suffix)340 gdaui_entry_new (const gchar *prefix, const gchar *suffix)
341 {
342 	GObject *obj;
343 
344 	obj = g_object_new (GDAUI_TYPE_ENTRY, "prefix", prefix, "suffix", suffix, NULL);
345 	return GTK_WIDGET (obj);
346 }
347 
348 /**
349  * gdaui_entry_set_max_length:
350  * @entry: a #GdauiEntry.
351  * @max: the maximum length of the entry, or 0 for no maximum.
352  *
353  * Sets the maximum allowed length of the contents of the widget.
354  * If the current contents are longer than the given length, then they will be truncated to fit.
355  *
356  * The difference with gtk_entry_set_max_length() is that the max length does not take into account
357  * the prefix and/or suffix parts which may have been set.
358  */
359 void
gdaui_entry_set_max_length(GdauiEntry * entry,gint max)360 gdaui_entry_set_max_length (GdauiEntry *entry, gint max)
361 {
362 	g_return_if_fail (GDAUI_IS_ENTRY (entry));
363 
364 	g_object_set (G_OBJECT (entry), "max-length", max, NULL);
365 }
366 
367 /**
368  * gdaui_entry_get_text:
369  * @entry: a #GdauiEntry.
370  *
371  * Get a new string containing the contents of the widget as a string without the
372  * prefix and/or suffix and/or format if they have been specified. This method differs
373  * from calling gtk_entry_get_text() since the latest will return the complete text
374  * in @entry including prefix and/or suffix and/or format.
375  *
376  * Note: %NULL may be returned if this method is called while the widget is working on some
377  * internal modifications, or if gdaui_entry_set_text() was called with a %NULL
378  * as its @text argument.
379  *
380  * Returns: a new string, or %NULL
381  */
382 gchar *
gdaui_entry_get_text(GdauiEntry * entry)383 gdaui_entry_get_text (GdauiEntry *entry)
384 {
385 	gchar *text;
386 
387 	g_return_val_if_fail (GDAUI_IS_ENTRY (entry), NULL);
388 
389 	if (entry->priv->isnull)
390 		text = NULL;
391 	else {
392 		const gchar *ctext;
393 		gint len;
394 		ctext = gtk_entry_get_text (GTK_ENTRY (entry));
395 		if (ctext) {
396 			len = strlen (ctext);
397 			text = g_strdup (ctext);
398 			if (entry->priv->prefix) {
399 				len -= entry->priv->prefix_len;
400 				memmove (text, text + entry->priv->prefix_len, len+1);
401 			}
402 			if (entry->priv->suffix) {
403 				len -= entry->priv->suffix_len;
404 				text [len] = 0;
405 			}
406 		}
407 		else
408 			text = g_strdup ("");
409 	}
410 
411 	return text;
412 }
413 
414 /**
415  * gdaui_entry_set_text:
416  * @entry: a #GdauiEntry widget
417  * @text: (allow-none): the text to set into @entry, or %NULL
418  *
419  * Sets @text into @entry.
420  *
421  * As a side effect, if @text is %NULL, then the entry will
422  * be completely empty, whereas if @text is the empty string (""), then
423  * @entry will display the prefix and/or suffix and/or format string if they have
424  * been set. Except this case, calling this method is similar to calling
425  * gtk_entry_set_text()
426  */
427 void
gdaui_entry_set_text(GdauiEntry * entry,const gchar * text)428 gdaui_entry_set_text (GdauiEntry *entry, const gchar *text)
429 {
430 	g_return_if_fail (GDAUI_IS_ENTRY (entry));
431 
432 	if (text) {
433 		entry->priv->isnull = TRUE;
434 		signal_handlers_block (entry);
435 		gtk_entry_set_text (GTK_ENTRY (entry), "");
436 		signal_handlers_unblock (entry);
437 		ENTER_INTERNAL_CHANGES(entry);
438 		gtk_entry_set_text (GTK_ENTRY (entry), text); /* emits the "insert-text" signal which is treated */
439 		entry->priv->isnull = FALSE; /* in case it has not been set */
440 		LEAVE_INTERNAL_CHANGES(entry);
441 		g_signal_emit_by_name (entry, "changed");
442 	}
443 	else {
444 		entry->priv->isnull = TRUE;
445 		signal_handlers_block (entry);
446 		gtk_entry_set_text (GTK_ENTRY (entry), "");
447 		signal_handlers_unblock (entry);
448 		g_signal_emit_by_name (entry, "changed");
449 	}
450 }
451 
452 /**
453  * gdaui_entry_set_prefix:
454  * @entry: a #GdauiEntry widget
455  * @prefix: a prefix string
456  *
457  * Sets @prefix as a prefix string of @entry: that string will always be displayed in the
458  * text entry, will not be modifiable, and won't be part of the returned text
459  */
460 void
gdaui_entry_set_prefix(GdauiEntry * entry,const gchar * prefix)461 gdaui_entry_set_prefix (GdauiEntry *entry, const gchar *prefix)
462 {
463 	g_return_if_fail (GDAUI_IS_ENTRY (entry));
464 
465 	g_object_set (G_OBJECT (entry), "prefix", prefix, NULL);
466 }
467 
468 /**
469  * gdaui_entry_set_suffix:
470  * @entry: a #GdauiEntry widget
471  * @suffix: a suffix string
472  *
473  * Sets @suffix as a suffix string of @entry: that string will always be displayed in the
474  * text entry, will not be modifiable, and won't be part of the returned text
475  */
476 void
gdaui_entry_set_suffix(GdauiEntry * entry,const gchar * suffix)477 gdaui_entry_set_suffix (GdauiEntry *entry, const gchar *suffix)
478 {
479 	g_return_if_fail (GDAUI_IS_ENTRY (entry));
480 
481 	g_object_set (G_OBJECT (entry), "suffix", suffix, NULL);
482 }
483 
484 
485 /**
486  * gdaui_entry_set_width_chars:
487  * @entry: a #GdauiEntry widget
488  * @max_width: maximum width, or -1
489  *
490  * Sets @entry's maximum width in characters, without taking into account
491  * any prefix or suffix (which will automatically be handled). If you want to take
492  * a prefix or suffix into account direclty, then use gtk_entry_set_width_chars()
493  */
494 void
gdaui_entry_set_width_chars(GdauiEntry * entry,gint max_width)495 gdaui_entry_set_width_chars (GdauiEntry *entry, gint max_width)
496 {
497 	g_return_if_fail (GDAUI_IS_ENTRY (entry));
498 	if (max_width < 0)
499 		gtk_entry_set_width_chars (GTK_ENTRY (entry), -1);
500 	else {
501 		max_width += entry->priv->prefix_clen;
502 		max_width += entry->priv->suffix_clen;
503 		gtk_entry_set_width_chars (GTK_ENTRY (entry), max_width);
504 	}
505 }
506 
507 /*
508  * callbacks
509  */
510 
511 static void
changed_cb(GtkEditable * editable,G_GNUC_UNUSED gpointer data)512 changed_cb (GtkEditable *editable, G_GNUC_UNUSED gpointer data)
513 {
514         GdauiEntry *entry = (GdauiEntry*) editable;
515         if (entry->priv->internal_changes > 0)
516                 g_signal_stop_emission_by_name (editable, "changed");
517 }
518 
519 static void
delete_text_cb(GtkEditable * editable,gint start_pos,gint end_pos,G_GNUC_UNUSED gpointer data)520 delete_text_cb (GtkEditable *editable, gint start_pos, gint end_pos, G_GNUC_UNUSED gpointer data)
521 {
522 	const gchar *otext = NULL;
523 	gint len = 0;
524 	gint nstart = start_pos, nend = end_pos;
525 	GdauiEntry *entry = GDAUI_ENTRY (editable);
526 
527 	signal_handlers_block (entry);
528 	if (entry->priv->prefix) {
529 		if (nstart < entry->priv->prefix_clen)
530 			nstart = entry->priv->prefix_clen;
531 	}
532 	if (nend < 0) {
533 		otext = gtk_entry_get_text ((GtkEntry*) entry);
534 		len = g_utf8_strlen (otext, -1);
535 		nend = len;
536 	}
537 
538 	if (nend - nstart < 1) {
539 		g_signal_stop_emission_by_name (editable, "delete-text");
540 		signal_handlers_unblock (entry);
541 		return;
542 	}
543 
544 	if (entry->priv->suffix) {
545 		if (!otext) {
546 			otext = gtk_entry_get_text ((GtkEntry*) entry);
547 			len = g_utf8_strlen (otext, -1);
548 		}
549 		if (nend - nstart == 1) {
550 			if ((nstart >= len - entry->priv->suffix_clen)) {
551 				nstart = len - entry->priv->suffix_clen - 1;
552 				nend = nstart + 1;
553 				g_signal_stop_emission_by_name (editable, "delete-text");
554 				signal_handlers_unblock (entry);
555 				gtk_editable_set_position (editable, nend);
556 				gtk_editable_delete_text (editable, nstart, nend);
557 				return;
558 			}
559 		}
560 		if (nend > len - entry->priv->suffix_clen)
561 			nend = len - entry->priv->suffix_clen;
562 	}
563 
564 	if (GDAUI_ENTRY_GET_CLASS (editable)->assume_delete) {
565 		g_signal_stop_emission_by_name (editable, "delete-text");
566 		GDAUI_ENTRY_GET_CLASS (editable)->assume_delete (entry, nstart - entry->priv->prefix_clen,
567 								 nend - entry->priv->prefix_clen,
568 								 entry->priv->prefix_clen);
569 		//g_print ("Subclass assumes text delete\n");
570 	}
571 	else if ((nstart != start_pos) || (nend != end_pos)) {
572 		g_signal_stop_emission_by_name (editable, "delete-text");
573 		if (nstart != nend)
574 			gtk_editable_delete_text (editable, nstart, nend);
575 	}
576 
577 	signal_handlers_unblock (entry);
578 	g_signal_emit_by_name (entry, "changed");
579 }
580 
581 
582 static void
insert_text_cb(GtkEditable * editable,const gchar * text,gint text_length,gint * position,G_GNUC_UNUSED gpointer data)583 insert_text_cb (GtkEditable *editable, const gchar *text, gint text_length, gint *position,
584 		G_GNUC_UNUSED gpointer data)
585 {
586 	const gchar *otext;
587 	gint clen;
588 	GdauiEntry *entry = GDAUI_ENTRY (editable);
589 	gint text_clen;
590 	gint start;
591 
592 	if (gtk_editable_get_selection_bounds (editable, &start, NULL))
593 		*position = start;
594 
595 	signal_handlers_block (entry);
596 
597  	if (entry->priv->isnull) {
598 		gchar *etext = NULL;
599 		entry->priv->isnull = FALSE;
600 		if (GDAUI_ENTRY_GET_CLASS (editable)->get_empty_text)
601 			etext = GDAUI_ENTRY_GET_CLASS (editable)->get_empty_text (entry);
602 		adjust_display (entry, etext ? etext : "");
603 		g_free (etext);
604 	}
605 
606 	otext = gtk_entry_get_text ((GtkEntry*) entry);
607 	clen = g_utf8_strlen (otext, -1);
608 
609 	/* adjust insert position */
610 	if (entry->priv->prefix) {
611 		if (*position < entry->priv->prefix_clen)
612 			*position = entry->priv->prefix_clen;
613 	}
614 	if (entry->priv->suffix) {
615 		if (*position > clen - entry->priv->suffix_clen)
616 			*position = clen - entry->priv->suffix_clen;
617 	}
618 
619 	/* test if the whole insertion is Ok */
620 	text_clen = g_utf8_strlen (text, text_length);
621 	if (clen - entry->priv->prefix_clen - entry->priv->suffix_clen + text_clen > entry->priv->maxlen) {
622 		gchar *itext;
623 		gint nallowed;
624 		nallowed = entry->priv->maxlen - (clen - entry->priv->prefix_clen - entry->priv->suffix_clen);
625 		g_signal_stop_emission_by_name (editable, "insert-text");
626 		itext = g_strdup (text);
627 		itext [nallowed] = 0; /* FIXME: convert nallowed to gchar */
628 		/*g_print ("Corrected by length insert text: [%s]\n", itext);*/
629 		if (*itext)
630 			gtk_editable_insert_text (editable, itext, nallowed, position);
631 		g_free (itext);
632 
633 		signal_handlers_unblock (entry);
634 		g_signal_emit_by_name (entry, "changed");
635 	}
636 	else if (GDAUI_ENTRY_GET_CLASS (editable)->assume_insert) {
637 		g_signal_stop_emission_by_name (editable, "insert-text");
638 		//g_print ("Subclass assumes text insert\n");
639 		gint pos = *position - entry->priv->prefix_clen;
640 		GDAUI_ENTRY_GET_CLASS (editable)->assume_insert (entry, text, text_length,
641 								 &pos, entry->priv->prefix_clen);
642 		*position = pos + entry->priv->prefix_clen;
643 
644 		signal_handlers_unblock (entry);
645 		g_signal_emit_by_name (entry, "changed");
646 	}
647 	else
648 		signal_handlers_unblock (entry);
649 }
650