[libgda] Improved data entry widgets for constrained typing



commit 79fdb04b80d9c253bab6bb70d4ffada0aac49136
Author: Vivien Malerba <malerba gnome-db org>
Date:   Mon Sep 28 20:34:58 2009 +0200

    Improved data entry widgets for constrained typing
    
    as the old implementation was not really maintainable because
    too complicated.

 configure.in                                       |    2 +-
 libgda-ui/data-entries/.gitignore                  |    4 +-
 libgda-ui/data-entries/Makefile.am                 |   14 +-
 .../gdaui-data-cell-renderer-textual.c             |    3 +
 libgda-ui/data-entries/gdaui-entry-common-time.c   |   74 +-
 libgda-ui/data-entries/gdaui-entry-number.c        |  503 ++++++
 libgda-ui/data-entries/gdaui-entry-number.h        |   61 +
 ...ing-number.xml.in => gdaui-entry-number.xml.in} |    4 +-
 libgda-ui/data-entries/gdaui-entry-string.c        |  188 +--
 ...ing-string.xml.in => gdaui-entry-string.xml.in} |    0
 libgda-ui/data-entries/gdaui-entry.c               |  619 +++++++
 libgda-ui/data-entries/gdaui-entry.h               |  101 ++
 libgda-ui/data-entries/gdaui-format-entry.c        | 1783 --------------------
 libgda-ui/data-entries/gdaui-format-entry.h        |   70 -
 libgda-ui/data-entries/gdaui-formatted-entry.c     |  489 ++++++
 libgda-ui/data-entries/gdaui-formatted-entry.h     |   57 +
 libgda-ui/data-entries/gdaui-numeric-entry.c       |  695 ++++++++
 libgda-ui/data-entries/gdaui-numeric-entry.h       |   57 +
 libgda-ui/data-entries/plugins/gdaui-entry-cidr.c  |   15 +-
 libgda-ui/gdaui-init.c                             |   21 +-
 libgda-ui/libgda-ui.symbols                        |   27 +-
 libgda/handlers/gda-handler-time.c                 |   35 +-
 po/POTFILES.in                                     |    4 +-
 testing/.gitignore                                 |    1 +
 testing/Makefile.am                                |   11 +-
 testing/gdaui-test-widget-entry.c                  |  409 +++++
 26 files changed, 3184 insertions(+), 2063 deletions(-)
---
diff --git a/configure.in b/configure.in
index 2138e3f..6837d40 100644
--- a/configure.in
+++ b/configure.in
@@ -30,7 +30,7 @@ m4_undefine([minor])
 m4_undefine([micro])
 
 dnl required versions of other tools.
-m4_define([req_ver_glib],	[2.12.0])
+m4_define([req_ver_glib],	[2.12.11])
 
 #
 # Making releases:
diff --git a/libgda-ui/data-entries/.gitignore b/libgda-ui/data-entries/.gitignore
index 9e4e717..a0680bc 100644
--- a/libgda-ui/data-entries/.gitignore
+++ b/libgda-ui/data-entries/.gitignore
@@ -1,2 +1,2 @@
-gdaui-entry-string-number.xml
-gdaui-entry-string-string.xml
+gdaui-entry-number.xml
+gdaui-entry-string.xml
diff --git a/libgda-ui/data-entries/Makefile.am b/libgda-ui/data-entries/Makefile.am
index 2a004ae..be69277 100644
--- a/libgda-ui/data-entries/Makefile.am
+++ b/libgda-ui/data-entries/Makefile.am
@@ -25,12 +25,15 @@ libgda_ui_data_entries_headers = \
 	gdaui-entry-none.h \
 	gdaui-entry-shell.h \
 	gdaui-entry-string.h \
+	gdaui-entry-number.h \
 	gdaui-entry-common-time.h \
 	gdaui-entry-time.h \
 	gdaui-entry-timestamp.h \
 	gdaui-entry-date.h \
 	gdaui-entry-wrapper.h \
-	gdaui-format-entry.h 
+	gdaui-entry.h \
+	gdaui-formatted-entry.h \
+	gdaui-numeric-entry.h
 
 libgda_ui_data_entries_la_SOURCES = \
 	$(libgda_ui_data_entries_headers) \
@@ -47,17 +50,20 @@ libgda_ui_data_entries_la_SOURCES = \
 	gdaui-entry-none.c \
 	gdaui-entry-shell.c \
 	gdaui-entry-string.c \
+	gdaui-entry-number.c \
 	gdaui-entry-common-time.c \
 	gdaui-entry-time.c \
 	gdaui-entry-timestamp.c \
 	gdaui-entry-date.c \
 	gdaui-entry-wrapper.c \
-	gdaui-format-entry.c
+	gdaui-entry.c \
+	gdaui-formatted-entry.c \
+	gdaui-numeric-entry.c
 
 xmldir   = $(datadir)/libgda-4.0/ui
 xml_in_files = \
-	gdaui-entry-string-string.xml.in \
-	gdaui-entry-string-number.xml.in
+	gdaui-entry-string.xml.in \
+	gdaui-entry-number.xml.in
 
 @INTLTOOL_XML_RULE@
 
diff --git a/libgda-ui/data-entries/gdaui-data-cell-renderer-textual.c b/libgda-ui/data-entries/gdaui-data-cell-renderer-textual.c
index a7058d1..06dcc53 100644
--- a/libgda-ui/data-entries/gdaui-data-cell-renderer-textual.c
+++ b/libgda-ui/data-entries/gdaui-data-cell-renderer-textual.c
@@ -29,6 +29,7 @@
 #include "gdaui-data-cell-renderer-textual.h"
 #include "gdaui-data-entry.h"
 #include "gdaui-entry-string.h"
+#include "gdaui-entry-number.h"
 #include "gdaui-entry-date.h"
 #include "gdaui-entry-time.h"
 #include "gdaui-entry-timestamp.h"
@@ -683,6 +684,8 @@ gdaui_data_cell_renderer_textual_start_editing (GtkCellRenderer      *cell,
 		entry = gdaui_entry_time_new (datacell->priv->dh);
 	else if (datacell->priv->type == GDA_TYPE_TIMESTAMP)
 		entry = gdaui_entry_timestamp_new (datacell->priv->dh);
+	else if (gdaui_entry_number_is_type_numeric (datacell->priv->type))
+		entry = gdaui_entry_number_new (datacell->priv->dh, datacell->priv->type, datacell->priv->options);
 	else
 		entry = gdaui_entry_string_new (datacell->priv->dh, datacell->priv->type, datacell->priv->options);
 
diff --git a/libgda-ui/data-entries/gdaui-entry-common-time.c b/libgda-ui/data-entries/gdaui-entry-common-time.c
index 2c4361e..1cabe04 100644
--- a/libgda-ui/data-entries/gdaui-entry-common-time.c
+++ b/libgda-ui/data-entries/gdaui-entry-common-time.c
@@ -23,7 +23,7 @@
 #include <libgda/gda-data-handler.h>
 #include <gdk/gdkkeysyms.h>
 #include <string.h>
-#include "gdaui-format-entry.h"
+#include "gdaui-formatted-entry.h"
 
 /* 
  * Main static functions 
@@ -328,51 +328,51 @@ real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
 	if (type == G_TYPE_DATE) {
 		if (value) {
 			if (gda_value_is_null ((GValue *) value))
-				gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date), NULL);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL);
 			else {
 				gchar *str;
 				
 				str = gda_data_handler_get_str_from_value (dh, value);
-				gtk_entry_set_text (GTK_ENTRY (mgtim->priv->entry_date), str);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), str);
 				g_free (str);
 			}
 		}
 		else 
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date), NULL);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL);
 	}
 	else if (type == GDA_TYPE_TIME) {
 		if (value) {
 			if (gda_value_is_null ((GValue *) value))
-				gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time), NULL);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL);
 			else {
 				gchar *str;
 				
 				str = gda_data_handler_get_str_from_value (dh, value);
-				gtk_entry_set_text (GTK_ENTRY (mgtim->priv->entry_time), str);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), str);
 				g_free (str);
 			}
 		}
 		else 
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time), NULL);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL);
 	}
 	else if (type == GDA_TYPE_TIMESTAMP) {
 		if (value) {
 			if (gda_value_is_null ((GValue *) value))
-				gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time), NULL);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL);
 			else {
 				gchar *str, *ptr;
 				
 				str = gda_data_handler_get_str_from_value (dh, value);
 				ptr = strtok (str, " ");
-				gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date), ptr);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), ptr);
 				ptr = strtok (NULL, " ");
-				gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time), ptr);
+				gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), ptr);
 				g_free (str);
 			}
 		}
 		else {
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date), NULL);
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time), NULL);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), NULL);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), NULL);
 		}
 	}
 	else
@@ -404,12 +404,12 @@ real_get_value (GdauiEntryWrapper *mgwrap)
 	dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
 
 	if (type == G_TYPE_DATE) {
-		str2 = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date));
+		str2 = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_date));
 		value = gda_data_handler_get_value_from_str (dh, str2, type); /* FIXME: not SQL but STR */
 		g_free (str2);
 	}
 	else if (type == GDA_TYPE_TIME) {
-		str2 = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time));
+		str2 = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_time));
 		value = gda_data_handler_get_value_from_str (dh, str2, type);
 		if (value && (G_VALUE_TYPE (value) != GDA_TYPE_NULL) &&
 		    mgtim->priv->last_value_set && 
@@ -427,8 +427,8 @@ real_get_value (GdauiEntryWrapper *mgwrap)
 	else if (type == GDA_TYPE_TIMESTAMP) {
 		gchar *tmpstr, *tmpstr2;
 
-		tmpstr = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time));
-		tmpstr2 = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date));
+		tmpstr = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_time));
+		tmpstr2 = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_date));
 		str2 = g_strdup_printf ("%s %s", tmpstr2, tmpstr);
 		g_free (tmpstr);
 		g_free (tmpstr2);
@@ -567,14 +567,20 @@ create_entry_date (GdauiEntryCommonTime *mgtim)
 
 	/* text entry */
 	dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgtim));
-	wid = gdaui_format_entry_new ();
 	if (GDA_IS_HANDLER_TIME (dh)) {
-		gchar *str;
+		gchar *str, *mask, *ptr;
 		str = gda_handler_time_get_format (GDA_HANDLER_TIME (dh), G_TYPE_DATE);
-		gdaui_format_entry_set_format (GDAUI_FORMAT_ENTRY (wid), str, NULL, NULL);
-		gtk_entry_set_width_chars (GTK_ENTRY (wid), g_utf8_strlen (str, -1));
+		mask = g_strdup (str);
+		for (ptr = mask; *ptr; ptr++) {
+			if (*ptr == '0')
+				*ptr = '-';
+		}
+		wid = gdaui_formatted_entry_new (str, mask);
 		g_free (str);
+		g_free (mask);
 	}
+	else
+		wid = gdaui_entry_new (NULL, NULL);
 	gtk_box_pack_start (GTK_BOX (hb), wid, FALSE, FALSE, 0);
 	gtk_widget_show (wid);
 	mgtim->priv->entry_date = wid;
@@ -633,17 +639,19 @@ internal_set_time (GtkWidget *widget, GdauiEntryCommonTime *mgtim)
 	type = gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgtim));
 	if (type == GDA_TYPE_TIMESTAMP) {
 		GdaDataHandler *dh;
-		gchar *str;
+		gchar *str, *str2;
 		GValue *value;
 
 		dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgtim));
-		str = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time));
-		value = gda_data_handler_get_value_from_str (dh, str, type);
+		str = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (mgtim->priv->entry_time));
+		str2 = g_strdup_printf ("01.01.2000 %s", str);
+		g_free (str);
+		value = gda_data_handler_get_value_from_str (dh, str2, type);
 		if (!value || gda_value_is_null (value)) 
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_time), "00:00:00");
+			gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_time), "00:00:00");
 		if (value)
 			gda_value_free (value);
-		g_free (str);
+		g_free (str2);
 	}
 }
 
@@ -722,7 +730,7 @@ date_day_selected (GtkCalendar *calendar, GdauiEntryCommonTime *mgtim)
         buffer[sizeof(buffer)-1] = '\0';
 
         str_utf8 = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
-        gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgtim->priv->entry_date), str_utf8);
+        gdaui_entry_set_text (GDAUI_ENTRY (mgtim->priv->entry_date), str_utf8);
         g_free (str_utf8);
 }
 
@@ -902,14 +910,20 @@ create_entry_time (GdauiEntryCommonTime *mgtim)
 
 	/* text entry */
 	dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgtim));
-        wid = gdaui_format_entry_new ();
 	if (GDA_IS_HANDLER_TIME (dh)) {
-		gchar *str;
+		gchar *str, *mask, *ptr;
 		str = gda_handler_time_get_format (GDA_HANDLER_TIME (dh), GDA_TYPE_TIME);
-		gdaui_format_entry_set_format (GDAUI_FORMAT_ENTRY (wid), str, NULL, NULL);
-		gtk_entry_set_width_chars (GTK_ENTRY (wid), g_utf8_strlen (str, -1));
+		mask = g_strdup (str);
+		for (ptr = mask; *ptr; ptr++) {
+			if (*ptr == '0')
+				*ptr = '-';
+		}
+		wid = gdaui_formatted_entry_new (str, mask);
 		g_free (str);
+		g_free (mask);
 	}
+	else
+		wid = gdaui_entry_new (NULL, NULL);
         mgtim->priv->entry_time = wid;
 
         /* format tooltip */
