2006-08-30 Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
[hildon] / hildon-widgets / hildon-code-dialog.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2006 Nokia Corporation.
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.
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 #include "hildon-code-dialog.h"
27 #include "hildon-defines.h"
28 #include "hildon-banner.h"
29
30 #include <gdk/gdkkeysyms.h>
31
32 #include<gtk/gtkbutton.h>
33 #include<gtk/gtkentry.h>
34 #include<gtk/gtkicontheme.h>
35 #include<gtk/gtktable.h>
36 #include<gtk/gtkvbox.h>
37 #include<gtk/gtkbbox.h>
38 #include<gtk/gtklabel.h>
39 #include<gtk/gtkalignment.h>
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44
45 #include <libintl.h>
46
47 #define HEIGHT (38-HILDON_MARGIN_DEFAULT)
48 #define WIDTH  (60-HILDON_MARGIN_DEFAULT)
49 #define BACKSPACE_ICON    "qgn_calculator_backspace"
50
51 #define _(String)  dgettext(PACKAGE, String)
52 #define c_(String) dgettext("hildon-common-strings", String)
53 #define DEVICELOCK_OK               _("secu_enter_lock_code_dialog_ok")
54 #define DEVICELOCK_CANCEL           _("secu_enter_lock_code_dialog_cancel")
55 #define DEVICELOCK_TITLE            _("secu_application_title")
56 #define DEVICELOCK_MAX_CHAR_REACHED c_("ckdg_ib_maximum_characters_reached")
57         
58
59
60 #define MAX_PINCODE_LEN   (10)
61
62
63
64 static void hildon_code_dialog_class_init (HildonCodeDialogClass *cd_class);
65 static void hildon_code_dialog_init (HildonCodeDialog *self);
66 static void hildon_code_dialog_finalize (GObject *self);
67 static void hildon_code_dialog_button_clicked (GtkButton *buttonm,
68                                                gpointer user_data);
69 static void hildon_code_dialog_insert_text (GtkEditable *editable,
70                                             gchar *new_text,
71                                             gint new_text_length,
72                                             gint *position,
73                                             gpointer user_data);
74
75 static gboolean hildon_code_dialog_key_press_event (GtkWidget *widget,
76                                                     GdkEventKey *event,
77                                                     gpointer user_data);
78
79
80 GtkDialogClass *parent_class;
81
82 struct _HildonCodeDialogPrivate
83 {
84     GtkWidget *entry;
85     GtkWidget *buttons[5][3]; 
86     GtkWidget *help_text;
87 };
88
89 GType hildon_code_dialog_get_type (void)
90 {
91     static GType type = 0;
92
93     if (!type)
94     {
95         static const GTypeInfo info = 
96         {
97             sizeof(HildonCodeDialogClass),
98             NULL, /* base_init */
99             NULL, /* base_finalize */
100             (GClassInitFunc) hildon_code_dialog_class_init,
101             NULL,       /* class_finalize */
102             NULL,       /* class_data */
103             sizeof(HildonCodeDialog),
104             0,  /* n_preallocs */
105             (GInstanceInitFunc) hildon_code_dialog_init
106         };
107         type = g_type_register_static (GTK_TYPE_DIALOG,
108                 "HildonCodeDialog", &info, 0);
109     }
110     return type;
111 }
112
113 static void hildon_code_dialog_class_init (HildonCodeDialogClass *cd_class)
114 {
115     /* Get convenience variables */
116     GObjectClass *object_class = G_OBJECT_CLASS (cd_class);
117
118     parent_class = GTK_DIALOG_CLASS (g_type_class_peek_parent (cd_class));
119
120     object_class->finalize = hildon_code_dialog_finalize;
121
122 }
123
124 static void hildon_code_dialog_init (HildonCodeDialog *dialog)
125 {
126     HildonCodeDialogPrivate *priv;
127     gint i, x, y;
128     GtkWidget *dialog_vbox1 = NULL;
129     GtkWidget *table = NULL;
130     GtkWidget *alignment = NULL;
131     GtkWidget *vbox1 = NULL;
132     GtkWidget *image1 = NULL;
133     GtkWidget *dialog_action_area1 = NULL;
134     GdkGeometry hints;
135     GtkWidget *okButton;
136     GtkWidget *cancelButton;
137
138     priv = dialog->priv = (HildonCodeDialogPrivate*) g_malloc0 
139         (sizeof (HildonCodeDialogPrivate));
140
141     const gchar* numstrs[10] = {
142         "0","1","2","3","4","5","6","7","8","9"
143     };
144
145     GdkPixbuf* pixbuf = NULL;
146     GtkIconTheme* icon_theme = NULL;
147     GtkIconInfo *icon_info = NULL;
148     gint base_size=0;
149
150     /* Set default title */
151     gtk_window_set_title (GTK_WINDOW (dialog), DEVICELOCK_TITLE);
152
153     gtk_window_set_type_hint(GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
154
155     hints.min_width  = -1;
156     hints.min_height = -1;
157     hints.max_width  = -1;
158     hints.max_height = -1;
159
160     gtk_window_set_geometry_hints(GTK_WINDOW(dialog), GTK_WIDGET(dialog), 
161             &hints,
162             GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
163
164     table = gtk_table_new (4, 3, FALSE);
165     gtk_table_set_row_spacings (GTK_TABLE (table), HILDON_MARGIN_DEFAULT );
166     gtk_table_set_col_spacings (GTK_TABLE (table), HILDON_MARGIN_DEFAULT );
167
168     dialog_vbox1 = GTK_DIALOG (dialog)->vbox;
169     vbox1 = gtk_vbox_new (FALSE, 0);
170     gtk_box_set_spacing (GTK_BOX(vbox1),HILDON_MARGIN_DOUBLE);
171
172     priv->help_text= gtk_label_new ("");
173     alignment=gtk_alignment_new(0.5,0,1,1);
174     gtk_container_add(GTK_CONTAINER(alignment),priv->help_text);
175
176     priv->entry = gtk_entry_new ();
177     
178     GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(priv->entry),GTK_CAN_FOCUS);
179     gtk_entry_set_invisible_char (GTK_ENTRY(priv->entry), g_utf8_get_char ("*"));
180     gtk_entry_set_alignment (GTK_ENTRY(priv->entry),1.0);
181
182     gtk_editable_set_editable (GTK_EDITABLE(priv->entry),FALSE);
183     gtk_entry_set_visibility (GTK_ENTRY(priv->entry), FALSE);
184
185     gtk_box_pack_start (GTK_BOX (vbox1),alignment,TRUE,FALSE,0);
186     gtk_box_pack_start (GTK_BOX (vbox1),priv->entry,TRUE,FALSE,0);
187     gtk_box_pack_start (GTK_BOX (vbox1),table,FALSE,TRUE,0);
188
189     gtk_box_pack_start (GTK_BOX (dialog_vbox1), vbox1,FALSE,TRUE,0);
190
191
192     for(i=1;i<=3;i++) {
193         priv->buttons[0][i-1] = gtk_button_new_with_mnemonic(numstrs[i]);
194         gtk_widget_set_size_request(priv->buttons[0][i-1],WIDTH,HEIGHT);
195         gtk_table_attach_defaults(GTK_TABLE(table), priv->buttons[0][i-1],
196                 i-1,i,0,1);
197     }
198
199     for(i=4;i<=6;i++) {
200         priv->buttons[1][i-4] = gtk_button_new_with_mnemonic (numstrs[i]);
201         gtk_widget_set_size_request(priv->buttons[1][i-4],WIDTH,HEIGHT);
202         gtk_table_attach_defaults(GTK_TABLE(table),priv->buttons[1][i-4],
203                 i-4,i-3,1,2);
204     }
205
206     for(i=7;i<=9;i++) {
207         priv->buttons[2][i-7] = gtk_button_new_with_mnemonic (numstrs[i]);
208         gtk_widget_set_size_request(priv->buttons[2][i-7], WIDTH,HEIGHT);
209         gtk_table_attach_defaults(GTK_TABLE(table), priv->buttons[2][i-7],
210                 i-7,i-6,2,3);
211     }
212
213     priv->buttons[3][0] = priv->buttons[3][1] = 
214         gtk_button_new_with_mnemonic (numstrs[0]);
215     gtk_widget_set_size_request (priv->buttons[3][0], WIDTH,HEIGHT);
216     gtk_table_attach (GTK_TABLE(table), priv->buttons[3][0],
217             0,2,3,4, (GtkAttachOptions) (GTK_FILL),
218             (GtkAttachOptions) (0), 0, 0);
219
220     priv->buttons[3][2] = gtk_button_new ();
221     gtk_widget_set_size_request(priv->buttons[3][2], WIDTH,HEIGHT);
222     gtk_table_attach_defaults(GTK_TABLE(table), priv->buttons[3][2],
223             2,3,3,4);
224
225     icon_theme = gtk_icon_theme_get_default ();
226
227     icon_info = gtk_icon_theme_lookup_icon(icon_theme, BACKSPACE_ICON, 1,
228             GTK_ICON_LOOKUP_NO_SVG);
229     base_size = gtk_icon_info_get_base_size(icon_info);
230     gtk_icon_info_free(icon_info);
231     icon_info = NULL;
232     pixbuf = gtk_icon_theme_load_icon (icon_theme,
233             BACKSPACE_ICON, base_size,
234             GTK_ICON_LOOKUP_NO_SVG,
235             NULL);
236     image1 = gtk_image_new_from_pixbuf (pixbuf);
237     g_object_unref (G_OBJECT(pixbuf));
238     gtk_container_add (GTK_CONTAINER (priv->buttons[3][2]), image1);
239     dialog_action_area1 = GTK_DIALOG (dialog)->action_area;
240     gtk_button_box_set_layout (GTK_BUTTON_BOX(dialog_action_area1),
241             GTK_BUTTONBOX_END);
242
243     okButton = gtk_dialog_add_button (GTK_DIALOG(dialog),DEVICELOCK_OK,
244             GTK_RESPONSE_OK);
245     cancelButton =
246         gtk_dialog_add_button (GTK_DIALOG(dialog),DEVICELOCK_CANCEL,
247                 GTK_RESPONSE_CANCEL);
248     
249     gtk_widget_set_sensitive (okButton, FALSE);
250
251     priv->buttons[4][0] = priv->buttons[4][1] = okButton;
252     priv->buttons[4][2] = cancelButton;
253
254     /*
255        Connect signals.
256     */
257     g_signal_connect (G_OBJECT(priv->entry), "insert_text",
258             G_CALLBACK (hildon_code_dialog_insert_text), dialog);
259     
260     gtk_entry_set_max_length(GTK_ENTRY(priv->entry),MAX_PINCODE_LEN);
261
262     for (x=0; x<3; x++)
263     {
264         for (y=0; y<3 ;y++)
265         {
266             g_signal_connect (G_OBJECT (priv->buttons[x][y]), "clicked",
267                 G_CALLBACK (hildon_code_dialog_button_clicked), dialog);
268             g_signal_connect (G_OBJECT (priv->buttons[x][y]), "key-press-event",
269                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
270         }
271     }
272             
273     g_signal_connect (G_OBJECT (priv->buttons[3][0]), "clicked",
274                 G_CALLBACK (hildon_code_dialog_button_clicked), dialog);
275     g_signal_connect (G_OBJECT (priv->buttons[3][0]), "key-press-event",
276                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
277     
278     g_signal_connect (G_OBJECT (priv->buttons[3][2]), "clicked",
279                 G_CALLBACK (hildon_code_dialog_button_clicked), dialog);
280     g_signal_connect (G_OBJECT (priv->buttons[3][2]), "key-press-event",
281                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
282         
283     g_signal_connect (G_OBJECT (okButton), "key-press-event",
284                 G_CALLBACK(hildon_code_dialog_key_press_event), dialog);
285     
286     g_signal_connect (G_OBJECT (cancelButton), "key-press-event",
287                 G_CALLBACK (hildon_code_dialog_key_press_event), dialog);
288
289 }
290
291 static void hildon_code_dialog_finalize (GObject *self)
292 {
293     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (self);
294
295     fprintf( stderr, "called destroy\n" );
296
297     g_free (dialog->priv);
298
299     G_OBJECT_CLASS (parent_class)->finalize (self);
300 }
301
302 /* Signal handlers */
303 void hildon_code_dialog_button_clicked (GtkButton *button, gpointer user_data)
304 {
305     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
306     HildonCodeDialogPrivate *priv = dialog->priv;
307     const char *number = gtk_button_get_label (button);
308
309     if (number && *number )
310     {
311         gtk_entry_append_text (GTK_ENTRY (priv->entry), number);
312     }
313     else
314     {
315         /* Backspace */
316         gchar *text = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
317         gchar *pos;
318         
319         pos = text;
320
321         while (*pos != '\0')
322         {
323             pos ++;
324         }
325
326         pos = g_utf8_find_prev_char (text, pos);
327
328         if (pos)
329         {
330             *pos=0;
331         }
332
333         gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
334
335         if (*text == 0)
336         {
337             gtk_widget_set_sensitive (priv->buttons[4][0], FALSE);
338         }
339
340         g_free (text);
341     }
342
343 }
344
345 static void hildon_code_dialog_insert_text (GtkEditable *editable,
346                                             gchar *new_text,
347                                             gint new_text_length,
348                                             gint *position,
349                                             gpointer user_data)
350 {
351     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
352     HildonCodeDialogPrivate *priv = dialog->priv;
353     gchar * text = g_strdup(gtk_entry_get_text (GTK_ENTRY (priv->entry)));
354     glong length = g_utf8_strlen (text, -1);
355     g_free (text);
356
357     if (length == MAX_PINCODE_LEN)
358     {
359         hildon_banner_show_information (dialog,
360                                         NULL,
361                                         DEVICELOCK_MAX_CHAR_REACHED);
362     }
363
364     else if (!length)
365     { 
366         /* make the Ok button sensitive */
367         gtk_widget_set_sensitive(priv->buttons[4][0], TRUE);
368     }
369 }
370
371 static gboolean hildon_code_dialog_key_press_event (GtkWidget *widget,
372                                                     GdkEventKey *event,
373                                                     gpointer user_data)
374 {
375     HildonCodeDialog *dialog = HILDON_CODE_DIALOG (user_data);
376     HildonCodeDialogPrivate *priv = dialog->priv;
377     GtkWidget *new_widget = widget;
378     gint x, y;
379
380     for (x = 0; x < 5; x++)
381     {
382         for (y = 0; y < 3; y++)
383         {
384             if (priv->buttons[x][y] == widget)
385                 goto found;
386         }
387     }
388     return FALSE;
389
390 found:
391
392     while (new_widget == widget)
393     {
394         switch (event->keyval)
395         {
396             case GDK_Up:
397                 x = (x+4)%5;
398                 break;
399
400             case GDK_Down:
401                 x = (x+1)%5;
402                 break;
403
404             case GDK_Left:
405                 y = (y+2)%3;
406                 break;
407
408             case GDK_Right:
409                 y = (y+1)%3;
410                 break;
411
412             default:
413                 return FALSE;
414         }
415                 
416         new_widget = priv->buttons[x][y];
417     }
418
419     gtk_widget_grab_focus (new_widget);
420
421     return TRUE;
422 }
423
424 /* Public methods */
425
426 /**
427  * hildon_code_dialog_new:
428  *
429  * Use this function to create a new HildonCodeDialog.
430  *
431  * Return value: A @HildonCodeDialog.
432  **/
433 GtkWidget *hildon_code_dialog_new()
434 {
435     HildonCodeDialog *dialog = g_object_new (HILDON_TYPE_CODE_DIALOG, NULL);
436
437     return GTK_WIDGET (dialog);
438 }
439
440 /**
441  * hildon_code_dialog_get_code:
442  * @dialog: The #HildonCodeDialog from which to get the entered code
443  *
444  * Use this function to access the code entered by the user.
445  *
446  * Return value: The entered code.
447  **/
448 const gchar *hildon_code_dialog_get_code (HildonCodeDialog *dialog)
449 {
450     g_return_val_if_fail (HILDON_IS_CODE_DIALOG (dialog), NULL);
451     return gtk_entry_get_text (GTK_ENTRY (dialog->priv->entry));
452 }
453
454 /**
455  * hildon_code_dialog_clear_clode:
456  * @dialog: The #HildonCodeDialog whose entry should be cleared:
457  *
458  * Use this function to clear the user entered code.
459  **/
460 void hildon_code_dialog_clear_code (HildonCodeDialog *dialog)
461 {
462     g_return_if_fail (HILDON_IS_CODE_DIALOG (dialog));
463     gtk_entry_set_text (GTK_ENTRY (dialog->priv->entry), "");
464     gtk_widget_set_sensitive (dialog->priv->buttons[4][0], FALSE);
465 }
466
467 /**
468  * hildon_code_dialog_set_help_text:
469  * @dialog: The #HildonCodeDialog whose entry should be cleared:
470  * @text: The text to use in the help label.
471  *
472  * Use this function to set the text that will be displayd in the
473  * help label
474  **/
475 void hildon_code_dialog_set_help_text (HildonCodeDialog *dialog, 
476                                        const gchar *text)
477 {
478     g_return_if_fail (HILDON_IS_CODE_DIALOG (dialog));
479     gtk_label_set_text (GTK_LABEL (dialog->priv->help_text), text);
480 }