added proposed fixes
[hildon] / hildon-widgets / hildon-number-editor.c
index 0610c64..64e7def 100644 (file)
 #include <stdlib.h>
 
 #include "hildon-number-editor.h"
+#include "hildon-marshalers.h"
 #include <hildon-widgets/gtk-infoprint.h>
 #include "hildon-composite-widget.h"
+#include <hildon-widgets/hildon-input-mode-hint.h>
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_NUMBER_EDITOR, \
         HildonNumberEditorPrivate));
 
-enum
-{
-   MAXIMUM_VALUE_EXCEED,
-   MINIMUM_VALUE_EXCEED,
-   ERRONEOUS_VALUE
-};
-
-
 typedef struct _HildonNumberEditorPrivate HildonNumberEditorPrivate;
 
 static void
@@ -91,9 +85,6 @@ static void
 set_widget_allocation (GtkWidget *widget, GtkAllocation *alloc,
                        GtkAllocation *allocation);
 
-static int
-hildon_number_editor_get_font_width (GtkWidget *widget);
-
 static void
 hildon_number_editor_size_allocate (GtkWidget *widget,
                                     GtkAllocation *allocation);
@@ -114,9 +105,6 @@ static gboolean
 hildon_number_editor_button_released (GtkWidget *widget,
                                       GdkEvent *event,
                                       HildonNumberEditor *editor);
-static void
-construct_error_message (HildonNumberEditor *editor, gint type);
-
 static gboolean
 do_mouse_timeout (HildonNumberEditor *editor);
 
@@ -142,9 +130,23 @@ 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,
+                                  HildonNumberEditorErrorType type);
+
+static gboolean
+hildon_number_editor_select_all (HildonNumberEditorPrivate *priv);
+    
+enum
+{
+  RANGE_ERROR,
+
+  LAST_SIGNAL
+};
 
 static GtkContainerClass *parent_class;
 
+static guint HildonNumberEditor_signal[LAST_SIGNAL] = {0};
 
 struct _HildonNumberEditorPrivate
 {
@@ -156,11 +158,10 @@ struct _HildonNumberEditorPrivate
     gint end;
     gint default_val;
     gint button_type;
-    gint button_event_id;
-    gint entry_len;
-    gint entry_width;
 
-    gboolean rem_sign_space;
+    guint button_event_id;
+    guint select_all_idle_id;
+
     gboolean negative;
 };
 
@@ -206,11 +207,21 @@ hildon_number_editor_class_init(HildonNumberEditorClass * editor_class)
     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;
+
     /* Because we derived our widget from GtkContainer, we should override 
        forall method */
     container_class->forall = hildon_number_editor_forall;
     GTK_OBJECT_CLASS(editor_class)->destroy = hildon_number_editor_destroy;
     G_OBJECT_CLASS(editor_class)->finalize = hildon_number_editor_finalize;
+
+    HildonNumberEditor_signal[RANGE_ERROR] =
+      g_signal_new("range_error", HILDON_TYPE_NUMBER_EDITOR,
+                  G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
+                  (HildonNumberEditorClass, error_handler),
+                  g_signal_accumulator_true_handled, NULL,
+                  _hildon_marshal_BOOLEAN__INT,
+                  G_TYPE_BOOLEAN, 1, G_TYPE_INT);
 }
 
 static void
@@ -268,6 +279,9 @@ hildon_number_editor_finalize (GObject *self)
    if (priv->button_event_id)
      g_source_remove (priv->button_event_id);
 
+   if (priv->select_all_idle_id)
+     g_source_remove (priv->select_all_idle_id);
+
     /* Call parent class finalize, if have one */
     if (G_OBJECT_CLASS (parent_class)->finalize)
         G_OBJECT_CLASS (parent_class)->finalize(self);
@@ -294,9 +308,8 @@ hildon_number_editor_init (HildonNumberEditor *editor)
     GTK_WIDGET_UNSET_FLAGS( priv->minus, GTK_CAN_FOCUS );
     GTK_WIDGET_UNSET_FLAGS( priv->plus, GTK_CAN_FOCUS );
     
