2009-03-19 Alejandro G. Castro <alex@igalia.com>
[hildon] / src / hildon-code-dialog.c
index 857a6c8..c886590 100644 (file)
@@ -3,12 +3,12 @@
  *
  * Copyright (C) 2006 Nokia Corporation, all rights reserved.
  *
- * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
+ * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
  *
  * 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; version 2.1 of
- * the License.
+ * 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
  * @short_description: A keypad-like widget used to enter pincodes.
  *
  * #HildonCodeDialog displays a keypad that can be used to enter 
- * numerical pin codes or lock codes. 
+ * numerical pin codes or lock codes. It emits a 'input' signal each time 
+ * an input action is performed on the dialog.
  *
  */
 
 /* FIXME We need property access in this widget */
 
+#undef                                          HILDON_DISABLE_DEPRECATED
+
 #ifdef                                          HAVE_CONFIG_H
 #include                                        <config.h>
 #endif
 
+#include                                        <libintl.h>
+#include                                        <gdk/gdkkeysyms.h>
+
 #include                                        "hildon-code-dialog.h"
 #include                                        "hildon-defines.h"
 #include                                        "hildon-banner.h"
-
-#include                                        <gdk/gdkkeysyms.h>
-#include                                        <gtk/gtkbutton.h>
-#include                                        <gtk/gtkentry.h>
-#include                                        <gtk/gtkicontheme.h>
-#include                                        <gtk/gtktable.h>
-#include                                        <gtk/gtkvbox.h>
-#include                                        <gtk/gtkbbox.h>
-#include                                        <gtk/gtklabel.h>
-#include                                        <gtk/gtkalignment.h>
-#include                                        <libintl.h>
 #include                                        "hildon-code-dialog-private.h"
 
-#define                                         HEIGHT (38-HILDON_MARGIN_DEFAULT)
+#define                                         HEIGHT (48-HILDON_MARGIN_DEFAULT)
 
-#define                                         WIDTH  (60-HILDON_MARGIN_DEFAULT)
+#define                                         WIDTH  (100-HILDON_MARGIN_DEFAULT)
 
-#define                                         BACKSPACE_ICON "qgn_calculator_backspace"
+#define                                         BACKSPACE_ICON "general_backspace"
 
 #define                                         _(String) \
                                                 dgettext("hildon-libs", String)
@@ -65,9 +60,7 @@
 #define                                         c_(String) \
                                                 dgettext("hildon-common-strings", String)
 
-#define                                         DEVICELOCK_OK _("secu_enter_lock_code_dialog_ok")
-
-#define                                         DEVICELOCK_CANCEL _("secu_enter_lock_code_dialog_cancel")
+#define                                         DEVICELOCK_OK _("wdgt_bd_done")
 
 #define                                         DEVICELOCK_TITLE _("secu_application_title")
 
