latest update
[hildon] / hildon-widgets / hildon-time-picker.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Luc Pionchon <luc.pionchon@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; either 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  * @file hildon-time-picker.c
27  * 
28  * This file implements the HildonTimePicker widget. This widget
29  * is used with HildonTimeEditor widget to set time.
30  *
31  */
32
33 /* HILDON DOC
34  * @shortdesc: TimePicker is a widget for setting a time.
35  * @longdesc: The TimePicker contains two fields for setting the
36  * time. Arrow buttons can be used to increment and decrement time. If
37  * specified in systems locale setting AM/PM button is displayed.
38  * 
39  * @seealso: #HildonTimeEditor
40  */
41
42 #include "hildon-time-picker.h"
43 #include <hildon-widgets/hildon-defines.h>
44 #include <hildon-widgets/gtk-infoprint.h>
45 #include <gtk/gtk.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <gdk/gdk.h>
48 #include <time.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <langinfo.h>
53 #include <libintl.h>
54
55 #ifdef HAVE_CONFIG_H
56 #include <config.h>
57 #endif
58
59 #define _(String) dgettext(PACKAGE, String)
60
61 #define HILDON_TIME_PICKER_GET_PRIVATE(obj) \
62     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
63      HILDON_TYPE_TIME_PICKER, HildonTimePickerPrivate))
64
65 #define DEFAULT_HOURS 1
66 #define DEFAULT_MINUTES 1
67 #define DEFAULT_ARROW_WIDTH 26
68 #define DEFAULT_ARROW_HEIGHT 26
69
70 enum
71 {
72   TABLE,
73
74   FRAME_HOURS,
75   FRAME_MMINUTES,
76   FRAME_LMINUTES,
77   FRAME_AMPM,
78
79   EBOX_HOURS,
80   EBOX_MMINUTES,
81   EBOX_LMINUTES,
82   EBOX_AMPM,
83
84   LABEL_HOURS,
85   LABEL_MMINUTES,
86   LABEL_LMINUTES,
87   LABEL_AMPM,
88   LABEL_COLON,
89
90   BUTTON_HOURS_UP,
91   BUTTON_HOURS_DOWN,
92   BUTTON_MMINUTES_UP,
93   BUTTON_MMINUTES_DOWN,
94   BUTTON_LMINUTES_UP,
95   BUTTON_LMINUTES_DOWN,
96
97   WIDGET_COUNT
98 };
99
100 static GtkDialogClass *parent_class;
101
102 /*< private > m == minutes and h == hours
103               m == more significant and l == less significant
104               mm == minutes more significant (==tens of minutes) etc.*/
105 struct _HildonTimePickerPrivate
106 {
107   GtkWidget *child[WIDGET_COUNT];
108
109   gchar *am_symbol;
110   gchar *pm_symbol;
111
112   guint key_repeat;
113   guint minutes;
114   gint mul;
115   guint timer_id;
116
117   guint c12h             : 1; /* 12 hour clock, show AM/PM */
118   guint ampm_pos         : 1; /* FALSE = AM/PM at left, TRUE = at right */
119   guint button_press     : 1;
120   guint start_key_repeat : 1;
121 };
122
123 enum
124 {
125   PROP_MINUTES = 1,
126   PROP_AMPM
127 };
128
129
130 static void
131 hildon_time_picker_class_init( HildonTimePickerClass *klass );
132
133 static void
134 hildon_time_picker_init( HildonTimePicker *picker );
135
136 static gboolean
137 hildon_time_picker_timeout( gpointer data );
138
139 static void
140 hildon_time_picker_change_time( HildonTimePicker *picker, guint minutes );
141
142 static gboolean
143 hildon_time_picker_ampm_release( GtkWidget *widget, GdkEvent *event,
144                                  HildonTimePicker *picker );
145
146 static gboolean
147 hildon_time_picker_arrow_press( GtkWidget *widget, GdkEvent *event,
148                                 HildonTimePicker *picker );
149 static gboolean
150 hildon_time_picker_arrow_release( GtkWidget *widget, GdkEvent *event,
151                                   HildonTimePicker *picker );
152
153 static void
154 hildon_time_picker_get_property( GObject *object, guint param_id,
155                                                    GValue *value, GParamSpec *pspec );
156
157 static void
158 hildon_time_picker_set_property( GObject *object, guint param_id,
159                                                    const GValue *value, GParamSpec *pspec );
160
161 static gboolean
162 hildon_time_picker_event_box_focus_in( GtkWidget *widget, GdkEvent *event,
163                                        gpointer data );
164
165 static gboolean
166 hildon_time_picker_event_box_focus_out( GtkWidget *widget, GdkEvent *event,
167                                         gpointer data );
168
169 static gboolean
170 hildon_time_picker_event_box_key_press( GtkWidget *widget,  GdkEventKey *event,
171                                         HildonTimePicker *picker );
172
173 static gboolean
174 hildon_time_picker_event_box_key_release( GtkWidget *widget,  GdkEventKey *event,
175                                           HildonTimePicker *picker );
176
177 static gboolean
178 hildon_time_picker_event_box_press( GtkWidget *widget,  GdkEventKey *event,
179                                     gpointer data );
180
181 static void
182 hildon_time_picker_map( GtkWidget *widget );
183
184
185 GType hildon_time_picker_get_type( void )
186 {
187   static GType picker_type = 0;
188
189   if( !picker_type )
190   {
191     static const GTypeInfo picker_info =
192       {
193         sizeof(HildonTimePickerClass),
194         NULL,       /* base_init */
195         NULL,       /* base_finalize */
196         (GClassInitFunc)hildon_time_picker_class_init,
197         NULL,       /* class_finalize */
198         NULL,       /* class_data */
199         sizeof(HildonTimePicker),
200         0,          /* n_preallocs */
201         (GInstanceInitFunc)hildon_time_picker_init,
202       };
203     picker_type = g_type_register_static( GTK_TYPE_DIALOG, "HildonTimePicker",
204                                           &picker_info, 0 );
205   }
206   return picker_type;
207 }
208
209
210 static void
211 hildon_time_picker_class_init( HildonTimePickerClass *klass )
212 {
213   GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
214   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
215   parent_class = g_type_class_peek_parent( klass );
216
217   gobject_class->get_property = hildon_time_picker_get_property;
218   gobject_class->set_property = hildon_time_picker_set_property;
219   widget_class->map = hildon_time_picker_map;
220
221   /**
222    * HildonTimePicker:minutes:
223    *
224    * Currently selected minutes.
225    */
226   g_object_class_install_property( gobject_class, PROP_MINUTES,
227                                    g_param_spec_uint("minutes",
228                                      "Current minutes",
229                                      "The selected minutes",
230                                      0, 1440, 0,
231                                      G_PARAM_READABLE | G_PARAM_WRITABLE) );
232
233   gtk_widget_class_install_style_property( widget_class,
234                                                                g_param_spec_uint("arrow-width",
235                                          _("Arrow width"),
236                                          _("Increase/decrease arrows width."),
237                                          0, G_MAXUINT,
238                                          DEFAULT_ARROW_WIDTH,
239                                          G_PARAM_READABLE) );
240
241   gtk_widget_class_install_style_property( widget_class,
242                                                                g_param_spec_uint("arrow-height",
243                                          _("Arrow height"),
244                                          _("Increase/decrease arrows height."),
245                                          0, G_MAXUINT,
246                                          DEFAULT_ARROW_HEIGHT,
247                                          G_PARAM_READABLE) );
248
249   g_type_class_add_private( klass, sizeof(HildonTimePickerPrivate) );
250 }
251
252 static void hildon_time_picker_init( HildonTimePicker *picker )
253 {
254   HildonTimePickerPrivate *priv = HILDON_TIME_PICKER_GET_PRIVATE(picker);
255   GtkSettings *settings = NULL;
256   GtkDialog *dialog = GTK_DIALOG(picker);
257   GtkTable *table = NULL;
258   GtkWidget *maintocenter;
259   struct tm *local = NULL;
260   time_t stamp;
261   gint i = 0;
262   GtkSizeGroup *size_group;
263
264   picker->priv = priv;
265
266   gtk_widget_push_composite_child();
267
268   priv->child[TABLE] = gtk_table_new( 3, 6, FALSE );
269   table = GTK_TABLE(priv->child[TABLE]);
270
271   maintocenter = gtk_alignment_new( 0.5, 0, 0, 0 );
272
273   for( i = FRAME_HOURS; i <= FRAME_LMINUTES; i++ )
274   {
275     priv->child[i] = gtk_frame_new( NULL );
276     gtk_container_set_border_width( GTK_CONTAINER( priv->child[i] ), 0 );
277   }
278
279   for( i = BUTTON_HOURS_UP; i <= BUTTON_LMINUTES_DOWN; i++ )
280     priv->child[i] = gtk_button_new();
281
282   for( i = EBOX_HOURS; i <= EBOX_LMINUTES; i++ )
283     priv->child[i] = gtk_event_box_new();
284
285   for( i = LABEL_HOURS; i <= LABEL_LMINUTES; i++ )
286   {
287     priv->child[i] = gtk_label_new( "00" );
288     gtk_misc_set_padding( GTK_MISC( priv->child[i] ), 0, 1 );
289     gtk_widget_set_name( priv->child[i], "osso-LargeFont" );
290   }
291
292   priv->child[LABEL_COLON] = gtk_label_new(_("Ecdg_ti_time_picker_separator"));
293   gtk_widget_set_name( priv->child[LABEL_COLON], "osso-LargeFont" );
294
295   priv->minutes = 0;
296   priv->mul = 0;
297   priv->key_repeat = 0;
298   priv->start_key_repeat = FALSE;
299   priv->timer_id = 0;
300   priv->button_press = FALSE;
301
302   /* Load locales */
303   priv->am_symbol = nl_langinfo(AM_STR);
304   priv->pm_symbol = nl_langinfo(PM_STR);
305
306   priv->c12h = priv->am_symbol[0] == '\0' ? FALSE : TRUE;
307
308   stamp = time( NULL );
309   local = localtime( &stamp );
310
311   gtk_table_attach( table, priv->child[BUTTON_HOURS_UP], 1, 2, 0, 1,
312                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
313   gtk_table_attach( table, priv->child[FRAME_HOURS], 1, 2, 1, 2,
314                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
315   gtk_table_attach( table, priv->child[BUTTON_HOURS_DOWN], 1, 2, 2, 3,
316                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
317   gtk_table_attach( table, priv->child[LABEL_COLON], 2, 3, 1, 2,
318                     GTK_SHRINK, GTK_SHRINK, 6, 0 );
319   gtk_table_attach( table, priv->child[BUTTON_MMINUTES_UP], 3, 4, 0, 1,
320                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
321   gtk_table_attach( table, priv->child[FRAME_MMINUTES], 3, 4, 1, 2,
322                     GTK_FILL, GTK_SHRINK, 0, 0 );
323   gtk_table_attach( table, priv->child[BUTTON_MMINUTES_DOWN], 3, 4, 2, 3,
324                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
325
326   gtk_table_attach( table, priv->child[BUTTON_LMINUTES_UP], 4, 5, 0, 1,
327                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
328   gtk_table_attach( table, priv->child[FRAME_LMINUTES], 4, 5, 1, 2,
329                     GTK_FILL, GTK_SHRINK, 0, 0 );
330   gtk_table_attach( table, priv->child[BUTTON_LMINUTES_DOWN], 4, 5, 2, 3,
331                     GTK_SHRINK, GTK_SHRINK, 0, 0 );
332
333   gtk_table_set_row_spacing( table, 0, 6 );
334   gtk_table_set_row_spacing( table, 1, 6 );
335
336   size_group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL );
337   gtk_size_group_add_widget( size_group, priv->child[BUTTON_MMINUTES_UP] );
338   gtk_size_group_add_widget( size_group, priv->child[FRAME_MMINUTES] );
339   gtk_size_group_add_widget( size_group, priv->child[BUTTON_MMINUTES_DOWN] );
340   gtk_size_group_add_widget( size_group, priv->child[BUTTON_LMINUTES_UP] );
341   gtk_size_group_add_widget( size_group, priv->child[FRAME_LMINUTES] );
342   gtk_size_group_add_widget( size_group, priv->child[BUTTON_LMINUTES_DOWN] );
343   g_object_unref( size_group ); /* Added widgets hold references */
344
345   if( priv->c12h )
346   {
347     GtkWidget *ampmtotop = NULL;
348     guint placement = 0;
349     priv->ampm_pos = strncmp(nl_langinfo(T_FMT_AMPM), "%p", 2) ? TRUE : FALSE;
350     ampmtotop = gtk_alignment_new( 0, 0.5, 0, 0 );
351     priv->child[FRAME_AMPM] = gtk_frame_new( NULL );
352     priv->child[EBOX_AMPM] = gtk_event_box_new();
353     priv->child[LABEL_AMPM] = gtk_label_new( priv->pm_symbol );
354
355     placement = priv->ampm_pos * 5;
356
357     gtk_container_add( GTK_CONTAINER(ampmtotop), priv->child[FRAME_AMPM] );
358     gtk_container_add( GTK_CONTAINER(priv->child[FRAME_AMPM]),
359                        priv->child[EBOX_AMPM] );
360     gtk_container_add( GTK_CONTAINER(priv->child[EBOX_AMPM]),
361                        priv->child[LABEL_AMPM] );
362     gtk_table_attach( table, ampmtotop, placement, placement + 1, 1, 2,
363                       GTK_SHRINK, GTK_SHRINK, 0, 0 );
364     gtk_table_set_col_spacing( table, placement - 1, 6 );
365
366     g_signal_connect( G_OBJECT(priv->child[EBOX_AMPM]), "button-release-event",
367                       (GCallback)hildon_time_picker_ampm_release, picker );
368     g_signal_connect( G_OBJECT(priv->child[EBOX_AMPM]), "focus-out-event",
369                       (GCallback)hildon_time_picker_event_box_focus_out,
370                       picker );
371     g_signal_connect( G_OBJECT(priv->child[EBOX_AMPM]), "focus-in-event",
372                       (GCallback)hildon_time_picker_event_box_focus_in, picker );
373     g_signal_connect( G_OBJECT(priv->child[EBOX_AMPM]), "key-release-event",
374                       (GCallback)hildon_time_picker_event_box_key_release,
375                       picker );
376     g_signal_connect( G_OBJECT(priv->child[EBOX_AMPM]), "key-press-event",
377                       (GCallback)hildon_time_picker_event_box_key_press,
378                       picker );
379
380     g_object_set( G_OBJECT(priv->child[EBOX_AMPM]), "can-focus", TRUE, NULL );
381     gtk_widget_set_events( priv->child[EBOX_AMPM],
382                            GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_PRESS_MASK );
383   }
384   else
385     priv->child[FRAME_AMPM] = priv->child[LABEL_AMPM] =
386                               priv->child[EBOX_AMPM] = NULL;
387
388   gtk_widget_pop_composite_child();
389
390   hildon_time_picker_set_time( picker, local->tm_hour, local->tm_min );
391
392   settings = gtk_settings_get_default();
393   g_object_get( settings, "gtk-update-timeout", &priv->key_repeat, NULL );
394
395   for( i = 0; i < 3; i++ )
396   {
397     gtk_container_add( GTK_CONTAINER(priv->child[FRAME_HOURS + i]),
398                        priv->child[EBOX_HOURS + i] );
399     gtk_container_add( GTK_CONTAINER(priv->child[EBOX_HOURS + i]),
400                        priv->child[LABEL_HOURS + i] );
401
402     /* Connect signals */
403     g_signal_connect( G_OBJECT(priv->child[EBOX_HOURS + i]), "key-release-event",
404                       (GCallback)hildon_time_picker_event_box_key_release,
405                       picker );
406     g_signal_connect( G_OBJECT(priv->child[EBOX_HOURS + i]), "key-press-event",
407                       (GCallback)hildon_time_picker_event_box_key_press,
408                       picker );
409     g_signal_connect( G_OBJECT(priv->child[EBOX_HOURS + i]), "focus-in-event",
410                       (GCallback)hildon_time_picker_event_box_focus_in, picker );
411     g_signal_connect( G_OBJECT(priv->child[EBOX_HOURS + i]), "focus-out-event",
412                       (GCallback)hildon_time_picker_event_box_focus_out,
413                       picker );
414     g_signal_connect( G_OBJECT(priv->child[EBOX_HOURS + i]),
415                       "button-press-event",
416                       (GCallback)hildon_time_picker_event_box_press, NULL );
417
418     /* Name the buttons */
419     gtk_widget_set_name( priv->child[BUTTON_HOURS_UP + i*2],
420                          "hildon-time-picker-up" );
421     gtk_widget_set_name( priv->child[BUTTON_HOURS_DOWN + i*2],
422                          "hildon-time-picker-down" );
423   }
424
425   for( i = BUTTON_HOURS_UP; i <= BUTTON_LMINUTES_DOWN; i++ )
426   {
427     g_object_set( G_OBJECT(priv->child[i]), "can-focus", FALSE, NULL );
428
429     /* Connect signals */
430     g_signal_connect( G_OBJECT(priv->child[i]), "button-press-event",
431                       (GCallback)hildon_time_picker_arrow_press, picker );
432     g_signal_connect( G_OBJECT(priv->child[i]), "button-release-event",
433                       (GCallback)hildon_time_picker_arrow_release, picker );
434   }
435
436   for( i = EBOX_HOURS; i <= EBOX_LMINUTES; i++ )
437   {
438     g_object_set( G_OBJECT(priv->child[i]), "can-focus", TRUE, NULL );
439
440     /* Set events */
441     gtk_widget_set_events( priv->child[i],
442                            GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_PRESS_MASK );
443   }
444
445   gtk_window_set_modal( GTK_WINDOW(dialog), FALSE );
446   gtk_dialog_set_has_separator( dialog, FALSE );
447   gtk_dialog_add_button( dialog, _("ecdg_bd_time_picker_close"),
448                          GTK_RESPONSE_OK );
449
450   gtk_container_add( GTK_CONTAINER(maintocenter), priv->child[TABLE] );
451   gtk_box_pack_start( GTK_BOX(dialog->vbox), maintocenter, TRUE, FALSE, 0 );
452   gtk_widget_show_all( maintocenter );
453 }
454
455 static void
456 hildon_time_picker_set_property( GObject *object, guint param_id,
457                                                     const GValue *value, GParamSpec *pspec )
458 {
459   HildonTimePicker *picker = HILDON_TIME_PICKER(object);
460
461   switch( param_id )
462   {
463     case PROP_MINUTES:
464       hildon_time_picker_change_time( picker, g_value_get_uint(value) );
465       break;
466
467     default:
468       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
469       break;
470   }
471 }
472
473 static void
474 hildon_time_picker_get_property( GObject *object, guint param_id,
475                                                     GValue *value, GParamSpec *pspec )
476 {
477   HildonTimePickerPrivate *priv = HILDON_TIME_PICKER(object)->priv;
478
479   switch( param_id )
480   {
481     case PROP_MINUTES:
482       g_value_set_uint( value, priv->minutes );
483       break;
484
485     default:
486       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
487       break;
488   }
489 }
490
491 static void
492 hildon_time_picker_map( GtkWidget *widget )
493 {
494   guint width, height;
495   gint i;
496   HildonTimePickerPrivate *priv = HILDON_TIME_PICKER(widget)->priv;
497
498   gdk_window_set_decorations( widget->window, GDK_DECOR_BORDER );
499
500   gtk_widget_style_get( widget,
501                         "arrow-width", &width,
502                         "arrow-height", &height, NULL );
503   for( i=BUTTON_HOURS_UP; i <= BUTTON_LMINUTES_DOWN; i++)
504     gtk_widget_set_size_request( priv->child[i], width, height );
505
506   GTK_WIDGET_CLASS(parent_class)->map( widget );
507 }
508
509
510 static gboolean
511 hildon_time_picker_event_box_press( GtkWidget *widget,  GdkEventKey *event,
512                                     gpointer data )
513 {
514   gtk_widget_grab_focus( widget );
515   return FALSE;
516 }
517
518 static gboolean
519 hildon_time_picker_ampm_release( GtkWidget *widget, GdkEvent *event,
520                                  HildonTimePicker *picker )
521 {
522   gtk_widget_grab_focus( widget );
523   hildon_time_picker_change_time( picker, picker->priv->minutes > 720 ?
524                                   picker->priv->minutes - 720 :
525                                   picker->priv->minutes + 720 );
526   return FALSE;
527 }
528
529 static gboolean
530 hildon_time_picker_arrow_press( GtkWidget *widget, GdkEvent *event,
531                                 HildonTimePicker *picker )
532 {
533   HildonTimePickerPrivate *priv = picker->priv;
534   gint newval = 0;
535
536   if( priv->button_press )
537     return FALSE;
538
539   priv->start_key_repeat = priv->button_press = TRUE;
540
541   if( widget == priv->child[BUTTON_HOURS_UP] )
542   {
543     priv->mul = 60;
544     gtk_widget_grab_focus( priv->child[EBOX_HOURS] );
545   }
546   else if( widget == priv->child[BUTTON_MMINUTES_UP] )
547   {
548     priv->mul = 10;
549     gtk_widget_grab_focus( priv->child[EBOX_MMINUTES] );
550   }
551   else if( widget == priv->child[BUTTON_LMINUTES_UP] )
552   {
553     priv->mul = 1;
554     gtk_widget_grab_focus( priv->child[EBOX_LMINUTES] );
555   }
556   else if( widget == priv->child[BUTTON_HOURS_DOWN] )
557   {
558     priv->mul = -60;
559     gtk_widget_grab_focus( priv->child[EBOX_HOURS] );
560   }
561   else if( widget == priv->child[BUTTON_MMINUTES_DOWN] )
562   {
563     priv->mul = -10;
564     gtk_widget_grab_focus( priv->child[EBOX_MMINUTES] );
565   }
566   else
567   {
568     priv->mul = -1;
569     gtk_widget_grab_focus( priv->child[EBOX_LMINUTES] );
570   }
571
572   newval = priv->minutes + priv->mul;
573   if( newval < 0 )
574     newval += 1440;
575
576   hildon_time_picker_change_time( picker, newval );
577   priv->timer_id = g_timeout_add( priv->key_repeat * 3, hildon_time_picker_timeout, picker );
578   return FALSE;
579 }
580
581 static gboolean
582 hildon_time_picker_arrow_release( GtkWidget *widget, GdkEvent *event,
583                                   HildonTimePicker *picker )
584 {
585   HildonTimePickerPrivate *priv = picker->priv;
586   if( priv->timer_id )
587   {
588     g_source_remove( priv->timer_id );
589     priv->timer_id = 0;
590   }
591   priv->button_press = FALSE;
592   return FALSE;
593 }
594
595 static gboolean
596 hildon_time_picker_event_box_focus_in( GtkWidget *widget, GdkEvent *event,
597                                        gpointer data )
598 {
599   gtk_widget_set_state( widget, GTK_STATE_SELECTED );
600   return FALSE;
601 }
602
603 static gboolean
604 hildon_time_picker_event_box_focus_out( GtkWidget *widget, GdkEvent *event,
605                                         gpointer data )
606 {
607   gtk_widget_set_state( widget, GTK_STATE_NORMAL );
608   return FALSE;
609 }
610
611 static gboolean
612 hildon_time_picker_event_box_key_press( GtkWidget *widget, GdkEventKey *event,
613                                         HildonTimePicker *picker )
614 {
615   HildonTimePickerPrivate *priv = picker->priv;
616
617   if( priv->timer_id )
618     return TRUE;
619
620   switch( event->keyval )
621   {
622     case GDK_Up:
623       if( widget == priv->child[EBOX_HOURS] )
624       {
625         hildon_time_picker_arrow_press( priv->child[BUTTON_HOURS_UP], NULL,
626                                         picker );
627         gtk_widget_set_state( priv->child[BUTTON_HOURS_UP], GTK_STATE_SELECTED );
628       }
629       else if( widget == priv->child[EBOX_MMINUTES] )
630       {
631         hildon_time_picker_arrow_press( priv->child[BUTTON_MMINUTES_UP], NULL,
632                                         picker );
633         gtk_widget_set_state( priv->child[BUTTON_MMINUTES_UP],
634                               GTK_STATE_SELECTED );
635       }
636       else if( widget == priv->child[EBOX_LMINUTES] )
637       {
638         hildon_time_picker_arrow_press( priv->child[BUTTON_LMINUTES_UP], NULL,
639                                         picker );
640         gtk_widget_set_state( priv->child[BUTTON_LMINUTES_UP],
641                               GTK_STATE_SELECTED );
642       }
643       else
644         hildon_time_picker_ampm_release( priv->child[EBOX_AMPM], NULL, picker );
645       return TRUE;
646
647     case GDK_Down:
648       if( widget == priv->child[EBOX_HOURS] )
649       {
650         hildon_time_picker_arrow_press( priv->child[BUTTON_HOURS_DOWN], NULL,
651                                         picker );
652         gtk_widget_set_state( priv->child[BUTTON_HOURS_DOWN],
653                               GTK_STATE_SELECTED );
654       }
655       else if( widget == priv->child[EBOX_MMINUTES] )
656       {
657         hildon_time_picker_arrow_press( priv->child[BUTTON_MMINUTES_DOWN], NULL,
658                                         picker );
659         gtk_widget_set_state( priv->child[BUTTON_MMINUTES_DOWN],
660                               GTK_STATE_SELECTED );
661       }
662       else if( widget == priv->child[EBOX_LMINUTES] )
663       {
664         hildon_time_picker_arrow_press( priv->child[BUTTON_LMINUTES_DOWN], NULL,
665                                         picker );
666         gtk_widget_set_state( priv->child[BUTTON_LMINUTES_DOWN],
667                               GTK_STATE_SELECTED );
668       }
669       else
670         hildon_time_picker_ampm_release( priv->child[EBOX_AMPM], NULL, picker );
671       return TRUE;
672
673     case GDK_Left:
674       if( priv->c12h )
675       {
676         if( !priv->ampm_pos )
677         {
678           if( widget == priv->child[EBOX_AMPM] )
679             return TRUE;
680         }
681         else if( widget == priv->child[EBOX_HOURS] )
682           return TRUE;
683       }
684       else if( widget == priv->child[EBOX_HOURS] )
685         return TRUE;
686
687     break;
688
689     case GDK_Right:
690       if( priv->c12h )
691       {
692         if( priv->ampm_pos )
693         {
694           if( widget == priv->child[EBOX_AMPM] )
695             return TRUE;
696         }
697         else if( widget == priv->child[EBOX_LMINUTES] )
698           return TRUE;
699       }
700       else if( widget == priv->child[EBOX_LMINUTES] )
701         return TRUE;
702     break;
703     
704     case GDK_Escape:
705       gtk_dialog_response (GTK_DIALOG (picker), GTK_RESPONSE_CANCEL);
706       return TRUE;
707
708     case GDK_Return:
709       gtk_dialog_response (GTK_DIALOG (picker), GTK_RESPONSE_OK);
710       return TRUE;
711   }
712
713   return FALSE;
714 }
715
716 static gboolean
717 hildon_time_picker_event_box_key_release( GtkWidget *widget, GdkEventKey *event,
718                                           HildonTimePicker *picker )
719 {
720   gint i;
721
722   switch( event->keyval )
723   {
724     case GDK_Up:
725       for( i = BUTTON_HOURS_UP; i <= BUTTON_LMINUTES_UP; i += 2 )
726         gtk_widget_set_state( picker->priv->child[i], GTK_STATE_NORMAL );
727       hildon_time_picker_arrow_release( widget, NULL, picker );
728     break;
729
730     case GDK_Down:
731       for( i = BUTTON_HOURS_DOWN; i <= BUTTON_LMINUTES_DOWN; i += 2 )
732         gtk_widget_set_state( picker->priv->child[i], GTK_STATE_NORMAL );
733       hildon_time_picker_arrow_release( widget, NULL, picker );
734       break;
735   }
736   return FALSE;
737 }
738
739 static gboolean
740 hildon_time_picker_timeout( gpointer data )
741 {
742   HildonTimePicker *picker = NULL;
743   HildonTimePickerPrivate *priv = NULL;
744   gint newval = 0;
745
746   if( !data )
747     return FALSE;
748
749   picker = HILDON_TIME_PICKER(data);
750   priv = picker->priv;
751
752   if( !picker->priv->timer_id )
753     return FALSE;
754
755   newval = priv->minutes + priv->mul;
756   if( newval < 0 )
757     newval += 1440;
758
759   hildon_time_picker_change_time( picker, newval );
760
761   if( priv->start_key_repeat )
762   {
763     priv->timer_id = g_timeout_add( priv->key_repeat, hildon_time_picker_timeout, picker );
764     priv->start_key_repeat = FALSE;
765     return FALSE;
766   }
767
768   return TRUE;
769 }
770
771
772
773 static void
774 hildon_time_picker_change_time( HildonTimePicker *picker, guint minutes )
775 {
776   HildonTimePickerPrivate *priv = picker->priv;
777   gchar str[3] = "00";
778   guint hours = 0;
779   gboolean ampm = TRUE;
780
781   minutes %= 1440;
782
783   if( priv->minutes == minutes )
784     return;
785
786   priv->minutes = minutes;
787
788   if( priv->c12h )
789   {
790     ampm = !((guint)(minutes / 720));
791     minutes %= 720;
792     if( minutes < 60 )
793       minutes += 720;
794
795     gtk_label_set_text( GTK_LABEL(priv->child[LABEL_AMPM]),
796                         ampm ? priv->am_symbol : priv->pm_symbol );
797   }
798
799   hours = minutes / 60;
800   minutes %= 60;
801
802   sprintf( str, "%02d", hours );
803   gtk_label_set_text( GTK_LABEL(priv->child[LABEL_HOURS]), str );
804
805   sprintf( str, "%02d", minutes );
806   gtk_label_set_text( GTK_LABEL(priv->child[LABEL_LMINUTES]), &str[1] );
807
808   str[1] = '\0';
809   gtk_label_set_text( GTK_LABEL(priv->child[LABEL_MMINUTES]), &str[0] );
810
811   g_object_notify( G_OBJECT(picker), "minutes" );
812 }
813
814 /**
815  * hildon_time_picker_new:
816  * @parent: parent window.
817  *
818  * Hildon Time Picker shows time picker dialog. Close button is placed
819  * in dialog's action area and time picker is placed in dialogs vbox.
820  * Actual time picker consists two #GtkLabel fields one for hour and
821  * one for minutes, arrow buttons and an AM/PM button. A : is placed
822  * between hour and minute fields.
823  *
824  * Return value: pointer to a new #HildonTimePicker widget.
825  **/
826 GtkWidget *hildon_time_picker_new( GtkWindow *parent )
827 {
828   GtkWidget *widget = g_object_new( HILDON_TYPE_TIME_PICKER, NULL );
829
830   if( parent )
831     gtk_window_set_transient_for( GTK_WINDOW(widget), parent );
832
833   return GTK_WIDGET(widget);
834 }
835
836 /**
837  * hildon_time_picker_set_time:
838  * @picker: the #HildonTimePicker widget.
839  * @hours: hours
840  * @minutes: minutes
841  *
842  * Sets the time of the #HildonTimePicker widget.
843  **/
844 void hildon_time_picker_set_time( HildonTimePicker *picker,
845                                   guint hours, guint minutes )
846 {
847   g_return_if_fail( HILDON_IS_TIME_PICKER(picker) );
848   hildon_time_picker_change_time( picker, hours * 60 + minutes );
849 }
850
851 /**
852  * hildon_time_picker_get_time:
853  * @picker: the #HildonTimePicker widget.
854  * @hours: hours
855  * @minutes: minutes
856  *
857  * Gets the time of the #HildonTimePicker widget.
858  **/
859 void hildon_time_picker_get_time( HildonTimePicker *picker,
860                                   guint *hours, guint *minutes )
861 {
862   guint current;
863   g_return_if_fail( HILDON_IS_TIME_PICKER(picker) );
864
865   current = picker->priv->minutes;
866   *hours = current / 60;
867   *minutes = current % 60;
868 }