thread safety
[hildon] / hildon-widgets / hildon-number-editor.c
index eb24107..ac54c2a 100644 (file)
  *
  */
 
-/*
- * @file hildon-number-editor.c
- *
- * This file contains the implementation of Hildon Number Editor
+/**
+ * SECTION:hildon-number-editor
+ * @short_description: A widget used to enter a number within a pre-defined range
  *
+ * HildonNumberEditor is used to enter a number from a specific range. 
+ * There are two buttons to scroll the value in number field. 
+ * Manual input is also possible.
  */
 
 #include <gdk/gdkkeysyms.h>
-#include <glib.h>
 #include <gtk/gtk.h>
-#include <pango/pango.h>
 
 #include <string.h>
 #include <stdio.h>
@@ -43,6 +43,8 @@
 #include <hildon-widgets/gtk-infoprint.h>
 #include "hildon-composite-widget.h"
 #include <hildon-widgets/hildon-input-mode-hint.h>
+#include <hildon-widgets/hildon-defines.h>
+#include "hildon-libs-enum-types.h"
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -52,9 +54,9 @@
 #define _(String) dgettext(PACKAGE, String)
 
 /*Pixel spec defines*/
-#define SPACE_BORDER 6
 #define NUMBER_EDITOR_HEIGHT 30
 
+/* Size of plus and minus buttons */
 #define BUTTON_HEIGHT 30
 #define BUTTON_WIDTH 30
 