-    priv->entry_width = 0;
-    priv->button_event_id = -1;
-    priv->rem_sign_space = TRUE;
+    priv->button_event_id = 0;
+    priv->select_all_idle_id = 0;
 
     gtk_widget_set_parent(priv->minus, GTK_WIDGET(editor));
     gtk_widget_set_parent(priv->num_entry, GTK_WIDGET(editor));
@@ -342,6 +355,9 @@ hildon_number_editor_init (HildonNumberEditor *editor)
                      G_CALLBACK(hildon_number_editor_button_released),
                      editor);
 
+    g_object_set( G_OBJECT(priv->num_entry),
+                  "input-mode", HILDON_INPUT_MODE_HINT_NUMERIC, NULL );
+    
     gtk_widget_show(priv->num_entry);
     gtk_widget_show(priv->minus);
     gtk_widget_show(priv->plus);
@@ -368,40 +384,6 @@ hildon_number_editor_entry_button_released (GtkWidget *widget,
   return FALSE;
 }
 
-static void
-construct_error_message (HildonNumberEditor *editor, gint type)
-{
-    gint min, max;
-    gchar *err_msg = NULL;
-    HildonNumberEditorPrivate *priv;
-
-    priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
-    min = priv->start;
-    max = priv->end;
-
-    /* Construct different error message */
-    switch (type)
-      {
-        case MAXIMUM_VALUE_EXCEED:
-          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);
-          break;
-        case ERRONEOUS_VALUE:
-          err_msg =
-            g_strdup_printf(_("Ckct_ib_set_a_value_within_range"), min, max);
-          break;
-      }
-
-    if (err_msg)
-      {
-        gtk_infoprint(GTK_WINDOW(gtk_widget_get_ancestor(GTK_WIDGET(editor),
-                      GTK_TYPE_WINDOW)), err_msg);
-        g_free(err_msg);
-      }
-}
-
 static gboolean
 hildon_number_editor_button_released (GtkWidget *widget, GdkEvent *event,
                                       HildonNumberEditor *editor)
@@ -409,10 +391,10 @@ hildon_number_editor_button_released (GtkWidget *widget, GdkEvent *event,
     HildonNumberEditorPrivate *priv =
         HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
         
-    if (priv->button_event_id != -1)
+    if (priv->button_event_id)
       {
         g_source_remove(priv->button_event_id);
-        priv->button_event_id = -1;
+        priv->button_event_id = 0;
       }
     return FALSE;
 }
@@ -438,7 +420,7 @@ hildon_number_editor_button_pressed (GtkWidget *widget, GdkEventButton *event,
     else
         priv->button_type = -1;
 
-    if (priv->button_event_id == -1)
+    if (!priv->button_event_id)
       {
         do_mouse_timeout(editor);
         priv->button_event_id = g_timeout_add (timeout,
@@ -496,6 +478,7 @@ do_mouse_timeout (HildonNumberEditor *editor)
 static gboolean
 change_numbers (HildonNumberEditor *editor, gint type, gint cursor_pos)
 {
+    gboolean r;
     gint nvalue;
     gchar *snvalue;
     HildonNumberEditorPrivate *priv =
@@ -508,7 +491,8 @@ change_numbers (HildonNumberEditor *editor, gint type, gint cursor_pos)
             nvalue += 1;
         else
           {
-            construct_error_message(editor, MAXIMUM_VALUE_EXCEED);
+           g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
+                         0, MAXIMUM_VALUE_EXCEED, &r);
             return FALSE;
           }
       }
@@ -519,7 +503,8 @@ change_numbers (HildonNumberEditor *editor, gint type, gint cursor_pos)
 
         else
           {
-            construct_error_message(editor, MINIMUM_VALUE_EXCEED);
+           g_signal_emit(editor, HildonNumberEditor_signal[RANGE_ERROR], 
+                         0, MINIMUM_VALUE_EXCEED, &r);
             return FALSE;
           }
       }
@@ -545,50 +530,67 @@ integer_to_string (gint nvalue)
 }
 
 static void