@@ -81,10 +74,27 @@ hildon_code_dialog_class_init                   (HildonCodeDialogClass *cd_class
 static void 
 hildon_code_dialog_init                         (HildonCodeDialog *self);
 
+static void
+hildon_code_dialog_realize                      (GtkWidget *widget);
+
+static void
+hildon_code_dialog_unrealize                    (GtkWidget *widget);
+
+static void
+hildon_code_dialog_finalize                     (GObject *object);
+
 static void 
 hildon_code_dialog_button_clicked               (GtkButton *buttonm,
                                                  gpointer user_data);
 
+static void
+hildon_code_dialog_backspace                    (HildonCodeDialog *dialog);
+
+static void
+hildon_code_dialog_im_commit                    (GtkIMContext *im_context,
+                                                 gchar *utf8,
+                                                 gpointer user_data);
+
 static void 
 hildon_code_dialog_insert_text                  (GtkEditable *editable,
                                                  gchar *new_text,
@@ -97,14 +107,22 @@ hildon_code_dialog_key_press_event              (GtkWidget *widget,
                                                  GdkEventKey *event,
                                                  gpointer user_data);
 
+static void 
+hildon_code_dialog_real_input                   (HildonCodeDialog *dialog);
+
+static void
+hildon_code_dialog_input                        (HildonCodeDialog *dialog);
+
 static GtkDialogClass*                          parent_class = NULL;
 
+static guint                                    input_signal;
+
 /**
  * hildon_code_dialog_get_type:
  *
  * Initializes and returns the type of a hildon code dialog.
  *
- * @Returns: GType of #HildonCodeDialog
+ * Returns: GType of #HildonCodeDialog
  */
 GType G_GNUC_CONST
 hildon_code_dialog_get_type                     (void)
@@ -134,8 +152,28 @@ hildon_code_dialog_get_type                     (void)
 static void
 hildon_code_dialog_class_init                   (HildonCodeDialogClass *cd_class)
 {
+    GObjectClass *gobject_class = G_OBJECT_CLASS (cd_class);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (cd_class);
+
     parent_class = GTK_DIALOG_CLASS (g_type_class_peek_parent (cd_class));
     g_type_class_add_private (cd_class, sizeof (HildonCodeDialogPrivate));
+
+    gobject_class->finalize = hildon_code_dialog_finalize;
+
+    widget_class->realize = hildon_code_dialog_realize;
+    widget_class->unrealize = hildon_code_dialog_unrealize;
+
+    cd_class->input = hildon_code_dialog_real_input;
+
+    /* FIXME Document this signal! */
+    input_signal = g_signal_new("input",
+                                HILDON_TYPE_CODE_DIALOG,
+                                G_SIGNAL_RUN_LAST,
+                                G_STRUCT_OFFSET (HildonCodeDialogClass, input),
+                                NULL, NULL,
+                                g_cclosure_marshal_VOID__VOID,
+                                G_TYPE_NONE,
+                                0);
 }
 
 static void 
@@ -151,7 +189,6 @@ hildon_code_dialog_init                         (HildonCodeDialog *dialog)
     GtkWidget *dialog_action_area1 = NULL;
     GdkGeometry hints;
     GtkWidget *okButton;
-    GtkWidget *cancelButton;
 
     priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
     g_assert (priv);
@@ -169,6 +206,7 @@ hildon_code_dialog_init                         (HildonCodeDialog *dialog)
     gtk_window_set_title (GTK_WINDOW (dialog), DEVICELOCK_TITLE);
 
     gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
+    gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
 
     hints.min_width  = -1;
     hints.min_height = -1;
@@ -255,22 +293,31 @@ hildon_code_dialog_init                         (HildonCodeDialog *dialog)
     gtk_container_add (GTK_CONTAINER (priv->buttons[3][2]), image1);
     dialog_action_area1 = GTK_DIALOG (dialog)->action_area;
     gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1),
-            GTK_BUTTONBOX_END);
+#if GTK_CHECK_VERSION(2,11,0) || defined(MAEMO_GTK)
+                               GTK_BUTTONBOX_CENTER);
+#else
+                               GTK_BUTTONBOX_DEFAULT_STYLE);
+#endif
 
     okButton = gtk_dialog_add_button (GTK_DIALOG (dialog) ,DEVICELOCK_OK,
             GTK_RESPONSE_OK);
-    cancelButton =
-        gtk_dialog_add_button (GTK_DIALOG (dialog), DEVICELOCK_CANCEL,
-                GTK_RESPONSE_CANCEL);
-    
+
     gtk_widget_set_sensitive (okButton, FALSE);
 
     priv->buttons[4][0] = priv->buttons[4][1] = okButton;
