f0de717062eb80ffe4d28579ba610f69ff373276
[hildon] / src / hildon-code-dialog.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-code-dialog
27  * @short_description: A keypad-like widget used to enter pincodes.
28  *
29  * #HildonCodeDialog displays a keypad that can be used to enter 
30  * numerical pin codes or lock codes. It emits a 'input' signal each time 
31  * an input action is performed on the dialog.
32  *
33  */
34
35 /* FIXME We need property access in this widget */
36
37 #ifdef                                          HAVE_CONFIG_H
38 #include                                        <config.h>
39 #endif
40
41 #include                                        "hildon-code-dialog.h"
42 #include                                        "hildon-defines.h"
43 #include                                        "hildon-banner.h"
44
45 #include                                        <gdk/gdkkeysyms.h>
46 #include                                        <gtk/gtkbutton.h>
47 #include                                        <gtk/gtkentry.h>
48 #include                                        <gtk/gtkicontheme.h>
49 #include                                        <gtk/gtktable.h>
50 #include                                        <gtk/gtkvbox.h>
51 #include                                        <gtk/gtkbbox.h>
52 #include                                        <gtk/gtklabel.h>
53 #include                                        <gtk/gtkalignment.h>
54 #include                                        <libintl.h>
55 #include                                        "hildon-code-dialog-private.h"
56
57 #define                                         HEIGHT (38-HILDON_MARGIN_DEFAULT)
58
59 #define                                         WIDTH  (60-HILDON_MARGIN_DEFAULT)
60
61 #define                                         BACKSPACE_ICON "qgn_calculator_backspace"
62
63 #define                                         _(String) \
64                                                 dgettext("hildon-libs", String)
65
66 #define                                         c_(String) \
67                                                 dgettext("hildon-common-strings", String)
68
69 #define                                         DEVICELOCK_OK _("secu_enter_lock_code_dialog_ok")
70
71 #define                                         DEVICELOCK_CANCEL _("secu_enter_lock_code_dialog_cancel")
72
73 #define                                         DEVICELOCK_TITLE _("secu_application_title")
74
75 #define                                         DEVICELOCK_MAX_CHAR_REACHED c_("ckdg_ib_maximum_characters_reached")
76         
77 #define                                         MAX_PINCODE_LEN (10)
78
79 static void 
80 hildon_code_dialog_class_init                   (HildonCodeDialogClass *cd_class);
81
82 static void 
83 hildon_code_dialog_init                         (HildonCodeDialog *self);
84
85 static void 
86 hildon_code_dialog_button_clicked               (GtkButton *buttonm,
87                                                  gpointer user_data);
88
89 static void 
90 hildon_code_dialog_insert_text                  (GtkEditable *editable,
91                                                  gchar *new_text,
92                                                  gint new_text_length,
93                                                  gint *position,
94                                                  gpointer user_data);
95
96 static gboolean 
97 hildon_code_dialog_key_press_event              (GtkWidget *widget,
98                                                  GdkEventKey *event,
99                                                  gpointer user_data);
100
101 static void 
102 hildon_code_dialog_real_input                   (HildonCodeDialog *dialog);
103
104 static void
105 hildon_code_dialog_input                        (HildonCodeDialog *dialog);
106
107 static GtkDialogClass*                          parent_class = NULL;
108
109 static guint                                    input_signal;
110
111 /**
112  * hildon_code_dialog_get_type:
113  *
114  * Initializes and returns the type of a hildon code dialog.
115  *
116  * @Returns: GType of #HildonCodeDialog
117  */
118 GType G_GNUC_CONST
119 hildon_code_dialog_get_type                     (void)
120 {
121     static GType type = 0;
122
123     if (!type)
124     {
125         static const GTypeInfo info = 
126         {
127             sizeof (HildonCodeDialogClass),
128             NULL, /* base_init */
129             NULL, /* base_finalize */
130             (GClassInitFunc) hildon_code_dialog_class_init,
131             NULL,       /* class_finalize */
132             NULL,       /* class_data */
133             sizeof (HildonCodeDialog),
134             0,  /* n_preallocs */
135             (GInstanceInitFunc) hildon_code_dialog_init
136         };
137         type = g_type_register_static (GTK_TYPE_DIALOG,
138                 "HildonCodeDialog", &info, 0);
139     }
140     return type;
141 }
142
143 static void
144 hildon_code_dialog_class_init                   (HildonCodeDialogClass *cd_class)
145 {
146     parent_class = GTK_DIALOG_CLASS (g_type_class_peek_parent (cd_class));
147     g_type_class_add_private (cd_class, sizeof (HildonCodeDialogPrivate));
148
149     cd_class->input = hildon_code_dialog_real_input;
150
151     /* FIXME Document this signal! */
152     input_signal = g_signal_new("input",
153                                 HILDON_TYPE_CODE_DIALOG,
154                                 G_SIGNAL_RUN_LAST,
155                                 G_STRUCT_OFFSET (HildonCodeDialogClass, input),
156                                 NULL, NULL,
157                                 g_cclosure_marshal_VOID__VOID,
158                                 G_TYPE_NONE,
159                                 0);
160 }
161
162 static void 
163 hildon_code_dialog_init                         (HildonCodeDialog *dialog)
164 {
165     HildonCodeDialogPrivate *priv;
166     gint i, x, y;
167     GtkWidget *dialog_vbox1 = NULL;
168     GtkWidget *table = NULL;
169     GtkWidget *alignment = NULL;
170     GtkWidget *vbox1 = NULL;
171     GtkWidget *image1 = NULL;
172     GtkWidget *dialog_action_area1 = NULL;
173     GdkGeometry hints;
174     GtkWidget *okButton;
175     GtkWidget *cancelButton;
176
177     priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
178     g_assert (priv);
179     
180     const gchar* numstrs[10] = {
181         "0","1","2","3","4","5","6","7","8","9"
182     };
183
184     GdkPixbuf* pixbuf = NULL;
185     GtkIconTheme* icon_theme = NULL;
186     GtkIconInfo *icon_info = NULL;
187     gint base_size = 0;
188
189     /* Set default title */
190     gtk_window_set_title (GTK_WINDOW (dialog), DEVICELOCK_TITLE);
191
192     gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
193     gtk_dialog_set_has_separator ((GtkDialog *) dialog, FALSE);
194
195     hints.min_width  = -1;
196     hints.min_height = -1;
197     hints.max_width  = -1;
198     hints.max_height = -1;
199
200     gtk_window_set_geometry_hints (GTK_WINDOW (dialog), GTK_WIDGET (dialog), &hints,
201             GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
202
203     table = gtk_table_new (4, 3, FALSE);
204     gtk_table_set_row_spacings (GTK_TABLE (table), HILDON_MARGIN_DEFAULT );
205     gtk_table_set_col_spacings (GTK_TABLE (table), HILDON_MARGIN_DEFAULT );
206
207     dialog_vbox1 = GTK_DIALOG (dialog)->vbox;
208     vbox1 = gtk_vbox_new (FALSE, 0);
209     gtk_box_set_spacing (GTK_BOX (vbox1), HILDON_MARGIN_DOUBLE);
210
211     priv->help_text = gtk_label_new ("");
212     alignment = gtk_alignment_new (0.5,0,1,1);
213     gtk_container_add (GTK_CONTAINER (alignment), priv->help_text);
214
215     priv->entry = gtk_entry_new ();
216     
217     GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (priv->entry), GTK_CAN_FOCUS);
218     gtk_entry_set_invisible_char (GTK_ENTRY (priv->entry), g_utf8_get_char ("*"));
219     gtk_entry_set_alignment (GTK_ENTRY (priv->entry), 1.0);
220
221     gtk_editable_set_editable (GTK_EDITABLE (priv->entry),FALSE);
222     gtk_entry_set_visibility (GTK_ENTRY (priv->entry), FALSE);
223
224     gtk_box_pack_start (GTK_BOX (vbox1), alignment, TRUE,FALSE,0);
225     gtk_box_pack_start (GTK_BOX (vbox1), priv->entry, TRUE,FALSE,0);
226     gtk_box_pack_start (GTK_BOX (vbox1), table, FALSE,TRUE,0);
227
228     gtk_box_pack_start (GTK_BOX (dialog_vbox1), vbox1, FALSE,TRUE,0);
229
230     for(i = 1;i <= 3; i++) {
231         priv->buttons[0][i-1] = gtk_button_new_with_mnemonic (numstrs[i]);
232         gtk_widget_set_size_request (priv->buttons[0][i-1], WIDTH, HEIGHT);
233         gtk_table_attach_defaults (GTK_TABLE (table), priv->buttons[0][i-1],
234                 i-1, i, 0, 1);
235     }
236
237     for(i = 4;i <= 6;i++) {
238         priv->buttons[1][i-4] = gtk_button_new_with_mnemonic (numstrs[i]);
239         gtk_widget_set_size_request (priv->buttons[1][i-4], WIDTH, HEIGHT);
240         gtk_table_attach_defaults (GTK_TABLE (table), priv->buttons[1][i-4],
241                 i-4, i-3, 1, 2);
242     }
243
244     for(i=7;i<=9;i++) {
245         priv->buttons[2][i-7] = gtk_button_new_with_mnemonic (numstrs[i]);
246         gtk_widget_set_size_request (priv->buttons[2][i-7], WIDTH, HEIGHT);
247         gtk_table_attach_defaults (GTK_TABLE (table), priv->buttons[2][i-7],
248                 i-7, i-6, 2, 3);
249     }
250
251     priv->buttons[3][0] = priv->buttons[3][1] = 
252         gtk_button_new_with_mnemonic (numstrs[0]);
253     gtk_widget_set_size_request (priv->buttons[3][0], WIDTH, HEIGHT);
254     gtk_table_attach (GTK_TABLE (table), priv->buttons[3][0],
255             0,2,3,4, (GtkAttachOptions) (GTK_FILL),
256             (GtkAttachOptions) (0), 0, 0);
257
258     priv->buttons[3][2] = gtk_button_new ();
259     gtk_widget_set_size_request (priv->buttons[3][2], WIDTH, HEIGHT);
260     gtk_table_attach_defaults (GTK_TABLE (table), priv->buttons[3][2],
261             2, 3, 3, 4);
262
263     icon_theme = gtk_icon_theme_get_default ();
264
265     icon_info = gtk_icon_theme_lookup_icon (icon_theme, BACKSPACE_ICON, 1,
266             GTK_ICON_LOOKUP_NO_SVG);
267     base_size = gtk_icon_info_get_base_size (icon_info);
268     gtk_icon_info_free (icon_info);
269     icon_info = NULL;
270     pixbuf = gtk_icon_theme_load_icon (icon_theme,
271             BACKSPACE_ICON, base_size,
272             GTK_ICON_LOOKUP_NO_SVG,
273             NULL);
274
275     image1 = gtk_image_new_from_pixbuf (pixbuf);
276     g_object_unref (G_OBJECT(pixbuf));
277     gtk_container_add (GTK_CONTAINER (priv->buttons[3][2]), image1);
278     dialog_action_area1 = GTK_DIALOG (dialog)->action_area;
279     gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1),
280             GTK_BUTTONBOX_END);
281
282     okButton = gtk_dialog_add_button (GTK_DIALOG (dialog) ,DEVICELOCK_OK,
283             GTK_RESPONSE_OK);
284     cancelButton =
285         gtk_dialog_add_button (GTK_DIALOG (dialog), DEVICELOCK_CANCEL,
286                 GTK_RESPONSE_CANCEL);
287     
288     gtk_widget_set_sensitive (okButton, FALSE);
289
290     priv->buttons[4][0] = priv->buttons[4][1] = okButton;
291     priv->buttons[4][2] = cancelButton;
292
293     /*
294        Connect signals.
295     */
296     g_signal_connect (G_OBJECT (priv->entry), "insert_text",
297             G_CALLBACK (hildon_code_dialog_insert_text), dialog);
298     
299     gtk_entry_set_max_length (GTK_ENTRY (priv->entry), MAX_PINCODE_LEN);
300
301     for (x = 0; x < 3; x++)
302     {
303         for (y = 0; y < 3; y++)
304         {
305             g_signal_connect (G_OBJECT (priv->buttons[x][y]), "clicked",
306                 G_CALLBACK (hildon_code_dialog_button_clicked), dialog);
307             g_signal_connect (G_OBJECT (priv->buttons[x][y]), "key-press-event",
308                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
309         }
310     }
311             
312     g_signal_connect (G_OBJECT (priv->buttons[3][0]), "clicked",
313                 G_CALLBACK (hildon_code_dialog_button_clicked), dialog);
314     g_signal_connect (G_OBJECT (priv->buttons[3][0]), "key-press-event",
315                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
316     
317     g_signal_connect (G_OBJECT (priv->buttons[3][2]), "clicked",
318                 G_CALLBACK (hildon_code_dialog_button_clicked), dialog);
319     g_signal_connect (G_OBJECT (priv->buttons[3][2]), "key-press-event",
320                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
321         
322     g_signal_connect (G_OBJECT (okButton), "key-press-event",
323                 G_CALLBACK(hildon_code_dialog_key_press_event), dialog);
324     
325     g_signal_connect (G_OBJECT (cancelButton), "key-press-event",
326                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
327
328     gtk_widget_show_all (GTK_WIDGET (GTK_DIALOG (dialog)->vbox));
329 }
330
331 static void 
332 hildon_code_dialog_button_clicked               (GtkButton *button, 
333                                                  gpointer user_data)
334 {
335     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
336     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
337     g_assert (priv);
338
339     const char *number = gtk_button_get_label (button);
340
341     if (number && *number )
342     {
343         gtk_entry_append_text (GTK_ENTRY (priv->entry), number);
344     }
345     else
346     {
347         /* Backspace */
348         gchar *text = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
349         gchar *pos;
350         
351         pos = text;
352
353         while (*pos != '\0')
354         {
355             pos ++;
356         }
357
358         pos = g_utf8_find_prev_char (text, pos);
359
360         if (pos)
361         {
362             *pos=0;
363         }
364
365         gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
366
367         if (*text == 0)
368         {
369             gtk_widget_set_sensitive (priv->buttons[4][0], FALSE);
370         }
371
372         g_free (text);
373     }
374 }
375
376 static void 
377 hildon_code_dialog_insert_text                  (GtkEditable *editable,
378                                                  gchar *new_text,
379                                                  gint new_text_length,
380                                                  gint *position,
381                                                  gpointer user_data)
382 {
383     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
384     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
385     gchar *text = g_strdup(gtk_entry_get_text (GTK_ENTRY (priv->entry)));
386     glong length = g_utf8_strlen (text, -1);
387     g_free (text);
388     g_assert (priv);
389
390     if (length == MAX_PINCODE_LEN)
391     {
392         hildon_banner_show_information (GTK_WIDGET (dialog),
393                                         NULL,
394                                         DEVICELOCK_MAX_CHAR_REACHED);
395     }
396
397     else if (! length)
398     { 
399         /* make the Ok button sensitive */
400         gtk_widget_set_sensitive (priv->buttons[4][0], TRUE);
401     }
402
403     hildon_code_dialog_input (dialog);
404 }
405
406 static gboolean 
407 hildon_code_dialog_key_press_event              (GtkWidget *widget,
408                                                  GdkEventKey *event,
409                                                  gpointer user_data)
410 {
411     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
412     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
413     GtkWidget *new_widget = widget;
414     gint x, y;
415     
416     g_assert (priv);
417
418     for (x = 0; x < 5; x++)
419     {
420         for (y = 0; y < 3; y++)
421         {
422             if (priv->buttons[x][y] == widget)
423                 goto found;
424         }
425     }
426     return FALSE;
427
428 found:
429
430     while (new_widget == widget)
431     {
432         switch (event->keyval)
433         {
434             case GDK_Up:
435                 x = (x+4)%5;
436                 break;
437
438             case GDK_Down:
439                 x = (x+1)%5;
440                 break;
441
442             case GDK_Left:
443                 y = (y+2)%3;
444                 break;
445
446             case GDK_Right:
447                 y = (y+1)%3;
448                 break;
449
450             default:
451                 return FALSE;
452         }
453                 
454         new_widget = priv->buttons[x][y];
455     }
456
457     gtk_widget_grab_focus (new_widget);
458
459     return TRUE;
460 }
461
462 /**
463  * hildon_code_dialog_new:
464  *
465  * Use this function to create a new HildonCodeDialog.
466  *
467  * Return value: A @HildonCodeDialog.
468  **/
469 GtkWidget*
470 hildon_code_dialog_new                          (void)
471 {
472     HildonCodeDialog *dialog = g_object_new (HILDON_TYPE_CODE_DIALOG, NULL);
473
474     return GTK_WIDGET (dialog);
475 }
476
477 /**
478  * hildon_code_dialog_get_code:
479  * @dialog: The #HildonCodeDialog from which to get the entered code
480  *
481  * Use this function to access the code entered by the user.
482  *
483  * Return value: The entered code.
484  **/
485 const gchar*
486 hildon_code_dialog_get_code                     (HildonCodeDialog *dialog)
487 {
488     g_return_val_if_fail (HILDON_IS_CODE_DIALOG (dialog), NULL);
489     
490     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
491     g_assert (priv);
492
493     return gtk_entry_get_text (GTK_ENTRY (priv->entry));
494 }
495
496 /**
497  * hildon_code_dialog_clear_clode:
498  * @dialog: The #HildonCodeDialog whose entry should be cleared:
499  *
500  * Use this function to clear the user entered code.
501  **/
502 void 
503 hildon_code_dialog_clear_code                   (HildonCodeDialog *dialog)
504 {
505     g_return_if_fail (HILDON_IS_CODE_DIALOG (dialog));
506
507     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
508     g_assert (priv);
509
510     gtk_entry_set_text (GTK_ENTRY (priv->entry), "");
511     gtk_widget_set_sensitive (priv->buttons[4][0], FALSE);
512 }
513
514 /**
515  * hildon_code_dialog_set_help_text:
516  * @dialog: The #HildonCodeDialog whose entry should be cleared:
517  * @text: The text to use in the help label.
518  *
519  * Use this function to set the text that will be displayd in the
520  * help label
521  **/
522 void 
523 hildon_code_dialog_set_help_text                (HildonCodeDialog *dialog, 
524                                                  const gchar *text)
525 {
526     g_return_if_fail (HILDON_IS_CODE_DIALOG (dialog));
527
528     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
529     g_assert (priv);
530
531     gtk_label_set_text (GTK_LABEL (priv->help_text), text);
532 }
533
534 static void 
535 hildon_code_dialog_real_input                   (HildonCodeDialog *dialog)
536 {
537 }
538
539 static void 
540 hildon_code_dialog_input                        (HildonCodeDialog *dialog)
541 {
542     /* Emit the signal */
543     g_signal_emit (dialog, input_signal, 0);
544 }
545
546 /**
547  * hildon_code_dialog_set_input_sensitive
548  * @dialog: The #HildonCodeDialog whose state is to be changed
549  * @sensitive: The new state 
550  *
551  * This function will block or enable the input on the code dialog by
552  * making the input button sensitive (or not).
553  **/
554 void
555 hildon_code_dialog_set_input_sensitive          (HildonCodeDialog *dialog, 
556                                                  gboolean sensitive)
557 {
558     int i;
559     int k;
560
561     g_return_if_fail (HILDON_IS_CODE_DIALOG (dialog));
562
563     HildonCodeDialogPrivate *priv = HILDON_CODE_DIALOG_GET_PRIVATE (dialog);
564     g_assert (priv);
565
566     for (i = 0; i < 5; i++)
567         for (k = 0; k < 3; k++) 
568             if (i != 4 && (k != 0 || k != 2))
569                 gtk_widget_set_sensitive (priv->buttons [i][k], sensitive);
570
571     gtk_widget_set_sensitive (priv->help_text, sensitive);
572     gtk_widget_set_sensitive (priv->entry, sensitive);
573 }