@@ -83,7 +85,7 @@ hildon_number_editor_size_request (GtkWidget *widget,
 
 static void
 set_widget_allocation (GtkWidget *widget, GtkAllocation *alloc,
-                       GtkAllocation *allocation);
+                       const GtkAllocation *allocation);
 
 static void
 hildon_number_editor_size_allocate (GtkWidget *widget,
@@ -108,11 +110,8 @@ hildon_number_editor_button_released (GtkWidget *widget,
 static gboolean
 do_mouse_timeout (HildonNumberEditor *editor);
 
-static gboolean
-change_numbers (HildonNumberEditor *editor, gint type, gint cursor_pos);
-
-static gchar *
-integer_to_string (gint value);
+static void
+change_numbers (HildonNumberEditor *editor, gint update);
 
 static void
 hildon_number_editor_forall (GtkContainer *container, gboolean include_internals,
@@ -128,14 +127,14 @@ static void
 hildon_number_editor_finalize (GObject *self);
 
 static gboolean
-hildon_number_editor_mnemonic_activate( GtkWidget *widget,
-                                        gboolean group_cycle );
-static gboolean
-hildon_number_editor_error_handler(HildonNumberEditor *editor,
+hildon_number_editor_range_error(HildonNumberEditor *editor,
                                   HildonNumberEditorErrorType type);
 
 static gboolean
 hildon_number_editor_select_all (HildonNumberEditorPrivate *priv);
+
+static void
+hildon_number_editor_validate_value(HildonNumberEditor *editor, gboolean allow_intermediate);
     
 static void hildon_number_editor_set_property(GObject * object,
                                      guint prop_id,
@@ -146,13 +145,14 @@ static void hildon_number_editor_get_property(GObject * object,
                                      guint prop_id,
                                      GValue * value, GParamSpec * pspec);
 
+/* Signal indices */
 enum
 {
   RANGE_ERROR,
-
   LAST_SIGNAL
 };
 
+/* Property indices */
 enum {
     PROP_0,
     PROP_VALUE
@@ -164,19 +164,20 @@ static guint HildonNumberEditor_signal[LAST_SIGNAL] = {0};
 
 struct _HildonNumberEditorPrivate
 {
+    /* Child widgets */
     GtkWidget *num_entry;
     GtkWidget *plus;
     GtkWidget *minus;
 
-    gint start;
-    gint end;
+    gint start; /* Minimum */
+    gint end;   /* Maximum */
     gint default_val;
-    gint button_type;
-
-    guint button_event_id;
-    guint select_all_idle_id;
+    gint button_type; /* Type of button pressed: 1 = plus, -1 = minus */
 
-    gboolean negative;
+    /* Timer IDs */
+    guint button_event_id; /* Repeat change when button is held */
+    guint select_all_idle_id; /* Selection repaint hack
+                                see hildon_number_editor_select_all */
 };
 
 
@@ -219,10 +220,9 @@ hildon_number_editor_class_init(HildonNumberEditorClass * editor_class)
 
     widget_class->size_request = hildon_number_editor_size_request;
     widget_class->size_allocate = hildon_number_editor_size_allocate;
-    widget_class->mnemonic_activate = hildon_number_editor_mnemonic_activate;
     widget_class->focus = hildon_composite_widget_focus;
 
-    editor_class->error_handler = hildon_number_editor_error_handler;
+    editor_class->range_error = hildon_number_editor_range_error;
 
     /* Because we derived our widget from GtkContainer, we should override 
        forall method */
@@ -243,10 +243,10 @@ hildon_number_editor_class_init(HildonNumberEditorClass * editor_class)
     HildonNumberEditor_signal[RANGE_ERROR] =
       g_signal_new("range_error", HILDON_TYPE_NUMBER_EDITOR,
                   G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
-                  (HildonNumberEditorClass, error_handler),
+                  (HildonNumberEditorClass, range_error),
                   g_signal_accumulator_true_handled, NULL,
-                  _hildon_marshal_BOOLEAN__INT,
-                  G_TYPE_BOOLEAN, 1, G_TYPE_INT);
+                  _hildon_marshal_BOOLEAN__ENUM,
+                  G_TYPE_BOOLEAN, 1, HILDON_TYPE_NUMBER_EDITOR_ERROR_TYPE);
 }
 
 static void
@@ -256,11 +256,12 @@ hildon_number_editor_forall(GtkContainer *container, gboolean include_internals,
     HildonNumberEditorPrivate *priv =
         HILDON_NUMBER_EDITOR_GET_PRIVATE(container);
 
-    g_return_if_fail(callback != NULL);              
+    g_assert(callback != NULL);              
 
     if (!include_internals)
         return;
 
+    /* Enumerate child widgets */
     (*callback) (priv->minus, callback_data);
     (*callback) (priv->num_entry, callback_data);
     (*callback) (priv->plus, callback_data);
@@ -273,6 +274,7 @@ hildon_number_editor_destroy(GtkObject *self)
 
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(self);
 
+    /* Free child widgets */
     if (priv->minus)
       {
         gtk_widget_unparent(priv->minus);
@@ -294,15 +296,24 @@ hildon_number_editor_destroy(GtkObject *self)
 }
 
 static void
+hildon_number_editor_stop_repeat_timer(HildonNumberEditorPrivate *priv)
+{
+    if (priv->button_event_id)
+    {
+        g_source_remove(priv->button_event_id);
+        priv->button_event_id = 0;
+    }
+}
+
+static void
 hildon_number_editor_finalize (GObject *self)
 {
    HildonNumberEditorPrivate *priv;
 
    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(self);
 
-   /* Free timer */
-   if (priv->button_event_id)
-     g_source_remove (priv->button_event_id);
+   /* Free timers */
+   hildon_number_editor_stop_repeat_timer(priv);
 
    if (priv->select_all_idle_id)
      g_source_remove (priv->select_all_idle_id);
@@ -320,6 +331,7 @@ hildon_number_editor_init (HildonNumberEditor *editor)
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
     GTK_WIDGET_SET_FLAGS(GTK_WIDGET(editor), GTK_NO_WINDOW);
 
+    /* Create child widgets */
     priv->num_entry = gtk_entry_new();
     priv->minus = gtk_button_new();
     priv->plus = gtk_button_new();
@@ -340,6 +352,7 @@ hildon_number_editor_init (HildonNumberEditor *editor)
     gtk_widget_set_parent(priv->num_entry, GTK_WIDGET(editor));
     gtk_widget_set_parent(priv->plus, GTK_WIDGET(editor));
 
+    /* Connect child widget signals */
     g_signal_connect(GTK_OBJECT(priv->num_entry), "changed",
                      G_CALLBACK(hildon_number_editor_entry_changed),
                      editor);
@@ -391,16 +404,6 @@ hildon_number_editor_init (HildonNumberEditor *editor)
 }
 
 static gboolean
-hildon_number_editor_mnemonic_activate (GtkWidget *widget,
-                                        gboolean group_cycle)
-{
-  HildonNumberEditorPrivate *priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(widget);
-  gtk_widget_grab_focus(priv->num_entry);
-  gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
-  return TRUE;
-}
-
-static gboolean
 hildon_number_editor_entry_button_released (GtkWidget *widget,
                                            GdkEventButton *event,
                                            gpointer data)
@@ -413,21 +416,28 @@ static gboolean
 hildon_number_editor_button_released (GtkWidget *widget, GdkEvent *event,
                                       HildonNumberEditor *editor)
 {
-    HildonNumberEditorPrivate *priv =
-        HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
-        
-    if (priv->button_event_id)
-      {
-        g_source_remove(priv->button_event_id);
-        priv->button_event_id = 0;
-      }
-    return FALSE;
+  HildonNumberEditorPrivate *priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);  
+  hildon_number_editor_stop_repeat_timer(priv);
+  return FALSE;
+}
+
+/* Format given number to editor field, no checks performed, all signals
+   are sent normally. */
+static void
+hildon_number_editor_real_set_value (HildonNumberEditorPrivate *priv, gint value)
+{
+    gchar buffer[32];
+  
+    /* Update text in entry to new value */
+    g_snprintf(buffer, sizeof(buffer), "%d", value);
+    gtk_entry_set_text(GTK_ENTRY(priv->num_entry), buffer);
 }
 
 static gboolean
 hildon_number_editor_button_pressed (GtkWidget *widget, GdkEventButton *event,
                                      gpointer data)
 {
+    /* FIXME: XXX Why aren't we using hildon_number_editor_start_timer here? XXX */
     /* Need to fetch current value from entry and increment or decrement
        it */
     HildonNumberEditor *editor;
@@ -435,16 +445,20 @@ hildon_number_editor_button_pressed (GtkWidget *widget, GdkEventButton *event,
     GtkSettings *settings;
     guint timeout;
 
+    g_assert(HILDON_IS_NUMBER_EDITOR(data));
+
     editor = HILDON_NUMBER_EDITOR(data);
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
     settings = gtk_settings_get_default();
     g_object_get(settings, "gtk-initial-timeout", &timeout, NULL);
 
+    /* Save type of button pressed */
     if (GTK_BUTTON(widget) == GTK_BUTTON(priv->plus))
         priv->button_type = 1;
     else
         priv->button_type = -1;
 
+    /* Start repetition timer */
     if (!priv->button_event_id)
       {
         do_mouse_timeout(editor);
@@ -477,81 +491,37 @@ static gboolean
 do_mouse_timeout (HildonNumberEditor *editor)
 {
     HildonNumberEditorPrivate *priv;
-    gboolean success;
 
-    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
-    success = TRUE;
+    GDK_THREADS_ENTER ();
+    
+    g_assert(HILDON_IS_NUMBER_EDITOR(editor));
 
-    if (priv->button_type == 1)
-      {
-        if (change_numbers(editor, 1, -1) == FALSE)
-            success = FALSE;
+    /* Update value based on button held */
+    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
+    change_numbers(editor, priv->button_type);
 
-      }
-    else if (change_numbers(editor, 2, -1) == FALSE)
-        success = FALSE;
+    GDK_THREADS_LEAVE ();
 
-    if (!success)
-      {
-        gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
-        return FALSE;
-      }
-    else
-        return TRUE;
+    return TRUE;
 }
 
-static gboolean
-change_numbers (HildonNumberEditor *editor, gint type, gint cursor_pos)
+/* Changes the current number value by the amount of update
+   and verifies the result. */
+static void
+change_numbers (HildonNumberEditor *editor, gint update)
 {
-    gboolean r;
-    gint nvalue;
-    gchar *snvalue;
-    HildonNumberEditorPrivate *priv =
-        HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
-    nvalue = atoi(GTK_ENTRY(priv->num_entry)->text);
-
-    if (type == 1)
-      {
-        if (nvalue < priv->end)
-            nvalue += 1;
-        else
-          {
-           g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
-                         0, MAXIMUM_VALUE_EXCEED, &r);
-            return FALSE;
-          }
-      }
-    else
-      {
-        if (nvalue > priv->start)
-            nvalue -= 1;
-
-        else
-          {
-           g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
-                         0, MINIMUM_VALUE_EXCEED, &r);
-            return FALSE;
-          }
-      }
-
-    /* write value to num_entry */
-    snvalue = integer_to_string(nvalue);
-    gtk_entry_set_text(GTK_ENTRY(priv->num_entry), snvalue);
-
-    if (cursor_pos != -1)
-        gtk_editable_set_position(GTK_EDITABLE(priv->num_entry),
-                                  cursor_pos);
+    HildonNumberEditorPrivate *priv;
+    gint current_value;
 
-    if (snvalue)
-        g_free(snvalue);
+    g_assert(HILDON_IS_NUMBER_EDITOR(editor));
 
-    return TRUE;
-}
+    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
+    current_value = hildon_number_editor_get_value(editor);
 
-static gchar *
-integer_to_string (gint nvalue)
-{
-    return g_strdup_printf("%d", nvalue);
+    /* We need to rerun validation by hand, since validation
+       done in "changed" callback allows intermediate values */
+    hildon_number_editor_real_set_value(priv, current_value + update);
+    hildon_number_editor_validate_value(editor, FALSE);
 }
 
 static void
@@ -565,59 +535,99 @@ add_select_all_idle (HildonNumberEditorPrivate *priv)
 }
 
 static void
-hildon_number_editor_entry_changed (GtkWidget *widget, gpointer data)
+hildon_number_editor_validate_value(HildonNumberEditor *editor, gboolean allow_intermediate)
 {
-  HildonNumberEditor *editor;
-  HildonNumberEditorPrivate *priv;
-  gchar *tmpstr;
-  gint value;
-  gchar *tail = NULL;
-  gboolean r;
-
-  editor = HILDON_NUMBER_EDITOR(data);
-  priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
+    HildonNumberEditorPrivate *priv;
+    gint error_code, fixup_value;
+    const gchar *text;
+    long value;
+    gchar *tail;
+    gboolean r;
 
-  tmpstr = GTK_ENTRY(priv->num_entry)->text;
+    g_assert(HILDON_IS_NUMBER_EDITOR(editor));
 
-  if (strlen(tmpstr) > 0)
-    {
-      tmpstr = NULL;
-      value = strtol(GTK_ENTRY(priv->num_entry)->text, &tail, 10);
-      if (!strncmp(tail, "\0", 1) || !strncmp(tail, "-", 1))
-       {    
-         if (atoi(GTK_ENTRY(priv->num_entry)->text) > priv->end)
-           {
-             g_signal_emit(editor,HildonNumberEditor_signal[RANGE_ERROR], 
-                           0, MAXIMUM_VALUE_EXCEED, &r);
-             tmpstr = integer_to_string(priv->end);
-             gtk_entry_set_text(GTK_ENTRY(priv->num_entry), tmpstr);
-              add_select_all_idle(priv);
-             if (tmpstr)
-               g_free(tmpstr);
-           }
-         else if (atoi(GTK_ENTRY(priv->num_entry)->text) < priv->start) {
-           g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
-                         0, MINIMUM_VALUE_EXCEED, &r);
-           tmpstr = integer_to_string(priv->start);
-           gtk_entry_set_text(GTK_ENTRY(priv->num_entry), tmpstr);
-            add_select_all_idle(priv);
-           if (tmpstr)
-             g_free(tmpstr);
+    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
+    text = gtk_entry_get_text(GTK_ENTRY(priv->num_entry));
+    error_code = -1;
+    fixup_value = priv->default_val;
+
+    if (text && text[0])
+    { 
+      /* Try to convert entry text to number */
+      value = strtol(text, &tail, 10);
+
+      /* Check if conversion succeeded */
+      if (tail[0] == 0)
+      {    
+        /* Check if value is in allowed range. This is tricky in those
+           cases when user is editing a value. 
+           For example: Range = [100, 500] and user have just inputted "4".
+           This should not lead into error message. Otherwise value is
+           resetted back to "100" and next "4" press will reset it back
+           and so on. */
+        if (allow_intermediate)
+        {
+            /* We now have the following error cases:
+                * If inputted value as above maximum and
+                  maximum is either positive or then maximum
+                  negative and value is positive.
+                * If inputted value is below minimum and minimum
+                  is negative or minumum positive and value
+                  negative.
+               In all other cases situation can be fixed just by
+               adding new numbers to the string.
+             */
+            if (value > priv->end && (priv->end >= 0 || (priv->end < 0 && value >= 0)))
+            {
+                error_code = MAXIMUM_VALUE_EXCEED;
+                fixup_value = priv->end;
+            }
+            else if (value < priv->start && (priv->start < 0 || (priv->start >= 0 && value < 0)))
+            {
+                error_code = MINIMUM_VALUE_EXCEED;
+                fixup_value = priv->start;
+            }
+        }
+        else
+        {
+            if (value > priv->end) {
+                error_code = MAXIMUM_VALUE_EXCEED;
+                fixup_value = priv->end;
+            }
+            else if (value < priv->start) {
+                error_code = MINIMUM_VALUE_EXCEED;
+                fixup_value = priv->start;
+            }
+        }
          }
+      /* The only valid case when conversion can fail is when we
+         have plain '-', intermediate forms are allowed AND
+         minimum bound is negative */
+      else if (!allow_intermediate || strcmp(text, "-") != 0 || priv->start >= 0)
+        error_code = ERRONEOUS_VALUE;
        }
-      else
-       {
-         g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
-                       0, ERRONEOUS_VALUE, &r);
-         tmpstr = integer_to_string(priv->start);
-         gtk_entry_set_text(GTK_ENTRY(priv->num_entry), tmpstr);
-          add_select_all_idle(priv);
-         if (tmpstr)
-           g_free(tmpstr);
-       }
+    else if (!allow_intermediate)
+        error_code = ERRONEOUS_VALUE;
+
+    if (error_code != -1)
+    {
+        /* If entry is empty and intermediate forms are nor allowed, 
+           emit error signal */
+        g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
+                     0, error_code, &r);
+        /* Change to default value */
+        hildon_number_editor_set_value(editor, fixup_value);
+        add_select_all_idle(priv);
     }
 }
 
+static void 
+hildon_number_editor_entry_changed(GtkWidget *widget, gpointer data)
+{
+    g_assert(HILDON_IS_NUMBER_EDITOR(data));
+    hildon_number_editor_validate_value(HILDON_NUMBER_EDITOR(data), TRUE);
+}
+
 static void
 hildon_number_editor_size_request (GtkWidget *widget,
                                   GtkRequisition *requisition)
@@ -629,6 +639,7 @@ hildon_number_editor_size_request (GtkWidget *widget,
     editor = HILDON_NUMBER_EDITOR(widget);
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
 
+    /* Requested size is size of all child widgets plus border space */
     gtk_widget_size_request(priv->minus, &req);
     requisition->width = req.width;
 
@@ -638,18 +649,22 @@ hildon_number_editor_size_request (GtkWidget *widget,
     gtk_widget_size_request(priv->plus, &req);
     requisition->width += req.width;
 
-    requisition->width += SPACE_BORDER * 2;
+    requisition->width += HILDON_MARGIN_DEFAULT * 2;
+
+    /* FIXME: XXX Height is fixed */
     requisition->height = NUMBER_EDITOR_HEIGHT;
 }
 
+/* Update @alloc->width so widget fits, update @alloc->x to point to free space */
 static void
 set_widget_allocation (GtkWidget *widget, GtkAllocation *alloc,
-                      GtkAllocation *allocation)
+                       const GtkAllocation *allocation)
 {
     GtkRequisition child_requisition;
 
     gtk_widget_get_child_requisition(widget, &child_requisition);
 
+    /* Fit to widget width */
     if (allocation->width + allocation->x >
         alloc->x + child_requisition.width)
         alloc->width = child_requisition.width;
@@ -661,6 +676,7 @@ set_widget_allocation (GtkWidget *widget, GtkAllocation *alloc,
       }
 
     gtk_widget_size_allocate(widget, alloc);
+    /* Update x position */
     alloc->x += alloc->width;
 }
 
@@ -677,9 +693,10 @@ hildon_number_editor_size_allocate (GtkWidget *widget,
 
   widget->allocation = *allocation;
 
-/*Init start values*/
+  /* Add upper border */
   alloc.y = widget->allocation.y + widget->style->ythickness;
 
+  /* Fix height */
   if (widget->allocation.height > NUMBER_EDITOR_HEIGHT)
     {
       alloc.height = NUMBER_EDITOR_HEIGHT - widget->style->ythickness * 2;
@@ -687,19 +704,19 @@ hildon_number_editor_size_allocate (GtkWidget *widget,
     }
   else
       alloc.height = widget->allocation.height - widget->style->ythickness * 2;
-
-
   
   if (alloc.height < 0)
     alloc.height = 0;
 
+  /* Add left border */
   alloc.x = allocation->x + widget->style->xthickness;
 
+  /* Allocate positions for widgets (left-to-right) */
   set_widget_allocation(priv->minus, &alloc, &widget->allocation);
-  alloc.x += SPACE_BORDER;
+  alloc.x += HILDON_MARGIN_DEFAULT;
 
   set_widget_allocation(priv->num_entry, &alloc, &widget->allocation);
-  alloc.x += SPACE_BORDER;
+  alloc.x += HILDON_MARGIN_DEFAULT;
 
   set_widget_allocation(priv->plus, &alloc, &widget->allocation);
 }
@@ -708,26 +725,8 @@ static gboolean
 hildon_number_editor_entry_focusout (GtkWidget *widget, GdkEventFocus *event,
                                      gpointer data)
 {
-    HildonNumberEditor *editor;
-    HildonNumberEditorPrivate *priv;
-    gchar *str;
-    gboolean r;
-
-    editor = HILDON_NUMBER_EDITOR(data);
-    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
-    
-    /* empty entry, must infoprint error message */
-    if (!strlen(GTK_ENTRY(priv->num_entry)->text))
-      {
-       g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
-                     0, ERRONEOUS_VALUE, &r);
-        /* Changing to default value */
-        str = integer_to_string(priv->default_val);
-        gtk_entry_set_text(GTK_ENTRY(priv->num_entry), str);
-        gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
-        if (str)
-            g_free(str);
-      }
+    g_assert(HILDON_IS_NUMBER_EDITOR(data));
+    hildon_number_editor_validate_value(HILDON_NUMBER_EDITOR(data), FALSE);
     return FALSE;
 }
 
@@ -735,74 +734,43 @@ static gboolean
 hildon_number_editor_entry_keypress (GtkWidget *widget, GdkEventKey *event,
                                      gpointer data)
 {
-    HildonNumberEditorPrivate *priv;
-    gint tmp_pos;
-    gchar *scnd_chr;
-    gboolean ret_val = FALSE;
+    GtkEditable *editable;
+    gint cursor_pos;
+
+    g_assert(HILDON_IS_NUMBER_EDITOR(data));
 
-    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(data);
-    tmp_pos = gtk_editable_get_position( GTK_EDITABLE(priv->num_entry) );
+    editable = GTK_EDITABLE(widget);
+    cursor_pos = gtk_editable_get_position(editable);
     
-    if( (event->keyval == GDK_Left) || (event->keyval == GDK_Right) )
-    {
-      if( ((event->keyval == GDK_Left) && tmp_pos == 0 ) ||
-           (event->keyval == GDK_Left && gtk_editable_get_selection_bounds 
-                                                (GTK_EDITABLE (priv->num_entry),
-                                                 0, NULL)) )
-      {
-        if (change_numbers(HILDON_NUMBER_EDITOR(data), 2, -1) == FALSE)
-        { 
-          gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
-          return TRUE;
-        }
-      }
-      else if ( (event->keyval == GDK_Right) )
-      {
-        gchar *str = gtk_editable_get_chars( GTK_EDITABLE(priv->num_entry),
-                                             0, -1 );
-        if( strlen(str) == tmp_pos )
-        {
-          if (change_numbers(HILDON_NUMBER_EDITOR(data), 1, tmp_pos) == FALSE)
-          { 
-            gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
-            g_free (str);
-            return TRUE;
-          }
-        }
-       g_free (str);
-      }
-      return FALSE;
-    }
-   
-    scnd_chr = gtk_editable_get_chars( GTK_EDITABLE(priv->num_entry),
-                                              0, 1 );
-    if (event->keyval == GDK_minus || event->keyval == GDK_KP_Subtract)
+    switch (event->keyval)
     {
-      if (tmp_pos > 0)
-           ret_val = TRUE;
-      if (!strncmp (scnd_chr, "-", 1))
-           ret_val = TRUE;
-    }
-    else if ((event->keyval == GDK_Up) || (event->keyval == GDK_Down)
-        || (event->keyval == GDK_KP_Up) || (event->keyval == GDK_KP_Down))
-        ret_val = FALSE;
-    else if (((event->keyval >= GDK_0) && (event->keyval <= GDK_9)) ||
-             ((event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9))
-             || (event->keyval == GDK_BackSpace)
-             || (event->keyval == GDK_Delete)
-             || (event->keyval == GDK_Return)
-             || (((event->keyval == GDK_minus)
-             || (event->keyval == GDK_KP_Subtract))))
-        ret_val = FALSE;
-    else
-      ret_val = TRUE;
+        case GDK_Left:
+            /* If the cursor is on the left, try to decrement */
+            if (cursor_pos == 0) {
+                change_numbers(HILDON_NUMBER_EDITOR(data), -1);
+                return TRUE;
+            }
+            break;
+
+        case GDK_Right:
+            /* If the cursor is on the right, try to increment */
+            if (cursor_pos >= g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(widget)), -1))
+            {
+                change_numbers(HILDON_NUMBER_EDITOR(data), 1);
+                gtk_editable_set_position(editable, cursor_pos);
+                return TRUE;
+            }
+            break;
+
+        default:
+            break;
+    };
 
-   g_free (scnd_chr);
-   return ret_val;
+    return FALSE;
 }
 
 static gboolean
-hildon_number_editor_error_handler(HildonNumberEditor *editor,
+hildon_number_editor_range_error(HildonNumberEditor *editor,
                                   HildonNumberEditorErrorType type)
 {
 
@@ -814,21 +782,22 @@ hildon_number_editor_error_handler(HildonNumberEditor *editor,
   min = priv->start;
   max = priv->end;
 
-  /* Construct different error message */
+  /* Construct error message */
   switch (type)
     {
     case MAXIMUM_VALUE_EXCEED:
-      err_msg = g_strdup_printf(_("Ckct_ib_maximum_value"), max, max);
+      err_msg = g_strdup_printf(_("ckct_ib_maximum_value"), max, max);
       break;
     case MINIMUM_VALUE_EXCEED:
-      err_msg = g_strdup_printf(_("Ckct_ib_minimum_value"), min, min);
+      err_msg = g_strdup_printf(_("ckct_ib_minimum_value"), min, min);
       break;
     case ERRONEOUS_VALUE:
       err_msg =
-       g_strdup_printf(_("Ckct_ib_set_a_value_within_range"), min, max);
+       g_strdup_printf(_("ckct_ib_set_a_value_within_range"), min, max);
       break;
     }
   
+  /* Infoprint error */
   if (err_msg)
     {
       gtk_infoprint(GTK_WINDOW(gtk_widget_get_ancestor(GTK_WIDGET(editor),
@@ -842,12 +811,12 @@ hildon_number_editor_error_handler(HildonNumberEditor *editor,
 
 /**
  * hildon_number_editor_new:
- * @min: Minimum accepted value
- * @max: Maximum accepted value
+ * @min: minimum accepted value
+ * @max: maximum accepted value
  * 
- * This function creates new number editor
+ * Creates new number editor
  *
- * Return value: a new #HildonNumberEditor widget.
+ * Returns: a new #HildonNumberEditor widget
  */
 GtkWidget *
 hildon_number_editor_new (gint min, gint max)
@@ -864,80 +833,52 @@ hildon_number_editor_new (gint min, gint max)
 /**
  * hildon_number_editor_set_range:
  * @editor: a #HildonNumberEditor widget
- * @min: Minimum accepted value
- * @max: Maximum accepted value
+ * @min: minimum accepted value
+ * @max: maximum accepted value
  *
- * This function set accepted number range for editor
+ * Sets accepted number range for editor
  */
 void
 hildon_number_editor_set_range (HildonNumberEditor *editor, gint min, gint max)
 {
     HildonNumberEditorPrivate *priv;
-    gchar *str, *str2;
-    gint a, b, entry_len;
+    gchar buffer_min[32], buffer_max[32];
+    gint a, b;
 
     g_return_if_fail(HILDON_IS_NUMBER_EDITOR(editor));
 
-    if (min > max)
-      {
-        gint temp = min;
-
-        min = max;
-        max = temp;
-      }
-
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
 
-    /* we need to store user inputted values */
-    priv->start = min;
-    priv->end = max;
-    priv->default_val = min;
-
-    priv->negative = min < 0 ? TRUE : FALSE;
-
-    str = integer_to_string(max);
-    str2 = integer_to_string(min);
-    a = strlen(str);
-    b = strlen(str2);
-
-    if (a >= b)
-        entry_len = a;
-    else
-        entry_len = b;
-    
-
-    gtk_entry_set_width_chars(GTK_ENTRY(priv->num_entry), entry_len);
+    /* Set preferences */
+    priv->start = MIN(min, max);
+    priv->end = MAX(min, max);
 
-    gtk_entry_set_text(GTK_ENTRY(priv->num_entry), str);
-    gtk_widget_queue_resize(GTK_WIDGET(editor));
+    /* Find maximum allowed length of value */
+    g_snprintf(buffer_min, sizeof(buffer_min), "%d", min);
+    g_snprintf(buffer_max, sizeof(buffer_max), "%d", max);
+    a = strlen(buffer_min);
+    b = strlen(buffer_max);
 
-    if (str)
-        g_free(str);
-    if (str2)
-        g_free(str2);
+    /* Set maximum size of entry */
+    gtk_entry_set_width_chars(GTK_ENTRY(priv->num_entry), MAX(a, b));
+    hildon_number_editor_set_value(editor, priv->start);
 }
 
 /**
  * hildon_number_editor_get_value:
  * @editor: pointer to #HildonNumberEditor
  *
- * This function returns current value of number editor
- *
- * Return value: Current NumberEditor value
+ * Returns: current NumberEditor value
  */
 gint
 hildon_number_editor_get_value (HildonNumberEditor *editor)
 {
     HildonNumberEditorPrivate *priv;
-    gint value;
 
     g_return_val_if_fail(HILDON_IS_NUMBER_EDITOR(editor), 0);
 
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
-
-    value = atoi(GTK_ENTRY(priv->num_entry)->text);
-
-    return value;
+    return atoi(gtk_entry_get_text(GTK_ENTRY(priv->num_entry)));
 }
 
 /**
@@ -945,13 +886,12 @@ hildon_number_editor_get_value (HildonNumberEditor *editor)
  * @editor: pointer to #HildonNumberEditor
  * @value: numeric value for number editor
  *
- * This function sets numeric value to number editor
+ * Sets numeric value for number editor
  */
 void
 hildon_number_editor_set_value (HildonNumberEditor *editor, gint value)
 {
     HildonNumberEditorPrivate *priv;
-    gchar *str;
 
     g_return_if_fail(HILDON_IS_NUMBER_EDITOR(editor));
 
@@ -961,13 +901,7 @@ hildon_number_editor_set_value (HildonNumberEditor *editor, gint value)
     g_return_if_fail(value >= priv->start);
 
     priv->default_val = value;
-        
-    str = integer_to_string(value);
-    gtk_entry_set_text(GTK_ENTRY(priv->num_entry), str);
-    if (str)
-      {
-        g_free(str);
-      }
+    hildon_number_editor_real_set_value(priv, value);
     g_object_notify (G_OBJECT(editor), "value");
 }
 
@@ -983,8 +917,10 @@ hildon_number_editor_set_value (HildonNumberEditor *editor, gint value)
 static gboolean
 hildon_number_editor_select_all (HildonNumberEditorPrivate *priv)
 {   
+    GDK_THREADS_ENTER ();
     gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
     priv->select_all_idle_id = 0;
+    GDK_THREADS_LEAVE ();
     return FALSE;
 } 
 
@@ -1024,4 +960,3 @@ hildon_number_editor_get_property(GObject * object,
         break;
     }
 }
-