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