diff --git a/libgda-ui/data-entries/gdaui-entry-number.c b/libgda-ui/data-entries/gdaui-entry-number.c
new file mode 100644
index 0000000..bdd1367
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-entry-number.c
@@ -0,0 +1,503 @@
+/* gdaui-entry-number.c
+ *
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <glib/gi18n-lib.h>
+#include "gdaui-entry-number.h"
+#include "gdaui-numeric-entry.h"
+#include <libgda/gda-data-handler.h>
+#include "gdk/gdkkeysyms.h"
+
+/* 
+ * Main static functions 
+ */
+static void gdaui_entry_number_class_init (GdauiEntryNumberClass *klass);
+static void gdaui_entry_number_init (GdauiEntryNumber *srv);
+static void gdaui_entry_number_dispose (GObject *object);
+static void gdaui_entry_number_finalize (GObject *object);
+
+static void gdaui_entry_number_set_property (GObject *object,
+					     guint param_id,
+					     const GValue *value,
+					     GParamSpec *pspec);
+static void gdaui_entry_number_get_property (GObject *object,
+					     guint param_id,
+					     GValue *value,
+					     GParamSpec *pspec);
+
+/* properties */
+enum
+{
+	PROP_0,
+	PROP_EDITING_CANCELLED,
+	PROP_OPTIONS
+};
+
+/* GtkCellEditable interface */
+static void gdaui_entry_number_cell_editable_init (GtkCellEditableIface *iface);
+static void gdaui_entry_number_start_editing (GtkCellEditable *iface, GdkEvent *event);
+static void sync_entry_options (GdauiEntryNumber *mgstr);
+
+/* virtual functions */
+static GtkWidget *create_entry (GdauiEntryWrapper *mgwrap);
+static void       real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value);
+static GValue    *real_get_value (GdauiEntryWrapper *mgwrap);
+static void       connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb);
+static gboolean   expand_in_layout (GdauiEntryWrapper *mgwrap);
+static void       set_editable (GdauiEntryWrapper *mgwrap, gboolean editable);
+static void       grab_focus (GdauiEntryWrapper *mgwrap);
+
+/* options */
+static void set_entry_options (GdauiEntryNumber *mgstr, const gchar *options);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+/* private structure */
+struct _GdauiEntryNumberPrivate
+{
+	GtkWidget     *entry;
+
+	guchar         thousand_sep;
+	guint16        nb_decimals;
+	gchar         *currency;
+
+	gulong         entry_change_sig;
+};
+
+static void
+gdaui_entry_number_cell_editable_init (GtkCellEditableIface *iface)
+{
+	iface->start_editing = gdaui_entry_number_start_editing;
+}
+
+GType
+gdaui_entry_number_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (GdauiEntryNumberClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gdaui_entry_number_class_init,
+			NULL,
+			NULL,
+			sizeof (GdauiEntryNumber),
+			0,
+			(GInstanceInitFunc) gdaui_entry_number_init
+		};
+		
+		static const GInterfaceInfo cell_editable_info = {
+			(GInterfaceInitFunc) gdaui_entry_number_cell_editable_init,    /* interface_init */
+			NULL,                                                 /* interface_finalize */
+			NULL                                                  /* interface_data */
+		};
+		
+		type = g_type_register_static (GDAUI_TYPE_ENTRY_WRAPPER, "GdauiEntryNumber", &info, 0);
+		g_type_add_interface_static (type, GTK_TYPE_CELL_EDITABLE, &cell_editable_info);
+	}
+	return type;
+}
+
+static void
+gdaui_entry_number_class_init (GdauiEntryNumberClass * klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = gdaui_entry_number_dispose;
+	object_class->finalize = gdaui_entry_number_finalize;
+
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->create_entry = create_entry;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->real_set_value = real_set_value;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->real_get_value = real_get_value;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->connect_signals = connect_signals;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->expand_in_layout = expand_in_layout;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->set_editable = set_editable;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->grab_focus = grab_focus;
+
+	/* Properties */
+	object_class->set_property = gdaui_entry_number_set_property;
+	object_class->get_property = gdaui_entry_number_get_property;
+
+	g_object_class_install_property (object_class, PROP_EDITING_CANCELLED,
+					 g_param_spec_boolean ("editing_cancelled", NULL, NULL, FALSE, G_PARAM_READABLE));
+	g_object_class_install_property (object_class, PROP_OPTIONS,
+					 g_param_spec_string ("options", NULL, NULL, NULL, G_PARAM_WRITABLE));
+}
+
+static void
+gdaui_entry_number_init (GdauiEntryNumber * mgstr)
+{
+	mgstr->priv = g_new0 (GdauiEntryNumberPrivate, 1);
+	mgstr->priv->entry = NULL;
+
+	mgstr->priv->thousand_sep = 0;
+	mgstr->priv->nb_decimals = G_MAXUINT16; /* unlimited number of decimals */
+	mgstr->priv->currency = NULL;
+
+	mgstr->priv->entry_change_sig = 0;
+}
+
+gboolean
+gdaui_entry_number_is_type_numeric (GType type)
+{
+	if ((type == G_TYPE_INT64) || (type == G_TYPE_UINT64) || (type == G_TYPE_DOUBLE) ||
+	    (type == G_TYPE_INT) || (type == GDA_TYPE_NUMERIC) || (type == G_TYPE_FLOAT) ||
+	    (type == GDA_TYPE_SHORT) || (type == GDA_TYPE_USHORT) || (type == G_TYPE_CHAR) ||
+	    (type == G_TYPE_UCHAR) || (type == G_TYPE_LONG) || (type ==  G_TYPE_ULONG) || (type == G_TYPE_UINT))
+		return TRUE;
+	else
+		return FALSE;
+}
+
+/**
+ * gdaui_entry_number_new
+ * @dh: the data handler to be used by the new widget
+ * @type: the requested data type (compatible with @dh)
+ *
+ * Creates a new widget which is mainly a GtkEntry
+ *
+ * Returns: the new widget
+ */
+GtkWidget *
+gdaui_entry_number_new (GdaDataHandler *dh, GType type, const gchar *options)
+{
+	GObject *obj;
+	GdauiEntryNumber *mgstr;
+
+	g_return_val_if_fail (GDA_IS_DATA_HANDLER (dh), NULL);
+	g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
+	g_return_val_if_fail (gda_data_handler_accepts_g_type (dh, type), NULL);
+	g_return_val_if_fail (gdaui_entry_number_is_type_numeric (type), NULL);
+
+	obj = g_object_new (GDAUI_TYPE_ENTRY_NUMBER, "handler", dh, NULL);
+	mgstr = GDAUI_ENTRY_NUMBER (obj);
+	gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (mgstr), type);
+
+	g_object_set (obj, "options", options, NULL);
+
+	return GTK_WIDGET (obj);
+}
+
+static gboolean focus_out_cb (GtkWidget *widget, GdkEventFocus *event, GdauiEntryNumber *mgstr);
+static void
+gdaui_entry_number_dispose (GObject   * object)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GDAUI_IS_ENTRY_NUMBER (object));
+
+	mgstr = GDAUI_ENTRY_NUMBER (object);
+	if (mgstr->priv) {
+		if (mgstr->priv->entry) {
+			g_signal_handlers_disconnect_by_func (G_OBJECT (mgstr->priv->entry),
+							      G_CALLBACK (focus_out_cb), mgstr);
+			mgstr->priv->entry = NULL;
+		}
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+static void
+gdaui_entry_number_finalize (GObject   * object)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GDAUI_IS_ENTRY_NUMBER (object));
+
+	mgstr = GDAUI_ENTRY_NUMBER (object);
+	if (mgstr->priv) {
+		g_free (mgstr->priv->currency);
+		g_free (mgstr->priv);
+		mgstr->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->finalize (object);
+}
+
+static void
+gdaui_entry_number_set_property (GObject *object,
+				    guint param_id,
+				    const GValue *value,
+				    GParamSpec *pspec)
+{
+	GdauiEntryNumber *mgstr;
+
+	mgstr = GDAUI_ENTRY_NUMBER (object);
+	if (mgstr->priv) {
+		switch (param_id) {
+		case PROP_OPTIONS:
+			set_entry_options (mgstr, g_value_get_string (value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+			break;
+		}
+	}
+}
+
+static void
+gdaui_entry_number_get_property (GObject *object,
+			     guint param_id,
+			     GValue *value,
+			     GParamSpec *pspec)
+{
+	GdauiEntryNumber *mgstr;
+
+	mgstr = GDAUI_ENTRY_NUMBER (object);
+	if (mgstr->priv) {
+		switch (param_id) {
+		case PROP_EDITING_CANCELLED:
+			g_value_set_boolean (value, GTK_ENTRY (mgstr->priv->entry)->editing_canceled);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+			break;
+		}
+	}
+}
+
+static GtkWidget *
+create_entry (GdauiEntryWrapper *mgwrap)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_val_if_fail (GDAUI_IS_ENTRY_NUMBER (mgwrap), NULL);
+	mgstr = GDAUI_ENTRY_NUMBER (mgwrap);
+
+	mgstr->priv->entry = gdaui_numeric_entry_new (gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgwrap)));
+	sync_entry_options (mgstr);
+
+	return mgstr->priv->entry;
+}
+
+static void
+real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
+{
+	GdauiEntryNumber *mgstr;
+	GdaDataHandler *dh;
+	gchar *text;
+
+	g_return_if_fail (GDAUI_IS_ENTRY_NUMBER (mgwrap));
+	mgstr = GDAUI_ENTRY_NUMBER (mgwrap);
+
+	dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
+
+	text = gda_data_handler_get_str_from_value (dh, value);
+	if (value) {
+		if (gda_value_is_null ((GValue *) value))
+			gdaui_entry_set_text (GDAUI_ENTRY (mgstr->priv->entry), NULL);
+		else 
+			gdaui_entry_set_text (GDAUI_ENTRY (mgstr->priv->entry), text);
+	}
+	else
+		gdaui_entry_set_text (GDAUI_ENTRY (mgstr->priv->entry), NULL);
+
+	g_free (text);
+}
+
+static GValue *
+real_get_value (GdauiEntryWrapper *mgwrap)
+{
+	GValue *value;
+	GdauiEntryNumber *mgstr;
+
+	g_return_val_if_fail (GDAUI_IS_ENTRY_NUMBER (mgwrap), NULL);
+	mgstr = GDAUI_ENTRY_NUMBER (mgwrap);
+	g_return_val_if_fail (mgstr->priv, NULL);
+
+	value = gdaui_numeric_entry_get_value (GDAUI_NUMERIC_ENTRY (mgstr->priv->entry));
+
+	if (!value) {
+		/* in case the contents of the GtkEntry cannot be interpreted as a GValue */
+		value = gda_value_new_null ();
+	}
+
+	return value;
+}
+
+typedef void (*Callback2) (gpointer, gpointer);
+static gboolean
+focus_out_cb (GtkWidget *widget, GdkEventFocus *event, GdauiEntryNumber *mgstr)
+{
+	GCallback activate_cb;
+	activate_cb = g_object_get_data (G_OBJECT (widget), "_activate_cb");
+	g_assert (activate_cb);
+	((Callback2)activate_cb) (widget, mgstr);
+
+	return FALSE;
+}
+
+static void
+connect_signals (GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_if_fail (GDAUI_IS_ENTRY_NUMBER (mgwrap));
+	mgstr = GDAUI_ENTRY_NUMBER (mgwrap);
+	g_return_if_fail (mgstr->priv);
+	g_object_set_data (G_OBJECT (mgstr->priv->entry), "_activate_cb", activate_cb);
+
+	mgstr->priv->entry_change_sig = g_signal_connect (G_OBJECT (mgstr->priv->entry), "changed",
+							  modify_cb, mgwrap);
+	g_signal_connect (G_OBJECT (mgstr->priv->entry), "activate",
+			  activate_cb, mgwrap);
+	g_signal_connect (G_OBJECT (mgstr->priv->entry), "focus-out-event",
+			  G_CALLBACK (focus_out_cb), mgstr);
+}
+
+static gboolean
+expand_in_layout (GdauiEntryWrapper *mgwrap)
+{
+	return FALSE;
+}
+
+static void
+set_editable (GdauiEntryWrapper *mgwrap, gboolean editable)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_if_fail (GDAUI_IS_ENTRY_NUMBER (mgwrap));
+	mgstr = GDAUI_ENTRY_NUMBER (mgwrap);
+
+	gtk_entry_set_editable (GTK_ENTRY (mgstr->priv->entry), editable);
+}
+
+static void
+grab_focus (GdauiEntryWrapper *mgwrap)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_if_fail (GDAUI_IS_ENTRY_NUMBER (mgwrap));
+	mgstr = GDAUI_ENTRY_NUMBER (mgwrap);
+
+	gtk_widget_grab_focus (mgstr->priv->entry);
+}
+
+/*
+ * GtkCellEditable interface
+ */
+static void
+gtk_cell_editable_entry_editing_done_cb (GtkEntry *entry, GdauiEntryNumber *mgstr) 
+{
+	gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (mgstr));
+}
+
+static void
+gtk_cell_editable_entry_remove_widget_cb (GtkEntry *entry, GdauiEntryNumber *mgstr) 
+{
+	gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (mgstr));
+}
+
+static void
+gdaui_entry_number_start_editing (GtkCellEditable *iface, GdkEvent *event)
+{
+	GdauiEntryNumber *mgstr;
+
+	g_return_if_fail (iface && GDAUI_IS_ENTRY_NUMBER (iface));
+	mgstr = GDAUI_ENTRY_NUMBER (iface);
+
+	g_object_set (G_OBJECT (mgstr->priv->entry), "has_frame", FALSE, "xalign", 0., NULL);
+
+	gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (mgstr->priv->entry), event);
+	g_signal_connect (G_OBJECT (mgstr->priv->entry), "editing_done",
+			  G_CALLBACK (gtk_cell_editable_entry_editing_done_cb), mgstr);
+	g_signal_connect (G_OBJECT (mgstr->priv->entry), "remove_widget",
+			  G_CALLBACK (gtk_cell_editable_entry_remove_widget_cb), mgstr);
+	gdaui_entry_shell_refresh (GDAUI_ENTRY_SHELL (mgstr));
+
+	gtk_widget_grab_focus (mgstr->priv->entry);
+	gtk_widget_queue_draw (GTK_WIDGET (mgstr));
+}
+
+/*
+ * Options handling
+ */
+
+static guchar
+get_default_thousands_sep ()
+{
+	static guchar value = 255;
+
+	if (value == 255) {
+		gchar text[20];
+		sprintf (text, "%'f", 1234.);
+		if (text[1] == '2')
+			value = ' ';
+		else
+			value = text[1];	
+	}
+	return value;
+}
+
+static void
+set_entry_options (GdauiEntryNumber *mgstr, const gchar *options)
+{
+	g_assert (mgstr->priv);
+
+	if (options && *options) {
+                GdaQuarkList *params;
+                const gchar *str;
+
+                params = gda_quark_list_new_from_string (options);
+
+		str = gda_quark_list_find (params, "THOUSAND_SEP");
+		if (str) {
+			if ((*str == 't') || (*str == 'T'))
+				mgstr->priv->thousand_sep = get_default_thousands_sep ();
+			else
+				mgstr->priv->thousand_sep = 0;
+		}
+		str = gda_quark_list_find (params, "NB_DECIMALS");
+		if (str) 
+			mgstr->priv->nb_decimals = atoi (str);
+		str = gda_quark_list_find (params, "CURRENCY");
+		if (str) {
+			g_free (mgstr->priv->currency);
+			mgstr->priv->currency = g_strdup_printf ("%s ", str);
+		}
+                gda_quark_list_free (params);
+		sync_entry_options (mgstr);
+        }
+}
+
+/* sets the correct options for mgstr->priv->entry if it exists */
+static void
+sync_entry_options (GdauiEntryNumber *mgstr)
+{
+	if (!mgstr->priv->entry)
+		return;
+
+	g_object_set (G_OBJECT (mgstr->priv->entry), 
+		      "type", gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgstr)),
+		      "n_decimals", mgstr->priv->nb_decimals,
+		      "thousands-sep", mgstr->priv->thousand_sep,
+		      "prefix", mgstr->priv->currency,
+		      NULL);
+	g_signal_emit_by_name (mgstr->priv->entry, "changed");
+}
diff --git a/libgda-ui/data-entries/gdaui-entry-number.h b/libgda-ui/data-entries/gdaui-entry-number.h
new file mode 100644
index 0000000..8d7110b
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-entry-number.h
@@ -0,0 +1,61 @@
+/* gdaui-entry-number.h
+ *
+ * Copyright (C) 2009 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+
+#ifndef __GDAUI_ENTRY_NUMBER_H_
+#define __GDAUI_ENTRY_NUMBER_H_
+
+#include "gdaui-entry-wrapper.h"
+
+G_BEGIN_DECLS
+
+#define GDAUI_TYPE_ENTRY_NUMBER          (gdaui_entry_number_get_type())
+#define GDAUI_ENTRY_NUMBER(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, gdaui_entry_number_get_type(), GdauiEntryNumber)
+#define GDAUI_ENTRY_NUMBER_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, gdaui_entry_number_get_type (), GdauiEntryNumberClass)
+#define GDAUI_IS_ENTRY_NUMBER(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, gdaui_entry_number_get_type ())
+
+
+typedef struct _GdauiEntryNumber GdauiEntryNumber;
+typedef struct _GdauiEntryNumberClass GdauiEntryNumberClass;
+typedef struct _GdauiEntryNumberPrivate GdauiEntryNumberPrivate;
+
+
+/* struct for the object's data */
+struct _GdauiEntryNumber
+{
+	GdauiEntryWrapper              object;
+	GdauiEntryNumberPrivate       *priv;
+};
+
+/* struct for the object's class */
+struct _GdauiEntryNumberClass
+{
+	GdauiEntryWrapperClass         parent_class;
+};
+
+GType        gdaui_entry_number_get_type        (void) G_GNUC_CONST;
+GtkWidget   *gdaui_entry_number_new             (GdaDataHandler *dh, GType type, const gchar *options);
+
+
+gboolean     gdaui_entry_number_is_type_numeric (GType type);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda-ui/data-entries/gdaui-entry-string-number.xml.in b/libgda-ui/data-entries/gdaui-entry-number.xml.in
similarity index 88%
rename from libgda-ui/data-entries/gdaui-entry-string-number.xml.in
rename to libgda-ui/data-entries/gdaui-entry-number.xml.in
index 2b644cb..4dcdeba 100644
--- a/libgda-ui/data-entries/gdaui-entry-string-number.xml.in
+++ b/libgda-ui/data-entries/gdaui-entry-number.xml.in
@@ -3,11 +3,9 @@
   <parameters>
     <parameter id="THOUSAND_SEP" _name="Use 1000s separators" _descr="Use thousands separator as specified by current locale" gdatype="gboolean">
       <gda_value>FALSE</gda_value>
-          <gda_value>FALSE</gda_value>
     </parameter>
     <parameter id="NB_DECIMALS" _name="Decimals" _descr="Number of decimals" gdatype="guint">
-      <gda_value>4</gda_value>
-          <gda_value>4</gda_value>
+      <gda_value>2</gda_value>
     </parameter>
     <parameter id="CURRENCY" _name="Currency symbol" _descr="A currency symbol" source="currencies:0" gdatype="gchararray"/>
   </parameters>
diff --git a/libgda-ui/data-entries/gdaui-entry-string.c b/libgda-ui/data-entries/gdaui-entry-string.c
index 3a36471..173754b 100644
--- a/libgda-ui/data-entries/gdaui-entry-string.c
+++ b/libgda-ui/data-entries/gdaui-entry-string.c
@@ -21,20 +21,19 @@
 #include <glib/gi18n-lib.h>
 #include <string.h>
 #include "gdaui-entry-string.h"
-#include "gdaui-format-entry.h"
+#include "gdaui-entry.h"
 #include <libgda/gda-data-handler.h>
 #include "gdk/gdkkeysyms.h"
-#include <pango/pango.h>
 
 #define MAX_ACCEPTED_STRING_LENGTH 500
 
 /* 
  * Main static functions 
  */
-static void gdaui_entry_string_class_init (GdauiEntryStringClass * class);
-static void gdaui_entry_string_init (GdauiEntryString * srv);
-static void gdaui_entry_string_dispose (GObject   * object);
-static void gdaui_entry_string_finalize (GObject   * object);
+static void gdaui_entry_string_class_init (GdauiEntryStringClass *klass);
+static void gdaui_entry_string_init (GdauiEntryString *srv);
+static void gdaui_entry_string_dispose (GObject *object);
+static void gdaui_entry_string_finalize (GObject *object);
 
 static void gdaui_entry_string_set_property (GObject *object,
 					  guint param_id,
@@ -74,18 +73,11 @@ static void set_entry_options (GdauiEntryString *mgstr, const gchar *options);
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass  *parent_class = NULL;
 
-typedef enum {
-	TYPE_UNKNOWN, /* when value is undefined */
-	TYPE_NUMERIC,
-	TYPE_NOT_NUMERIC
-} InternalType;
-
 /* private structure */
 struct _GdauiEntryStringPrivate
 {
 	gboolean       multiline;
 	gboolean       hidden;
-	InternalType   internal_type;
 	GtkWidget     *vbox;
 
 	GtkWidget     *entry;
@@ -95,9 +87,6 @@ struct _GdauiEntryStringPrivate
 	GtkWidget     *view;
 
 	gint           maxsize;
-	guchar         thousand_sep;
-	gint           nb_decimals;
-	gchar         *currency;
 
 	gulong         entry_change_sig;
 };
@@ -139,22 +128,22 @@ gdaui_entry_string_get_type (void)
 }
 
 static void