-    priv->buttons[4][2] = cancelButton;
+
+    priv->im_context = gtk_im_multicontext_new();
+#ifdef MAEMO_GTK
+    g_object_set (G_OBJECT (priv->im_context), "hildon-input-mode",
+                  HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
+#endif
 
     /*
        Connect signals.
     */
+    g_signal_connect (G_OBJECT (priv->im_context), "commit",
+                      G_CALLBACK (hildon_code_dialog_im_commit), dialog);
+
     g_signal_connect (G_OBJECT (priv->entry), "insert_text",
             G_CALLBACK (hildon_code_dialog_insert_text), dialog);
     
@@ -300,12 +347,82 @@ hildon_code_dialog_init                         (HildonCodeDialog *dialog)
     g_signal_connect (G_OBJECT (okButton), "key-press-event",
                 G_CALLBACK(hildon_code_dialog_key_press_event), dialog);
     
-    g_signal_connect (G_OBJECT (cancelButton), "key-press-event",
-                G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
-
     gtk_widget_show_all (GTK_WIDGET (GTK_DIALOG (dialog)->vbox));
 }
 
+static void
+hildon_code_dialog_realize                      (GtkWidget *widget)
+{
+    HildonCodeDialog *dialog = HILDON_CODE_DIALOG (widget);
+    HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
+
+    if (GTK_WIDGET_CLASS (parent_class)->realize)
+      (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
+
+    gtk_im_context_set_client_window (GTK_IM_CONTEXT (priv->im_context),
+                                     GTK_WIDGET (dialog)->window);
+    gtk_im_context_focus_in (priv->im_context);
+}
+
+static void
+hildon_code_dialog_unrealize                    (GtkWidget *widget)
+{
+    HildonCodeDialog *dialog = HILDON_CODE_DIALOG (widget);
+    HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
+
+    gtk_im_context_set_client_window (GTK_IM_CONTEXT (priv->im_context), NULL);
+
+    if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+      (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+hildon_code_dialog_finalize                     (GObject *object)
+{
+    HildonCodeDialog *dialog = HILDON_CODE_DIALOG (object);
+    HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
+
+    g_object_unref (priv->im_context);
+
+    G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+hildon_code_dialog_backspace                    (HildonCodeDialog *dialog)
+{
+    HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
+    gchar *text, *pos;
+
+    g_assert (priv);
+
+    text = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
+        
+    pos = text;
+
+    while (*pos != '\0')
+    {
+      pos ++;
+    }
+
+    pos = g_utf8_find_prev_char (text, pos);
+
+    if (pos)
+    {
+      *pos=0;
+    }
+
+    gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
+
+    if (*text == 0)
+    {
+      gtk_widget_set_sensitive (priv->buttons[4][0], FALSE);
+    }
+
+    gtk_editable_set_position (GTK_EDITABLE (priv->entry), -1);
+
+    g_free (text);
+}
+
 static void 
 hildon_code_dialog_button_clicked               (GtkButton *button, 
                                                  gpointer user_data)
@@ -318,36 +435,44 @@ hildon_code_dialog_button_clicked               (GtkButton *button,
 
     if (number && *number )
     {
-        gtk_entry_append_text (GTK_ENTRY (priv->entry), number);
+        gtk_editable_set_editable (GTK_EDITABLE (priv->entry), TRUE);
+
+        g_signal_emit_by_name (GTK_ENTRY (priv->entry)->im_context, "commit",
+                               number);
+
+        gtk_editable_set_editable (GTK_EDITABLE (priv->entry), FALSE);
+
+        gtk_editable_set_position (GTK_EDITABLE (priv->entry), -1);
     }
     else
     {
-        /* Backspace */
-        gchar *text = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
-        gchar *pos;
-        
-        pos = text;
+        hildon_code_dialog_backspace (dialog);
+    }
+}
 
-        while (*pos != '\0')
-        {
-            pos ++;
-        }
+static void
+hildon_code_dialog_im_commit                    (GtkIMContext *im_context,
+                                                 gchar *utf8,
+                                                 gpointer user_data)
+{
+    HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
+    HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
+    gint digit;
 
-        pos = g_utf8_find_prev_char (text, pos);
+    g_assert (priv);
 
-        if (pos)
-        {
-            *pos=0;
-        }
+    digit = g_ascii_strtod(utf8, NULL);
 
-        gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
+    if (g_ascii_isdigit(*utf8))
+    {
+        gtk_editable_set_editable (GTK_EDITABLE (priv->entry), TRUE);
 
-        if (*text == 0)
-        {
-            gtk_widget_set_sensitive (priv->buttons[4][0], FALSE);
-        }
+        g_signal_emit_by_name (GTK_ENTRY (priv->entry)->im_context, "commit",
+                               utf8);
 
-        g_free (text);
+        gtk_editable_set_editable (GTK_EDITABLE (priv->entry), FALSE);
+
+        gtk_editable_set_position (GTK_EDITABLE (priv->entry), -1);
     }
 }
 
@@ -377,6 +502,8 @@ hildon_code_dialog_insert_text                  (GtkEditable *editable,
         /* make the Ok button sensitive */
         gtk_widget_set_sensitive (priv->buttons[4][0], TRUE);
     }
+
+    hildon_code_dialog_input (dialog);
 }
 
 static gboolean 
@@ -391,6 +518,15 @@ hildon_code_dialog_key_press_event              (GtkWidget *widget,
     
     g_assert (priv);
 
+    if (gtk_im_context_filter_keypress (priv->im_context, event))
+        return TRUE;
+
+    if (event->keyval == GDK_BackSpace)
+    {
+        hildon_code_dialog_backspace (dialog);
+        return TRUE;
+    }
+
     for (x = 0; x < 5; x++)
     {
         for (y = 0; y < 3; y++)
@@ -506,3 +642,44 @@ hildon_code_dialog_set_help_text                (HildonCodeDialog *dialog,
 
     gtk_label_set_text (GTK_LABEL (priv->help_text), text);
 }
+
+static void 
+hildon_code_dialog_real_input                   (HildonCodeDialog *dialog)
+{
+}
+
+static void 
+hildon_code_dialog_input                        (HildonCodeDialog *dialog)
+{
+    /* Emit the signal */
+    g_signal_emit (dialog, input_signal, 0);
+}
+
+/**
+ * hildon_code_dialog_set_input_sensitive
+ * @dialog: The #HildonCodeDialog whose state is to be changed
+ * @sensitive: The new state 
+ *
+ * This function will block or enable the input on the code dialog by
+ * making the input button sensitive (or not).
+ **/
+void
+hildon_code_dialog_set_input_sensitive          (HildonCodeDialog *dialog, 
+                                                 gboolean sensitive)
+{
+    int i;
+    int k;
+
+    g_return_if_fail (HILDON_IS_CODE_DIALOG (dialog));
+
+    HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
+    g_assert (priv);
+
+    for (i = 0; i < 5; i++)
+        for (k = 0; k < 3; k++) 
+            if (i != 4 && (k != 0 || k != 2))
+                gtk_widget_set_sensitive (priv->buttons [i][k], sensitive);
+
+    gtk_widget_set_sensitive (priv->help_text, sensitive);
+    gtk_widget_set_sensitive (priv->entry, sensitive);
+}