Make HildonNote request always the full width of the screen
[hildon] / hildon / hildon-time-editor.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Rodrigo Novo <rodrigo.novo@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-time-editor
27  * @short_description: A widget used to enter time or duration in hours, minutes,
28  * and optional seconds.
29  * @see_also: #HildonTimePicker
30  * 
31  * HildonTimeEditor is used to edit time or duration. Time mode is
32  * restricted to normal 24 hour cycle, but Duration mode can select any
33  * amount of time up to 99 hours.  It consists of entries for hours,
34  * minutes and seconds, and pm/am indicator as well as a button which
35  * popups a #HildonTimePicker dialog.
36  *
37  * <note>
38  *   <para>
39  * #HildonTimeEditor has been deprecated since Hildon 2.2 and should not
40  * be used in newly written code. See
41  * <link linkend="hildon-migrating-time-widgets">Migrating Time Widgets</link>
42  * section to know how to migrate this deprecated widget.
43  *   </para>
44  * </note>
45  *
46  * <example>
47  * <title>HildonTimePicker example</title>
48  * <programlisting>
49  * <!-- -->
50  * editor = hildon_time_editor_new ();
51  * hildon_time_editor_set_time (editor, h, m, s);
52  * <!-- -->
53  * gtk_box_pack_start (..., editor)
54  * <!-- -->
55  * hildon_time_editor_get_time (editor, &amp;h, &amp;m, &amp;s);
56  * <!-- -->
57  * </programlisting>
58  * </example>
59  *
60  */
61
62 #undef                                          HILDON_DISABLE_DEPRECATED
63
64 #ifdef                                          HAVE_CONFIG_H
65 #include                                        <config.h>
66 #endif
67
68 #include                                        <string.h>
69 #include                                        <time.h>
70 #include                                        <stdlib.h>
71 #include                                        <langinfo.h>
72 #include                                        <libintl.h>
73 #include                                        <gdk/gdkkeysyms.h>
74
75 #include                                        "hildon-time-editor.h"
76 #include                                        "hildon-defines.h"
77 #include                                        "hildon-time-picker.h"
78 #include                                        "hildon-banner.h"
79 #include                                        "hildon-private.h"
80 #include                                        "hildon-marshalers.h"
81 #include                                        "hildon-enum-types.h"
82 #include                                        "hildon-time-editor-private.h"
83
84 #define                                         _(String) dgettext("hildon-libs", String)
85
86 #define                                         c_(String) dgettext("hildon-common-strings", String)
87
88 #define                                         TICKS(h,m,s) \
89                                                 ((h) * 3600 + (m) * 60 + (s))
90
91 #define                                         TIME_EDITOR_HEIGHT 30
92
93 #define                                         ICON_PRESSED 4
94
95 #define                                         ICON_NAME "widgets_time_editor"
96
97 #define                                         ICON_SIZE "timepicker-size"
98
99 #define                                         MIN_DURATION 0
100
101 #define                                         MAX_DURATION TICKS(99, 59, 59)
102
103 /* Default values for properties */
104
105 #define                                         HILDON_TIME_EDITOR_TICKS_VALUE 0
106
107 #define                                         HILDON_TIME_EDITOR_DURATION_MODE FALSE
108
109 #define                                         HILDON_TIME_EDITOR_DURATION_LOWER_VALUE 0
110
111 #define                                         HILDON_TIME_EDITOR_DURATION_UPPER_VALUE TICKS(99, 59, 59)
112
113 #define                                         HOURS_MAX_24 23
114
115 #define                                         HOURS_MAX_12 12
116
117 #define                                         HOURS_MIN_24 0
118
119 #define                                         HOURS_MIN_12 1
120
121 #define                                         MINUTES_MAX 59
122
123 #define                                         SECONDS_MAX 59
124
125 #define                                         MINUTES_MIN 0
126
127 #define                                         SECONDS_MIN 0
128
129 static GtkContainerClass*                       parent_class;
130
131 enum
132 {
133     PROP_0,
134     PROP_TICKS,
135     PROP_DURATION_MODE,
136     PROP_DURATION_MIN,
137     PROP_DURATION_MAX,
138     PROP_SHOW_SECONDS,
139     PROP_SHOW_HOURS
140 };
141
142 /* Signals */
143 enum {
144     TIME_ERROR,
145     LAST_SIGNAL
146 };
147
148 /* Error codes categories */
149 enum {
150     MAX_VALUE,
151     MIN_VALUE,
152     WITHIN_RANGE,
153     NUM_ERROR_CODES
154 };
155
156 static guint                                    time_editor_signals[LAST_SIGNAL] = { 0 };
157
158 static guint                                    hour_errors[NUM_ERROR_CODES] = {
159                                                 HILDON_DATE_TIME_ERROR_MAX_HOURS, 
160                                                 HILDON_DATE_TIME_ERROR_MIN_HOURS, 
161                                                 HILDON_DATE_TIME_ERROR_EMPTY_HOURS };
162
163 static guint                                    min_errors[NUM_ERROR_CODES] = { 
164                                                 HILDON_DATE_TIME_ERROR_MAX_MINS,  
165                                                 HILDON_DATE_TIME_ERROR_MIN_MINS,  
166                                                 HILDON_DATE_TIME_ERROR_EMPTY_MINS };
167
168 static guint                                    sec_errors[NUM_ERROR_CODES] = { 
169                                                 HILDON_DATE_TIME_ERROR_MAX_SECS, 
170                                                 HILDON_DATE_TIME_ERROR_MIN_SECS, 
171                                                 HILDON_DATE_TIME_ERROR_EMPTY_SECS };
172
173 static void 
174 hildon_time_editor_class_init                   (HildonTimeEditorClass *editor_class);
175
176 static void 
177 hildon_time_editor_init                         (HildonTimeEditor *editor);
178
179 static void 
180 hildon_time_editor_finalize                     (GObject *obj_self);
181
182 static void
183 hildon_time_editor_set_property                 (GObject *object,
184                                                  guint param_id,
185                                                  const GValue *value,
186                                                  GParamSpec *pspec);
187
188 static void 
189 hildon_time_editor_get_property                 (GObject *object,
190                                                  guint param_id,
191                                                  GValue *value,
192                                                  GParamSpec *pspec);
193
194 static void
195 hildon_time_editor_forall                       (GtkContainer *container,
196                                                  gboolean include_internals,
197                                                  GtkCallback callback,
198                                                  gpointer callback_data);
199                           
200 static void
201 hildon_time_editor_destroy                      (GtkObject *self);
202
203 static gboolean
204 hildon_time_editor_entry_focus_out              (GtkWidget *widget,
205                                                  GdkEventFocus *event,
206                                                  gpointer data);
207
208 static gboolean 
209 hildon_time_editor_entry_focus_in               (GtkWidget *widget,
210                                                  GdkEventFocus *event, 
211                                                  gpointer data);
212
213 static gboolean
214 hildon_time_editor_time_error                   (HildonTimeEditor *editor,
215                                                  HildonDateTimeError type);
216
217 static gboolean 
218 hildon_time_editor_ampm_clicked                 (GtkWidget *widget,
219                                                  gpointer data);
220
221 static gboolean 
222 hildon_time_editor_icon_clicked                 (GtkWidget *widget,
223                                                  gpointer data);
224
225 static void     
226 hildon_time_editor_size_request                 (GtkWidget *widget,
227                                                  GtkRequisition *requisition);
228
229 static void    
230 hildon_time_editor_size_allocate                (GtkWidget *widget,
231                                                  GtkAllocation *allocation);
232
233 static gboolean
234 hildon_time_editor_focus                        (GtkWidget *widget,
235                                                  GtkDirectionType direction);
236
237 static gboolean
238 hildon_time_editor_entry_keypress (GtkEntry *entry,
239                                    GdkEventKey* event,
240                                    gpointer user_data);
241
242 static gboolean
243 hildon_time_editor_check_locale                 (HildonTimeEditor *editor);
244
245 #ifdef MAEMO_GTK 
246 static void 
247 hildon_time_editor_tap_and_hold_setup           (GtkWidget *widget,
248                                                  GtkWidget *menu,
249                                                  GtkCallback func,
250                                                  GtkWidgetTapAndHoldFlags flags);
251 #endif
252
253 static void
254 hildon_time_editor_validate                     (HildonTimeEditor *editor, 
255                                                  gboolean allow_intermediate);
256
257 static void 
258 hildon_time_editor_set_to_current_time          (HildonTimeEditor *editor);
259
260 static gboolean
261 hildon_time_editor_entry_select_all             (GtkWidget *widget);
262
263 static void 
264 convert_to_12h                                  (guint *h, 
265                                                  gboolean *am);
266
267 static void 
268 convert_to_24h                                  (guint *h, 
269                                                  gboolean am);
270
271 static void 
272 ticks_to_time                                   (guint ticks,
273                                                  guint *hours,
274                                                  guint *minutes,
275                                                  guint *seconds);
276
277 static void
278 hildon_time_editor_inserted_text                (GtkEditable *editable,
279                                                  gchar *new_text,
280                                                  gint new_text_length,
281                                                  gint *position,
282                                                  gpointer user_data);
283
284 /**
285  * hildon_time_editor_get_type:
286  *
287  * Initializes and returns the type of a hildon time editor.
288  *
289  * Returns: GType of #HildonTimeEditor
290  */
291 GType G_GNUC_CONST
292 hildon_time_editor_get_type                     (void)
293 {
294     static GType editor_type = 0;
295
296     if (! editor_type) {
297         static const GTypeInfo editor_info = {
298             sizeof(HildonTimeEditorClass),
299             NULL,       /* base_init      */
300             NULL,       /* base_finalize  */
301             (GClassInitFunc) hildon_time_editor_class_init,
302             NULL,       /* class_finalize */
303             NULL,       /* class_data     */
304             sizeof(HildonTimeEditor),
305             0,          /* n_preallocs    */
306             (GInstanceInitFunc) hildon_time_editor_init,
307         };
308         editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
309                 "HildonTimeEditor",
310                 &editor_info, 0);
311     }
312
313     return editor_type;
314 }
315
316 static void 
317 hildon_time_editor_forall                       (GtkContainer *container,
318                                                  gboolean include_internals,
319                                                  GtkCallback callback,
320                                                  gpointer callback_data)
321 {
322     HildonTimeEditorPrivate *priv;
323
324     g_assert (HILDON_IS_TIME_EDITOR (container));
325     g_assert (callback != NULL);
326
327     priv = HILDON_TIME_EDITOR_GET_PRIVATE (container);
328
329     g_assert (priv);
330
331     if (! include_internals)
332         return;
333
334     /* widget that are always shown */
335     (*callback) (priv->iconbutton, callback_data);
336     (*callback) (priv->frame, callback_data);
337 }
338
339 static void 
340 hildon_time_editor_destroy                      (GtkObject *self)
341 {
342     HildonTimeEditorPrivate *priv;
343
344     priv = HILDON_TIME_EDITOR_GET_PRIVATE (self);
345     g_assert (priv);
346
347     if (priv->iconbutton) {
348         gtk_widget_unparent (priv->iconbutton);
349         priv->iconbutton = NULL;
350     }
351     if (priv->frame) {
352         gtk_widget_unparent (priv->frame);
353         priv->frame = NULL;
354     }
355
356     if (GTK_OBJECT_CLASS (parent_class)->destroy)
357         GTK_OBJECT_CLASS (parent_class)->destroy (self);
358 }
359
360 static void
361 hildon_time_editor_class_init                   (HildonTimeEditorClass *editor_class)
362 {
363     GObjectClass *object_class = G_OBJECT_CLASS (editor_class);
364     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
365     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
366
367     parent_class = g_type_class_peek_parent (editor_class);
368
369     g_type_class_add_private (editor_class, sizeof (HildonTimeEditorPrivate));
370
371     object_class->get_property                  = hildon_time_editor_get_property;
372     object_class->set_property                  = hildon_time_editor_set_property;
373     widget_class->size_request                  = hildon_time_editor_size_request;
374     widget_class->size_allocate                 = hildon_time_editor_size_allocate;
375     widget_class->focus                         = hildon_time_editor_focus;
376
377     container_class->forall                     = hildon_time_editor_forall;
378     GTK_OBJECT_CLASS (editor_class)->destroy    = hildon_time_editor_destroy;
379
380     object_class->finalize                      = hildon_time_editor_finalize;
381
382     editor_class->time_error                    = hildon_time_editor_time_error; 
383
384     time_editor_signals[TIME_ERROR] =
385         g_signal_new ("time-error",
386                 G_OBJECT_CLASS_TYPE (object_class),
387                 G_SIGNAL_RUN_LAST,
388                 G_STRUCT_OFFSET (HildonTimeEditorClass, time_error),
389                 g_signal_accumulator_true_handled, NULL,
390                 _hildon_marshal_BOOLEAN__ENUM,
391                 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
392
393     /**
394      * HildonTimeEditor:ticks:
395      *
396      * If editor is in duration mode, contains the duration seconds.
397      * If not, contains seconds since midnight.
398      */
399     g_object_class_install_property (object_class, PROP_TICKS,
400             g_param_spec_uint ("ticks",
401                 "Duration value",
402                 "Current value of duration",
403                 0, G_MAXUINT,
404                 HILDON_TIME_EDITOR_TICKS_VALUE,
405                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
406
407     /**
408      * HildonTimeEditor:show_seconds:
409      *
410      * Controls whether seconds are shown in the editor
411      */
412     g_object_class_install_property (object_class, PROP_SHOW_SECONDS,
413             g_param_spec_boolean ("show_seconds",
414                 "Show seconds property",
415                 "Controls whether the seconds are shown in the editor",
416                 FALSE,
417                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
418
419     /**
420      * HildonTimeEditor:show_hours:
421      *
422      * Controls whether hours are shown in the editor
423      */
424     g_object_class_install_property (object_class, PROP_SHOW_HOURS,
425             g_param_spec_boolean ("show_hours",
426                 "Show hours field",
427                 "Controls whether the hours field is shown in the editor",
428                 TRUE,
429                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
430
431     /**
432      * HildonTimeEditor:duration_mode:
433      *
434      * Controls whether the TimeEditor is in duration mode
435      */
436     g_object_class_install_property (object_class, PROP_DURATION_MODE,
437             g_param_spec_boolean ("duration_mode",
438                 "Duration mode",
439                 "Controls whether the TimeEditor is in duration mode",
440                 HILDON_TIME_EDITOR_DURATION_MODE,
441                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
442
443     /**
444      * HildonTimeEditor:duration_min:
445      *
446      * Minimum allowed duration value.
447      */
448     g_object_class_install_property (object_class, PROP_DURATION_MIN,
449             g_param_spec_uint ("duration_min",
450                 "Minumum duration value",
451                 "Smallest possible duration value",
452                 MIN_DURATION, MAX_DURATION,
453                 HILDON_TIME_EDITOR_DURATION_LOWER_VALUE,
454                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
455
456     /**
457      * HildonTimeEditor:duration_max:
458      *
459      * Maximum allowed duration value.
460      */
461     g_object_class_install_property (object_class, PROP_DURATION_MAX,
462             g_param_spec_uint ("duration_max",
463                 "Maximum duration value",
464                 "Largest possible duration value",
465                 0, G_MAXUINT,
466                 HILDON_TIME_EDITOR_DURATION_UPPER_VALUE,
467                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
468 }
469
470 #ifdef MAEMO_GTK 
471 static void 
472 hildon_time_editor_tap_and_hold_setup           (GtkWidget *widget,
473                                                  GtkWidget *menu,
474                                                  GtkCallback func,
475                                                  GtkWidgetTapAndHoldFlags flags)
476 {
477     HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE (widget);
478     gint i;
479
480     /* Forward this tap_and_hold_setup signal to all our child widgets */
481     for (i = 0; i < ENTRY_COUNT; i++)
482     {
483         gtk_widget_tap_and_hold_setup (priv->entries[i], menu, func,
484                 GTK_TAP_AND_HOLD_NO_SIGNALS);
485     }
486     gtk_widget_tap_and_hold_setup (priv->ampm_button, menu, func,
487             GTK_TAP_AND_HOLD_NO_SIGNALS);
488     gtk_widget_tap_and_hold_setup (priv->iconbutton, menu, func,
489             GTK_TAP_AND_HOLD_NONE);
490 }
491 #endif
492
493 static void 
494 hildon_time_editor_entry_changed                (GtkWidget *widget, 
495                                                  gpointer data)
496 {
497     g_assert (HILDON_IS_TIME_EDITOR (data));
498     hildon_time_editor_validate (HILDON_TIME_EDITOR (data), TRUE);
499 }
500
501 static void 
502 hildon_time_editor_init                         (HildonTimeEditor *editor)
503 {
504     HildonTimeEditorPrivate *priv;
505     GtkWidget *hbox, *icon;
506     gint i;
507
508     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
509     g_assert (priv);
510
511     gtk_widget_push_composite_child ();
512
513     /* Setup defaults and create widgets */
514     priv->ticks          = 0;
515     priv->show_seconds   = FALSE;
516     priv->show_hours     = TRUE;
517     priv->ampm_pos_after = TRUE;
518     priv->clock_24h      = TRUE;
519     priv->duration_mode  = FALSE;
520     priv->iconbutton     = gtk_button_new();
521     priv->ampm_label     = gtk_label_new(NULL);
522     priv->hm_label       = gtk_label_new(NULL);
523     priv->sec_label      = gtk_label_new(NULL);
524     priv->frame          = gtk_frame_new(NULL);
525     priv->ampm_button    = gtk_button_new();
526     priv->skipper        = FALSE;
527
528     icon = gtk_image_new_from_icon_name (ICON_NAME, HILDON_ICON_SIZE_SMALL);
529     hbox = gtk_hbox_new (FALSE, 0);
530
531     GTK_WIDGET_SET_FLAGS (editor, GTK_NO_WINDOW);
532     GTK_WIDGET_UNSET_FLAGS (priv->iconbutton, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
533
534     gtk_container_set_border_width (GTK_CONTAINER(priv->frame), 0);
535
536     gtk_container_add (GTK_CONTAINER (priv->iconbutton), icon);
537     gtk_container_add (GTK_CONTAINER (priv->ampm_button), priv->ampm_label);
538     gtk_button_set_relief(GTK_BUTTON (priv->ampm_button), GTK_RELIEF_NONE);
539     gtk_button_set_focus_on_click (GTK_BUTTON (priv->ampm_button), FALSE);
540
541     /* Create hour, minute and second entries */
542     for (i = 0; i < ENTRY_COUNT; i++)
543     {
544         priv->entries[i] = gtk_entry_new ();
545
546         /* No frames for entries, so that they all appear to be inside one long entry */
547         gtk_entry_set_has_frame (GTK_ENTRY (priv->entries[i]), FALSE);
548
549 #ifdef MAEMO_GTK 
550         /* Set the entries to accept only numeric characters */
551         g_object_set (priv->entries[i], "hildon-input-mode", HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
552 #endif
553
554         /* The entry fields all take exactly two characters */
555         gtk_entry_set_max_length (GTK_ENTRY (priv->entries[i]), 2);
556         gtk_entry_set_width_chars (GTK_ENTRY (priv->entries[i]), 2);
557
558         g_signal_connect (priv->entries[i], "focus-in-event",
559                 G_CALLBACK (hildon_time_editor_entry_focus_in), editor);
560         g_signal_connect (priv->entries[i], "focus-out-event",
561                 G_CALLBACK (hildon_time_editor_entry_focus_out), editor);
562         g_signal_connect (priv->entries[i], "key-press-event",
563                 G_CALLBACK (hildon_time_editor_entry_keypress), editor);
564         g_signal_connect (priv->entries[i], "changed",
565                 G_CALLBACK (hildon_time_editor_entry_changed), editor);
566
567         /* inserted signal sets time */
568         g_signal_connect_after (G_OBJECT(priv->entries[i]), "insert_text",
569                 G_CALLBACK (hildon_time_editor_inserted_text), 
570                 editor);
571     }
572
573     /* clicked signal for am/pm label */
574     g_signal_connect (G_OBJECT (priv->ampm_button), "clicked",
575             G_CALLBACK (hildon_time_editor_ampm_clicked), editor);
576
577     /* clicked signal for icon */
578     g_signal_connect (G_OBJECT (priv->iconbutton), "clicked",
579             G_CALLBACK (hildon_time_editor_icon_clicked), editor);
580
581     /* Set ourself as the parent of all the widgets we created */
582     gtk_widget_set_parent (priv->iconbutton, GTK_WIDGET(editor));
583     gtk_box_pack_start (GTK_BOX (hbox), priv->entries[ENTRY_HOURS], FALSE, FALSE, 0);
584     gtk_box_pack_start (GTK_BOX (hbox), priv->hm_label,             FALSE, FALSE, 0);
585     gtk_box_pack_start (GTK_BOX (hbox), priv->entries[ENTRY_MINS],  FALSE, FALSE, 0);
586     gtk_box_pack_start (GTK_BOX (hbox), priv->sec_label,            FALSE, FALSE, 0);
587     gtk_box_pack_start (GTK_BOX (hbox), priv->entries[ENTRY_SECS],  FALSE, FALSE, 0);
588     gtk_box_pack_start (GTK_BOX (hbox), priv->ampm_button,          FALSE, FALSE, 0);
589     gtk_misc_set_padding (GTK_MISC (priv->ampm_label), 0, 0);
590
591     gtk_container_add (GTK_CONTAINER (priv->frame), hbox);
592
593     /* Show created widgets */
594     gtk_widget_set_parent (priv->frame, GTK_WIDGET(editor));
595     gtk_widget_show_all (priv->frame);
596     gtk_widget_show_all (priv->iconbutton);
597
598     /* Update AM/PM and time separators settings from locale */
599     if (! hildon_time_editor_check_locale (editor)) {
600         /* Using 12h clock */
601         priv->clock_24h = FALSE;
602     } else {
603         gtk_widget_hide (priv->ampm_button);
604     }
605
606     if (! priv->show_seconds) {
607         gtk_widget_hide (priv->sec_label);
608         gtk_widget_hide (priv->entries[ENTRY_SECS]);
609     }
610
611     /* set the default time to current time. */
612     hildon_time_editor_set_to_current_time (editor);
613
614     gtk_widget_pop_composite_child ();
615
616 #ifdef MAEMO_GTK 
617     g_signal_connect (editor, "tap-and-hold-setup",
618                       G_CALLBACK (hildon_time_editor_tap_and_hold_setup),
619                       NULL);
620 #endif
621
622 }
623
624 static void 
625 hildon_time_editor_set_property                 (GObject *object,
626                                                  guint param_id,
627                                                  const GValue *value,
628                                                  GParamSpec *pspec)
629 {
630     HildonTimeEditor *time_editor = HILDON_TIME_EDITOR (object);
631
632     switch (param_id)
633     {
634         case PROP_TICKS:
635             hildon_time_editor_set_ticks (time_editor, g_value_get_uint(value));
636             break;
637
638         case PROP_SHOW_SECONDS:
639             hildon_time_editor_set_show_seconds (time_editor, g_value_get_boolean(value));
640             break;
641
642         case PROP_SHOW_HOURS:
643             hildon_time_editor_set_show_hours (time_editor, g_value_get_boolean(value));
644             break;
645
646         case PROP_DURATION_MODE:
647             hildon_time_editor_set_duration_mode (time_editor, g_value_get_boolean(value));
648             break;
649
650         case PROP_DURATION_MIN:
651             hildon_time_editor_set_duration_min (time_editor, g_value_get_uint(value));
652             break;
653
654         case PROP_DURATION_MAX:
655             hildon_time_editor_set_duration_max (time_editor, g_value_get_uint(value));
656             break;
657
658         default:
659             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
660             break;
661     }
662 }
663
664 static void 
665 hildon_time_editor_get_property                 (GObject *object,
666                                                  guint param_id,
667                                                  GValue *value,
668                                                  GParamSpec *pspec)
669 {
670     HildonTimeEditor *time_editor = HILDON_TIME_EDITOR (object);
671
672     switch (param_id)
673     {
674
675         case PROP_TICKS:
676             g_value_set_uint (value, hildon_time_editor_get_ticks (time_editor));
677             break;
678
679         case PROP_SHOW_SECONDS:
680             g_value_set_boolean (value, hildon_time_editor_get_show_seconds (time_editor));
681             break;
682
683         case PROP_SHOW_HOURS:
684             g_value_set_boolean (value, hildon_time_editor_get_show_hours (time_editor));
685             break;
686
687         case PROP_DURATION_MODE:
688             g_value_set_boolean (value, hildon_time_editor_get_duration_mode (time_editor));
689             break;
690
691         case PROP_DURATION_MIN:
692             g_value_set_uint (value, hildon_time_editor_get_duration_min (time_editor));
693             break;
694
695         case PROP_DURATION_MAX:
696             g_value_set_uint (value, hildon_time_editor_get_duration_max (time_editor));
697             break;
698
699         default:
700             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
701             break;
702     }
703 }
704
705 /**
706  * hildon_time_editor_new:
707  *
708  * This function creates a new time editor. 
709  *
710  * Returns: pointer to a new #HildonTimeEditor widget
711  */
712 GtkWidget*
713 hildon_time_editor_new                          (void)
714 {
715     return GTK_WIDGET (g_object_new (HILDON_TYPE_TIME_EDITOR, NULL));
716 }
717
718 static void 
719 hildon_time_editor_finalize                     (GObject *obj_self)
720 {
721     HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE (obj_self);
722     g_assert (priv);
723
724     if (priv->am_symbol) 
725             g_free (priv->am_symbol);
726
727     if (priv->pm_symbol)
728             g_free (priv->pm_symbol);
729
730     if (priv->highlight_idle)
731         g_source_remove (priv->highlight_idle);
732
733     if (G_OBJECT_CLASS (parent_class)->finalize)
734         G_OBJECT_CLASS (parent_class)->finalize (obj_self);
735 }
736
737 /**
738  * hildon_time_editor_get_time_separators:
739  * @hm_sep_label: the label that will show the hour:minutes separator
740  * @ms_sep_label: the label that will show the minutes:seconds separator
741  *
742  * Gets hour-minute separator and minute-second separator from current
743  * locale and sets then to the labels we set as parameters. Both
744  * parameters can be NULL if you just want to assing one separator.
745  *
746  */
747 void 
748 hildon_time_editor_get_time_separators          (GtkLabel *hm_sep_label,
749                                                  GtkLabel *ms_sep_label)
750 {
751     gchar buffer[256];
752     gchar *separator;
753     GDate locale_test_date;
754     gchar *iter, *endp = NULL;
755
756     /* Get localized time string */
757     g_date_set_dmy (&locale_test_date, 1, 2, 1970);
758     (void) g_date_strftime (buffer, sizeof (buffer), "%X", &locale_test_date);
759
760     /* Find h-m separator */
761     iter = buffer;
762     while (*iter && g_ascii_isdigit (*iter)) iter++;
763     /* Extract h-m separator*/
764     endp = iter;
765     while (*endp && ! g_ascii_isdigit (*endp)) endp++;
766
767     if (hm_sep_label != NULL)
768     {
769         separator = g_strndup (iter, endp - iter);
770         gtk_label_set_label (hm_sep_label, separator);
771         g_free (separator);
772     }
773
774     if (ms_sep_label != NULL)
775     {      
776         /* Find m-s separator */
777         iter = endp;
778         while (*iter && g_ascii_isdigit (*iter)) iter++;
779
780         /* Extract m-s separator*/
781         endp = iter;
782         while (*endp && ! g_ascii_isdigit (*endp)) endp++;
783         separator = g_strndup (iter, endp - iter);
784         gtk_label_set_label (ms_sep_label, separator);
785         g_free (separator);
786     }
787 }
788
789 /* Convert ticks to H:M:S. Ticks = seconds since 00:00:00. */
790 static void 
791 ticks_to_time                                   (guint ticks,
792                                                  guint *hours,
793                                                  guint *minutes,
794                                                  guint *seconds)
795 {
796     guint left;
797
798     *hours = ticks / 3600;
799     left   = ticks % 3600;
800     *minutes = left / 60;
801     *seconds = left % 60;
802 }
803
804 /**
805  * hildon_time_editor_set_ticks:
806  * @editor: the #HildonTimeEditor widget
807  * @ticks: the duration to set, in seconds
808  *
809  * Sets the current duration in seconds. This means seconds from
810  * midnight, if not in duration mode. In case of any errors, it tries
811  * to fix it.
812  */
813
814 void 
815 hildon_time_editor_set_ticks                    (HildonTimeEditor *editor,
816                                                  guint ticks)
817 {
818     HildonTimeEditorPrivate *priv;
819     guint i, h, m, s;
820     gchar str[3];
821
822     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
823
824     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
825     g_assert (priv);
826
827     /* Validate ticks. If it's too low or too high, set it to
828        min/max value for the current mode. */
829     if (priv->duration_mode)
830         priv->ticks = CLAMP (ticks, priv->duration_min, priv->duration_max);
831     else {
832         /* Check that ticks value is valid. We only need to check that hours
833            don't exceed 23. */
834         ticks_to_time (ticks, &h, &m, &s);
835         if (h > HOURS_MAX_24)
836             ticks = TICKS(HOURS_MAX_24, m, s);
837
838         priv->ticks = ticks;
839     }
840
841     /* Get the time in H:M:S. */
842     ticks_to_time (priv->ticks, &h, &m, &s);
843
844     if (!priv->clock_24h && ! priv->duration_mode)
845     {
846         /* Convert 24h H:M:S values to 12h mode, and update AM/PM state */
847         convert_to_12h (&h, &priv->am);
848     }
849
850     /* Set H:M:S values to entries. We  do not want to invoke validation
851        callbacks (since they can cause new call to this function), so we 
852        block signals while setting values. */
853     for (i = 0; i < ENTRY_COUNT; i++)
854     {
855         g_signal_handlers_block_by_func(priv->entries[i],
856                 (gpointer) hildon_time_editor_entry_changed, editor);
857
858         g_signal_handlers_block_by_func(priv->entries[i],
859                 (gpointer) hildon_time_editor_inserted_text, editor);
860
861         g_signal_handlers_block_by_func(priv->entries[i],
862                 (gpointer) hildon_time_editor_entry_focus_out, editor);
863     }
864
865     g_snprintf (str, sizeof (str), "%02u", h);
866     gtk_entry_set_text (GTK_ENTRY (priv->entries[ENTRY_HOURS]), str);
867
868     g_snprintf(str, sizeof (str), "%02u", m);
869     gtk_entry_set_text (GTK_ENTRY (priv->entries[ENTRY_MINS]), str);
870
871     g_snprintf(str, sizeof (str), "%02u", s);
872     gtk_entry_set_text (GTK_ENTRY (priv->entries[ENTRY_SECS]), str);
873
874     for (i = 0; i < ENTRY_COUNT; i++)
875     {
876         g_signal_handlers_unblock_by_func (priv->entries[i],
877                 (gpointer) hildon_time_editor_entry_changed, editor);
878
879         g_signal_handlers_unblock_by_func (priv->entries[i],
880                 (gpointer) hildon_time_editor_inserted_text, editor);
881
882         g_signal_handlers_unblock_by_func (priv->entries[i],
883                 (gpointer) hildon_time_editor_entry_focus_out, editor);
884     }
885
886     /* Update AM/PM label in case we're in 12h mode */
887     gtk_label_set_label( GTK_LABEL (priv->ampm_label),
888             priv->am ? priv->am_symbol : priv->pm_symbol);
889
890     g_object_notify (G_OBJECT (editor), "ticks");
891 }
892
893 static void
894 hildon_time_editor_set_to_current_time          (HildonTimeEditor *editor)
895 {
896     time_t now;
897     const struct tm *tm;
898
899     now = time (NULL);
900     tm = localtime (&now);
901
902     if (tm != NULL)
903         hildon_time_editor_set_time (editor, tm->tm_hour, tm->tm_min, tm->tm_sec);
904 }
905
906 /**
907  * hildon_time_editor_get_ticks:
908  * @editor: the #HildonTimeEditor widget
909  *
910  * This function returns the current duration, in seconds.
911  * This means seconds from midnight, if not in duration mode.
912  * 
913  * Returns: current duration in seconds 
914  */
915 guint 
916 hildon_time_editor_get_ticks                    (HildonTimeEditor *editor)
917 {
918     HildonTimeEditorPrivate *priv;
919
920     g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), 0);
921
922     priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
923     g_assert (priv);
924
925     return (priv->ticks);
926 }
927
928 /**
929  * hildon_time_editor_set_show_seconds:
930  * @editor: the #HildonTimeEditor
931  * @show_seconds: enable or disable showing of seconds
932  *
933  * This function shows or hides the seconds field.
934  */
935 void 
936 hildon_time_editor_set_show_seconds             (HildonTimeEditor *editor,
937                                                  gboolean show_seconds)
938 {
939     HildonTimeEditorPrivate *priv;
940
941     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
942
943     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
944     g_assert (priv);
945
946     if (show_seconds != priv->show_seconds) {
947         priv->show_seconds = show_seconds;
948
949         /* show/hide seconds field and its ':' label if the value changed. */
950         if (show_seconds) {
951             gtk_widget_show (priv->entries[ENTRY_SECS]);
952             gtk_widget_show (priv->sec_label);        
953         } else {    
954             gtk_widget_hide (priv->entries[ENTRY_SECS]);
955             gtk_widget_hide (priv->sec_label);
956         }
957
958         g_object_notify (G_OBJECT (editor), "show_seconds");
959     }
960 }
961
962 /**
963  * hildon_time_editor_get_show_seconds:
964  * @editor: the #HildonTimeEditor widget
965  *
966  * This function returns a boolean indicating the visibility of
967  * seconds in the #HildonTimeEditor
968  *
969  * Returns: TRUE if the seconds are visible 
970  */
971 gboolean 
972 hildon_time_editor_get_show_seconds             (HildonTimeEditor *editor)
973 {
974     HildonTimeEditorPrivate *priv;
975
976     g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
977     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
978     g_assert (priv);
979
980     return (priv->show_seconds);
981 }
982
983 /**
984  * hildon_time_editor_set_duration_mode:
985  * @editor: the #HildonTimeEditor
986  * @duration_mode: enable or disable duration editor mode
987  *
988  * This function sets the duration editor mode in which the maximum hours
989  * is 99.
990  */
991 void 
992 hildon_time_editor_set_duration_mode            (HildonTimeEditor *editor,
993                                                  gboolean duration_mode)
994 {
995     HildonTimeEditorPrivate *priv;
996
997     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
998
999     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1000     g_assert (priv);
1001
1002     if (duration_mode != priv->duration_mode) {
1003         priv->duration_mode = duration_mode;
1004
1005         if (duration_mode) {
1006             /* FIXME: Why do we reset the duration range here?
1007                Would change API, so won't touch this for now. */
1008             hildon_time_editor_set_duration_range (editor, MIN_DURATION, MAX_DURATION);
1009             /* There's no AM/PM label or time picker icon in duration mode.
1010                Make sure they're hidden. */
1011             gtk_widget_hide (GTK_WIDGET (priv->ampm_label));
1012             gtk_widget_hide (GTK_WIDGET (priv->ampm_button));
1013             gtk_widget_hide (GTK_WIDGET (priv->iconbutton));
1014             /* Duration mode has seconds by default. */
1015             hildon_time_editor_set_show_seconds (editor, TRUE);
1016         } else {
1017             /* Make sure AM/PM label and time picker icons are visible if needed */
1018             if (! priv->clock_24h)
1019                 gtk_widget_show (GTK_WIDGET (priv->ampm_label));
1020
1021             gtk_widget_show (GTK_WIDGET (priv->ampm_button));
1022             gtk_widget_show (GTK_WIDGET (priv->iconbutton));        
1023
1024             /* Reset the ticks to current time. Anything set in duration mode
1025              * is bound to be invalid or useless in time mode.
1026              */
1027             hildon_time_editor_set_to_current_time (editor);
1028         }
1029
1030         g_object_notify (G_OBJECT (editor), "duration_mode");
1031     }
1032 }
1033
1034 /**
1035  * hildon_time_editor_get_duration_mode:
1036  * @editor: the #HildonTimeEditor widget
1037  *
1038  * This function returns a boolean indicating whether the #HildonTimeEditor
1039  * is in the duration mode.
1040  * 
1041  * Returns: TRUE if the #HildonTimeEditor is in duration mode 
1042  */
1043 gboolean 
1044 hildon_time_editor_get_duration_mode            (HildonTimeEditor *editor)
1045 {
1046     HildonTimeEditorPrivate *priv;
1047
1048     g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
1049     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1050     g_assert (priv);
1051
1052     return (priv->duration_mode);
1053 }
1054
1055 /**
1056  * hildon_time_editor_set_duration_min:
1057  * @editor: the #HildonTimeEditor widget
1058  * @duration_min: mimimum allowed duration
1059  *
1060  * Sets the minimum allowed duration for the duration mode.
1061  * Note: Has no effect in time mode
1062  */
1063 void 
1064 hildon_time_editor_set_duration_min             (HildonTimeEditor *editor,
1065                                                  guint duration_min)
1066 {
1067     HildonTimeEditorPrivate *priv;
1068
1069     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1070     g_return_if_fail (duration_min >= MIN_DURATION);
1071
1072     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1073     g_assert (priv);
1074
1075     if (! priv->duration_mode )
1076         return;
1077
1078     priv->duration_min = duration_min;
1079
1080     /* Clamp the current value to the minimum if necessary */
1081     if (priv->ticks < duration_min)
1082     {
1083         hildon_time_editor_set_ticks (editor, duration_min);
1084     }
1085
1086     g_object_notify (G_OBJECT (editor), "duration_min");
1087 }
1088
1089 /**
1090  * hildon_time_editor_get_duration_min:
1091  * @editor: the #HildonTimeEditor widget
1092  *
1093  * This function returns the smallest duration the #HildonTimeEditor
1094  * allows in the duration mode.
1095  * 
1096  * Returns: minimum allowed duration in seconds 
1097  */
1098 guint 
1099 hildon_time_editor_get_duration_min             (HildonTimeEditor *editor)
1100 {
1101     HildonTimeEditorPrivate *priv;
1102
1103     g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), 0);
1104
1105     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1106     g_assert (priv);
1107
1108     if(! priv->duration_mode )
1109         return (0);
1110
1111     return (priv->duration_min);
1112 }
1113
1114 /**
1115  * hildon_time_editor_set_duration_max:
1116  * @editor: the #HildonTimeEditor widget
1117  * @duration_max: maximum allowed duration in seconds
1118  *
1119  * Sets the maximum allowed duration in seconds for the duration mode.
1120  * Note: Has no effect in time mode
1121  */
1122 void 
1123 hildon_time_editor_set_duration_max             (HildonTimeEditor *editor,
1124                                                  guint duration_max)
1125 {
1126     HildonTimeEditorPrivate *priv;
1127
1128     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1129     g_return_if_fail (duration_max <= MAX_DURATION);
1130
1131     priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1132     g_assert (priv);
1133
1134     if (! priv->duration_mode)
1135         return;
1136
1137     priv->duration_max = duration_max;
1138
1139     /* Clamp the current value to the maximum if necessary */
1140     if (priv->ticks > duration_max)
1141     {
1142         hildon_time_editor_set_ticks (editor, duration_max);
1143     }
1144
1145     g_object_notify (G_OBJECT (editor), "duration_max");
1146 }
1147
1148 /**
1149  * hildon_time_editor_get_duration_max:
1150  * @editor: the #HildonTimeEditor widget
1151  *
1152  * This function returns the longest duration the #HildonTimeEditor
1153  * allows in the duration mode.
1154  * 
1155  * Returns: maximum allowed duration in seconds 
1156  */
1157 guint 
1158 hildon_time_editor_get_duration_max             (HildonTimeEditor * editor)
1159 {
1160     HildonTimeEditorPrivate *priv;
1161
1162     g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), 0);
1163
1164     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1165     g_assert (priv);
1166
1167     if (! priv->duration_mode)
1168         return (0);
1169
1170     return (priv->duration_max);
1171 }
1172
1173 /**
1174  * hildon_time_editor_set_time:
1175  * @editor: the #HildonTimeEditor widget
1176  * @hours: hours
1177  * @minutes: minutes
1178  * @seconds: seconds
1179  *
1180  * This function sets the time on an existing time editor. If the
1181  * time specified by the arguments is invalid, it's fixed.
1182  * The time is assumed to be in 24h format.
1183  */
1184 void 
1185 hildon_time_editor_set_time                     (HildonTimeEditor *editor, 
1186                                                  guint hours,
1187                                                  guint minutes, 
1188                                                  guint seconds)
1189 {
1190     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1191
1192     hildon_time_editor_set_ticks (editor, TICKS(hours, minutes, seconds));
1193 }
1194
1195 /**
1196  * hildon_time_editor_get_time:
1197  * @editor: the #HildonTimeEditor widget
1198  * @hours: hours
1199  * @minutes: minutes
1200  * @seconds: seconds
1201  *
1202  * Gets the time of the #HildonTimeEditor widget. The time returned is
1203  * always in 24h format.
1204  */
1205 void 
1206 hildon_time_editor_get_time                     (HildonTimeEditor *editor,
1207                                                  guint *hours,
1208                                                  guint *minutes, 
1209                                                  guint *seconds)
1210 {
1211     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1212
1213     ticks_to_time (hildon_time_editor_get_ticks (editor), hours, minutes, seconds);
1214 }
1215
1216 /**
1217  * hildon_time_editor_set_duration_range:
1218  * @editor: the #HildonTimeEditor widget
1219  * @min_seconds: minimum allowed time in seconds
1220  * @max_seconds: maximum allowed time in seconds
1221  *
1222  * Sets the duration editor time range of the #HildonTimeEditor widget.
1223  */
1224 void 
1225 hildon_time_editor_set_duration_range           (HildonTimeEditor *editor,
1226                                                  guint min_seconds,
1227                                                  guint max_seconds)
1228 {
1229     HildonTimeEditorPrivate *priv;
1230     guint tmp;
1231
1232     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1233
1234     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1235     g_assert (priv);
1236
1237     /* Swap values if reversed */
1238     if (min_seconds > max_seconds)
1239     {
1240         tmp = max_seconds;
1241         max_seconds = min_seconds;
1242         min_seconds = tmp;
1243     }
1244
1245     hildon_time_editor_set_duration_max (editor, max_seconds);
1246     hildon_time_editor_set_duration_min (editor, min_seconds);
1247
1248     if (priv->duration_mode) {
1249         /* Set minimum allowed value for duration editor.
1250            FIXME: Shouldn't it be changed only if it's not in range?
1251            Would change API, so won't touch this for now. */
1252         hildon_time_editor_set_ticks (editor, min_seconds);
1253     }
1254 }
1255
1256 /**
1257  * hildon_time_editor_get_duration_range:
1258  * @editor: the #HildonTimeEditor widget
1259  * @min_seconds: pointer to guint
1260  * @max_seconds: pointer to guint
1261  *
1262  * Gets the duration editor time range of the #HildonTimeEditor widget.
1263  */
1264 void 
1265 hildon_time_editor_get_duration_range           (HildonTimeEditor *editor,
1266                                                  guint *min_seconds,
1267                                                  guint *max_seconds)
1268 {
1269     HildonTimeEditorPrivate *priv;
1270
1271     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1272
1273     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1274     g_assert (priv);
1275
1276     *min_seconds = priv->duration_min;
1277     *max_seconds = priv->duration_max;
1278 }
1279
1280 static gboolean 
1281 hildon_time_editor_check_locale                 (HildonTimeEditor *editor)
1282 {
1283     HildonTimeEditorPrivate *priv;
1284
1285     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1286     g_assert (priv);
1287
1288     /* Update time separator symbols */
1289     hildon_time_editor_get_time_separators (GTK_LABEL (priv->hm_label), GTK_LABEL (priv->sec_label));
1290
1291     /* Get AM/PM symbols. */
1292     priv->am_symbol = g_strdup (nl_langinfo (AM_STR));
1293     priv->pm_symbol = g_strdup (nl_langinfo (PM_STR));
1294
1295     if (priv->am_symbol[0] == '\0')
1296         return TRUE;
1297     else {
1298         /* 12h clock mode. Check if AM/PM should be before or after time.
1299            %p is the AM/PM string, so we assume that if the format string
1300            begins with %p it's in the beginning, and in any other case it's
1301            in the end (although that's not necessarily the case). */
1302         if (strncmp (nl_langinfo (T_FMT_AMPM), "%p", 2) == 0)
1303             priv->ampm_pos_after = FALSE;
1304         return FALSE;
1305     }
1306 }
1307
1308 static gboolean
1309 hildon_time_editor_entry_focus_in               (GtkWidget *widget,
1310                                                  GdkEventFocus *event, 
1311                                                  gpointer data)
1312 {
1313     g_idle_add ((GSourceFunc) hildon_time_editor_entry_select_all,
1314             GTK_ENTRY (widget));
1315
1316     return FALSE;
1317 }
1318
1319 static gboolean 
1320 hildon_time_editor_time_error                   (HildonTimeEditor *editor,
1321                                                  HildonDateTimeError type)
1322 {
1323     return TRUE;
1324 }
1325
1326 /* Returns negative if we didn't get value,
1327  * and should stop further validation 
1328  */
1329 static gint 
1330 validated_conversion                            (HildonTimeEditorPrivate *priv,
1331                                                  GtkWidget *field,
1332                                                  gint min,
1333                                                  gint max,
1334                                                  gint def_value,
1335                                                  gboolean allow_intermediate,
1336                                                  guint *error_code,
1337                                                  GString *error_string)
1338 {
1339     const gchar *text;
1340     gchar *tail;
1341     long value;
1342
1343     text = gtk_entry_get_text (GTK_ENTRY (field));
1344
1345     if (text && text[0])
1346     {
1347         /* Try to convert entry text to number */
1348         value = strtol (text, &tail, 10);
1349
1350         /* Check if conversion succeeded */
1351         if ((tail[0] == 0) && !(text[0] == '-'))
1352         {    
1353             if (value > max) {
1354                 g_string_printf (error_string, _("ckct_ib_maximum_value"), max);
1355                 priv->error_widget = field;
1356                 *error_code = MAX_VALUE;
1357                 return max;
1358             }
1359
1360             if (value < min && !allow_intermediate) {
1361                 g_string_printf (error_string, _("ckct_ib_minimum_value"), min);
1362                 priv->error_widget = field;
1363                 *error_code = MIN_VALUE;
1364                 return min;
1365             }
1366
1367             return value;
1368         }
1369
1370         /* We'll handle failed conversions soon */
1371         else
1372         {
1373             if ((tail[0] == '-') || (text[0] == '-'))
1374             {
1375                 g_string_printf (error_string, _("ckct_ib_minimum_value"), min);
1376                 priv->error_widget = field;
1377                 *error_code = MIN_VALUE;
1378                 return min;
1379             }
1380         }
1381     }
1382     else if (allow_intermediate) 
1383         return -1;  /* Empty field while user is still editing. No error, but
1384                        cannot validate either... */
1385     else /* Empty field: show error and set value to minimum allowed */
1386     {
1387         g_string_printf (error_string, _("ckct_ib_set_a_value_within_range"), min, max);
1388         priv->error_widget = field;
1389         *error_code = WITHIN_RANGE;
1390         return def_value;
1391     }
1392
1393     /* Empty field and not allowed intermediated OR failed conversion */
1394     g_string_printf (error_string, _("ckct_ib_set_a_value_within_range"), min, max);
1395     priv->error_widget = field;
1396     *error_code = WITHIN_RANGE;
1397     return -1;
1398 }
1399
1400 static void
1401 hildon_time_editor_real_validate                (HildonTimeEditor *editor, 
1402                                                  gboolean allow_intermediate, 
1403                                                  GString *error_string)
1404 {
1405     HildonTimeEditorPrivate *priv;
1406     guint h, m, s, ticks;
1407     guint error_code;
1408     guint max_hours, min_hours, def_hours;
1409     guint max_minutes, min_minutes, def_minutes;
1410     guint max_seconds, min_seconds, def_seconds;
1411     gboolean r;
1412
1413     g_assert (HILDON_IS_TIME_EDITOR (editor));
1414
1415     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1416     g_assert (priv);
1417
1418     /* Find limits for field based validation. */
1419     if (priv->duration_mode)
1420     {
1421         ticks_to_time (priv->duration_min, &min_hours, &min_minutes, &min_seconds);
1422         ticks_to_time (priv->duration_max, &max_hours, &max_minutes, &max_seconds);
1423     } else {
1424         if (priv->clock_24h) {
1425             max_hours = HOURS_MAX_24;
1426             min_hours = HOURS_MIN_24;
1427         } else {
1428             max_hours = HOURS_MAX_12;
1429             min_hours = HOURS_MIN_12;
1430         }
1431     }
1432
1433     hildon_time_editor_get_time (editor, &def_hours, &def_minutes, &def_seconds);
1434
1435     /* Get time components from fields and validate them... */
1436     if (priv->show_hours) {
1437         h = validated_conversion (priv, priv->entries[ENTRY_HOURS], min_hours, max_hours, def_hours,
1438                 allow_intermediate, &error_code, error_string);
1439         if (priv->error_widget == priv->entries[ENTRY_HOURS])
1440             g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, hour_errors[error_code], &r);
1441         if ((gint) h < 0) return;
1442     }
1443     else h = 0;
1444     m = validated_conversion (priv, priv->entries[ENTRY_MINS], MINUTES_MIN, MINUTES_MAX, def_minutes,
1445             allow_intermediate, &error_code, error_string);
1446     if (priv->error_widget == priv->entries[ENTRY_MINS])
1447         g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, min_errors[error_code], &r);
1448     if ((gint) m < 0) return;
1449     if (priv->show_seconds) {
1450         s = validated_conversion (priv, priv->entries[ENTRY_SECS], SECONDS_MIN, SECONDS_MAX, def_seconds,
1451                 allow_intermediate, &error_code, error_string);
1452         if (priv->error_widget == priv->entries[ENTRY_SECS])
1453             g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, sec_errors[error_code], &r);
1454         if ((gint) s < 0) return;
1455     } 
1456     else s = 0;
1457
1458     /* Ok, we now do separate check that tick count is valid for duration mode */      
1459     if (priv->duration_mode)
1460     {          
1461         ticks = TICKS(h, m, s);
1462
1463         if (ticks < priv->duration_min && !allow_intermediate)
1464         {
1465             g_string_printf (error_string,
1466                     _("ckct_ib_min_allowed_duration_hts"), 
1467                     min_hours, min_minutes, min_seconds);
1468             hildon_time_editor_set_ticks (editor, priv->duration_min);
1469             priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
1470             g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, HILDON_DATE_TIME_ERROR_MIN_DURATION, &r);
1471             return;
1472         }
1473         else if (ticks > priv->duration_max)
1474         {
1475             g_string_printf (error_string,
1476                     _("ckct_ib_max_allowed_duration_hts"), 
1477                     max_hours, max_minutes, max_seconds);
1478             hildon_time_editor_set_ticks (editor, priv->duration_max);
1479             priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
1480             g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, HILDON_DATE_TIME_ERROR_MAX_DURATION, &r);
1481             return;
1482         }
1483     }
1484     else if (! priv->clock_24h)
1485         convert_to_24h (&h, priv->am);
1486
1487     /* The only case when we do not want to refresh the
1488        time display, is when the user is editing a value 
1489        (unless the value was out of bounds and we have to fix it) */
1490     if (! allow_intermediate || priv->error_widget)
1491         hildon_time_editor_set_time (editor, h, m, s);
1492 }
1493
1494 /* Setting text to entries causes entry to recompute itself
1495    in idle callback, which remove selection. Because of this
1496    we need to do selection in idle as well. */
1497 static gboolean 
1498 highlight_callback                              (gpointer data)
1499 {
1500     HildonTimeEditorPrivate *priv;
1501     GtkWidget *widget;
1502     gint i;
1503
1504     g_assert (HILDON_IS_TIME_EDITOR (data));
1505     priv = HILDON_TIME_EDITOR_GET_PRIVATE (data);
1506     g_assert (priv);
1507
1508     GDK_THREADS_ENTER ();
1509
1510     widget = priv->error_widget;
1511     priv->error_widget = NULL;
1512
1513     if (GTK_IS_WIDGET (widget) == FALSE)
1514         goto Done;
1515
1516     /* Avoid revalidation because it will issue the date_error signal
1517        twice when there is an empty field. We must block the signal
1518        for all the entries because we do not know where the focus
1519        comes from */
1520     for (i = 0; i < ENTRY_COUNT; i++)
1521         g_signal_handlers_block_by_func (priv->entries[i],
1522                 (gpointer) hildon_time_editor_entry_focus_out, data);
1523     gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1524     gtk_widget_grab_focus (widget);
1525     for (i = 0; i < ENTRY_COUNT; i++)
1526         g_signal_handlers_unblock_by_func (priv->entries[i],
1527                 (gpointer) hildon_time_editor_entry_focus_out, data);
1528
1529 Done:
1530     priv->highlight_idle = 0;
1531     GDK_THREADS_LEAVE ();
1532
1533     return FALSE;
1534 }
1535
1536 /* Update ticks from current H:M:S entries. If they're invalid, show an
1537    infoprint and update the fields unless they're empty. */
1538 static void
1539 hildon_time_editor_validate                     (HildonTimeEditor *editor, 
1540                                                  gboolean allow_intermediate)
1541 {
1542     HildonTimeEditorPrivate *priv;
1543     GString *error_message;
1544
1545     g_assert (HILDON_IS_TIME_EDITOR(editor));
1546     priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1547     g_assert (priv);
1548
1549     /* if there is already an error we do nothing until it will be managed by the idle */
1550     if (priv->highlight_idle == 0 && priv->skipper == FALSE)
1551     {
1552         priv->skipper = TRUE;
1553         error_message = g_string_new (NULL);
1554         hildon_time_editor_real_validate (editor, 
1555                 allow_intermediate, error_message);
1556
1557         if (priv->error_widget) 
1558         {
1559             hildon_banner_show_information (priv->error_widget, NULL,
1560                     error_message->str);
1561
1562             priv->highlight_idle = g_idle_add (highlight_callback, editor);
1563         }
1564
1565         priv->skipper = FALSE;
1566         g_string_free (error_message, TRUE);
1567     }
1568 }
1569
1570 /* on inserted text, if entry has two digits, jumps to the next field. */
1571 static void
1572 hildon_time_editor_inserted_text                (GtkEditable *editable,
1573                                                  gchar *new_text,
1574                                                  gint new_text_length,
1575                                                  gint *position,
1576                                                  gpointer user_data) 
1577 {
1578     HildonTimeEditor *editor;
1579     GtkEntry *entry;
1580     gchar *value;
1581     HildonTimeEditorPrivate *priv;
1582
1583     entry = GTK_ENTRY (editable);
1584     editor = HILDON_TIME_EDITOR (user_data);
1585
1586     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1587     g_assert (priv);
1588
1589     /* if there is already an error we don't have to do anything */ 
1590     if (! priv->error_widget)
1591     {
1592         value = (gchar *) gtk_entry_get_text (entry);
1593
1594         if (strlen (value) == 2)
1595         {
1596             if (GTK_WIDGET (editable) == priv->entries[ENTRY_HOURS]) 
1597             {
1598                 /* We already checked the input in changed signal, but 
1599                  * now we will re-check it again in focus-out we 
1600                  * intermediate flag set to FALSE */
1601                 gtk_widget_grab_focus (priv->entries[ENTRY_MINS]);
1602                 *position = -1;
1603             }
1604             else if (GTK_WIDGET (editable) == priv->entries[ENTRY_MINS] &&
1605                     GTK_WIDGET_VISIBLE (priv->entries[ENTRY_SECS])) 
1606             {
1607                 /* See above */
1608                 gtk_widget_grab_focus (priv->entries[ENTRY_SECS]);
1609                 *position = -1;
1610             }
1611         }
1612     }   
1613 }
1614
1615 static gboolean 
1616 hildon_time_editor_entry_focus_out              (GtkWidget *widget,
1617                                                  GdkEventFocus *event,
1618                                                  gpointer data)
1619 {
1620     g_assert (HILDON_IS_TIME_EDITOR (data));
1621
1622     /* Validate the given time and update ticks. */
1623     hildon_time_editor_validate (HILDON_TIME_EDITOR (data), FALSE);
1624
1625     return FALSE;
1626 }
1627
1628 static gboolean
1629 hildon_time_editor_ampm_clicked                 (GtkWidget *widget,
1630                                                  gpointer data)
1631 {
1632     HildonTimeEditor *editor;
1633     HildonTimeEditorPrivate *priv;
1634
1635     g_assert (GTK_IS_WIDGET (widget));
1636     g_assert (HILDON_IS_TIME_EDITOR (data));
1637
1638     editor = HILDON_TIME_EDITOR (data);
1639     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1640     g_assert (priv);
1641
1642     /* First validate the given time and update ticks. */
1643     hildon_time_editor_validate (editor, FALSE);
1644
1645     /* Apply the AM/PM change by moving the current time by 12 hours */
1646     if (priv->am) {
1647         /* 00:00 .. 11:59 -> 12:00 .. 23:59 */
1648         hildon_time_editor_set_ticks (editor, priv->ticks + 12 * 3600);
1649     } else {
1650         /* 12:00 .. 23:59 -> 00:00 .. 11:59 */
1651         hildon_time_editor_set_ticks (editor, priv->ticks - 12 * 3600);
1652     }
1653
1654     return FALSE;
1655 }
1656
1657 static gboolean
1658 hildon_time_editor_icon_clicked                 (GtkWidget *widget, 
1659                                                  gpointer data)
1660 {
1661     HildonTimeEditor *editor;
1662     GtkWidget *picker;
1663     GtkWidget *parent;
1664     guint h, m, s, result;
1665     HildonTimeEditorPrivate *priv;
1666
1667     g_assert (HILDON_IS_TIME_EDITOR (data));
1668
1669     editor = HILDON_TIME_EDITOR (data);
1670     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1671     g_assert (priv);
1672
1673     /* icon is passive in duration editor mode */
1674     if (hildon_time_editor_get_duration_mode (editor))
1675         return FALSE;
1676
1677     /* Validate and do not launch if broken */
1678     hildon_time_editor_validate (HILDON_TIME_EDITOR (data), FALSE);
1679     if (priv->error_widget != NULL)
1680         return FALSE;
1681
1682     /* Launch HildonTimePicker dialog */
1683     parent = gtk_widget_get_ancestor (GTK_WIDGET (editor), GTK_TYPE_WINDOW);
1684     picker = hildon_time_picker_new (GTK_WINDOW (parent));
1685
1686     hildon_time_editor_get_time (editor, &h, &m, &s);
1687     hildon_time_picker_set_time (HILDON_TIME_PICKER (picker), h, m);
1688
1689     result = gtk_dialog_run (GTK_DIALOG (picker));
1690     switch (result) {
1691
1692         case GTK_RESPONSE_OK:
1693         case GTK_RESPONSE_ACCEPT:
1694             /* Use the selected time */
1695             hildon_time_picker_get_time (HILDON_TIME_PICKER (picker), &h, &m);
1696             hildon_time_editor_set_time (editor, h, m, 0);
1697             break;
1698
1699         default:
1700             break;
1701     }
1702
1703     gtk_widget_destroy (picker);
1704     return FALSE;
1705 }
1706
1707 static void 
1708 hildon_time_editor_size_request                 (GtkWidget *widget,
1709                                                  GtkRequisition *requisition)
1710 {
1711     HildonTimeEditor *editor;
1712     HildonTimeEditorPrivate *priv;
1713     GtkRequisition req;
1714
1715     editor = HILDON_TIME_EDITOR (widget);
1716     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1717
1718     /* Get frame's size */
1719     gtk_widget_size_request (priv->frame, requisition);
1720
1721     if (GTK_WIDGET_VISIBLE (priv->iconbutton))
1722     {
1723         gtk_widget_size_request (priv->iconbutton, &req);
1724         /* Reserve space for icon */
1725         requisition->width += req.width + ICON_PRESSED +
1726             HILDON_MARGIN_DEFAULT;
1727     }
1728
1729     /* FIXME: It's evil to use hardcoded TIME_EDITOR_HEIGHT. For now we'll
1730        want to force this since themes might have varying thickness values
1731        which cause the height to change. */
1732     requisition->height = TIME_EDITOR_HEIGHT;
1733 }
1734
1735 static void 
1736 hildon_time_editor_size_allocate                (GtkWidget *widget,
1737                                                  GtkAllocation *allocation)
1738 {
1739     HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE (widget);
1740     GtkAllocation alloc;
1741     GtkRequisition req, max_req;
1742     gboolean rtl;
1743
1744     g_assert (priv);
1745
1746     rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1747     widget->allocation = *allocation;
1748     gtk_widget_get_child_requisition (widget, &max_req);
1749
1750     /* Center horizontally */
1751     alloc.x = allocation->x + MAX (allocation->width - max_req.width, 0) / 2;
1752     /* Center vertically */
1753     alloc.y = allocation->y + MAX (allocation->height - max_req.height, 0) / 2;
1754
1755     /* allocate frame */
1756     if (rtl)
1757         gtk_widget_get_child_requisition (priv->iconbutton, &req);
1758     else
1759         gtk_widget_get_child_requisition (priv->frame, &req);
1760
1761     alloc.width = req.width;
1762     alloc.height = max_req.height;
1763     if (rtl)
1764         gtk_widget_size_allocate (priv->iconbutton, &alloc);
1765     else
1766         gtk_widget_size_allocate (priv->frame, &alloc);
1767
1768     /* allocate icon */
1769     if (GTK_WIDGET_VISIBLE (priv->iconbutton)) {
1770         if (rtl)
1771             gtk_widget_get_child_requisition (priv->frame, &req);
1772         else
1773             gtk_widget_get_child_requisition (priv->iconbutton, &req);
1774
1775         alloc.x += alloc.width + HILDON_MARGIN_DEFAULT;
1776         alloc.width = req.width;
1777
1778         if (rtl)
1779             gtk_widget_size_allocate (priv->frame, &alloc);
1780         else
1781             gtk_widget_size_allocate (priv->iconbutton, &alloc);        
1782     }
1783
1784     /* FIXME: ugly way to move labels up. They just don't seem move up
1785        otherwise. This is likely because we force the editor to be
1786        smaller than it otherwise would be. */
1787     alloc = priv->ampm_label->allocation;
1788     alloc.y = allocation->y - 2;
1789     alloc.height = max_req.height + 2;
1790     gtk_widget_size_allocate (priv->ampm_label, &alloc);
1791
1792     alloc = priv->hm_label->allocation;
1793     alloc.y = allocation->y - 2;
1794     alloc.height = max_req.height + 2;
1795     gtk_widget_size_allocate (priv->hm_label, &alloc);
1796
1797     alloc = priv->sec_label->allocation;
1798     alloc.y = allocation->y - 2;
1799     alloc.height = max_req.height + 2;
1800     gtk_widget_size_allocate (priv->sec_label, &alloc);
1801 }
1802
1803 static gboolean
1804 hildon_time_editor_focus                      (GtkWidget *widget,
1805                                                GtkDirectionType direction)
1806 {
1807   gboolean retval;
1808   GtkDirectionType effective_direction;
1809
1810   g_assert (HILDON_IS_TIME_EDITOR (widget));
1811
1812   retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1813
1814   if (retval == TRUE)
1815     return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1816   else
1817     return FALSE;
1818 }
1819
1820 static gboolean
1821 hildon_time_editor_entry_keypress (GtkEntry *entry,
1822                                    GdkEventKey *event,
1823                                    gpointer data)
1824 {
1825   switch (event->keyval)
1826     {
1827     case GDK_Return:
1828     case GDK_ISO_Enter:
1829       hildon_time_editor_icon_clicked (GTK_WIDGET (entry), data);
1830       return TRUE;
1831     default:
1832       return FALSE;
1833     }
1834
1835   g_assert_not_reached ();
1836 }
1837
1838 static void
1839 convert_to_12h                                  (guint *h, 
1840                                                  gboolean *am)
1841 {
1842     g_assert (0 <= *h && *h < 24);
1843
1844     /* 00:00 to 00:59  add 12 hours      */
1845     /* 01:00 to 11:59  straight to am    */
1846     /* 12:00 to 12:59  straight to pm    */
1847     /* 13:00 to 23:59  subtract 12 hours */
1848
1849     if      (       *h == 0       ) { *am = TRUE;  *h += 12;}
1850     else if (  1 <= *h && *h < 12 ) { *am = TRUE;           }
1851     else if ( 12 <= *h && *h < 13 ) { *am = FALSE;          }
1852     else                            { *am = FALSE; *h -= 12;}
1853 }
1854
1855 static void
1856 convert_to_24h                                  (guint *h, 
1857                                                  gboolean am)
1858 {
1859     if (*h == 12 && am) /* 12 midnight - 12:59 AM  subtract 12 hours  */
1860     {
1861         *h -= 12;
1862     }
1863
1864     else if (! am && 1 <= *h && *h < 12)    /* 1:00 PM - 11:59 AM   add 12 hours */
1865     {
1866         *h += 12;
1867     }
1868 }
1869
1870 /**
1871  * hildon_time_editor_set_show_hours:
1872  * @editor: The #HildonTimeEditor.
1873  * @show_hours: Enable or disable showing of hours.
1874  *
1875  * This function shows or hides the hours field.
1876  *
1877  **/
1878 void 
1879 hildon_time_editor_set_show_hours               (HildonTimeEditor *editor,
1880                                                  gboolean show_hours)
1881 {
1882     HildonTimeEditorPrivate *priv;
1883
1884     g_return_if_fail (HILDON_IS_TIME_EDITOR (editor));
1885
1886     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1887     g_assert (priv);
1888
1889     if (show_hours != priv->show_hours) {
1890         priv->show_hours = show_hours;
1891
1892         /* show/hide hours field and its ':' label if the value changed. */
1893         if (show_hours) {
1894             gtk_widget_show (priv->entries[ENTRY_HOURS]);
1895             gtk_widget_show (priv->hm_label);        
1896         } else {    
1897             gtk_widget_hide (priv->entries[ENTRY_HOURS]);
1898             gtk_widget_hide (priv->hm_label);
1899         }
1900
1901         g_object_notify (G_OBJECT (editor), "show_hours");
1902     }
1903 }
1904
1905 /**
1906  * hildon_time_editor_get_show_hours:
1907  * @editor: the @HildonTimeEditor widget.
1908  *
1909  * This function returns a boolean indicating the visibility of
1910  * hours in the @HildonTimeEditor
1911  *
1912  * Return value: TRUE if hours are visible. 
1913  *
1914  **/
1915 gboolean 
1916 hildon_time_editor_get_show_hours               (HildonTimeEditor *editor)
1917 {
1918     HildonTimeEditorPrivate *priv;
1919
1920     g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
1921     priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1922     g_assert (priv);
1923
1924     return priv->show_hours;
1925 }
1926
1927 /* Idle callback */
1928 static gboolean
1929 hildon_time_editor_entry_select_all             (GtkWidget *widget)
1930 {
1931     GDK_THREADS_ENTER ();
1932     gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1933     GDK_THREADS_LEAVE ();
1934
1935     return FALSE;
1936 }