-gdaui_entry_string_class_init (GdauiEntryStringClass * class)
+gdaui_entry_string_class_init (GdauiEntryStringClass * klass)
 {
-	GObjectClass   *object_class = G_OBJECT_CLASS (class);
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
-	parent_class = g_type_class_peek_parent (class);
+	parent_class = g_type_class_peek_parent (klass);
 
 	object_class->dispose = gdaui_entry_string_dispose;
 	object_class->finalize = gdaui_entry_string_finalize;
 
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->create_entry = create_entry;
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->real_set_value = real_set_value;
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->real_get_value = real_get_value;
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->connect_signals = connect_signals;
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->expand_in_layout = expand_in_layout;
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->set_editable = set_editable;
-	GDAUI_ENTRY_WRAPPER_CLASS (class)->grab_focus = grab_focus;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->create_entry = create_entry;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->real_set_value = real_set_value;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->real_get_value = real_get_value;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->connect_signals = connect_signals;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->expand_in_layout = expand_in_layout;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->set_editable = set_editable;
+	GDAUI_ENTRY_WRAPPER_CLASS (klass)->grab_focus = grab_focus;
 
 	/* Properties */
 	object_class->set_property = gdaui_entry_string_set_property;
@@ -181,27 +170,11 @@ gdaui_entry_string_init (GdauiEntryString * mgstr)
 	mgstr->priv->view = NULL;
 	mgstr->priv->sw = NULL;
 
-	mgstr->priv->internal_type = TYPE_UNKNOWN;
-	mgstr->priv->maxsize = 0; /* unlimited */
-	mgstr->priv->thousand_sep = 0;
-	mgstr->priv->nb_decimals = -1; /* unlimited number of decimals */
-	mgstr->priv->currency = NULL;
+	mgstr->priv->maxsize = 65535; /* eg. unlimited for GtkEntry */
 
 	mgstr->priv->entry_change_sig = 0;
 }
 
-static InternalType
-determine_internal_type (GType type)
-{
-	if ((type == G_TYPE_INT64) || (type == G_TYPE_UINT64) || (type == G_TYPE_DOUBLE) ||
-	    (type == G_TYPE_INT) || (type == GDA_TYPE_NUMERIC) || (type == G_TYPE_FLOAT) ||
-	    (type == GDA_TYPE_SHORT) || (type == GDA_TYPE_USHORT) || (type == G_TYPE_CHAR) ||
-	    (type == G_TYPE_UCHAR) || (type == G_TYPE_LONG) || (type ==  G_TYPE_ULONG) || (type == G_TYPE_UINT))
-		return TYPE_NUMERIC;
-	else
-		return TYPE_NOT_NUMERIC;
-}
-
 /**
  * gdaui_entry_string_new
  * @dh: the data handler to be used by the new widget
@@ -220,12 +193,12 @@ gdaui_entry_string_new (GdaDataHandler *dh, GType type, const gchar *options)
 	g_return_val_if_fail (GDA_IS_DATA_HANDLER (dh), NULL);
 	g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
 	g_return_val_if_fail (gda_data_handler_accepts_g_type (dh, type), NULL);
+	g_return_val_if_fail (type == G_TYPE_STRING, NULL);
 
 	obj = g_object_new (GDAUI_TYPE_ENTRY_STRING, "handler", dh, NULL);
 	mgstr = GDAUI_ENTRY_STRING (obj);
 	gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (mgstr), type);
 
-	mgstr->priv->internal_type = determine_internal_type (type);
 	g_object_set (obj, "options", options, NULL);
 
 	return GTK_WIDGET (obj);
@@ -269,7 +242,6 @@ gdaui_entry_string_finalize (GObject   * object)
 
 	mgstr = GDAUI_ENTRY_STRING (object);
 	if (mgstr->priv) {
-		g_free (mgstr->priv->currency);
 		g_free (mgstr->priv);
 		mgstr->priv = NULL;
 	}
@@ -290,14 +262,6 @@ gdaui_entry_string_set_property (GObject *object,
 	if (mgstr->priv) {
 		switch (param_id) {
 		case PROP_MULTILINE:
-			if (mgstr->priv->internal_type == TYPE_UNKNOWN)
-				mgstr->priv->internal_type = 
-					determine_internal_type (gdaui_data_entry_get_value_type 
-								 (GDAUI_DATA_ENTRY (mgstr)));
-
-			if (mgstr->priv->internal_type == TYPE_NUMERIC)
-				return;
-
 			if (g_value_get_boolean (value) != mgstr->priv->multiline) {
 				mgstr->priv->multiline = g_value_get_boolean (value);
 				if (mgstr->priv->multiline) {
@@ -360,7 +324,7 @@ create_entry (GdauiEntryWrapper *mgwrap)
 	mgstr->priv->vbox = vbox;
 
 	/* one line entry */
-	mgstr->priv->entry = gdaui_format_entry_new ();
+	mgstr->priv->entry = gdaui_entry_new (NULL, NULL);
 	sync_entry_options (mgstr);
 	gtk_box_pack_start (GTK_BOX (vbox), mgstr->priv->entry, FALSE, TRUE, 0);
 	g_signal_connect_after (G_OBJECT (mgstr->priv->entry), "show", 
@@ -436,12 +400,12 @@ real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
 	/* fill the single line widget */
 	if (value) {
 		if (gda_value_is_null ((GValue *) value))
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgstr->priv->entry), NULL);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgstr->priv->entry), NULL);
 		else 
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgstr->priv->entry), text);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgstr->priv->entry), text);
 	}
 	else
-		gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgstr->priv->entry), NULL);
+		gdaui_entry_set_text (GDAUI_ENTRY (mgstr->priv->entry), NULL);
 
 	/* fill the multiline widget */
 	if (value) {
@@ -470,7 +434,7 @@ real_get_value (GdauiEntryWrapper *mgwrap)
 	dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
 	if (! mgstr->priv->multiline) {
 		gchar *cstr;
-		cstr = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgstr->priv->entry));
+		cstr = gdaui_entry_get_text (GDAUI_ENTRY (mgstr->priv->entry));
 		value = gda_data_handler_get_value_from_str (dh, cstr, gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgwrap)));
 		g_free (cstr);
 	}
@@ -616,23 +580,6 @@ gdaui_entry_string_start_editing (GtkCellEditable *iface, GdkEvent *event)
 /*
  * Options handling
  */