+add_select_all_idle (HildonNumberEditorPrivate *priv)
+{
+  if (!priv->select_all_idle_id)
+    {
+      priv->select_all_idle_id =
+        g_idle_add((GSourceFunc) hildon_number_editor_select_all, priv);
+    }    
+}
+
+static void
 hildon_number_editor_entry_changed (GtkWidget *widget, gpointer data)
 {
-    HildonNumberEditor *editor;
-    HildonNumberEditorPrivate *priv;
-    gchar *tmpstr;
-    gint value;
-    gchar *tail = NULL;
+  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);
+  editor = HILDON_NUMBER_EDITOR(data);
+  priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
 
-    tmpstr = GTK_ENTRY(priv->num_entry)->text;
+  tmpstr = GTK_ENTRY(priv->num_entry)->text;
 
-    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)
-              {
-                construct_error_message(editor, MAXIMUM_VALUE_EXCEED);
-                tmpstr = integer_to_string(priv->end);
-                gtk_entry_set_text(GTK_ENTRY(priv->num_entry), tmpstr);
-                if (tmpstr)
-                    g_free(tmpstr);
-              }
-            else if (atoi(GTK_ENTRY(priv->num_entry)->text) < priv->start) {
-                construct_error_message(editor, MINIMUM_VALUE_EXCEED);
-                tmpstr = integer_to_string(priv->start);
-                gtk_entry_set_text(GTK_ENTRY(priv->num_entry), tmpstr);
-                if (tmpstr)
-                    g_free(tmpstr);
-              }
-          }
-        else
-          {
-            construct_error_message(editor, ERRONEOUS_VALUE);
-            tmpstr = integer_to_string(priv->start);
-            gtk_entry_set_text(GTK_ENTRY(priv->num_entry), tmpstr);
-            if (tmpstr)
-                g_free(tmpstr);
-          }
-      }
+  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);
+         }
+       }
+      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);
+       }
+    }
 }
 
 static void