-
-static guchar
-get_default_thousands_sep ()
-{
-	static guchar value = 255;
-
-	if (value == 255) {
-		gchar text[20];
-		sprintf (text, "%f", 1234.);
-		if (text[1] == '2')
-			value = 0;
-		else
-			value = text[1];	
-	}
-	return value;
-}
-
 static void
 set_entry_options (GdauiEntryString *mgstr, const gchar *options)
 {
@@ -642,64 +589,39 @@ set_entry_options (GdauiEntryString *mgstr, const gchar *options)
                 GdaQuarkList *params;
                 const gchar *str;
 		
-		if (mgstr->priv->internal_type == TYPE_UNKNOWN)
-			mgstr->priv->internal_type = 
-				determine_internal_type (gdaui_data_entry_get_value_type 
-							 (GDAUI_DATA_ENTRY (mgstr)));
-
                 params = gda_quark_list_new_from_string (options);
-		if (mgstr->priv->internal_type == TYPE_NOT_NUMERIC) {
-			/* STRING specific options */
-			str = gda_quark_list_find (params, "MAX_SIZE");
-			if (str) 
-				mgstr->priv->maxsize = atoi (str);
-
-			str = gda_quark_list_find (params, "MULTILINE");
-			if (str) {
-				if ((*str == 't') || (*str == 'T'))
-					mgstr->priv->multiline = TRUE;
-				else
-					mgstr->priv->multiline = FALSE;
-			       
-			}
-
-			str = gda_quark_list_find (params, "HIDDEN");
-			if (str) {
-				if ((*str == 't') || (*str == 'T'))
-					mgstr->priv->hidden = TRUE;
-				else
-					mgstr->priv->hidden = FALSE;
-			}
 
-			if (mgstr->priv->entry) {
-				if (mgstr->priv->multiline) {
-					gtk_widget_hide (mgstr->priv->entry);
-					gtk_widget_show (mgstr->priv->sw);
-				}
-				else {
-					gtk_widget_show (mgstr->priv->entry);
-					gtk_widget_hide (mgstr->priv->sw);
-					gtk_entry_set_visibility (GTK_ENTRY (mgstr->priv->entry), 
-								  !mgstr->priv->hidden);
-				}
-			}
-                }
-		else {
-			/* NUMERIC specific options */
-			str = gda_quark_list_find (params, "THOUSAND_SEP");
-			if (str) {
-				if ((*str == 't') || (*str == 'T'))
-					mgstr->priv->thousand_sep = get_default_thousands_sep ();
-				else
-					mgstr->priv->thousand_sep = 0;
+		str = gda_quark_list_find (params, "MAX_SIZE");
+		if (str) 
+			mgstr->priv->maxsize = atoi (str);
+		
+		str = gda_quark_list_find (params, "MULTILINE");
+		if (str) {
+			if ((*str == 't') || (*str == 'T'))
+				mgstr->priv->multiline = TRUE;
+			else
+				mgstr->priv->multiline = FALSE;
+			
+		}
+		
+		str = gda_quark_list_find (params, "HIDDEN");
+		if (str) {
+			if ((*str == 't') || (*str == 'T'))
+				mgstr->priv->hidden = TRUE;
+			else
+				mgstr->priv->hidden = FALSE;
+		}
+		
+		if (mgstr->priv->entry) {
+			if (mgstr->priv->multiline) {
+				gtk_widget_hide (mgstr->priv->entry);
+				gtk_widget_show (mgstr->priv->sw);
 			}
-			str = gda_quark_list_find (params, "NB_DECIMALS");
-			if (str) 
-				mgstr->priv->nb_decimals = atoi (str);
-			str = gda_quark_list_find (params, "CURRENCY");
-			if (str) {
-				g_free (mgstr->priv->currency);
-				mgstr->priv->currency = g_strdup_printf ("%s ", str);
+			else {
+				gtk_widget_show (mgstr->priv->entry);
+				gtk_widget_hide (mgstr->priv->sw);
+				gtk_entry_set_visibility (GTK_ENTRY (mgstr->priv->entry), 
+							  !mgstr->priv->hidden);
 			}
 		}
                 gda_quark_list_free (params);
@@ -715,11 +637,7 @@ sync_entry_options (GdauiEntryString *mgstr)
 		return;
 
 	g_object_set (G_OBJECT (mgstr->priv->entry), 
-		      "edited_type", gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgstr)),
-		      "n_decimals", mgstr->priv->nb_decimals,
-		      "thousands_sep", mgstr->priv->thousand_sep,
-		      "prefix", mgstr->priv->currency,
-		      "max_length", mgstr->priv->maxsize,
+		      "max-length", mgstr->priv->maxsize,
 		      NULL);
 	g_signal_emit_by_name (mgstr->priv->entry, "changed");
 }
diff --git a/libgda-ui/data-entries/gdaui-entry-string-string.xml.in b/libgda-ui/data-entries/gdaui-entry-string.xml.in
similarity index 100%
rename from libgda-ui/data-entries/gdaui-entry-string-string.xml.in
rename to libgda-ui/data-entries/gdaui-entry-string.xml.in
diff --git a/libgda-ui/data-entries/gdaui-entry.c b/libgda-ui/data-entries/gdaui-entry.c
new file mode 100644
index 0000000..cf54df0
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-entry.c
@@ -0,0 +1,619 @@
+/* gdaui-entry.c
+ *
+ * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <string.h>
+
+#include "gdaui-entry.h"
+
+struct _GdauiEntryPrivate {
+	gchar   *prefix;
+	gint     prefix_len;
+	gint     prefix_clen; /* UTF8 len */
+	gchar   *suffix;
+	gint     suffix_len;
+	gint     suffix_clen; /* UTF8 len */
+	gint     maxlen; /* UTF8 len */
+	gboolean isnull;
+	guchar   internal_changes;
+};
+
+#define ENTER_INTERNAL_CHANGES(entry) (entry)->priv->internal_changes ++
+#define LEAVE_INTERNAL_CHANGES(entry) (entry)->priv->internal_changes --
+
+static void gdaui_entry_class_init   (GdauiEntryClass *klass);
+static void gdaui_entry_init         (GdauiEntry *entry);
+static void gdaui_entry_finalize     (GObject *object);
+static void gdaui_entry_set_property (GObject *object,
+				      guint param_id,
+				      const GValue *value,
+				      GParamSpec *pspec);
+static void gdaui_entry_get_property (GObject *object,
+				      guint param_id,
+				      GValue *value,
+				      GParamSpec *pspec);
+
+void
+_gdaui_entry_block_changes (GdauiEntry *entry)
+{
+	ENTER_INTERNAL_CHANGES(entry);
+}
+
+void
+_gdaui_entry_unblock_changes (GdauiEntry *entry)
+{
+	LEAVE_INTERNAL_CHANGES(entry);
+}
+
+
+static gchar *truncate_utf8_string (gchar *text, gint pos);
+static void adjust_display (GdauiEntry *entry, gchar *existing_text);
+
+/* properties */
+enum
+{
+        PROP_0,
+	PROP_PREFIX,
+	PROP_SUFFIX,
+	PROP_MAXLEN
+};
+
+static void signal_handlers_block (GdauiEntry *entry);
+static void signal_handlers_unblock (GdauiEntry *entry);
+
+static void changed_cb (GtkEditable *editable, gpointer data);
+static void delete_text_cb (GtkEditable *editable, gint start_pos, gint end_pos, gpointer data);
+static void insert_text_cb (GtkEditable *editable, const gchar *text, gint length, gint *position, gpointer data);
+
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gdaui_entry_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GdauiEntryClass),
+			NULL,		/* base_init */
+			NULL,		/* base_finalize */
+			(GClassInitFunc) gdaui_entry_class_init,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			sizeof (GdauiEntry),
+			0,		/* n_preallocs */
+			(GInstanceInitFunc) gdaui_entry_init,
+		};
+		
+		type = g_type_register_static (GTK_TYPE_ENTRY, "GdauiEntry", &type_info, 0);
+	}
+
+	return type;
+}
+
+static void
+gdaui_entry_class_init (GdauiEntryClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = gdaui_entry_finalize;
+	klass->assume_insert = NULL;
+	klass->assume_delete = NULL;
+	klass->get_empty_text = NULL;
+
+	/* Properties */
+        object_class->set_property = gdaui_entry_set_property;
+        object_class->get_property = gdaui_entry_get_property;
+
+        g_object_class_install_property (object_class, PROP_PREFIX,
+                                         g_param_spec_string ("prefix", NULL, NULL, NULL, 
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+        g_object_class_install_property (object_class, PROP_SUFFIX,
+                                         g_param_spec_string ("suffix", NULL, NULL, NULL, 
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_override_property (object_class, PROP_MAXLEN, "max-length");
+}
+
+static void
+gdaui_entry_init (GdauiEntry *entry)
+{
+	entry->priv = g_new0 (GdauiEntryPrivate, 1);
+	entry->priv->prefix = NULL;
+	entry->priv->suffix = NULL;
+	entry->priv->maxlen = 65535; /* eg. unlimited for GtkEntry */
+	entry->priv->isnull = TRUE;
+	entry->priv->internal_changes = 0;
+
+	g_signal_connect (G_OBJECT (entry), "delete-text",
+			  G_CALLBACK (delete_text_cb), NULL);
+
+	g_signal_connect (G_OBJECT (entry), "insert-text",
+			  G_CALLBACK (insert_text_cb), NULL);
+
+	g_signal_connect (G_OBJECT (entry), "changed",
+			  G_CALLBACK (changed_cb), NULL);
+}
+
+static void 
+gdaui_entry_finalize (GObject *object)
+{
+	GdauiEntry *entry;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDAUI_IS_ENTRY (object));
+
+        entry = GDAUI_ENTRY (object);
+        if (entry->priv) {
+		g_free (entry->priv->prefix);
+		g_free (entry->priv->suffix);
+                g_free (entry->priv);
+                entry->priv = NULL;
+        }
+
+        /* parent class */
+        parent_class->finalize (object);
+}
+
+static void 
+gdaui_entry_set_property (GObject *object,
+				    guint param_id,
+				    const GValue *value,
+				    GParamSpec *pspec)
+{
+	GdauiEntry *entry;
+	const gchar *str;
+	gchar *otext;
+
+        entry = GDAUI_ENTRY (object);
+        if (entry->priv) {
+                switch (param_id) {
+                case PROP_PREFIX:
+			otext = gdaui_entry_get_text (entry);
+			g_free (entry->priv->prefix);
+			entry->priv->prefix = NULL;
+			entry->priv->prefix_len = 0;
+
+			str = g_value_get_string (value);
+			if (str) {
+				if (! g_utf8_validate (str, -1, NULL))
+					g_warning (_("Invalid UTF-8 format!"));
+				else {
+					entry->priv->prefix = g_strdup (str);
+					entry->priv->prefix_len = strlen (str);
+					entry->priv->prefix_clen = g_utf8_strlen (str, -1);
+				}
+			}
+			adjust_display (entry, otext);
+			g_free (otext);
+                        break;
+                case PROP_SUFFIX:
+			otext = gdaui_entry_get_text (entry);
+			g_free (entry->priv->suffix);
+			entry->priv->suffix = NULL;
+			entry->priv->suffix_len = 0;
+
+			str = g_value_get_string (value);
+			if (str) {
+				if (! g_utf8_validate (str, -1, NULL))
+					g_warning (_("Invalid UTF-8 format!"));
+				else {
+					entry->priv->suffix = g_strdup (str);
+					entry->priv->suffix_len = strlen (str);
+					entry->priv->suffix_clen = g_utf8_strlen (str, -1);
+				}
+			}
+			adjust_display (entry, otext);
+			g_free (otext);
+                        break;
+		case PROP_MAXLEN:
+			entry->priv->maxlen = g_value_get_int (value);
+			otext = gdaui_entry_get_text (entry);
+			adjust_display (entry, otext);
+			g_free (otext);
+			break;
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }	
+}
+
+static void
+gdaui_entry_get_property (GObject *object,
+			     guint param_id,
+			     GValue *value,
+			     GParamSpec *pspec)
+{
+	GdauiEntry *entry;
+
+        entry = GDAUI_ENTRY (object);
+        if (entry->priv) {
+                switch (param_id) {
+                case PROP_PREFIX:
+			g_value_set_string (value, entry->priv->prefix);
+                        break;
+                case PROP_SUFFIX:
+			g_value_set_string (value, entry->priv->suffix);
+                        break;
+		case PROP_MAXLEN:
+			g_value_set_int (value, entry->priv->maxlen);
+			break;
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+}
+
+static void
+signal_handlers_block (GdauiEntry *entry)
+{
+	ENTER_INTERNAL_CHANGES (entry);
+	g_signal_handlers_block_by_func (entry, G_CALLBACK (insert_text_cb), NULL);
+	g_signal_handlers_block_by_func (entry, G_CALLBACK (delete_text_cb), NULL);
+}
+
+static void
+signal_handlers_unblock (GdauiEntry *entry)
+{
+	g_signal_handlers_unblock_by_func (entry, G_CALLBACK (insert_text_cb), NULL);
+	g_signal_handlers_unblock_by_func (entry, G_CALLBACK (delete_text_cb), NULL);
+	LEAVE_INTERNAL_CHANGES (entry);
+}
+
+/*
+ * truncate_utf8_string
+ * @text: a string, not %NULL
+ * @pos: the position where the string wil be truncated <=> text[pos]=0 for ASCII
+ *
+ * Returns: @text
+ */
+static gchar *
+truncate_utf8_string (gchar *text, gint pos)
+{
+	gchar *ptr;
+	gint i;
+	for (ptr = text, i = 0; (i < pos) && ptr && *ptr; ptr = g_utf8_next_char (ptr), i++);
+	if (i == pos)
+		*ptr = 0;
+	return text;
+}
+
+/*
+ * Computes new new display
+ *
+ * WARNING: @existing_text may be modified!!!
+ */
+static void
+adjust_display (GdauiEntry *entry, gchar *existing_text)
+{
+	gchar *tmp;
+
+	if (!entry->priv->isnull) {
+		signal_handlers_block (entry);
+		if (g_utf8_strlen (existing_text, -1) > entry->priv->maxlen)
+			truncate_utf8_string (existing_text, entry->priv->maxlen);
+		tmp = g_strdup_printf ("%s%s%s",
+				       entry->priv->prefix ? entry->priv->prefix : "",
+				       existing_text ? existing_text : "",
+				       entry->priv->suffix ? entry->priv->suffix : "");
+		
+		gtk_entry_set_text (GTK_ENTRY (entry), tmp); /* emits a "changed" signal */
+		g_free (tmp);
+		signal_handlers_unblock (entry);
+	}
+}
+
+/**
+ * gdaui_entry_new:
+ * @prefix: a prefix (not modifiable) string, or %NULL
+ * @suffix: a suffix (not modifiable) string, or %NULL
+ *
+ * Creates a new #GdauiEntry widget.
+ *
+ * Returns: the newly created #GdauiEntry widget.
+ */
+GtkWidget*
+gdaui_entry_new (const gchar *prefix, const gchar *suffix)
+{
+	GObject *obj;
+
+	obj = g_object_new (GDAUI_TYPE_ENTRY, "prefix", prefix, "suffix", suffix, NULL);
+	return GTK_WIDGET (obj);
+}
+
+/**
+ * gdaui_entry_set_max_length
+ * @entry: a #GdauiEntry.
+ * @max: the maximum length of the entry, or 0 for no maximum.
+ *
+ * Sets the maximum allowed length of the contents of the widget.
+ * If the current contents are longer than the given length, then they will be truncated to fit.
+ *
+ * The difference with gtk_entry_set_max_length() is that the max length does not take into account
+ * the prefix and/or suffix parts which may have been set.
+ */
+void
+gdaui_entry_set_max_length (GdauiEntry *entry, gint max)
+{
+	g_return_if_fail (GDAUI_IS_ENTRY (entry));
+
+	g_object_set (G_OBJECT (entry), "max-length", max, NULL);
+}
+
+/**
+ * gdaui_entry_get_text:
+ * @entry: a #GdauiEntry.
+ *
+ * Get a new string containing the contents of the widget as a string without the
+ * prefix and/or suffix and/or format if they have been specified. This method differs
+ * from calling gtk_entry_get_text() since the latest will return the complete text
+ * in @entry including prefix and/or suffix and/or format.
+ *
+ * Note: %NULL may be returned if this method is called while the widget is working on some
+ * internal modifications, or if gdaui_entry_set_text() was called with a %NULL
+ * as its @text argument.
+ *
+ * Returns: a new string, or %NULL
+ */
+gchar *
+gdaui_entry_get_text (GdauiEntry *entry)
+{
+	gchar *text;
+
+	g_return_val_if_fail (GDAUI_IS_ENTRY (entry), NULL);
+	g_return_val_if_fail (entry->priv, NULL);
+
+	if (entry->priv->isnull)
+		text = NULL;
+	else {
+		const gchar *ctext;
+		gint len;
+		ctext = gtk_entry_get_text (GTK_ENTRY (entry));
+		if (ctext) {
+			len = strlen (ctext);
+			text = g_strdup (ctext);
+			if (entry->priv->prefix) {
+				len -= entry->priv->prefix_len;
+				g_memmove (text, text + entry->priv->prefix_len, len+1);
+			}
+			if (entry->priv->suffix) {
+				len -= entry->priv->suffix_len;
+				text [len] = 0;
+			}
+		}
+		else
+			text = g_strdup ("");
+	}
+
+	return text;
+}
+
+/**
+ * gdaui_entry_set_text
+ * @entry: a #GdauiEntry widget
+ * @text: the text to set into @entry, or %NULL
+ *
+ * Sets @text into @entry. 
+ *
+ * As a side effect, if @text is %NULL, then the entry will
+ * be completely empty, whereas if @text is the empty string (""), then
+ * @entry will display the prefix and/or suffix and/or format string if they have
+ * been set. Except this case, calling this method is similar to calling
+ * gtk_entry_set_text()
+ */
+void
+gdaui_entry_set_text (GdauiEntry *entry, const gchar *text)
+{
+	g_return_if_fail (GDAUI_IS_ENTRY (entry));
+	g_return_if_fail (entry->priv);
+
+	if (text) {
+		entry->priv->isnull = TRUE;
+		signal_handlers_block (entry);
+		gtk_entry_set_text (GTK_ENTRY (entry), "");
+		signal_handlers_unblock (entry);
+		ENTER_INTERNAL_CHANGES(entry);
+		gtk_entry_set_text (GTK_ENTRY (entry), text); /* emits the "insert-text" signal which is treated */
+		LEAVE_INTERNAL_CHANGES(entry);
+		g_signal_emit_by_name (entry, "changed");
+	}
+	else {
+		entry->priv->isnull = TRUE;
+		signal_handlers_block (entry);
+		gtk_entry_set_text (GTK_ENTRY (entry), "");
+		signal_handlers_unblock (entry);
+		g_signal_emit_by_name (entry, "changed");
+	}
+}
+
+/**
+ * gdaui_entry_set_prefix
+ * @entry: a #GdauiEntry widget
+ * @prefix: a prefix string
+ *
+ * Sets @prefix as a prefix string of @entry: that string will always be displayed in the
+ * text entry, will not be modifiable, and won't be part of the returned text
+ */
+void
+gdaui_entry_set_prefix (GdauiEntry *entry, const gchar *prefix)
+{
+	g_return_if_fail (GDAUI_IS_ENTRY (entry));
+	g_return_if_fail (entry->priv);
+
+	g_object_set (G_OBJECT (entry), "prefix", prefix, NULL);
+}
+
+/**
+ * gdaui_entry_set_suffix
+ * @entry: a #GdauiEntry widget
+ * @suffix: a suffix string
+ *
+ * Sets @suffix as a suffix string of @entry: that string will always be displayed in the
+ * text entry, will not be modifiable, and won't be part of the returned text
+ */
+void
+gdaui_entry_set_suffix (GdauiEntry *entry, const gchar *suffix)
+{
+	g_return_if_fail (GDAUI_IS_ENTRY (entry));
+	g_return_if_fail (entry->priv);
+
+	g_object_set (G_OBJECT (entry), "suffix", suffix, NULL);
+}
+
+
+/*
+ * callbacks
+ */
+
+static void
+changed_cb (GtkEditable *editable, gpointer data)
+{
+        GdauiEntry *entry = (GdauiEntry*) editable;
+        if (entry->priv->internal_changes > 0)
+                g_signal_stop_emission_by_name (editable, "changed");
+}
+
+static void
+delete_text_cb (GtkEditable *editable, gint start_pos, gint end_pos, gpointer data)
+{
+	const gchar *otext = NULL;
+	gint len;
+	gint nstart = start_pos, nend = end_pos;
+	GdauiEntry *entry = GDAUI_ENTRY (editable);
+
+	signal_handlers_block (entry);
+	if (entry->priv->prefix) {
+		if (nstart < entry->priv->prefix_clen)
+			nstart = entry->priv->prefix_clen;
+	}
+	if (nend < 0) {
+		otext = gtk_entry_get_text ((GtkEntry*) entry);
+		len = g_utf8_strlen (otext, -1);
+		nend = len;
+	}
+
+	if (nend - nstart < 1) {
+		g_signal_stop_emission_by_name (editable, "delete-text");
+		signal_handlers_unblock (entry);
+		return;
+	}
+
+	if (entry->priv->suffix) {
+		if (!otext) {
+			otext = gtk_entry_get_text ((GtkEntry*) entry);
+			len = g_utf8_strlen (otext, -1);
+		}
+		if (nend - nstart == 1) {
+			if ((nstart >= len - entry->priv->suffix_clen)) {
+				nstart = len - entry->priv->suffix_clen - 1;
+				nend = nstart + 1;
+				g_signal_stop_emission_by_name (editable, "delete-text");
+				signal_handlers_unblock (entry);
+				gtk_editable_set_position (editable, nend);
+				gtk_editable_delete_text (editable, nstart, nend);
+				return;
+			}
+		}
+		if (nend > len - entry->priv->suffix_clen)
+			nend = len - entry->priv->suffix_clen;
+	}
+
+	if (GDAUI_ENTRY_GET_CLASS (editable)->assume_delete) {
+		g_signal_stop_emission_by_name (editable, "delete-text");
+		GDAUI_ENTRY_GET_CLASS (editable)->assume_delete (entry, nstart - entry->priv->prefix_clen,
+								 nend - entry->priv->prefix_clen,
+								 entry->priv->prefix_clen);
+		//g_print ("Subclass assumes text delete\n");
+	}
+	else if ((nstart != start_pos) || (nend != end_pos)) {
+		g_signal_stop_emission_by_name (editable, "delete-text");
+		if (nstart != nend)
+			gtk_editable_delete_text (editable, nstart, nend);
+	}
+	
+	signal_handlers_unblock (entry);
+	g_signal_emit_by_name (entry, "changed");
+}
+
+
+static void
+insert_text_cb (GtkEditable *editable, const gchar *text, gint text_length, gint *position, gpointer data)
+{
+	const gchar *otext;
+	gint clen;
+	GdauiEntry *entry = GDAUI_ENTRY (editable);
+	gint text_clen;
+
+	signal_handlers_block (entry);
+
+ 	if (entry->priv->isnull) {
+		gchar *etext = NULL;
+		entry->priv->isnull = FALSE;
+		if (GDAUI_ENTRY_GET_CLASS (editable)->get_empty_text)
+			etext = GDAUI_ENTRY_GET_CLASS (editable)->get_empty_text (entry);
+		adjust_display (entry, etext ? etext : "");
+		g_free (etext);
+	}
+
+	otext = gtk_entry_get_text ((GtkEntry*) entry);
+	clen = g_utf8_strlen (otext, -1);
+
+	/* adjust insert position */
+	if (entry->priv->prefix) {
+		if (*position < entry->priv->prefix_clen)
+			*position = entry->priv->prefix_clen;
+	}
+	if (entry->priv->suffix) {
+		if (*position > clen - entry->priv->suffix_clen)
+			*position = clen - entry->priv->suffix_clen;
+	}
+
+	/* test if the whole insertion is Ok */
+	text_clen = g_utf8_strlen (text, text_length);
+	if (clen - entry->priv->prefix_clen - entry->priv->suffix_clen + text_clen > entry->priv->maxlen) {
+		gchar *itext;
+		gint nallowed;
+		nallowed = entry->priv->maxlen - (clen - entry->priv->prefix_clen - entry->priv->suffix_clen);
+		g_signal_stop_emission_by_name (editable, "insert-text");
+		itext = g_strdup (text);
+		itext [nallowed] = 0; /* FIXME: convert nallowed to gchar */
+		/*g_print ("Corrected by length insert text: [%s]\n", itext);*/
+		if (*itext)
+			gtk_editable_insert_text (editable, itext, nallowed, position);
+		g_free (itext);
+	}
+	else if (GDAUI_ENTRY_GET_CLASS (editable)->assume_insert) {
+		g_signal_stop_emission_by_name (editable, "insert-text");
+		//g_print ("Subclass assumes text insert\n");
+		gint pos = *position - entry->priv->prefix_clen;
+		GDAUI_ENTRY_GET_CLASS (editable)->assume_insert (entry, text, text_length,
+								 &pos, entry->priv->prefix_clen);
+		*position = pos + entry->priv->prefix_clen;
+	}
+
+	signal_handlers_unblock (entry);
+	g_signal_emit_by_name (entry, "changed");
+}
diff --git a/libgda-ui/data-entries/gdaui-entry.h b/libgda-ui/data-entries/gdaui-entry.h
new file mode 100644
index 0000000..a5f3875
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-entry.h
@@ -0,0 +1,101 @@
+/* gdaui-entry.h
+ *
+ * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDAUI_ENTRY_H__
+#define __GDAUI_ENTRY_H__
+
+#include <gtk/gtkentry.h>
+
+G_BEGIN_DECLS
+
+#define GDAUI_TYPE_ENTRY                 (gdaui_entry_get_type ())
+#define GDAUI_ENTRY(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDAUI_TYPE_ENTRY, GdauiEntry))
+#define GDAUI_ENTRY_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GDAUI_TYPE_ENTRY, GdauiEntryClass))
+#define GDAUI_IS_ENTRY(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDAUI_TYPE_ENTRY))
+#define GDAUI_IS_ENTRY_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GDAUI_TYPE_ENTRY))
+#define GDAUI_ENTRY_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GDAUI_TYPE_ENTRY, GdauiEntryClass))
+
+typedef struct _GdauiEntry        GdauiEntry;
+typedef struct _GdauiEntryClass   GdauiEntryClass;
+typedef struct _GdauiEntryPrivate GdauiEntryPrivate;
+
+struct _GdauiEntry
+{
+	GtkEntry                   entry;
+	GdauiEntryPrivate *priv;
+};
+
+struct _GdauiEntryClass
+{
+	GtkEntryClass              parent_class;
+
+	/* virtual methods */
+	/**
+	 * GdauiEntryClass::get_empty_text
+	 *
+	 * If defined, sould return a text suitable to display EMPTY value, it will be called when
+	 * entry was set to NULL and is becomming not NULL
+	 *
+	 * Returs: a newt string, or %NULL
+	 */
+	gchar                  *(*get_empty_text) (GdauiEntry *entry);
+
+	/**
+	 * GdauiEntryClass::assume_insert
+	 * @entry:
+	 * @text: the text to be inserted
+	 * @text_length: @text's length in bytes (not characters)
+	 * @virt_pos: the position where @text is to be inserted
+	 * @offset: an offset to add to positions using @virt_pos as reference to call gtk_editable_*()
+	 * 
+	 * To be defined by children classes to handle insert themselves
+	 */
+	void                    (*assume_insert) (GdauiEntry *entry, const gchar *text, gint text_length,
+						  gint *virt_pos, gint offset);
+	/**
+	 * GdauiEntryClass::assume_delete
+	 * @entry:
+	 * @virt_start_pos: the starting position.
+	 * @virt_end_pos: the end position (not included in deletion), always > @start_pos
+	 * @offset: an offset to add to positions using @virt_start_pos or @virt_end_pos as reference
+	 *          to call gtk_editable_*()
+	 *
+	 * To be defined by children classes to handle delete themselves
+	 */
+	void                     (*assume_delete) (GdauiEntry *entry, gint virt_start_pos, gint virt_end_pos, gint offset);
+};
+
+GType                 gdaui_entry_get_type           (void) G_GNUC_CONST;
+GtkWidget            *gdaui_entry_new                (const gchar *prefix, const gchar *suffix);
+
+void                  gdaui_entry_set_max_length     (GdauiEntry *entry, gint max);
+void                  gdaui_entry_set_prefix         (GdauiEntry *entry, const gchar *prefix);
+void                  gdaui_entry_set_suffix         (GdauiEntry *entry, const gchar *suffix);
+
+void                  gdaui_entry_set_text           (GdauiEntry *entry, const gchar *text);
+gchar                *gdaui_entry_get_text           (GdauiEntry *entry);
+
+/* for sub classes */
+void                  _gdaui_entry_block_changes     (GdauiEntry *entry);
+void                  _gdaui_entry_unblock_changes   (GdauiEntry *entry);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda-ui/data-entries/gdaui-formatted-entry.c b/libgda-ui/data-entries/gdaui-formatted-entry.c
new file mode 100644
index 0000000..f0640c3
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-formatted-entry.c
@@ -0,0 +1,489 @@
+/* gdaui-formatted-entry.c
+ *
+ * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <string.h>
+
+#include "gdaui-formatted-entry.h"
+
+struct _GdauiFormattedEntryPrivate {
+	gchar   *format; /* UTF-8! */
+	gint     format_clen; /* in characters, not gchar */
+	gchar   *mask; /* ASCII! */
+	gint     mask_len; /* in gchar */
+};
+
+static void gdaui_formatted_entry_class_init   (GdauiFormattedEntryClass *klass);
+static void gdaui_formatted_entry_init         (GdauiFormattedEntry *entry);
+static void gdaui_formatted_entry_finalize     (GObject *object);
+static void gdaui_formatted_entry_set_property (GObject *object,
+						guint param_id,
+						const GValue *value,
+						GParamSpec *pspec);
+static void gdaui_formatted_entry_get_property (GObject *object,
+						guint param_id,
+						GValue *value,
+						GParamSpec *pspec);
+static gchar *gdaui_formatted_entry_get_empty_text (GdauiEntry *entry);
+static void gdaui_formatted_entry_assume_insert (GdauiEntry *entry, const gchar *text, gint text_length, gint *virt_pos, gint offset);
+static void gdaui_formatted_entry_assume_delete (GdauiEntry *entry, gint virt_start_pos, gint virt_end_pos, gint offset);
+
+/* properties */
+enum
+{
+        PROP_0,
+	PROP_FORMAT,
+	PROP_MASK
+};
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gdaui_formatted_entry_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GdauiFormattedEntryClass),
+			NULL,		/* base_init */
+			NULL,		/* base_finalize */
+			(GClassInitFunc) gdaui_formatted_entry_class_init,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			sizeof (GdauiFormattedEntry),
+			0,		/* n_preallocs */
+			(GInstanceInitFunc) gdaui_formatted_entry_init,
+		};
+		
+		type = g_type_register_static (GDAUI_TYPE_ENTRY, "GdauiFormattedEntry", &type_info, 0);
+	}
+
+	return type;
+}
+
+static void
+gdaui_formatted_entry_class_init (GdauiFormattedEntryClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = gdaui_formatted_entry_finalize;
+	GDAUI_ENTRY_CLASS (klass)->assume_insert = gdaui_formatted_entry_assume_insert;
+	GDAUI_ENTRY_CLASS (klass)->assume_delete = gdaui_formatted_entry_assume_delete;
+	GDAUI_ENTRY_CLASS (klass)->get_empty_text = gdaui_formatted_entry_get_empty_text;
+
+	/* Properties */
+        object_class->set_property = gdaui_formatted_entry_set_property;
+        object_class->get_property = gdaui_formatted_entry_get_property;
+
+        g_object_class_install_property (object_class, PROP_FORMAT,
+                                         g_param_spec_string ("format", NULL, NULL, NULL, 
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+        g_object_class_install_property (object_class, PROP_MASK,
+                                         g_param_spec_string ("mask", NULL, NULL, NULL, 
+							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+}
+
+static void
+gdaui_formatted_entry_init (GdauiFormattedEntry *entry)
+{
+	entry->priv = g_new0 (GdauiFormattedEntryPrivate, 1);
+	entry->priv->format = NULL;
+	entry->priv->mask = NULL;
+}
+
+static void 
+gdaui_formatted_entry_finalize (GObject *object)
+{
+	GdauiFormattedEntry *entry;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDAUI_IS_ENTRY (object));
+
+        entry = GDAUI_FORMATTED_ENTRY (object);
+        if (entry->priv) {
+		g_free (entry->priv->format);
+		g_free (entry->priv->mask);
+                g_free (entry->priv);
+                entry->priv = NULL;
+        }
+
+        /* parent class */
+        parent_class->finalize (object);
+}
+
+static void 
+gdaui_formatted_entry_set_property (GObject *object,
+				    guint param_id,
+				    const GValue *value,
+				    GParamSpec *pspec)
+{
+	GdauiFormattedEntry *entry;
+	const gchar *str;
+	gchar *otext;
+
+        entry = GDAUI_FORMATTED_ENTRY (object);
+	otext = gdaui_entry_get_text (GDAUI_ENTRY (entry));
+        if (entry->priv) {
+                switch (param_id) {
+                case PROP_FORMAT:
+			g_free (entry->priv->format);
+			entry->priv->format = NULL;
+			entry->priv->format_clen = 0;
+
+			str = g_value_get_string (value);
+			if (str) {
+				if (! g_utf8_validate (str, -1, NULL))
+					g_warning (_("Invalid UTF-8 format!"));
+				else {
+					entry->priv->format = g_strdup (str);
+					entry->priv->format_clen = g_utf8_strlen (str, -1);
+					gtk_entry_set_max_length (GTK_ENTRY (entry), entry->priv->format_clen);
+				}
+			}
+                        break;
+                case PROP_MASK:
+			g_free (entry->priv->mask);
+			entry->priv->mask = NULL;
+			entry->priv->mask_len = 0;
+
+			str = g_value_get_string (value);
+			if (str) {
+				entry->priv->mask = g_strdup (str);
+				entry->priv->mask_len = strlen (str);
+			}
+                        break;
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+	gdaui_entry_set_text (GDAUI_ENTRY (entry), otext);
+	g_free (otext);
+}
+
+static void
+gdaui_formatted_entry_get_property (GObject *object,
+				    guint param_id,
+				    GValue *value,
+				    GParamSpec *pspec)
+{
+	GdauiFormattedEntry *entry;
+
+        entry = GDAUI_FORMATTED_ENTRY (object);
+        if (entry->priv) {
+                switch (param_id) {
+                case PROP_FORMAT:
+			g_value_set_string (value, entry->priv->format);
+                        break;
+                case PROP_MASK:
+			g_value_set_string (value, entry->priv->mask);
+                        break;
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+}
+
+/*
+ * is_writable
+ * @fentry:
+ * @pos: the position (in characters) in @fentry->priv->format
+ * @ptr: the character (in @fentry->priv->format)
+ *
+ * Returns: %TRUE if it is a writable loaction
+ */
+static gboolean
+is_writable (GdauiFormattedEntry *fentry, gint pos, const gchar *ptr)
+{
+	if (((*ptr == '0') ||
+	     (*ptr == '9') ||
+	     (*ptr == '@') ||
+	     (*ptr == '^') ||
+	     (*ptr == '#') ||
+	     (*ptr == '*')) &&
+	    (!fentry->priv->mask ||
+	     (fentry->priv->mask &&
+	      (pos < fentry->priv->mask_len) &&
+	      (fentry->priv->mask [pos] != ' '))))
+		return TRUE;
+	else
+		return FALSE;
+}
+
+/*
+ * is_allowed
+ * @fentry:
+ * @ptr: the character (in @fentry->priv->format)
+ * @wc: the character to be inserted
+ *
+ * Returns: %TRUE if @wc can be used to replace @ptr
+ */
+static gboolean
+is_allowed (GdauiFormattedEntry *fentry, const gchar *ptr, const gunichar wc, gunichar *out_wc)
+{
+	gunichar fwc;
+
+	fwc = g_utf8_get_char (ptr);
+	*out_wc = wc;
+	if (*ptr == '0')
+		return g_unichar_isdigit (wc);
+	else if (*ptr == '9')
+		return g_unichar_isdigit (wc) && (wc != g_utf8_get_char ("0"));
+	else if (*ptr == '@')
+		return g_unichar_isalpha (wc);
+	else if (*ptr == '^') {
+		gboolean isa = g_unichar_isalpha (wc);
+		if (isa)
+			*out_wc = g_unichar_toupper (wc);
+		return isa;
+	}
+	else if (*ptr == '#')
+		return g_unichar_isalnum (wc);
+	else if (*ptr == '*')
+		return g_unichar_isprint (wc);
+	else {
+		g_warning (_("Unknown format character starting at %s"), ptr);
+		return FALSE;
+	}
+}
+
+static gchar *
+gdaui_formatted_entry_get_empty_text (GdauiEntry *entry)
+{
+	GdauiFormattedEntry *fentry;
+
+	fentry = (GdauiFormattedEntry*) entry;
+	if (fentry->priv->format) {
+		GString *string;
+
+		string = g_string_new ("");
+		gchar *ptr;
+		gint i;
+		for (ptr = fentry->priv->format, i = 0;
+		     ptr && *ptr;
+		     ptr = g_utf8_next_char (ptr), i++) {
+			if (is_writable (fentry, i, ptr))
+				g_string_append_c (string, '_');
+			else {
+				gunichar wc;
+				wc = g_utf8_get_char (ptr);
+				g_string_append_unichar (string, wc);
+			}
+		}
+		return g_string_free (string, FALSE);
+	}
+	else
+		return NULL;
+}
+
+static void
+gdaui_formatted_entry_assume_insert (GdauiEntry *entry, const gchar *text, gint text_length,
+				     gint *virt_pos, gint offset)
+{
+	GdauiFormattedEntry *fentry;
+	gint i, pos;
+
+	fentry = (GdauiFormattedEntry*) entry;
+
+	const gchar *ptr, *fptr;
+	pos = *virt_pos;
+	for (fptr = fentry->priv->format, i = 0;
+	     (i < pos) && fptr && *fptr;
+	     fptr = g_utf8_next_char (fptr), i++);
+	
+	if (i != pos)
+		return;
+
+	_gdaui_entry_block_changes (entry);
+	for (ptr = text, i = 0; ptr && *ptr && *fptr; ptr = g_utf8_next_char (ptr)) {
+		while ((pos < fentry->priv->format_clen) &&
+		       !is_writable (fentry, pos, fptr)) {
+			fptr = g_utf8_next_char (fptr);
+			if (!fptr || !*fptr)
+				return;
+			pos++;
+		}
+		
+		gunichar wc;
+		wc = g_utf8_get_char (ptr);
+		if ((pos < fentry->priv->format_clen) &&
+		    is_allowed (fentry, fptr, wc, &wc)){
+			/* Ok, insert *ptr (<=> text[i] if it was ASCII) */
+			gint rpos = pos + offset;
+			gint usize;
+			gchar buf [6];
+
+			usize = g_unichar_to_utf8 (wc, buf);
+			gtk_editable_delete_text ((GtkEditable*) entry, rpos, rpos + 1);
+			gtk_editable_insert_text ((GtkEditable*) entry, buf, usize, &rpos);
+			pos++;
+			fptr = g_utf8_next_char (fptr);
+		}
+	}
+	_gdaui_entry_unblock_changes (entry);
+	*virt_pos = pos;
+}
+
+static void
+gdaui_formatted_entry_assume_delete (GdauiEntry *entry, gint virt_start_pos, gint virt_end_pos, gint offset)
+{
+	GdauiFormattedEntry *fentry;
+	gchar *fptr;
+	gint i;
+
+	fentry = (GdauiFormattedEntry*) entry;
+
+#ifdef GDA_DEBUG
+	gint clen;
+	gchar *otext;
+	otext = gdaui_entry_get_text (entry);
+	if (otext) {
+		clen = g_utf8_strlen (otext, -1);
+		g_assert (clen == fentry->priv->format_clen);
+		g_free (otext);
+	}
+#endif
+
+	g_assert (virt_end_pos <= fentry->priv->format_clen);
+	
+	/* move fptr to the @virt_start_pos in fentry->priv->format */
+	for (fptr = fentry->priv->format, i = 0;
+	     (i < virt_start_pos) && *fptr;
+	     fptr = g_utf8_next_char (fptr), i++);
+	if (i != virt_start_pos)
+		return;
+
+	_gdaui_entry_block_changes (entry);
+	for (;
+	     (i < virt_end_pos) && fptr && *fptr;
+	     fptr = g_utf8_next_char (fptr), i++) {
+		if (!is_writable (fentry, i, fptr)) {
+			if (virt_end_pos - virt_start_pos == 1) {
+				gint npos;
+				npos = gtk_editable_get_position ((GtkEditable*) entry);
+				while ((i >= 0) && !is_writable (fentry, i, fptr)) {
+					virt_start_pos --;
+					virt_end_pos --;
+					i--;
+					fptr = g_utf8_find_prev_char (fentry->priv->format, fptr);
+					npos --;
+				}
+				if (i < 0)
+					return;
+				else
+					gtk_editable_set_position ((GtkEditable*) entry, npos);
+			}
+			else
+				continue;
+		}
+		gint rpos = i + offset;
+		gtk_editable_delete_text ((GtkEditable*) entry, rpos, rpos + 1);
+		gtk_editable_insert_text ((GtkEditable*) entry, "_", 1, &rpos);
+	}
+	_gdaui_entry_unblock_changes (entry);
+}
+
+/**
+ * gdaui_formatted_entry_new:
+ * @format: a format string
+ * @mask: a mask string, or %NULL
+ *
+ * Creates a new #GdauiFormattedEntry widget.
+ *
+ * Characters in @format are of two types:
+ *   writeable: writeable characters which will be replaced with and underscore and where text will be entered
+ *   fixed: every other characters are fixed characters, where text cant' be edited, and will be displayed AS IS
+ *
+ * Possible values for writeable characters are:
+ * <itemizedlist>
+ *   <listitem><para>'0': digits</para></listitem>
+ *   <listitem><para>'9': digits excluded 0</para></listitem>
+ *   <listitem><para>'@': alpha</para></listitem>
+ *   <listitem><para>'^': alpha converted to upper case</para></listitem>
+ *   <listitem><para>'#': alphanumeric</para></listitem>
+ *   <listitem><para>'*': any char</para></listitem>
+ * </itemizedlist>
+ *
+ * if @mask is not %NULL, then it should only contains the follogin characters, which are used side by side with
+ * @format's characters:
+ * <itemizedlist>
+ *   <listitem><para>'_': the corresponding character in @format is actually used as a writable character</para></listitem>
+ *   <listitem><para>'-': the corresponding character in @format is actually used as a writable character, but
+ *              the character will be removed from gdaui_formatted_entry_get_text()'s result if it was not
+ *              filled by the user</para></listitem>
+ *   <listitem><para>' ': the corresponding character in @format will not be considered as a writable character
+ *              but as a non writable character</para></listitem>
+ * </itemizedlist>
+ * it is then interpreted in the following way: for a character C in @format, if the character at the same
+ * position in @mask is the space character (' '), then C will not interpreted as a writable format
+ * character as defined above. @mask does not be to have the same length as @format.
+ *
+ * Returns: the newly created #GdauiFormattedEntry widget.
+ */
+GtkWidget*
+gdaui_formatted_entry_new (const gchar *format, const gchar *mask)
+{
+	GObject *obj;
+
+	obj = g_object_new (GDAUI_TYPE_FORMATTED_ENTRY, "format", format, "mask", mask, NULL);
+	return GTK_WIDGET (obj);
+}
+
+/**
+ * gdaui_formatted_entry_get_text
+ * @entry: a #GdauiFormattedEntry widget
+ *
+ * Get @entry's contents. This function is similar to gdaui_get_text() except
+ * that it optionnally uses the information contained in @mask when gdaui_formatted_entry_new()
+ * was called to format the output differently.
+ *
+ * Returns: a new string, or %NULL
+ */
+gchar *
+gdaui_formatted_entry_get_text (GdauiFormattedEntry *entry)
+{
+	gchar *text;
+	g_return_val_if_fail (GDAUI_IS_FORMATTED_ENTRY (entry), NULL);
+	
+	text = gdaui_entry_get_text ((GdauiEntry*) entry);
+	if (text && entry->priv->mask) {
+		gchar *tptr, *mptr;
+		gint len;
+		len = strlen (text);
+		for (tptr = text, mptr = entry->priv->mask;
+		     *tptr && *mptr;
+		     mptr++) {
+			if ((*mptr == '-') && (*tptr == '_')) {
+				/* remove that char */
+				g_memmove (tptr, tptr+1, len - (tptr - text));
+			}
+			else
+				tptr = g_utf8_next_char (tptr);
+		}
+	}
+
+	return text;
+}
diff --git a/libgda-ui/data-entries/gdaui-formatted-entry.h b/libgda-ui/data-entries/gdaui-formatted-entry.h
new file mode 100644
index 0000000..9c869d1
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-formatted-entry.h
@@ -0,0 +1,57 @@
+/* gdaui-formatted-entry.h
+ *
+ * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDAUI_FORMATTED_ENTRY_H__
+#define __GDAUI_FORMATTED_ENTRY_H__
+
+#include "gdaui-entry.h"
+
+G_BEGIN_DECLS
+
+#define GDAUI_TYPE_FORMATTED_ENTRY                 (gdaui_formatted_entry_get_type ())
+#define GDAUI_FORMATTED_ENTRY(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDAUI_TYPE_FORMATTED_ENTRY, GdauiFormattedEntry))
+#define GDAUI_FORMATTED_ENTRY_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GDAUI_TYPE_FORMATTED_ENTRY, GdauiFormattedEntry))
+#define GDAUI_IS_FORMATTED_ENTRY(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDAUI_TYPE_FORMATTED_ENTRY))
+#define GDAUI_IS_FORMATTED_ENTRY_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GDAUI_TYPE_FORMATTED_ENTRY))
+#define GDAUI_FORMATTED_ENTRY_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GDAUI_TYPE_FORMATTED_ENTRY, GdauiFormattedEntry))
+
+
+typedef struct _GdauiFormattedEntry        GdauiFormattedEntry;
+typedef struct _GdauiFormattedEntryClass   GdauiFormattedEntryClass;
+typedef struct _GdauiFormattedEntryPrivate GdauiFormattedEntryPrivate;
+
+struct _GdauiFormattedEntry
+{
+	GdauiEntry                  entry;
+	GdauiFormattedEntryPrivate *priv;
+};
+
+struct _GdauiFormattedEntryClass
+{
+	GdauiEntryClass             parent_class;
+};
+
+GType                 gdaui_formatted_entry_get_type           (void) G_GNUC_CONST;
+GtkWidget            *gdaui_formatted_entry_new                (const gchar *format, const gchar *mask);
+gchar                *gdaui_formatted_entry_get_text           (GdauiFormattedEntry *entry);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda-ui/data-entries/gdaui-numeric-entry.c b/libgda-ui/data-entries/gdaui-numeric-entry.c
new file mode 100644
index 0000000..29bbd6b
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-numeric-entry.c
@@ -0,0 +1,695 @@
+/* gdaui-numeric-entry.c
+ *
+ * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <libgda/gda-value.h>
+#include <errno.h>
+
+#include <math.h>
+
+#include "gdaui-numeric-entry.h"
+
+typedef struct {
+        gboolean  is_numerical;
+        gint64    imin;
+        gint64    imax;
+        guint64   uimax;
+        gdouble   fmax;
+        gboolean  is_int;
+        gboolean  is_signed;
+} NumAttr;
+
+struct _GdauiNumericEntryPrivate {
+	GType   type;
+	gchar   decimal_sep; /* default obtained automatically */
+	gchar   thousands_sep; /* 0 for no separator */
+	guint16 nb_decimals; /* G_MAXUINT16 for no limit */
+
+	NumAttr num_attr;
+};
+
+static void gdaui_numeric_entry_class_init   (GdauiNumericEntryClass *klass);
+static void gdaui_numeric_entry_init         (GdauiNumericEntry *entry);
+static void gdaui_numeric_entry_finalize     (GObject *object);
+static void gdaui_numeric_entry_set_property (GObject *object,
+						guint param_id,
+						const GValue *value,
+						GParamSpec *pspec);
+static void gdaui_numeric_entry_get_property (GObject *object,
+						guint param_id,
+						GValue *value,
+						GParamSpec *pspec);
+static void gdaui_numeric_entry_assume_insert (GdauiEntry *entry, const gchar *text, gint text_length, gint *virt_pos, gint offset);
+static void gdaui_numeric_entry_assume_delete (GdauiEntry *entry, gint virt_start_pos, gint virt_end_pos, gint offset);
+
+/* properties */
+enum
+{
+        PROP_0,
+	PROP_TYPE,
+	PROP_N_DECIMALS,
+        PROP_DECIMAL_SEP,
+        PROP_THOUSANDS_SEP
+};
+
+static GObjectClass *parent_class = NULL;
+
+GType
+gdaui_numeric_entry_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GdauiNumericEntryClass),
+			NULL,		/* base_init */
+			NULL,		/* base_finalize */
+			(GClassInitFunc) gdaui_numeric_entry_class_init,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			sizeof (GdauiNumericEntry),
+			0,		/* n_preallocs */
+			(GInstanceInitFunc) gdaui_numeric_entry_init,
+		};
+		
+		type = g_type_register_static (GDAUI_TYPE_ENTRY, "GdauiNumericEntry", &type_info, 0);
+	}
+
+	return type;
+}
+
+static void
+gdaui_numeric_entry_class_init (GdauiNumericEntryClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = gdaui_numeric_entry_finalize;
+	GDAUI_ENTRY_CLASS (klass)->assume_insert = gdaui_numeric_entry_assume_insert;
+	GDAUI_ENTRY_CLASS (klass)->assume_delete = gdaui_numeric_entry_assume_delete;
+	GDAUI_ENTRY_CLASS (klass)->get_empty_text = NULL;
+
+	/* Properties */
+        object_class->set_property = gdaui_numeric_entry_set_property;
+        object_class->get_property = gdaui_numeric_entry_get_property;
+
+        g_object_class_install_property (object_class, PROP_TYPE,
+                                         g_param_spec_gtype ("type", NULL, NULL, G_TYPE_NONE,
+							     G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class, PROP_N_DECIMALS,
+                                         g_param_spec_uint ("n-decimals", NULL, NULL,
+							    0, G_MAXUINT16, G_MAXUINT16,
+							    G_PARAM_READABLE | G_PARAM_WRITABLE));
+        g_object_class_install_property (object_class, PROP_DECIMAL_SEP,
+                                         g_param_spec_char ("decimal-sep", NULL, NULL,
+							    0, 127, '.',
+							    G_PARAM_READABLE | G_PARAM_WRITABLE));
+        g_object_class_install_property (object_class, PROP_THOUSANDS_SEP,
+                                         g_param_spec_char ("thousands_sep", NULL, NULL,
+							    0, 127, ',',
+							    G_PARAM_READABLE | G_PARAM_WRITABLE));
+}
+
+static void
+compute_numeric_attributes (GType type, NumAttr *attr)
+{
+        attr->imin = 0;
+        attr->imax = 0;
+        attr->uimax = 0;
+        attr->fmax = 0.;
+        attr->is_int = FALSE;
+        attr->is_signed = TRUE;
+        attr->is_numerical = TRUE;
+
+        if (type == G_TYPE_INT64) {
+                attr->imax = G_MAXINT64;
+                attr->imin = G_MININT64;
+                attr->is_int = TRUE;
+        }
+        else if (type == G_TYPE_UINT64) {
+                attr->uimax = G_MAXUINT64;
+                attr->is_int = TRUE;
+                attr->is_signed = FALSE;
+        }
+        else if (type == G_TYPE_LONG) {
+                attr->imax = G_MAXLONG;
+                attr->imin = G_MINLONG;
+                attr->is_int = TRUE;
+        }
+        else if (type == G_TYPE_ULONG) {
+                attr->uimax = G_MAXULONG;
+                attr->is_int = TRUE;
+                attr->is_signed = FALSE;
+        }
+        else if (type == G_TYPE_INT) {
+                attr->imax = G_MAXINT;
+                attr->imin = G_MININT;
+                attr->is_int = TRUE;
+        }
+        else if (type == G_TYPE_UINT) {
+                attr->uimax = G_MAXUINT;
+                attr->is_int = TRUE;
+                attr->is_signed = FALSE;
+        }
+        else if (type == G_TYPE_CHAR) {
+                attr->imax = 127;
+                attr->imin = -128;
+                attr->is_int = TRUE;
+        }
+        else if (type == G_TYPE_UCHAR) {
+                attr->uimax = 255;
+                attr->is_int = TRUE;
+                attr->is_signed = FALSE;}
+        else if (type == G_TYPE_FLOAT) {
+                attr->fmax = G_MAXFLOAT;
+        }
+        else if (type == G_TYPE_DOUBLE) {
+                attr->fmax = G_MAXDOUBLE;
+        }
+        else if (type == GDA_TYPE_NUMERIC) {
+        }
+        else if (type == GDA_TYPE_SHORT) {
+                attr->imax = G_MAXSHORT;
+                attr->imin = G_MINSHORT;
+                attr->is_int = TRUE;
+        }
+        else if (type == GDA_TYPE_USHORT) {
+                attr->uimax = G_MAXUSHORT;
+                attr->is_int = TRUE;
+                attr->is_signed = FALSE;
+        }
+        else {
+                attr->is_numerical = FALSE;
+        }
+}
+
+static gchar
+get_default_decimal_sep ()
+{
+        static gchar value = 0;
+
+        if (value == 0) {
+                gchar text[20];
+                sprintf (text, "%f", 1.23);
+                value = text[1];
+        }
+        return value;
+}
+
+static void
+gdaui_numeric_entry_init (GdauiNumericEntry *entry)
+{
+	entry->priv = g_new0 (GdauiNumericEntryPrivate, 1);
+	entry->priv->type = G_TYPE_INT64;
+	compute_numeric_attributes (entry->priv->type, &(entry->priv->num_attr));
+	entry->priv->decimal_sep = get_default_decimal_sep ();
+	entry->priv->thousands_sep = 0;
+	entry->priv->nb_decimals = G_MAXUINT16;
+}
+
+static void 
+gdaui_numeric_entry_finalize (GObject *object)
+{
+	GdauiNumericEntry *entry;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDAUI_IS_ENTRY (object));
+
+        entry = GDAUI_NUMERIC_ENTRY (object);
+        if (entry->priv) {
+                g_free (entry->priv);
+                entry->priv = NULL;
+        }
+
+        /* parent class */
+        parent_class->finalize (object);
+}
+
+static void 
+gdaui_numeric_entry_set_property (GObject *object,
+				  guint param_id,
+				  const GValue *value,
+				  GParamSpec *pspec)
+{
+	GdauiNumericEntry *entry;
+	gchar *otext;
+
+        entry = GDAUI_NUMERIC_ENTRY (object);
+	otext = gdaui_entry_get_text (GDAUI_ENTRY (entry));
+        if (entry->priv) {
+                switch (param_id) {
+                case PROP_TYPE: {
+			NumAttr num_attr;
+			compute_numeric_attributes (g_value_get_gtype (value), &num_attr);
+			if (num_attr.is_numerical == FALSE)
+				g_warning (_("Type %s is not numerical"), g_type_name (g_value_get_gtype (value)));
+			else {
+				entry->priv->type = g_value_get_gtype (value);
+				entry->priv->num_attr = num_attr;
+			}
+                        break;
+		}
+		case PROP_N_DECIMALS:
+                        entry->priv->nb_decimals = g_value_get_uint (value);
+                        break;
+                case PROP_DECIMAL_SEP: {
+                        gchar sep = g_value_get_char (value);
+                        if ((sep == 0) || (sep == '+') || (sep == '-'))
+                                g_warning (_("Decimal separator cannot be the '%c' character"), sep ? sep : '0');
+                        else {
+                                entry->priv->decimal_sep = g_value_get_char (value);
+                        }
+                        break;
+                }
+                case PROP_THOUSANDS_SEP: {
+                        gchar sep = g_value_get_char (value);
+                        if ((sep == '+') || (sep == '-') || (sep == '_'))
+                                g_warning (_("Decimal thousands cannot be the '%c' character"), sep);
+                        else {
+                                entry->priv->thousands_sep = g_value_get_char (value);
+                        }
+                        break;
+                }
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+	gdaui_entry_set_text (GDAUI_ENTRY (entry), otext);
+	g_free (otext);
+}
+
+static void
+gdaui_numeric_entry_get_property (GObject *object,
+				    guint param_id,
+				    GValue *value,
+				    GParamSpec *pspec)
+{
+	GdauiNumericEntry *entry;
+
+        entry = GDAUI_NUMERIC_ENTRY (object);
+        if (entry->priv) {
+                switch (param_id) {
+                case PROP_TYPE:
+			g_value_set_gtype (value, entry->priv->type);
+                        break;
+		case PROP_N_DECIMALS:
+                        g_value_set_uint (value, entry->priv->nb_decimals);
+                        break;
+                case PROP_DECIMAL_SEP:
+                        g_value_set_char (value, entry->priv->decimal_sep);
+                        break;
+                case PROP_THOUSANDS_SEP:
+                        g_value_set_char (value, entry->priv->thousands_sep);
+                        break;
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                        break;
+                }
+        }
+}
+
+/*
+ * text_unformat
+ * @pos: an inout parameter to keep track of a position
+ *
+ * Removes any thousand separator
+ *
+ * Returns: @str
+ */
+static gchar *
+text_unformat (GdauiNumericEntry *entry, gchar *str, gint *pos)
+{
+	g_assert (str);
+	gchar *ptr;
+	gint i, len;
+	len = strlen (str);
+	for (ptr = str, i = 0;
+	     *ptr;
+	     i++) {
+		if (*ptr == entry->priv->thousands_sep) {
+			g_memmove (ptr, ptr+1, len - (ptr - str));
+			len--;
+			if (*pos >= i)
+				*pos = *pos - 1;
+		}
+		else if ((*ptr == entry->priv->decimal_sep) &&
+			 (ptr[1] == entry->priv->decimal_sep)) {
+			g_memmove (ptr, ptr+1, len - (ptr - str));
+			len--;
+			/*if (*pos > i)
+			 *pos = *pos - 1;*/
+		}
+		else
+			ptr++;
+	}
+
+	return str;
+}
+
+/*
+ * text_reformat
+ * @pos: an inout parameter to keep track of a position
+ *
+ * Retunrs: a new string
+ */
+static gchar *
+text_reformat (GdauiNumericEntry *entry, gchar *str, gint *pos)
+{
+	g_assert (str);
+	GString *string;
+	gint len;
+	gchar *ptr;
+	gint i, last_th_sep_pos;
+	gint dec_pos = -1; /* position of the decimal */
+	gint cpos;
+
+	len = strlen (str);
+	string = g_string_new ("");
+	if (entry->priv->num_attr.is_int)
+		last_th_sep_pos = len - 1;
+	else
+		last_th_sep_pos = -1;
+
+	cpos = *pos;
+	for (i = len - 1, ptr = str + len - 1;
+	     ptr >= str;
+	     ptr --, i --) {
+		if (*ptr == entry->priv->decimal_sep) {
+			last_th_sep_pos = i - 1;
+			dec_pos = len - i - 1;
+		}
+		else if (i == last_th_sep_pos - 3) {
+			last_th_sep_pos = i;
+			if (entry->priv->thousands_sep) {
+				g_string_append_c (string, entry->priv->thousands_sep);
+				if (i < cpos)
+					cpos ++;
+			}
+		}
+			
+		g_string_append_c (string, *ptr);
+	}
+	if (last_th_sep_pos == -1) {
+		cpos = *pos;
+		g_string_truncate (string, 0);
+		last_th_sep_pos = len - 1;
+		for (i = len - 1, ptr = str + len - 1;
+		     ptr >= str;
+		     ptr --, i --) {
+			if (i == last_th_sep_pos - 3) {
+				last_th_sep_pos = i;
+				if (entry->priv->thousands_sep) {
+					g_string_append_c (string, entry->priv->thousands_sep);
+					if (i < cpos)
+						cpos ++;
+				}
+			}
+				
+			g_string_append_c (string, *ptr);
+		}
+	}
+	g_strreverse (string->str);
+
+	/* fix the number of decimals if necessary */
+	if ((! entry->priv->num_attr.is_int) &&
+	    entry->priv->nb_decimals != G_MAXUINT16) {
+		if (dec_pos == -1) {
+			/* no decimal */
+			if (entry->priv->nb_decimals > 0) {
+				g_string_append_c (string, entry->priv->decimal_sep);
+				for (i = 0; i < entry->priv->nb_decimals; i++)
+					g_string_append_c (string, '0');
+			}
+		}
+		else {
+			gint nb_dec;
+			len = strlen (string->str);
+			nb_dec = dec_pos; /* FIXME */
+			if (nb_dec < entry->priv->nb_decimals) {
+				for (i = nb_dec; i < entry->priv->nb_decimals; i++)
+					g_string_append_c (string, '0');
+			}
+			else if (nb_dec > entry->priv->nb_decimals)
+				g_string_truncate (string, len - (nb_dec - entry->priv->nb_decimals));
+		}
+	}
+
+	*pos = cpos;
+
+	return g_string_free (string, FALSE);
+}
+
+static gboolean
+test_text_validity (GdauiNumericEntry *entry, const gchar *text)
+{
+	gboolean retval = TRUE;
+	gchar *endptr [1];
+
+#ifdef GDA_DEBUG_NO
+	g_print ("Validity text: #%s#", text);
+#endif
+	
+	if (text && ((*text == '-') || (*text == '+')) && (text[1] == 0))
+		;
+	else {
+		gchar *tmp = g_strdup (text);
+		if (entry->priv->num_attr.is_int) {
+			gchar *ptr;
+			
+			for (ptr = tmp; *ptr; ) {
+				if (*ptr == entry->priv->thousands_sep) 
+					/* remove that char */
+					memmove (ptr, ptr + 1, strlen (ptr));
+				else
+					ptr++;
+			}
+			if (entry->priv->num_attr.is_signed) {
+				gint64 value;
+				errno = 0;
+				value = g_ascii_strtoll (tmp, endptr, 10);
+				if (((value == G_MININT64) || (value == G_MAXINT64)) &&
+				    (errno == ERANGE))
+					retval = FALSE;
+				if ((**endptr != 0) || (value < entry->priv->num_attr.imin) || (value > entry->priv->num_attr.imax))
+					retval = FALSE;
+			}
+			else {
+				guint64 value;
+				errno = 0;
+				value = g_ascii_strtoull (tmp, endptr, 10);
+				if ((value == G_MAXUINT64) && (errno == ERANGE))
+					retval = FALSE;
+				if ((**endptr != 0) || (value > entry->priv->num_attr.uimax))
+					retval = FALSE;
+			}
+		}
+		else {
+			gchar *ptr;
+			gdouble value;
+			
+			for (ptr = tmp; *ptr; ) {
+				if (*ptr == entry->priv->decimal_sep) {
+					*ptr = get_default_decimal_sep ();
+					ptr++;
+				}
+				else if (*ptr == entry->priv->thousands_sep)
+					memmove (ptr, ptr + 1, strlen (ptr));
+				else
+					ptr++;
+			}
+			errno = 0;
+			value = g_strtod (tmp, endptr);
+			if (((value == HUGE_VAL) || (value == -HUGE_VAL)) && 
+			    (errno == ERANGE))
+				retval = FALSE;
+			if ((**endptr != 0) || 
+			    ((entry->priv->num_attr.fmax > 0) && (value > entry->priv->num_attr.fmax)))
+				retval  = FALSE;
+		}
+		g_free (tmp);
+	}
+#ifdef GDA_DEBUG_NO
+	g_print ("retval=%d\n", retval);
+#endif
+	return retval;
+}
+
+
+static void
+gdaui_numeric_entry_assume_insert (GdauiEntry *entry, const gchar *text, gint text_length,
+				   gint *virt_pos, gint offset)
+{
+	GdauiNumericEntry *fentry;
+	gchar *otext, *ptr, *ntext;
+	gchar tmp;
+	gint i;
+	GString *string;
+	gint olen, nlen;
+
+	fentry = (GdauiNumericEntry*) entry;
+	otext = gdaui_entry_get_text (GDAUI_ENTRY (entry));
+	olen = strlen (otext);
+	for (ptr = otext, i = 0; (i < *virt_pos) && *ptr; ptr = g_utf8_next_char (ptr), i++);
+	if (i != *virt_pos)
+		return;
+	tmp = *ptr;
+	*ptr = 0;
+	string = g_string_new ("");
+	g_string_append (string, otext);
+	*ptr = tmp;
+	g_string_append (string, text);
+	g_string_append (string, ptr);
+	g_free (otext);
+
+	/*g_print ("RAW: [%s]", string->str);*/
+	*virt_pos += text_length;
+	text_unformat (fentry, string->str, virt_pos);
+	/*g_print ("SANITIZED: [%s]", string->str);*/
+
+	if (!test_text_validity (fentry, string->str)) {
+		g_string_free (string, TRUE);
+		/*g_print ("ERROR!\n");*/
+		return;
+	}
+	ntext = text_reformat (fentry, string->str, virt_pos);
+	g_string_free (string, TRUE);
+	/*g_print ("NEW: [%s]\n", ntext);*/
+
+	i = offset;
+	nlen = strlen (ntext);
+	gtk_editable_delete_text ((GtkEditable*) entry, offset, olen + offset);
+	gtk_editable_insert_text ((GtkEditable*) entry, ntext, nlen, &i);
+	g_free (ntext);
+}
+
+static void
+gdaui_numeric_entry_assume_delete (GdauiEntry *entry, gint virt_start_pos, gint virt_end_pos, gint offset)
+{
+	GdauiNumericEntry *fentry;
+	GString *string;
+	gchar *otext, *ntext = NULL;
+	gint i, nlen, ndel, olen = 0;
+	gint cursor_pos;
+
+	fentry = (GdauiNumericEntry*) entry;
+	ndel = virt_end_pos - virt_start_pos;
+	otext = gdaui_entry_get_text (GDAUI_ENTRY (entry));
+	cursor_pos = gtk_editable_get_position (GTK_EDITABLE (entry));
+
+	if (otext) {
+		if ((ndel == 1) && (otext[virt_start_pos] == fentry->priv->decimal_sep) &&
+		    (fentry->priv->nb_decimals != G_MAXUINT16)) {
+			gtk_editable_set_position (GTK_EDITABLE (entry), cursor_pos - 1);
+			g_free (otext);
+			return;
+		}
+		string = g_string_new (otext);
+		olen = g_utf8_strlen (otext, -1);
+		g_free (otext);
+		g_string_erase (string, virt_start_pos, ndel);
+	}
+	else
+		string = g_string_new (NULL);
+	
+	cursor_pos -= (virt_end_pos - virt_start_pos);
+	/*g_print ("RAW: [%s]", string->str);*/
+	text_unformat (fentry, string->str, &cursor_pos);
+	/*g_print ("SANITIZED: [%s]", string->str);*/
+
+	if (!test_text_validity (fentry, string->str)) {
+		if ((string->str[0] == fentry->priv->decimal_sep) &&
+		    string->str[1] == 0)
+			ntext = g_strdup ("");
+		g_string_free (string, TRUE);
+		if (!ntext) {
+			/*g_print ("ERROR!\n");*/
+			return;
+		}
+	}
+	else {
+		ntext = text_reformat (fentry, string->str, &cursor_pos);
+		g_string_free (string, TRUE);
+	}
+	/*g_print ("NEW: [%s]\n", ntext);*/
+
+	i = offset;
+	nlen = strlen (ntext);
+	gtk_editable_delete_text ((GtkEditable*) entry, offset, olen + offset);
+	gtk_editable_insert_text ((GtkEditable*) entry, ntext, nlen, &i);
+	g_free (ntext);	
+	gtk_editable_set_position (GTK_EDITABLE (entry), cursor_pos);
+}
+
+/**
+ * gdaui_numeric_entry_new:
+ * @type: the numeric type
+ *
+ * Creates a new #GdauiNumericEntry widget.
+ *
+ * Returns: the newly created #GdauiNumericEntry widget.
+ */
+GtkWidget*
+gdaui_numeric_entry_new (GType type)
+{
+	GObject *obj;
+
+	obj = g_object_new (GDAUI_TYPE_NUMERIC_ENTRY, "type", type, NULL);
+	return GTK_WIDGET (obj);
+}
+
+/**
+ * gdaui_numeric_entry_get_text
+ * @entry: a #GdauiNumericEntry widget
+ *
+ * Get @entry's contents as a #GValue.
+ *
+ * Returns: a new #GValue, or %NULL
+ */
+GValue *
+gdaui_numeric_entry_get_value (GdauiNumericEntry *entry)
+{
+	gchar *text;
+	GValue *value = NULL;
+	g_return_val_if_fail (GDAUI_IS_NUMERIC_ENTRY (entry), NULL);
+	
+	text = gdaui_entry_get_text ((GdauiEntry*) entry);
+	if (text) {
+		if (entry->priv->thousands_sep) {
+			gchar *ptr;
+			gint len;
+			len = strlen (text);
+			for (ptr = text; *ptr; ) {
+				if (*ptr == entry->priv->thousands_sep)
+					g_memmove (ptr, ptr+1, len - (ptr - text));
+				else
+					ptr++;
+			}
+		}
+		value = gda_value_new_from_string (text, entry->priv->type);
+		g_free (text);
+	}
+
+	return value;
+}
diff --git a/libgda-ui/data-entries/gdaui-numeric-entry.h b/libgda-ui/data-entries/gdaui-numeric-entry.h
new file mode 100644
index 0000000..815b70a
--- /dev/null
+++ b/libgda-ui/data-entries/gdaui-numeric-entry.h
@@ -0,0 +1,57 @@
+/* gdaui-numeric-entry.h
+ *
+ * Copyright (C) 2009 Vivien Malerba <malerba gnome-db org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDAUI_NUMERIC_ENTRY_H__
+#define __GDAUI_NUMERIC_ENTRY_H__
+
+#include "gdaui-entry.h"
+
+G_BEGIN_DECLS
+
+#define GDAUI_TYPE_NUMERIC_ENTRY                 (gdaui_numeric_entry_get_type ())
+#define GDAUI_NUMERIC_ENTRY(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDAUI_TYPE_NUMERIC_ENTRY, GdauiNumericEntry))
+#define GDAUI_NUMERIC_ENTRY_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GDAUI_TYPE_NUMERIC_ENTRY, GdauiNumericEntry))
+#define GDAUI_IS_NUMERIC_ENTRY(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDAUI_TYPE_NUMERIC_ENTRY))
+#define GDAUI_IS_NUMERIC_ENTRY_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GDAUI_TYPE_NUMERIC_ENTRY))
+#define GDAUI_NUMERIC_ENTRY_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GDAUI_TYPE_NUMERIC_ENTRY, GdauiNumericEntry))
+
+
+typedef struct _GdauiNumericEntry        GdauiNumericEntry;
+typedef struct _GdauiNumericEntryClass   GdauiNumericEntryClass;
+typedef struct _GdauiNumericEntryPrivate GdauiNumericEntryPrivate;
+
+struct _GdauiNumericEntry
+{
+	GdauiEntry                  entry;
+	GdauiNumericEntryPrivate *priv;
+};
+
+struct _GdauiNumericEntryClass
+{
+	GdauiEntryClass             parent_class;
+};
+
+GType                 gdaui_numeric_entry_get_type           (void) G_GNUC_CONST;
+GtkWidget            *gdaui_numeric_entry_new                (GType type);
+GValue               *gdaui_numeric_entry_get_value          (GdauiNumericEntry *entry);
+
+G_END_DECLS
+
+#endif
diff --git a/libgda-ui/data-entries/plugins/gdaui-entry-cidr.c b/libgda-ui/data-entries/plugins/gdaui-entry-cidr.c
index e908e60..5ddbd53 100644
--- a/libgda-ui/data-entries/plugins/gdaui-entry-cidr.c
+++ b/libgda-ui/data-entries/plugins/gdaui-entry-cidr.c
@@ -22,7 +22,7 @@
 #include "gdaui-entry-cidr.h"
 #include <libgda/gda-data-handler.h>
 #include <string.h>
-#include "gdaui-format-entry.h"
+#include "gdaui-formatted-entry.h"
 
 /* 
  * Main static functions 
@@ -190,11 +190,8 @@ create_entry (GdauiEntryWrapper *mgwrap)
 	mgcidr = GDAUI_ENTRY_CIDR (mgwrap);
 	g_return_val_if_fail (mgcidr->priv, NULL);
 
-	entry = gdaui_format_entry_new ();
+	entry = gdaui_formatted_entry_new ("000.000.000.000/000.000.000.000", "---.---.---.---/---.---.---.---");
 	mgcidr->priv->entry = entry;
-	gdaui_format_entry_set_format (GDAUI_FORMAT_ENTRY (entry), 
-					  "000.000.000.000/000.000.000.000", NULL,
-					  "   .   .   .   /   .   .   .   ");
 	gtk_entry_set_width_chars (GTK_ENTRY (entry), 19);
 
 	g_signal_connect (G_OBJECT (entry), "focus-out-event",
@@ -336,7 +333,7 @@ real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
 
 	if (value) {
 		if (gda_value_is_null ((GValue *) value))
-			gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgcidr->priv->entry), NULL);
+			gdaui_entry_set_text (GDAUI_ENTRY (mgcidr->priv->entry), NULL);
 		else {
 			SplitValues *svalues;
 			gchar *str, *ptr, *tok;
@@ -373,7 +370,7 @@ real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
 		}
 	}
 	else
-		gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgcidr->priv->entry), NULL);
+		gdaui_entry_set_text (GDAUI_ENTRY (mgcidr->priv->entry), NULL);
 }
 
 static void truncate_entries_to_mask_length (GdauiEntryCidr *mgcidr, gboolean target_mask, guint mask_nb_bits)
@@ -611,7 +608,7 @@ split_values_get (GdauiEntryCidr *mgcidr)
 	gchar *str;
 
 	values = g_new0 (SplitValues, 1);
-	str = gdaui_format_entry_get_text (GDAUI_FORMAT_ENTRY (mgcidr->priv->entry));
+	str = gdaui_entry_get_text (GDAUI_ENTRY (mgcidr->priv->entry));
 	if (!str)
 		return NULL;
 
@@ -673,7 +670,7 @@ split_values_set (GdauiEntryCidr *mgcidr, SplitValues *svalues)
 	ip_str = g_strjoinv (".", svalues->ip_array);
 	mask_str = g_strjoinv (".", svalues->mask_array);
 	str = g_strdup_printf ("%s/%s", ip_str, mask_str);
-	gdaui_format_entry_set_text (GDAUI_FORMAT_ENTRY (mgcidr->priv->entry), str);
+	gdaui_entry_set_text (GDAUI_ENTRY (mgcidr->priv->entry), str);
 	g_free (str);
 }
 
diff --git a/libgda-ui/gdaui-init.c b/libgda-ui/gdaui-init.c
index f1f9ff4..c5ccd32 100644
--- a/libgda-ui/gdaui-init.c
+++ b/libgda-ui/gdaui-init.c
@@ -31,6 +31,7 @@
 #include "data-entries/gdaui-entry-boolean.h"
 #include "data-entries/gdaui-entry-bin.h"
 #include "data-entries/gdaui-entry-string.h"
+#include "data-entries/gdaui-entry-number.h"
 #include "data-entries/gdaui-entry-time.h"
 #include "data-entries/gdaui-entry-date.h"
 #include "data-entries/gdaui-entry-timestamp.h"
@@ -138,7 +139,7 @@ gdaui_new_data_entry (GType type, const gchar *plugin_name)
 			 (type == G_TYPE_UCHAR) ||
 			 (type == G_TYPE_ULONG) ||
 			 (type == G_TYPE_UINT))
-			entry = (GdauiDataEntry *) gdaui_entry_string_new (dh, type, spec_options);
+			entry = (GdauiDataEntry *) gdaui_entry_number_new (dh, type, spec_options);
 		else if (type == G_TYPE_BOOLEAN)
 			entry = (GdauiDataEntry *) gdaui_entry_boolean_new (dh, G_TYPE_BOOLEAN);
 		else if ((type == GDA_TYPE_BLOB) ||
@@ -225,6 +226,7 @@ static GdauiDataEntry *entry_none_create_func (GdaDataHandler *handler, GType ty
 static GdauiDataEntry *entry_boolean_create_func (GdaDataHandler *handler, GType type, const gchar *options);
 static GdauiDataEntry *entry_bin_create_func (GdaDataHandler *handler, GType type, const gchar *options);
 static GdauiDataEntry *entry_string_create_func (GdaDataHandler *handler, GType type, const gchar *options);
+static GdauiDataEntry *entry_number_create_func (GdaDataHandler *handler, GType type, const gchar *options);
 static GdauiDataEntry *entry_time_create_func (GdaDataHandler *handler, GType type, const gchar *options);
 static GdauiDataEntry *entry_timestamp_create_func (GdaDataHandler *handler, GType type, const gchar *options);
 static GdauiDataEntry *entry_date_create_func (GdaDataHandler *handler, GType type, const gchar *options);
@@ -292,7 +294,7 @@ init_plugins_hash (void)
 	plugin->entry_create_func = entry_string_create_func;
 	plugin->cell_create_func = cell_textual_create_func;
 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
-	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "ui", "gdaui-entry-string-string.xml", NULL);
+	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "ui", "gdaui-entry-string.xml", NULL);
 	if (! g_file_test (file, G_FILE_TEST_EXISTS)) {
 		g_message ("Could not find file '%s': '%s' data entry will not report any possible option",
 			   file, plugin->plugin_name);
@@ -322,10 +324,10 @@ init_plugins_hash (void)
 	plugin->valid_g_types [10] = G_TYPE_ULONG;
 	plugin->valid_g_types [11] = G_TYPE_UINT;
 	plugin->options_xml_spec = NULL;
-	plugin->entry_create_func = entry_string_create_func;
+	plugin->entry_create_func = entry_number_create_func;
 	plugin->cell_create_func = cell_textual_create_func;
 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
-	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "ui", "gdaui-entry-string-number.xml", NULL);
+	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "ui", "gdaui-entry-number.xml", NULL);
 	xmlChar *xml_spec = get_spec_with_isocodes (file);
 	if (xml_spec) {
 		plugin->options_xml_spec = g_strdup (xml_spec);
@@ -337,8 +339,9 @@ init_plugins_hash (void)
 	plugin->plugin_name = "textual";
 	plugin->plugin_descr = "Textual entry";
 	plugin->plugin_file = NULL;
-	plugin->nb_g_types = 0;
-	plugin->valid_g_types = NULL;
+	plugin->nb_g_types = 1;
+	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
+	plugin->valid_g_types [0] = G_TYPE_STRING;
 	plugin->options_xml_spec = NULL;
 	plugin->entry_create_func = entry_string_create_func;
 	plugin->cell_create_func = cell_textual_create_func;
@@ -484,6 +487,12 @@ entry_string_create_func (GdaDataHandler *handler, GType type, const gchar *opti
 }
 
 static GdauiDataEntry *
+entry_number_create_func (GdaDataHandler *handler, GType type, const gchar *options)
+{
+	return (GdauiDataEntry *) gdaui_entry_number_new (handler, type, options);
+}
+
+static GdauiDataEntry *
 entry_time_create_func (GdaDataHandler *handler, GType type, const gchar *options)
 {
 	return (GdauiDataEntry *) gdaui_entry_time_new (handler);
diff --git a/libgda-ui/libgda-ui.symbols b/libgda-ui/libgda-ui.symbols
index ea72fc2..3337f5d 100644
--- a/libgda-ui/libgda-ui.symbols
+++ b/libgda-ui/libgda-ui.symbols
@@ -96,8 +96,18 @@
 	gdaui_entry_common_time_new
 	gdaui_entry_date_get_type
 	gdaui_entry_date_new
+	gdaui_entry_get_text
+	gdaui_entry_get_type
+	gdaui_entry_new
 	gdaui_entry_none_get_type
 	gdaui_entry_none_new
+	gdaui_entry_number_get_type
+	gdaui_entry_number_is_type_numeric
+	gdaui_entry_number_new
+	gdaui_entry_set_max_length
+	gdaui_entry_set_prefix
+	gdaui_entry_set_suffix
+	gdaui_entry_set_text
 	gdaui_entry_shell_get_type
 	gdaui_entry_shell_pack_entry
 	gdaui_entry_shell_refresh
@@ -110,19 +120,11 @@
 	gdaui_entry_wrapper_contents_activated
 	gdaui_entry_wrapper_contents_changed
 	gdaui_entry_wrapper_get_type
-	gdaui_format_entry_get_text
-	gdaui_format_entry_get_type
-	gdaui_format_entry_new
-	gdaui_format_entry_set_decimal_places
-	gdaui_format_entry_set_edited_type
-	gdaui_format_entry_set_format
-	gdaui_format_entry_set_max_length
-	gdaui_format_entry_set_prefix
-	gdaui_format_entry_set_separators
-	gdaui_format_entry_set_suffix
-	gdaui_format_entry_set_text
 	gdaui_form_get_type
 	gdaui_form_new
+	gdaui_formatted_entry_get_text
+	gdaui_formatted_entry_get_type
+	gdaui_formatted_entry_new
 	gdaui_grid_get_selection
 	gdaui_grid_get_type
 	gdaui_grid_new
@@ -136,6 +138,9 @@
 	gdaui_login_set_dsn
 	gdaui_login_set_mode
 	gdaui_new_data_entry
+	gdaui_numeric_entry_get_type
+	gdaui_numeric_entry_get_value
+	gdaui_numeric_entry_new
 	gdaui_plugins_hash
 	gdaui_provider_selector_get_provider
 	gdaui_provider_selector_get_provider_obj
diff --git a/libgda/handlers/gda-handler-time.c b/libgda/handlers/gda-handler-time.c
index b65e091..3f797fa 100644
--- a/libgda/handlers/gda-handler-time.c
+++ b/libgda/handlers/gda-handler-time.c
@@ -919,6 +919,8 @@ make_date (GdaHandlerTime *hdl, GDate *date, const gchar *value, LocaleSetting *
  * 123015-02
  * 123015.123
  * taken from libgda/gda-value.h
+ *
+ * Also works if there is only 0 or 1 digit instead of 2
  */
 static gboolean
 make_time (GdaHandlerTime *hdl, GdaTime *timegda, const gchar *value)
@@ -933,25 +935,39 @@ make_time (GdaHandlerTime *hdl, GdaTime *timegda, const gchar *value)
 	timegda->timezone = GDA_TIMEZONE_INVALID;
 	ptr = value;
 	if ((*ptr >= '0') && (*ptr <= '9') &&
-	    (*(ptr+1) >= '0') && (*(ptr+1) <= '9'))
+	    (*(ptr+1) >= '0') && (*(ptr+1) <= '9')) {
 		timegda->hour = (*ptr - '0') * 10 + *(ptr+1) - '0';
+		ptr += 2;
+	}
+	else if ((*ptr >= '0') && (*ptr <= '9') && (ptr[1] == ':')) {
+		timegda->hour = *ptr - '0';
+		ptr++;
+	}
+	else if (*ptr == ':')
+		timegda->hour = 0;
 	else
 		return FALSE;
 
 	/* minute */
-	ptr += 2;
 	if (! *ptr)
 		return FALSE;
 	if (*ptr == ':')
 		ptr++;
 	if ((*ptr >= '0') && (*ptr <= '9') &&
-	    (*(ptr+1) >= '0') && (*(ptr+1) <= '9'))
+	    (*(ptr+1) >= '0') && (*(ptr+1) <= '9')) {
 		timegda->minute = (*ptr - '0') * 10 + *(ptr+1) - '0';
+		ptr += 2;
+	}
+	else if ((*ptr >= '0') && (*ptr <= '9') && (ptr[1] == ':')) {
+		timegda->minute = *ptr - '0';
+		ptr++;
+	}
+	else if (*ptr == ':')
+		timegda->minute = 0;
 	else
 		return FALSE;
 
 	/* second */
-	ptr += 2;
 	timegda->second = 0;
 	if (! *ptr) {
 		if ((timegda->hour > 24) || (timegda->minute > 60))
@@ -962,11 +978,18 @@ make_time (GdaHandlerTime *hdl, GdaTime *timegda, const gchar *value)
 	if (*ptr == ':')
 		ptr++;
 	if ((*ptr >= '0') && (*ptr <= '9') &&
-	    (*(ptr+1) >= '0') && (*(ptr+1) <= '9'))
+	    (*(ptr+1) >= '0') && (*(ptr+1) <= '9')) {
 		timegda->second = (*ptr - '0') * 10 + *(ptr+1) - '0';
+		ptr += 2;
+	}
+	else if ((*ptr >= '0') && (*ptr <= '9')) {
+		timegda->second = *ptr - '0';
+		ptr++;
+	}
+	else if (*ptr == ':')
+		timegda->second = 0;
 	
 	/* extra */
-	ptr += 2;
 	if (! *ptr) {
 		if ((timegda->hour > 24) || (timegda->minute > 60) || 
 		    (timegda->second > 60))
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cbc474e..4b8c75b 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -87,8 +87,8 @@ libgda-ui/data-entries/gdaui-data-cell-renderer-textual.c
 libgda-ui/data-entries/gdaui-entry-common-time.c
 libgda-ui/data-entries/gdaui-entry-none.c
 libgda-ui/data-entries/gdaui-entry-shell.c
-libgda-ui/data-entries/gdaui-entry-string-number.xml.in
-libgda-ui/data-entries/gdaui-entry-string-string.xml.in
+libgda-ui/data-entries/gdaui-entry-number.xml.in
+libgda-ui/data-entries/gdaui-entry-string.xml.in
 libgda-ui/data-entries/gdaui-entry-string.c
 libgda-ui/data-entries/gdaui-format-entry.c
 libgda-ui/data-entries/plugins/common-pict.c
diff --git a/testing/.gitignore b/testing/.gitignore
index b32f743..9fe6261 100644
--- a/testing/.gitignore
+++ b/testing/.gitignore
@@ -2,4 +2,5 @@ gda-test-connection-4.*
 gda-test-blob
 gda-provider-status
 gdaui-test-data-entries
+gdaui-test-widget-entry
 index.html
diff --git a/testing/Makefile.am b/testing/Makefile.am
index 8e553dc..ebcd1d5 100644
--- a/testing/Makefile.am
+++ b/testing/Makefile.am
@@ -6,7 +6,7 @@ AM_CPPFLAGS = \
 
 bin_PROGRAMS = gda-test-connection-4.0
 if HAVE_UI
-UI_PROGS=gdaui-test-data-entries
+UI_PROGS=gdaui-test-data-entries gdaui-test-widget-entry
 endif
 noinst_PROGRAMS = gda-test-blob gda-provider-status $(UI_PROGS)
 
@@ -44,4 +44,13 @@ gdaui_test_data_entries_LDADD = \
         $(top_builddir)/libgda-ui/libgda-ui-4.0.la \
         $(LIBGDA_LIBS)
 
+gdaui_test_widget_entry_CFLAGS = $(GTK_CFLAGS)
+gdaui_test_widget_entry_SOURCES = \
+        gdaui-test-widget-entry.c
+
+gdaui_test_widget_entry_LDADD = \
+        $(top_builddir)/libgda/libgda-4.0.la \
+        $(top_builddir)/libgda-ui/libgda-ui-4.0.la \
+        $(LIBGDA_LIBS)
+
 EXTRA_DIST = 
diff --git a/testing/gdaui-test-widget-entry.c b/testing/gdaui-test-widget-entry.c
new file mode 100644
index 0000000..fabf4e8
--- /dev/null
+++ b/testing/gdaui-test-widget-entry.c
@@ -0,0 +1,409 @@
+#include <gtk/gtk.h>
+#include <libgda-ui/data-entries/gdaui-entry.h>
+#include <libgda-ui/data-entries/gdaui-formatted-entry.h>
+#include <libgda-ui/data-entries/gdaui-numeric-entry.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NB_ENTRIES 50
+static GtkWidget *entries[NB_ENTRIES];
+
+static void
+print_unicode (const gchar *ptr)
+{
+	gunichar wc;
+	wc = g_utf8_get_char_validated (ptr, -1);
+	g_assert (wc >= 0);
+	g_print ("%s <=> %u, IS_print: %d\n", ptr, wc, g_unichar_isprint (wc));
+}
+
+static void
+entry_changed_cb (GdauiEntry *entry, const gchar *id)
+{
+	gchar *tmp;
+	tmp = gdaui_entry_get_text (entry);
+	g_print ("Entry %s changed to: [%s]", id, tmp);
+	if (GDAUI_IS_FORMATTED_ENTRY (entry)) {
+		g_free (tmp);
+		tmp = gdaui_formatted_entry_get_text (GDAUI_FORMATTED_ENTRY (entry));
+		g_print (" => [%s]", tmp);
+	}
+	g_print ("\n");
+		
+	g_free (tmp);
+}
+
+static void
+prop_length_set_cb (GtkButton *buntton, GdauiEntry *entry)
+{
+	gchar *tmp;
+	gint max, i;
+	tmp = gdaui_entry_get_text (entry);
+	max = atoi (tmp);
+	g_free (tmp);
+	g_print ("Setting Max entries' length to %d\n", max);
+
+	for (i = 0; i < NB_ENTRIES; i++)
+		if (entries [i])
+			gdaui_entry_set_max_length (GDAUI_ENTRY (entries [i]), max);
+}
+
+static void
+prop_text_set_cb (GtkButton *buntton, GdauiEntry *entry)
+{
+	gchar *tmp;
+	gint i;
+	tmp = gdaui_entry_get_text (entry);
+	g_print ("Setting entries' text to [%s]\n", tmp);
+	for (i = 0; i < NB_ENTRIES; i++)
+		if (entries [i])
+			gdaui_entry_set_text (GDAUI_ENTRY (entries [i]), tmp);
+	g_free (tmp);
+}
+
+static void
+prop_text_null_cb (GtkButton *buntton, gpointer data)
+{
+	gint i;
+
+	g_print ("Setting entries' text to NULL\n");
+	for (i = 0; i < NB_ENTRIES; i++)
+		if (entries [i])
+			gdaui_entry_set_text (GDAUI_ENTRY (entries [i]), NULL);
+}
+
+GtkWidget *
+make_label (gint index, const gchar *text)
+{
+	GtkWidget *label;
+	gchar *tmp;
+	tmp = g_strdup_printf ("#%d: %s", index, text ? text : "");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	
+	return label;
+}
+
+int
+main (int argc, char* argv[])
+{
+	GtkWidget *entry, *label, *button;
+	GtkWidget *window, *hbox, *vbox, *table, *top_vbox;
+	gint index = 0;
+	gchar *tmp;
+
+	gtk_init (&argc, &argv);
+	memset (entries, 0, sizeof (entries));
+
+	print_unicode ("0");
+	print_unicode ("9");
+	print_unicode ("@");
+	print_unicode ("^");
+	print_unicode ("#");
+	print_unicode ("â?¬");
+
+
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+	top_vbox = gtk_vbox_new (FALSE, 0);
+	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
+	gtk_container_add(GTK_CONTAINER(window), top_vbox);
+
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (top_vbox), hbox, FALSE, FALSE, 0);
+
+	/*
+	 * GdauiEntry widgets
+	 */
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), "<b>GdauiEntry</b>");
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+#define HAVE_NORMAL
+#ifdef HAVE_NORMAL
+	/* #0 */
+	tmp = g_strdup_printf ("#%d", index);
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_entry_new (NULL, NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+	/* #1 */
+	tmp = g_strdup_printf ("#%d", index);
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_entry_new ("â?¬ ", NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #2 */
+	tmp = g_strdup_printf ("#%d", index);
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_entry_new (NULL, " Ã?");
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #3 */
+	tmp = g_strdup_printf ("#%d", index);
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_entry_new ("�� ", " êê");
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+#endif
+
+#define HAVE_FORMATTED
+#ifdef HAVE_FORMATTED
+	/*
+	 * GdauiFormattedEntry widgets
+	 */
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), "<b>GdauiFormattedEntry</b>");
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	/* #4 */
+	tmp = g_strdup_printf ("#%d", index);
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_formatted_entry_new ("TIME=00:00:00", NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #5 */
+	tmp = g_strdup_printf ("#%d", index);
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_formatted_entry_new ("TIME=00â?¬00:00",
+					   "------- --   ");
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+#endif
+
+#define HAVE_NUMERIC
+#ifdef HAVE_NUMERIC
+	/*
+	 * GdauiNumericEntry widgets
+	 */
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), "<b>GdauiNumericEntry</b>");
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	/* #6 */
+	tmp = g_strdup_printf ("#%d: %s", index, "G_TYPE_CHAR");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_CHAR);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #7 */
+	tmp = g_strdup_printf ("#%d: %s", index, "G_TYPE_UINT, thousand sep=','");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_UINT);
+	g_object_set (G_OBJECT (entry), "thousands-sep", ',', NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #8 */
+	tmp = g_strdup_printf ("#%d: %s", index, "G_TYPE_FLOAT, n_decimals=2");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_FLOAT);
+	g_object_set (G_OBJECT (entry), "n-decimals", 2, NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #9 */
+	tmp = g_strdup_printf ("#%d: %s", index, "G_TYPE_DOUBLE");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_DOUBLE);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #10 */
+	tmp = g_strdup_printf ("#%d: %s", index, "G_TYPE_FLOAT, thousand sep=' ', n_decimals=2");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_FLOAT);
+	g_object_set (G_OBJECT (entry), "n-decimals", 2, NULL);
+	g_object_set (G_OBJECT (entry), "thousands-sep", ' ', NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+#endif
+
+#define HAVE_COMPLETE
+#ifdef HAVE_COMPLETE
+	/*
+	 * complete tests
+	 */
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), "<b>Complete tests</b>");
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	/* #11 */
+	tmp = g_strdup_printf ("#%d: %s", index, "2 decimals with EURO");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_FLOAT);
+	g_object_set (G_OBJECT (entry), "suffix", "â?¬", "n-decimals", 2, NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #12 */
+	tmp = g_strdup_printf ("#%d: %s", index, "3 decimals between markers");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_numeric_entry_new (G_TYPE_FLOAT);
+	g_object_set (G_OBJECT (entry), "prefix", "[", "suffix", "]", "n-decimals", 3, NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #13 */
+	tmp = g_strdup_printf ("#%d: %s", index, "**.* between markers");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_formatted_entry_new ("**.*", NULL);
+	g_object_set (G_OBJECT (entry), "suffix", "]", "prefix", "[", NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+	/* #14 */
+	tmp = g_strdup_printf ("#%d: %s", index, "900//@@@//^^^/##** between markers");
+	label = gtk_label_new (tmp);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+	entry = gdaui_formatted_entry_new ("900//@@@//^^^/##**", NULL);
+	g_object_set (G_OBJECT (entry), "suffix", "]", "prefix", "[", NULL);
+	entries[index] = entry;
+	index++;
+	gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (entry_changed_cb), tmp);
+
+#endif
+
+	/* properties */
+	label = gtk_label_new ("");
+	gtk_label_set_markup (GTK_LABEL (label), "<b>Common properties:</b>");
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (top_vbox), label, FALSE, FALSE, 10);
+	table = gtk_table_new (2, 3, FALSE);
+	gtk_box_pack_start (GTK_BOX (top_vbox), table, TRUE, TRUE, 0);
+
+	label = gtk_label_new ("MaxLen:");
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, 0, 0, 0, 0);
+	entry = gdaui_entry_new (NULL, NULL); /* FIXME: use a gint data entry */
+	gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
+	button = gtk_button_new_with_label ("Set");
+	gtk_table_attach (GTK_TABLE (table), button, 2, 3, 0, 1, 0, 0, 0, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (prop_length_set_cb), entry);
+
+	label = gtk_label_new ("Force text:");
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, 0, 0, 0, 0);
+	entry = gdaui_entry_new (NULL, NULL);
+	gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 1, 2);
+	button = gtk_button_new_with_label ("Set");
+	gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2, 0, 0, 0, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (prop_text_set_cb), entry);
+
+	label = gtk_label_new ("Force text to NULL:");
+	gtk_table_attach (GTK_TABLE (table), label, 0, 2, 2, 3, 0, 0, 0, 0);
+	button = gtk_button_new_with_label ("Set");
+	gtk_table_attach (GTK_TABLE (table), button, 2, 3, 2, 3, 0, 0, 0, 0);
+	g_signal_connect (button, "clicked",
+			  G_CALLBACK (prop_text_null_cb), entry);
+
+
+	gtk_widget_show_all(window);
+	gtk_main();
+	return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]