@@ -602,29 +604,14 @@ hildon_number_editor_size_request (GtkWidget *widget,
     editor = HILDON_NUMBER_EDITOR(widget);
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
 
-/* FIXME -- If it's needed to fix button sizes.. priv->minus and priv->plus
- * requisitions are right places to do that.
- */
-
     gtk_widget_size_request(priv->minus, &req);
-    *requisition = req;
+    requisition->width = req.width;
 
     gtk_widget_size_request(priv->num_entry, &req);
     requisition->width += req.width;
 
-    if (!priv->entry_width)
-      {
-        gint font_w = hildon_number_editor_get_font_width(priv->num_entry);
-
-        priv->entry_width = (priv->entry_len + 1) * font_w +
-            widget->style->xthickness * 2 -
-            (font_w / 2 * priv->rem_sign_space);
-        gtk_widget_set_size_request(priv->num_entry, priv->entry_width,
-                                    req.height);
-      }
-
     gtk_widget_size_request(priv->plus, &req);
-    requisition->width += priv->entry_len;
+    requisition->width += req.width;
 
     requisition->width += SPACE_BORDER * 2;
     requisition->height = NUMBER_EDITOR_HEIGHT;
@@ -652,27 +639,6 @@ set_widget_allocation (GtkWidget *widget, GtkAllocation *alloc,
     alloc->x += alloc->width;
 }
 
-static int
-hildon_number_editor_get_font_width (GtkWidget *widget)
-{
-    PangoContext *context;
-    PangoFontMetrics *metrics;
-    gint digit_width;
-
-    context = gtk_widget_get_pango_context(widget);
-    metrics = pango_context_get_metrics(context,
-                                        widget->style->font_desc,
-                                        pango_context_get_language
-                                        (context));
-
-    digit_width = pango_font_metrics_get_approximate_digit_width(metrics);
-    digit_width = PANGO_PIXELS(digit_width);
-
-    pango_font_metrics_unref(metrics);
-
-    return digit_width;
-}
-
 static void
 hildon_number_editor_size_allocate (GtkWidget *widget,
                                     GtkAllocation *allocation)
@@ -720,6 +686,7 @@ hildon_number_editor_entry_focusout (GtkWidget *widget, GdkEventFocus *event,
     HildonNumberEditor *editor;
     HildonNumberEditorPrivate *priv;
     gchar *str;
+    gboolean r;
 
     editor = HILDON_NUMBER_EDITOR(data);
     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
@@ -727,7 +694,8 @@ hildon_number_editor_entry_focusout (GtkWidget *widget, GdkEventFocus *event,
     /* empty entry, must infoprint error message */
     if (!strlen(GTK_ENTRY(priv->num_entry)->text))
       {
-        construct_error_message(editor, ERRONEOUS_VALUE);
+       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);
@@ -808,6 +776,44 @@ hildon_number_editor_entry_keypress (GtkWidget *widget, GdkEventKey *event,
    return ret_val;
 }
 
+static gboolean
+hildon_number_editor_error_handler(HildonNumberEditor *editor,
+                                  HildonNumberEditorErrorType type)
+{
+
+  gint min, max;
+  gchar *err_msg = NULL;
+  HildonNumberEditorPrivate *priv;
+
+  priv = HILDON_NUMBER_EDITOR_GET_PRIVATE(editor);
+  min = priv->start;
+  max = priv->end;
+
+  /* Construct different error message */
+  switch (type)
+    {
+    case MAXIMUM_VALUE_EXCEED:
+      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);
+      break;
+    case ERRONEOUS_VALUE:
+      err_msg =
+       g_strdup_printf(_("Ckct_ib_set_a_value_within_range"), min, max);
+      break;
+    }
+  
+  if (err_msg)
+    {
+      gtk_infoprint(GTK_WINDOW(gtk_widget_get_ancestor(GTK_WIDGET(editor),
+                                                      GTK_TYPE_WINDOW)), err_msg);
+      g_free(err_msg);
+    }
+
+  return TRUE;
+}
+
 
 /**
  * hildon_number_editor_new:
@@ -843,7 +849,7 @@ hildon_number_editor_set_range (HildonNumberEditor *editor, gint min, gint max)
 {
     HildonNumberEditorPrivate *priv;
     gchar *str, *str2;
-    gint a, b;
+    gint a, b, entry_len;
 
     g_return_if_fail(HILDON_IS_NUMBER_EDITOR(editor));
 
@@ -870,23 +876,12 @@ hildon_number_editor_set_range (HildonNumberEditor *editor, gint min, gint max)
     b = strlen(str2);
 
     if (a >= b)
-      {
-        priv->entry_len = a;
-        priv->rem_sign_space = FALSE;
-      }
+        entry_len = a;
     else
-      {
-        priv->entry_len = b;
-        priv->rem_sign_space = TRUE;
-      }
+        entry_len = b;
     
-    /* reserving space for the minus sign */
-    if (priv->negative)
-        priv->entry_len++;
-
-/*  priv->entry_len = a >= b ? a : b;*/
 
-    priv->entry_width = 0;
+    gtk_entry_set_width_chars(GTK_ENTRY(priv->num_entry), entry_len);
 
     gtk_entry_set_text(GTK_ENTRY(priv->num_entry), str);
     gtk_widget_queue_resize(GTK_WIDGET(editor));
@@ -947,3 +942,21 @@ hildon_number_editor_set_value (HildonNumberEditor *editor, gint value)
     if (str)
         g_free(str);
 }
+
+/* When calling gtk_entry_set_text, the entry widget does things that can
+ * cause the whole widget to redraw. This redrawing is delayed and if any
+ * selections are made right after calling the gtk_entry_set_text the
+ * setting of the selection might seem to have no effect.
+ *
+ * If the selection is delayed with a lower priority than the redrawing,
+ * the selection should stick. Calling this function with g_idle_add should
+ * do it.
+ */
+static gboolean
+hildon_number_editor_select_all (HildonNumberEditorPrivate *priv)
+{   
+    gtk_editable_select_region(GTK_EDITABLE(priv->num_entry), 0, -1);
+    priv->select_all_idle_id = 0;
+    return FALSE;
+} 
+