2 * This file is part of hildon-libs
4 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
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
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.
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
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
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.
39 #include "hildon-time-editor.h"
40 #include <gtk/gtkhbox.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkbutton.h>
43 #include <gtk/gtklabel.h>
44 #include <gtk/gtkframe.h>
45 #include <gdk/gdkkeysyms.h>
51 #include "hildon-defines.h"
52 #include "hildon-time-picker.h"
53 #include "hildon-banner.h"
54 #include "hildon-input-mode-hint.h"
55 #include "hildon-private.h"
56 #include "hildon-composite-widget.h"
57 #include "hildon-marshalers.h"
58 #include "hildon-enum-types.h"
60 #define _(String) dgettext(PACKAGE, String)
62 #define HILDON_TIME_EDITOR_GET_PRIVATE(obj) \
63 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
64 HILDON_TYPE_TIME_EDITOR, HildonTimeEditorPrivate));
66 #define TICKS(h,m,s) ((h) * 3600 + (m) * 60 + (s))
68 #define TIME_EDITOR_HEIGHT 30
69 #define ICON_PRESSED 4
70 #define ICON_NAME "qgn_widg_timedit"
71 #define ICON_SIZE "timepicker-size"
72 #define MIN_DURATION 0
73 #define MAX_DURATION TICKS(99, 59, 59)
75 /* Default values for properties */
76 #define HILDON_TIME_EDITOR_TICKS_VALUE 0
77 #define HILDON_TIME_EDITOR_DURATION_MODE FALSE
78 #define HILDON_TIME_EDITOR_DURATION_LOWER_VALUE 0
79 #define HILDON_TIME_EDITOR_DURATION_UPPER_VALUE TICKS(99, 59, 59)
81 #define HOURS_MAX_24 23
82 #define HOURS_MAX_12 12
83 #define HOURS_MIN_24 0
84 #define HOURS_MIN_12 1
85 #define MINUTES_MAX 59
86 #define SECONDS_MAX 59
90 static GtkContainerClass *parent_class;
92 typedef struct _HildonTimeEditorPrivate HildonTimeEditorPrivate;
104 /* Indices for h/m/s entries in priv->entries */
119 /* Error codes categories */
127 static guint time_editor_signals[LAST_SIGNAL] = { 0 };
128 static guint hour_errors[NUM_ERROR_CODES] = { HILDON_DATE_TIME_ERROR_MAX_HOURS, HILDON_DATE_TIME_ERROR_MIN_HOURS, HILDON_DATE_TIME_ERROR_EMPTY_HOURS };
129 static guint min_errors[NUM_ERROR_CODES] = { HILDON_DATE_TIME_ERROR_MAX_MINS, HILDON_DATE_TIME_ERROR_MIN_MINS, HILDON_DATE_TIME_ERROR_EMPTY_MINS };
130 static guint sec_errors[NUM_ERROR_CODES] = { HILDON_DATE_TIME_ERROR_MAX_SECS, HILDON_DATE_TIME_ERROR_MIN_SECS, HILDON_DATE_TIME_ERROR_EMPTY_SECS };
132 struct _HildonTimeEditorPrivate {
133 guint ticks; /* Current duration in seconds */
138 GtkWidget *iconbutton; /* button for icon */
140 GtkWidget *frame; /* frame around the entries */
141 GtkWidget *entries[ENTRY_COUNT]; /* h, m, s entries */
142 GtkWidget *hm_label; /* between hour and minute */
143 GtkWidget *sec_label; /* between minute and second */
144 GtkWidget *ampm_label; /* label for showing am or pm */
146 GtkWidget *error_widget; /* field to highlight in idle */
147 GtkWidget *ampm_button; /* am/pm change button */
150 gboolean duration_mode; /* In HildonDurationEditor mode */
151 gboolean show_seconds; /* show seconds */
152 gboolean show_hours; /* show hours */
154 gboolean ampm_pos_after; /* is am/pm shown after others */
155 gboolean clock_24h; /* whether to show a 24h clock */
156 gboolean am; /* TRUE == am, FALSE == pm */
158 guint duration_min; /* duration editor ranges */
159 guint duration_max; /* duration editor ranges */
161 guint highlight_idle;
162 gboolean skipper; /* FIXME (MDK): To prevent us from looping inside the validation events.
163 When set to TRUE further validations (that can happen from-inside other validations)
164 are being skipped. Nasty hack to cope with a bad design. */
171 static void hildon_time_editor_class_init (HildonTimeEditorClass *editor_class);
172 static void hildon_time_editor_init (HildonTimeEditor *editor);
174 static void hildon_time_editor_finalize (GObject *obj_self);
176 static void hildon_time_editor_set_property(GObject *object,
181 static void hildon_time_editor_get_property(GObject *object,
186 static void hildon_time_editor_forall(GtkContainer *container,
187 gboolean include_internals,
188 GtkCallback callback,
189 gpointer callback_data);
191 static void hildon_time_editor_destroy(GtkObject * self);
197 static gboolean hildon_time_editor_entry_focusout(GtkWidget *widget,
198 GdkEventFocus *event,
201 static gboolean hildon_time_editor_entry_focusin(GtkWidget *widget,
202 GdkEventFocus *event,
205 static gboolean hildon_time_editor_time_error(HildonTimeEditor *editor,
206 HildonDateTimeEditorError type);
208 static gboolean hildon_time_editor_ampm_clicked(GtkWidget *widget,
211 static gboolean hildon_time_editor_icon_clicked(GtkWidget *widget,
214 static void hildon_time_editor_size_request(GtkWidget *widget,
215 GtkRequisition *requisition);
217 static void hildon_time_editor_size_allocate(GtkWidget *widget,
218 GtkAllocation *allocation);
220 static gboolean hildon_time_editor_entry_keypress(GtkWidget *widget,
228 static gboolean hildon_time_editor_check_locale(HildonTimeEditor * editor);
231 void hildon_time_editor_tap_and_hold_setup(GtkWidget * widget,
234 GtkWidgetTapAndHoldFlags flags);
236 hildon_time_editor_validate (HildonTimeEditor *editor, gboolean allow_intermediate);
238 static void hildon_time_editor_set_to_current_time (HildonTimeEditor * editor);
241 _hildon_time_editor_entry_select_all(GtkWidget *widget);
248 static void convert_to_12h (guint *h, gboolean *am);
249 static void convert_to_24h (guint *h, gboolean am);
251 static void ticks_to_time (guint ticks,
257 hildon_time_editor_inserted_text (GtkEditable * editable,
259 gint new_text_length,
263 GType hildon_time_editor_get_type(void)
265 static GType editor_type = 0;
268 static const GTypeInfo editor_info = {
269 sizeof(HildonTimeEditorClass),
270 NULL, /* base_init */
271 NULL, /* base_finalize */
272 (GClassInitFunc) hildon_time_editor_class_init,
273 NULL, /* class_finalize */
274 NULL, /* class_data */
275 sizeof(HildonTimeEditor),
277 (GInstanceInitFunc) hildon_time_editor_init,
279 editor_type = g_type_register_static(GTK_TYPE_CONTAINER,
286 static void hildon_time_editor_forall(GtkContainer * container,
287 gboolean include_internals,
288 GtkCallback callback,
289 gpointer callback_data)
291 HildonTimeEditor *editor;
292 HildonTimeEditorPrivate *priv;
294 g_assert(HILDON_IS_TIME_EDITOR(container));
295 g_assert(callback != NULL);
297 editor = HILDON_TIME_EDITOR(container);
298 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
300 if (!include_internals)
303 /* widget that are always shown */
304 (*callback) (priv->iconbutton, callback_data);
305 (*callback) (priv->frame, callback_data);
308 static void hildon_time_editor_destroy(GtkObject * self)
310 HildonTimeEditorPrivate *priv;
312 priv = HILDON_TIME_EDITOR_GET_PRIVATE(self);
314 if (priv->iconbutton) {
315 gtk_widget_unparent(priv->iconbutton);
316 priv->iconbutton = NULL;
319 gtk_widget_unparent(priv->frame);
323 if (GTK_OBJECT_CLASS(parent_class)->destroy)
324 GTK_OBJECT_CLASS(parent_class)->destroy(self);
329 hildon_time_editor_class_init(HildonTimeEditorClass * editor_class)
331 GObjectClass *object_class = G_OBJECT_CLASS(editor_class);
332 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(editor_class);
333 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(editor_class);
335 parent_class = g_type_class_peek_parent(editor_class);
337 g_type_class_add_private(editor_class,
338 sizeof(HildonTimeEditorPrivate));
340 object_class->get_property = hildon_time_editor_get_property;
341 object_class->set_property = hildon_time_editor_set_property;
342 widget_class->size_request = hildon_time_editor_size_request;
343 widget_class->size_allocate = hildon_time_editor_size_allocate;
344 widget_class->tap_and_hold_setup =
345 hildon_time_editor_tap_and_hold_setup;
346 widget_class->focus = hildon_composite_widget_focus;
348 container_class->forall = hildon_time_editor_forall;
349 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_time_editor_destroy;
351 object_class->finalize = hildon_time_editor_finalize;
353 editor_class->time_error = hildon_time_editor_time_error;
355 time_editor_signals[TIME_ERROR] =
356 g_signal_new("time-error",
357 G_OBJECT_CLASS_TYPE(object_class),
359 G_STRUCT_OFFSET(HildonTimeEditorClass, time_error),
360 g_signal_accumulator_true_handled, NULL,
361 _hildon_marshal_BOOLEAN__ENUM,
362 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_EDITOR_ERROR);
365 * HildonTimeEditor:ticks:
367 * If editor is in duration mode, contains the duration seconds.
368 * If not, contains seconds since midnight.
370 g_object_class_install_property( object_class, PROP_TICKS,
371 g_param_spec_uint("ticks",
373 "Current value of duration",
375 HILDON_TIME_EDITOR_TICKS_VALUE,
376 G_PARAM_READABLE | G_PARAM_WRITABLE) );
379 * HildonTimeEditor:show_seconds:
381 * Controls whether seconds are shown in the editor
383 g_object_class_install_property( object_class, PROP_SHOW_SECONDS,
384 g_param_spec_boolean("show_seconds",
385 "Show seconds property",
386 "Controls whether the seconds are shown in the editor",
388 G_PARAM_READABLE | G_PARAM_WRITABLE) );
391 * HildonTimeEditor:show_hours:
393 * Controls whether hours are shown in the editor
395 g_object_class_install_property( object_class, PROP_SHOW_HOURS,
396 g_param_spec_boolean("show_hours",
398 "Controls whether the hours field is shown in the editor",
400 G_PARAM_READABLE | G_PARAM_WRITABLE) );
403 * HildonTimeEditor:duration_mode:
405 * Controls whether the TimeEditor is in duration mode
407 g_object_class_install_property( object_class, PROP_DURATION_MODE,
408 g_param_spec_boolean("duration_mode",
410 "Controls whether the TimeEditor is in duration mode",
411 HILDON_TIME_EDITOR_DURATION_MODE,
412 G_PARAM_READABLE | G_PARAM_WRITABLE) );
415 * HildonTimeEditor:duration_min:
417 * Minimum allowed duration value.
419 g_object_class_install_property( object_class, PROP_DURATION_MIN,
420 g_param_spec_uint("duration_min",
421 "Minumum duration value",
422 "Smallest possible duration value",
423 MIN_DURATION, MAX_DURATION,
424 HILDON_TIME_EDITOR_DURATION_LOWER_VALUE,
425 G_PARAM_READABLE | G_PARAM_WRITABLE) );
428 * HildonTimeEditor:duration_max:
430 * Maximum allowed duration value.
432 g_object_class_install_property( object_class, PROP_DURATION_MAX,
433 g_param_spec_uint("duration_max",
434 "Maximum duration value",
435 "Largest possible duration value",
437 HILDON_TIME_EDITOR_DURATION_UPPER_VALUE,
438 G_PARAM_READABLE | G_PARAM_WRITABLE) );
442 void hildon_time_editor_tap_and_hold_setup(GtkWidget * widget,
445 GtkWidgetTapAndHoldFlags flags)
447 HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE(widget);
450 /* Forward this tap_and_hold_setup signal to all our child widgets */
451 for (i = 0; i < ENTRY_COUNT; i++)
453 gtk_widget_tap_and_hold_setup(priv->entries[i], menu, func,
454 GTK_TAP_AND_HOLD_NO_SIGNALS);
456 gtk_widget_tap_and_hold_setup(priv->ampm_button, menu, func,
457 GTK_TAP_AND_HOLD_NO_SIGNALS);
458 gtk_widget_tap_and_hold_setup(priv->iconbutton, menu, func,
459 GTK_TAP_AND_HOLD_NONE);
462 static void hildon_time_editor_entry_changed(GtkWidget *widget, gpointer data)
464 g_assert(HILDON_IS_TIME_EDITOR(data));
465 hildon_time_editor_validate(HILDON_TIME_EDITOR(data), TRUE);
468 static void hildon_time_editor_init(HildonTimeEditor * editor)
470 HildonTimeEditorPrivate *priv;
471 GtkWidget *hbox, *icon;
474 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
476 gtk_widget_push_composite_child();
478 /* Setup defaults and create widgets */
480 priv->show_seconds = FALSE;
481 priv->show_hours = TRUE;
482 priv->ampm_pos_after = TRUE;
483 priv->clock_24h = TRUE;
484 priv->duration_mode = FALSE;
485 priv->iconbutton = gtk_button_new();
486 priv->ampm_label = gtk_label_new(NULL);
487 priv->hm_label = gtk_label_new(NULL);
488 priv->sec_label = gtk_label_new(NULL);
489 priv->frame = gtk_frame_new(NULL);
490 priv->ampm_button = gtk_button_new();
491 priv->skipper = FALSE;
493 icon = gtk_image_new_from_icon_name(ICON_NAME, HILDON_ICON_SIZE_WIDG);
494 hbox = gtk_hbox_new(FALSE, 0);
496 GTK_WIDGET_SET_FLAGS(editor, GTK_NO_WINDOW);
497 GTK_WIDGET_UNSET_FLAGS(priv->iconbutton, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
499 gtk_container_set_border_width(GTK_CONTAINER(priv->frame), 0);
501 gtk_container_add(GTK_CONTAINER(priv->iconbutton), icon);
502 gtk_container_add(GTK_CONTAINER(priv->ampm_button), priv->ampm_label);
503 gtk_button_set_relief(GTK_BUTTON(priv->ampm_button), GTK_RELIEF_NONE);
504 gtk_button_set_focus_on_click(GTK_BUTTON(priv->ampm_button), FALSE);
506 /* Create hour, minute and second entries */
507 for (i = 0; i < ENTRY_COUNT; i++)
509 priv->entries[i] = gtk_entry_new();
511 /* No frames for entries, so that they all appear to be inside one long entry */
512 gtk_entry_set_has_frame(GTK_ENTRY(priv->entries[i]), FALSE);
514 /* Set the entries to accept only numeric characters */
515 g_object_set (priv->entries[i], "input-mode",
516 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
518 /* The entry fields all take exactly two characters */
519 gtk_entry_set_max_length (GTK_ENTRY(priv->entries[i]), 2);
520 gtk_entry_set_width_chars (GTK_ENTRY(priv->entries[i]), 2);
522 g_signal_connect(priv->entries[i], "focus-in-event",
523 G_CALLBACK(hildon_time_editor_entry_focusin), editor);
524 g_signal_connect(priv->entries[i], "focus-out-event",
525 G_CALLBACK(hildon_time_editor_entry_focusout), editor);
526 g_signal_connect(priv->entries[i], "key-press-event",
527 G_CALLBACK(hildon_time_editor_entry_keypress), editor);
528 g_signal_connect(priv->entries[i], "changed",
529 G_CALLBACK(hildon_time_editor_entry_changed), editor);
531 /* inserted signal sets time */
532 g_signal_connect_after (G_OBJECT(priv->entries[i]), "insert_text",
533 G_CALLBACK (hildon_time_editor_inserted_text),
537 /* clicked signal for am/pm label */
538 g_signal_connect(G_OBJECT(priv->ampm_button), "clicked",
539 G_CALLBACK(hildon_time_editor_ampm_clicked), editor);
541 /* clicked signal for icon */
542 g_signal_connect(G_OBJECT(priv->iconbutton), "clicked",
543 G_CALLBACK(hildon_time_editor_icon_clicked), editor);
545 /* Set ourself as the parent of all the widgets we created */
546 gtk_widget_set_parent(priv->iconbutton, GTK_WIDGET(editor));
547 gtk_box_pack_start(GTK_BOX(hbox), priv->entries[ENTRY_HOURS], FALSE, FALSE, 0);
548 gtk_box_pack_start(GTK_BOX(hbox), priv->hm_label, FALSE, FALSE, 0);
549 gtk_box_pack_start(GTK_BOX(hbox), priv->entries[ENTRY_MINS], FALSE, FALSE, 0);
550 gtk_box_pack_start(GTK_BOX(hbox), priv->sec_label, FALSE, FALSE, 0);
551 gtk_box_pack_start(GTK_BOX(hbox), priv->entries[ENTRY_SECS], FALSE, FALSE, 0);
552 gtk_box_pack_start(GTK_BOX(hbox), priv->ampm_button, FALSE, FALSE, 0);
553 gtk_misc_set_padding(GTK_MISC(priv->ampm_label), 0, 0);
555 gtk_container_add(GTK_CONTAINER(priv->frame), hbox);
557 /* Show created widgets */
558 gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
559 gtk_widget_show_all(priv->frame);
560 gtk_widget_show_all(priv->iconbutton);
562 /* Update AM/PM and time separators settings from locale */
563 if (!hildon_time_editor_check_locale(editor)) {
564 /* Using 12h clock */
565 priv->clock_24h = FALSE;
567 gtk_widget_hide(priv->ampm_button);
570 if (!priv->show_seconds) {
571 gtk_widget_hide(priv->sec_label);
572 gtk_widget_hide(priv->entries[ENTRY_SECS]);
575 /* set the default time to current time. */
576 hildon_time_editor_set_to_current_time (editor);
578 gtk_widget_pop_composite_child();
581 static void hildon_time_editor_set_property (GObject *object,
586 HildonTimeEditor *time_editor = HILDON_TIME_EDITOR(object);
590 hildon_time_editor_set_ticks (time_editor, g_value_get_uint(value));
593 case PROP_SHOW_SECONDS:
594 hildon_time_editor_set_show_seconds (time_editor, g_value_get_boolean(value));
597 case PROP_SHOW_HOURS:
598 hildon_time_editor_set_show_hours (time_editor, g_value_get_boolean(value));
601 case PROP_DURATION_MODE:
602 hildon_time_editor_set_duration_mode (time_editor, g_value_get_boolean(value));
605 case PROP_DURATION_MIN:
606 hildon_time_editor_set_duration_min (time_editor, g_value_get_uint(value));
609 case PROP_DURATION_MAX:
610 hildon_time_editor_set_duration_max (time_editor, g_value_get_uint(value));
614 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
619 static void hildon_time_editor_get_property (GObject *object,
624 HildonTimeEditor *time_editor = HILDON_TIME_EDITOR(object);
629 g_value_set_uint (value, hildon_time_editor_get_ticks (time_editor));
632 case PROP_SHOW_SECONDS:
633 g_value_set_boolean (value, hildon_time_editor_get_show_seconds (time_editor));
636 case PROP_SHOW_HOURS:
637 g_value_set_boolean (value, hildon_time_editor_get_show_hours (time_editor));
640 case PROP_DURATION_MODE:
641 g_value_set_boolean (value, hildon_time_editor_get_duration_mode (time_editor));
644 case PROP_DURATION_MIN:
645 g_value_set_uint (value, hildon_time_editor_get_duration_min (time_editor));
648 case PROP_DURATION_MAX:
649 g_value_set_uint (value, hildon_time_editor_get_duration_max (time_editor));
653 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
659 * hildon_time_editor_new:
661 * This function creates a new time editor.
663 * Returns: pointer to a new #HildonTimeEditor widget
666 GtkWidget *hildon_time_editor_new(void)
668 return GTK_WIDGET(g_object_new(HILDON_TYPE_TIME_EDITOR, NULL));
671 static void hildon_time_editor_finalize(GObject * obj_self)
673 HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE(obj_self);
675 g_free(priv->am_symbol);
676 g_free(priv->pm_symbol);
678 if (priv->highlight_idle)
679 g_source_remove(priv->highlight_idle);
681 if (G_OBJECT_CLASS(parent_class)->finalize)
682 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
686 * _hildon_time_editor_get_time_separators:
687 * @editor: the #HildonTimeEditor
688 * @hm_sep_label: the label that will show the hour:minutes separator
689 * @ms_sep_label: the label that will show the minutes:seconds separator
691 * Gets hour-minute separator and minute-second separator from current
692 * locale and sets then to the labels we set as parameters. Both
693 * parameters can be NULL if you just want to assing one separator.
697 _hildon_time_editor_get_time_separators(GtkLabel *hm_sep_label,
698 GtkLabel *ms_sep_label)
702 GDate locale_test_date;
703 gchar *iter, *endp = NULL;
705 /* Get localized time string */
706 g_date_set_dmy(&locale_test_date, 1, 2, 1970);
707 (void) g_date_strftime(buffer, sizeof(buffer), "%X", &locale_test_date);
709 if (hm_sep_label != NULL)
711 /* Find h-m separator */
713 while (*iter && g_ascii_isdigit(*iter)) iter++;
715 /* Extract h-m separator*/
717 while (*endp && !g_ascii_isdigit(*endp)) endp++;
718 separator = g_strndup(iter, endp - iter);
719 gtk_label_set_label(hm_sep_label, separator);
723 if (ms_sep_label != NULL)
725 /* Find m-s separator */
727 while (*iter && g_ascii_isdigit(*iter)) iter++;
729 /* Extract m-s separator*/
731 while (*endp && !g_ascii_isdigit(*endp)) endp++;
732 separator = g_strndup(iter, endp - iter);
733 gtk_label_set_label(ms_sep_label, separator);
739 /* Convert ticks to H:M:S. Ticks = seconds since 00:00:00. */
740 static void ticks_to_time (guint ticks,
747 *hours = ticks / 3600;
749 *minutes = left / 60;
750 *seconds = left % 60;
754 * hildon_time_editor_set_ticks:
755 * @editor: the #HildonTimeEditor widget
756 * @ticks: the duration to set, in seconds
758 * Sets the current duration in seconds. This means seconds from
759 * midnight, if not in duration mode. In case of any errors, it tries
763 void hildon_time_editor_set_ticks (HildonTimeEditor * editor,
766 HildonTimeEditorPrivate *priv;
770 g_assert(HILDON_IS_TIME_EDITOR(editor));
772 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
774 /* Validate ticks. If it's too low or too high, set it to
775 min/max value for the current mode. */
776 if (priv->duration_mode)
777 priv->ticks = CLAMP(ticks, priv->duration_min, priv->duration_max);
779 /* Check that ticks value is valid. We only need to check that hours
781 ticks_to_time (ticks, &h, &m, &s);
782 if (h > HOURS_MAX_24)
783 ticks = TICKS(HOURS_MAX_24, m, s);
788 /* Get the time in H:M:S. */
789 ticks_to_time (priv->ticks, &h, &m, &s);
791 if (!priv->clock_24h && !priv->duration_mode)
793 /* Convert 24h H:M:S values to 12h mode, and update AM/PM state */
794 convert_to_12h (&h, &priv->am);
797 /* Set H:M:S values to entries. We do not want to invoke validation
798 callbacks (since they can cause new call to this function), so we
799 block signals while setting values. */
800 for (i = 0; i < ENTRY_COUNT; i++)
802 g_signal_handlers_block_by_func(priv->entries[i],
803 (gpointer) hildon_time_editor_entry_changed, editor);
805 g_signal_handlers_block_by_func(priv->entries[i],
806 (gpointer) hildon_time_editor_inserted_text, editor);
808 g_signal_handlers_block_by_func(priv->entries[i],
809 (gpointer) hildon_time_editor_entry_focusout, editor);
813 g_snprintf(str, sizeof(str), "%02u", h);
814 gtk_entry_set_text(GTK_ENTRY(priv->entries[ENTRY_HOURS]), str);
816 g_snprintf(str, sizeof(str), "%02u", m);
817 gtk_entry_set_text(GTK_ENTRY(priv->entries[ENTRY_MINS]), str);
819 g_snprintf(str, sizeof(str), "%02u", s);
820 gtk_entry_set_text(GTK_ENTRY(priv->entries[ENTRY_SECS]), str);
822 for (i = 0; i < ENTRY_COUNT; i++)
824 g_signal_handlers_unblock_by_func(priv->entries[i],
825 (gpointer) hildon_time_editor_entry_changed, editor);
827 g_signal_handlers_unblock_by_func(priv->entries[i],
828 (gpointer) hildon_time_editor_inserted_text, editor);
830 g_signal_handlers_unblock_by_func(priv->entries[i],
831 (gpointer) hildon_time_editor_entry_focusout, editor);
835 /* Update AM/PM label in case we're in 12h mode */
836 gtk_label_set_label(GTK_LABEL(priv->ampm_label),
837 priv->am ? priv->am_symbol : priv->pm_symbol);
839 g_object_notify (G_OBJECT (editor), "ticks");
843 hildon_time_editor_set_to_current_time (HildonTimeEditor * editor)
849 tm = localtime(&now);
852 hildon_time_editor_set_time(editor, tm->tm_hour, tm->tm_min, tm->tm_sec);
856 * hildon_time_editor_get_ticks:
857 * @editor: the #HildonTimeEditor widget
859 * This function returns the current duration, in seconds.
860 * This means seconds from midnight, if not in duration mode.
862 * Returns: current duration in seconds
865 guint hildon_time_editor_get_ticks (HildonTimeEditor * editor)
867 HildonTimeEditorPrivate *priv;
869 g_return_val_if_fail(editor, 0);
870 g_return_val_if_fail(HILDON_IS_TIME_EDITOR(editor), 0);
872 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
874 return (priv->ticks);
878 * hildon_time_editor_set_show_seconds:
879 * @editor: the #HildonTimeEditor
880 * @show_seconds: enable or disable showing of seconds
882 * This function shows or hides the seconds field.
885 void hildon_time_editor_set_show_seconds (HildonTimeEditor * editor,
886 gboolean show_seconds)
888 HildonTimeEditorPrivate *priv;
890 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
892 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
894 if (show_seconds != priv->show_seconds) {
895 priv->show_seconds = show_seconds;
897 /* show/hide seconds field and its ':' label if the value changed. */
899 gtk_widget_show(priv->entries[ENTRY_SECS]);
900 gtk_widget_show(priv->sec_label);
902 gtk_widget_hide(priv->entries[ENTRY_SECS]);
903 gtk_widget_hide(priv->sec_label);
906 g_object_notify (G_OBJECT (editor), "show_seconds");
911 * hildon_time_editor_get_show_seconds:
912 * @editor: the #HildonTimeEditor widget
914 * This function returns a boolean indicating the visibility of
915 * seconds in the #HildonTimeEditor
917 * Returns: TRUE if the seconds are visible
920 gboolean hildon_time_editor_get_show_seconds (HildonTimeEditor * editor)
922 HildonTimeEditorPrivate *priv;
924 g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
925 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
927 return (priv->show_seconds);
931 * hildon_time_editor_set_duration_mode:
932 * @editor: the #HildonTimeEditor
933 * @duration_mode: enable or disable duration editor mode
935 * This function sets the duration editor mode in which the maximum hours
939 void hildon_time_editor_set_duration_mode (HildonTimeEditor * editor,
940 gboolean duration_mode)
942 HildonTimeEditorPrivate *priv;
944 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
946 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
948 if (duration_mode != priv->duration_mode) {
949 priv->duration_mode = duration_mode;
952 /* FIXME: Why do we reset the duration range here?
953 Would change API, so won't touch this for now. */
954 hildon_time_editor_set_duration_range(editor, MIN_DURATION,
956 /* There's no AM/PM label or time picker icon in duration mode.
957 Make sure they're hidden. */
958 gtk_widget_hide(GTK_WIDGET(priv->ampm_label));
959 gtk_widget_hide(GTK_WIDGET(priv->ampm_button));
960 gtk_widget_hide(GTK_WIDGET(priv->iconbutton));
961 /* Duration mode has seconds by default. */
962 hildon_time_editor_set_show_seconds(editor, TRUE);
964 /* Make sure AM/PM label and time picker icons are visible if needed */
965 if (!priv->clock_24h)
966 gtk_widget_show(GTK_WIDGET(priv->ampm_label));
968 gtk_widget_show(GTK_WIDGET(priv->ampm_button));
969 gtk_widget_show(GTK_WIDGET(priv->iconbutton));
971 /* Reset the ticks to current time. Anything set in duration mode
972 * is bound to be invalid or useless in time mode.
974 hildon_time_editor_set_to_current_time (editor);
977 g_object_notify (G_OBJECT (editor), "duration_mode");
982 * hildon_time_editor_get_duration_mode:
983 * @editor: the #HildonTimeEditor widget
985 * This function returns a boolean indicating whether the #HildonTimeEditor
986 * is in the duration mode.
988 * Returns: TRUE if the #HildonTimeEditor is in duration mode
991 gboolean hildon_time_editor_get_duration_mode (HildonTimeEditor * editor)
993 HildonTimeEditorPrivate *priv;
995 g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
996 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
998 return (priv->duration_mode);
1002 * hildon_time_editor_set_duration_min:
1003 * @editor: the #HildonTimeEditor widget
1004 * @duration_min: mimimum allowed duration
1006 * Sets the minimum allowed duration for the duration mode.
1007 * Note: Has no effect in time mode
1010 void hildon_time_editor_set_duration_min (HildonTimeEditor * editor,
1013 HildonTimeEditorPrivate *priv;
1015 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1016 g_return_if_fail(duration_min >= MIN_DURATION);
1018 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1020 if( !priv->duration_mode )
1023 priv->duration_min = duration_min;
1025 /* Clamp the current value to the minimum if necessary */
1026 if (priv->ticks < duration_min)
1028 hildon_time_editor_set_ticks (editor, duration_min);
1031 g_object_notify (G_OBJECT (editor), "duration_min");
1035 * hildon_time_editor_get_duration_min:
1036 * @editor: the #HildonTimeEditor widget
1038 * This function returns the smallest duration the #HildonTimeEditor
1039 * allows in the duration mode.
1041 * Returns: minimum allowed duration in seconds
1044 guint hildon_time_editor_get_duration_min (HildonTimeEditor * editor)
1046 HildonTimeEditorPrivate *priv;
1048 g_return_val_if_fail(HILDON_IS_TIME_EDITOR(editor), 0);
1050 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1052 if( !priv->duration_mode )
1055 return (priv->duration_min);
1059 * hildon_time_editor_set_duration_max:
1060 * @editor: the #HildonTimeEditor widget
1061 * @duration_max: maximum allowed duration in seconds
1063 * Sets the maximum allowed duration in seconds for the duration mode.
1064 * Note: Has no effect in time mode
1067 void hildon_time_editor_set_duration_max (HildonTimeEditor * editor,
1070 HildonTimeEditorPrivate *priv;
1072 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1073 g_return_if_fail(duration_max <= MAX_DURATION);
1075 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1077 if( !priv->duration_mode )
1080 priv->duration_max = duration_max;
1082 /* Clamp the current value to the maximum if necessary */
1083 if (priv->ticks > duration_max)
1085 hildon_time_editor_set_ticks (editor, duration_max);
1088 g_object_notify (G_OBJECT (editor), "duration_max");
1092 * hildon_time_editor_get_duration_max:
1093 * @editor: the #HildonTimeEditor widget
1095 * This function returns the longest duration the #HildonTimeEditor
1096 * allows in the duration mode.
1098 * Returns: maximum allowed duration in seconds
1101 guint hildon_time_editor_get_duration_max (HildonTimeEditor * editor)
1103 HildonTimeEditorPrivate *priv;
1105 g_return_val_if_fail(HILDON_IS_TIME_EDITOR(editor), 0);
1107 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1109 if( !priv->duration_mode )
1112 return (priv->duration_max);
1117 * hildon_time_editor_set_time:
1118 * @editor: the #HildonTimeEditor widget
1123 * This function sets the time on an existing time editor. If the
1124 * time specified by the arguments is invalid, it's fixed.
1125 * The time is assumed to be in 24h format.
1128 void hildon_time_editor_set_time(HildonTimeEditor * editor, guint hours,
1129 guint minutes, guint seconds)
1131 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1133 hildon_time_editor_set_ticks (editor, TICKS(hours, minutes, seconds));
1137 * hildon_time_editor_get_time:
1138 * @editor: the #HildonTimeEditor widget
1143 * Gets the time of the #HildonTimeEditor widget. The time returned is
1144 * always in 24h format.
1147 void hildon_time_editor_get_time(HildonTimeEditor * editor,
1149 guint * minutes, guint * seconds)
1151 HildonTimeEditorPrivate *priv;
1153 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1155 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1157 ticks_to_time (hildon_time_editor_get_ticks (editor),
1158 hours, minutes, seconds);
1162 * hildon_time_editor_set_duration_range:
1163 * @editor: the #HildonTimeEditor widget
1164 * @min_seconds: minimum allowed time in seconds
1165 * @max_seconds: maximum allowed time in seconds
1167 * Sets the duration editor time range of the #HildonTimeEditor widget.
1170 void hildon_time_editor_set_duration_range(HildonTimeEditor * editor,
1174 HildonTimeEditorPrivate *priv;
1177 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1179 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1180 /* Swap values if reversed */
1181 if (min_seconds > max_seconds)
1184 max_seconds = min_seconds;
1188 hildon_time_editor_set_duration_max (editor, max_seconds);
1189 hildon_time_editor_set_duration_min (editor, min_seconds);
1191 if (priv->duration_mode) {
1192 /* Set minimum allowed value for duration editor.
1193 FIXME: Shouldn't it be changed only if it's not in range?
1194 Would change API, so won't touch this for now. */
1195 hildon_time_editor_set_ticks(editor, min_seconds);
1200 * hildon_time_editor_get_duration_range:
1201 * @editor: the #HildonTimeEditor widget
1202 * @min_seconds: pointer to guint
1203 * @max_seconds: pointer to guint
1205 * Gets the duration editor time range of the #HildonTimeEditor widget.
1208 void hildon_time_editor_get_duration_range(HildonTimeEditor * editor,
1209 guint * min_seconds,
1210 guint * max_seconds)
1212 HildonTimeEditorPrivate *priv;
1214 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1216 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1218 *min_seconds = priv->duration_min;
1219 *max_seconds = priv->duration_max;
1222 static gboolean hildon_time_editor_check_locale(HildonTimeEditor * editor)
1224 HildonTimeEditorPrivate *priv;
1226 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1228 /* Update time separator symbols */
1229 _hildon_time_editor_get_time_separators(GTK_LABEL(priv->hm_label), GTK_LABEL(priv->sec_label));
1231 /* Get AM/PM symbols. */
1232 priv->am_symbol = g_strdup(nl_langinfo(AM_STR));
1233 priv->pm_symbol = g_strdup(nl_langinfo(PM_STR));
1235 if (priv->am_symbol[0] == '\0')
1238 /* 12h clock mode. Check if AM/PM should be before or after time.
1239 %p is the AM/PM string, so we assume that if the format string
1240 begins with %p it's in the beginning, and in any other case it's
1241 in the end (although that's not necessarily the case). */
1242 if (strncmp(nl_langinfo(T_FMT_AMPM), "%p", 2) == 0)
1243 priv->ampm_pos_after = FALSE;
1248 static gboolean hildon_time_editor_entry_focusin(GtkWidget * widget,
1249 GdkEventFocus * event,
1252 g_idle_add((GSourceFunc) _hildon_time_editor_entry_select_all,
1259 hildon_time_editor_time_error(HildonTimeEditor *editor,
1260 HildonDateTimeEditorError type)
1265 /* Returns negative if we didn't get value,
1266 * and should stop further validation
1268 static gint validated_conversion(HildonTimeEditorPrivate *priv,
1273 gboolean allow_intermediate,
1275 GString *error_string)
1281 text = gtk_entry_get_text(GTK_ENTRY(field));
1283 if (text && text[0])
1285 /* Try to convert entry text to number */
1286 value = strtol(text, &tail, 10);
1288 /* Check if conversion succeeded */
1289 if ((tail[0] == 0) && !(text[0] == '-'))
1292 g_string_printf(error_string, _("ckct_ib_maximum_value"), max);
1293 priv->error_widget = field;
1294 *error_code = MAX_VALUE;
1297 if (value < min && !allow_intermediate) {
1298 g_string_printf(error_string, _("ckct_ib_minimum_value"), min);
1299 priv->error_widget = field;
1300 *error_code = MIN_VALUE;
1306 /* We'll handle failed conversions soon */
1309 if ((tail[0] == '-') || (text[0] == '-'))
1311 g_string_printf(error_string, _("ckct_ib_minimum_value"), min);
1312 priv->error_widget = field;
1313 *error_code = MIN_VALUE;
1318 else if (allow_intermediate)
1319 return -1; /* Empty field while user is still editing. No error, but
1320 cannot validate either... */
1321 else /* Empty field: show error and set value to minimum allowed */
1323 g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max);
1324 priv->error_widget = field;
1325 *error_code = WITHIN_RANGE;
1329 /* Empty field and not allowed intermediated OR failed conversion */
1330 g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max);
1331 priv->error_widget = field;
1332 *error_code = WITHIN_RANGE;
1337 hildon_time_editor_real_validate(HildonTimeEditor *editor,
1338 gboolean allow_intermediate, GString *error_string)
1340 HildonTimeEditorPrivate *priv;
1341 guint h, m, s, ticks;
1343 guint max_hours, min_hours, def_hours;
1344 guint max_minutes, min_minutes, def_minutes;
1345 guint max_seconds, min_seconds, def_seconds;
1348 g_assert(HILDON_IS_TIME_EDITOR(editor));
1350 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1352 /* Find limits for field based validation. */
1353 if (priv->duration_mode)
1355 ticks_to_time(priv->duration_min, &min_hours, &min_minutes, &min_seconds);
1356 ticks_to_time(priv->duration_max, &max_hours, &max_minutes, &max_seconds);
1358 if (priv->clock_24h) {
1359 max_hours = HOURS_MAX_24;
1360 min_hours = HOURS_MIN_24;
1362 max_hours = HOURS_MAX_12;
1363 min_hours = HOURS_MIN_12;
1367 hildon_time_editor_get_time(editor, &def_hours, &def_minutes, &def_seconds);
1369 /* Get time components from fields and validate them... */
1370 if (priv->show_hours) {
1371 h = validated_conversion(priv, priv->entries[ENTRY_HOURS], min_hours, max_hours, def_hours,
1372 allow_intermediate, &error_code, error_string);
1373 if (priv->error_widget == priv->entries[ENTRY_HOURS])
1374 g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, hour_errors[error_code], &r);
1375 if ((gint) h < 0) return;
1378 m = validated_conversion(priv, priv->entries[ENTRY_MINS], MINUTES_MIN, MINUTES_MAX, def_minutes,
1379 allow_intermediate, &error_code, error_string);
1380 if (priv->error_widget == priv->entries[ENTRY_MINS])
1381 g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, min_errors[error_code], &r);
1382 if ((gint) m < 0) return;
1383 if (priv->show_seconds) {
1384 s = validated_conversion(priv, priv->entries[ENTRY_SECS], SECONDS_MIN, SECONDS_MAX, def_seconds,
1385 allow_intermediate, &error_code, error_string);
1386 if (priv->error_widget == priv->entries[ENTRY_SECS])
1387 g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, sec_errors[error_code], &r);
1388 if ((gint) s < 0) return;
1392 /* Ok, we now do separate check that tick count is valid for duration mode */
1393 if (priv->duration_mode)
1395 ticks = TICKS(h, m, s);
1397 if (ticks < priv->duration_min && !allow_intermediate)
1399 g_string_printf(error_string,
1400 _("ckct_ib_min_allowed_duration_hts"),
1401 min_hours, min_minutes, min_seconds);
1402 hildon_time_editor_set_ticks (editor, priv->duration_min);
1403 priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
1404 g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, HILDON_DATE_TIME_ERROR_MIN_DURATION, &r);
1407 else if (ticks > priv->duration_max)
1409 g_string_printf(error_string,
1410 _("ckct_ib_max_allowed_duration_hts"),
1411 max_hours, max_minutes, max_seconds);
1412 hildon_time_editor_set_ticks (editor, priv->duration_max);
1413 priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
1414 g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, HILDON_DATE_TIME_ERROR_MAX_DURATION, &r);
1418 else if (!priv->clock_24h)
1419 convert_to_24h (&h, priv->am);
1421 /* The only case when we do not want to refresh the
1422 time display, is when the user is editing a value
1423 (unless the value was out of bounds and we have to fix it) */
1424 if (!allow_intermediate || priv->error_widget)
1425 hildon_time_editor_set_time (editor, h, m, s);
1428 /* Setting text to entries causes entry to recompute itself
1429 in idle callback, which remove selection. Because of this
1430 we need to do selection in idle as well. */
1431 static gboolean highlight_callback(gpointer data)
1433 HildonTimeEditorPrivate *priv;
1437 g_assert(HILDON_IS_TIME_EDITOR(data));
1438 priv = HILDON_TIME_EDITOR_GET_PRIVATE(data);
1440 GDK_THREADS_ENTER ();
1442 widget = priv->error_widget;
1443 priv->error_widget = NULL;
1445 if (GTK_IS_WIDGET(widget) == FALSE)
1448 /* Avoid revalidation because it will issue the date_error signal
1449 twice when there is an empty field. We must block the signal
1450 for all the entries because we do not know where the focus
1452 for (i = 0; i < ENTRY_COUNT; i++)
1453 g_signal_handlers_block_by_func(priv->entries[i],
1454 (gpointer) hildon_time_editor_entry_focusout, data);
1455 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1456 gtk_widget_grab_focus(widget);
1457 for (i = 0; i < ENTRY_COUNT; i++)
1458 g_signal_handlers_unblock_by_func(priv->entries[i],
1459 (gpointer) hildon_time_editor_entry_focusout, data);
1462 priv->highlight_idle = 0;
1463 GDK_THREADS_LEAVE ();
1468 /* Update ticks from current H:M:S entries. If they're invalid, show an
1469 infoprint and update the fields unless they're empty. */
1471 hildon_time_editor_validate (HildonTimeEditor *editor, gboolean allow_intermediate)
1473 HildonTimeEditorPrivate *priv;
1474 GString *error_message;
1476 g_assert(HILDON_IS_TIME_EDITOR(editor));
1478 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1480 /* if there is already an error we do nothing until it will be managed by the idle */
1481 if (priv->highlight_idle == 0 && priv->skipper == FALSE)
1483 priv->skipper = TRUE;
1484 error_message = g_string_new(NULL);
1485 hildon_time_editor_real_validate(editor,
1486 allow_intermediate, error_message);
1488 if (priv->error_widget)
1490 hildon_banner_show_information(priv->error_widget, NULL,
1491 error_message->str);
1493 priv->highlight_idle = g_idle_add(highlight_callback, editor);
1496 priv->skipper = FALSE;
1497 g_string_free(error_message, TRUE);
1501 /* on inserted text, if entry has two digits, jumps to the next field. */
1503 hildon_time_editor_inserted_text (GtkEditable * editable,
1505 gint new_text_length,
1509 HildonTimeEditor *editor;
1512 HildonTimeEditorPrivate *priv;
1514 entry = GTK_ENTRY(editable);
1515 editor = HILDON_TIME_EDITOR(user_data);
1517 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1519 /* if there is already an error we don't have to do anything */
1520 if (!priv->error_widget)
1523 value = (gchar *) gtk_entry_get_text(entry);
1525 if (strlen(value) == 2)
1527 if (GTK_WIDGET(editable) == priv->entries[ENTRY_HOURS])
1529 /* We already checked the input in changed signal, but
1530 * now we will re-check it again in focus-out we
1531 * intermediate flag set to FALSE */
1532 gtk_widget_grab_focus(priv->entries[ENTRY_MINS]);
1535 else if (GTK_WIDGET(editable) == priv->entries[ENTRY_MINS] &&
1536 GTK_WIDGET_VISIBLE (priv->entries[ENTRY_SECS]))
1539 gtk_widget_grab_focus(priv->entries[ENTRY_SECS]);
1546 static gboolean hildon_time_editor_entry_focusout(GtkWidget * widget,
1547 GdkEventFocus * event,
1550 g_assert(HILDON_IS_TIME_EDITOR(data));
1552 /* Validate the given time and update ticks. */
1553 hildon_time_editor_validate(HILDON_TIME_EDITOR(data), FALSE);
1559 hildon_time_editor_ampm_clicked(GtkWidget * widget,
1562 HildonTimeEditor *editor;
1563 HildonTimeEditorPrivate *priv;
1565 g_assert(GTK_IS_WIDGET(widget));
1566 g_assert(HILDON_IS_TIME_EDITOR(data));
1568 editor = HILDON_TIME_EDITOR(data);
1569 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1571 /* First validate the given time and update ticks. */
1572 hildon_time_editor_validate (editor, FALSE);
1574 /* Apply the AM/PM change by moving the current time by 12 hours */
1576 /* 00:00 .. 11:59 -> 12:00 .. 23:59 */
1577 hildon_time_editor_set_ticks (editor, priv->ticks + 12*3600);
1579 /* 12:00 .. 23:59 -> 00:00 .. 11:59 */
1580 hildon_time_editor_set_ticks (editor, priv->ticks - 12*3600);
1586 hildon_time_editor_icon_clicked(GtkWidget * widget, gpointer data)
1588 HildonTimeEditor *editor;
1591 guint h, m, s, result;
1592 HildonTimeEditorPrivate *priv;
1594 g_assert(HILDON_IS_TIME_EDITOR(data));
1596 editor = HILDON_TIME_EDITOR(data);
1597 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1599 /* icon is passive in duration editor mode */
1600 if (hildon_time_editor_get_duration_mode(editor))
1603 /* Validate and do not launch if broken */
1604 hildon_time_editor_validate(HILDON_TIME_EDITOR(data), FALSE);
1605 if (priv->error_widget != NULL)
1608 /* Launch HildonTimePicker dialog */
1609 parent = gtk_widget_get_ancestor(GTK_WIDGET(editor), GTK_TYPE_WINDOW);
1610 picker = hildon_time_picker_new(GTK_WINDOW(parent));
1612 hildon_time_editor_get_time(editor, &h, &m, &s);
1613 hildon_time_picker_set_time(HILDON_TIME_PICKER(picker), h, m);
1615 result = gtk_dialog_run(GTK_DIALOG(picker));
1617 case GTK_RESPONSE_OK:
1618 case GTK_RESPONSE_ACCEPT:
1619 /* Use the selected time */
1620 hildon_time_picker_get_time(HILDON_TIME_PICKER(picker), &h, &m);
1621 hildon_time_editor_set_time(editor, h, m, 0);
1627 gtk_widget_destroy(picker);
1631 static void hildon_time_editor_size_request(GtkWidget * widget,
1632 GtkRequisition * requisition)
1634 HildonTimeEditor *editor;
1635 HildonTimeEditorPrivate *priv;
1638 editor = HILDON_TIME_EDITOR(widget);
1639 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1641 /* Get frame's size */
1642 gtk_widget_size_request(priv->frame, requisition);
1644 if (GTK_WIDGET_VISIBLE(priv->iconbutton))
1646 gtk_widget_size_request(priv->iconbutton, &req);
1647 /* Reserve space for icon */
1648 requisition->width += req.width + ICON_PRESSED +
1649 HILDON_MARGIN_DEFAULT;
1652 /* FIXME: It's evil to use hardcoded TIME_EDITOR_HEIGHT. For now we'll
1653 want to force this since themes might have varying thickness values
1654 which cause the height to change. */
1655 requisition->height = TIME_EDITOR_HEIGHT;
1658 static void hildon_time_editor_size_allocate(GtkWidget * widget,
1659 GtkAllocation * allocation)
1661 HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE(widget);
1662 GtkAllocation alloc;
1663 GtkRequisition req, max_req;
1665 widget->allocation = *allocation;
1666 gtk_widget_get_child_requisition(widget, &max_req);
1668 /* Center horizontally */
1669 alloc.x = allocation->x + MAX(allocation->width - max_req.width, 0) / 2;
1670 /* Center vertically */
1671 alloc.y = allocation->y + MAX(allocation->height - max_req.height, 0) / 2;
1673 /* allocate frame */
1674 gtk_widget_get_child_requisition(priv->frame, &req);
1676 alloc.width = req.width;
1677 alloc.height = max_req.height;
1678 gtk_widget_size_allocate(priv->frame, &alloc);
1681 if (GTK_WIDGET_VISIBLE(priv->iconbutton)) {
1682 gtk_widget_get_child_requisition(priv->iconbutton, &req);
1684 alloc.x += alloc.width + HILDON_MARGIN_DEFAULT;
1685 alloc.width = req.width;
1686 gtk_widget_size_allocate(priv->iconbutton, &alloc);
1689 /* FIXME: ugly way to move labels up. They just don't seem move up
1690 otherwise. This is likely because we force the editor to be
1691 smaller than it otherwise would be. */
1692 alloc = priv->ampm_label->allocation;
1693 alloc.y = allocation->y - 2;
1694 alloc.height = max_req.height + 2;
1695 gtk_widget_size_allocate(priv->ampm_label, &alloc);
1697 alloc = priv->hm_label->allocation;
1698 alloc.y = allocation->y - 2;
1699 alloc.height = max_req.height + 2;
1700 gtk_widget_size_allocate(priv->hm_label, &alloc);
1702 alloc = priv->sec_label->allocation;
1703 alloc.y = allocation->y - 2;
1704 alloc.height = max_req.height + 2;
1705 gtk_widget_size_allocate(priv->sec_label, &alloc);
1708 static gboolean hildon_time_editor_entry_keypress(GtkWidget * widget,
1709 GdkEventKey * event,
1712 HildonTimeEditor *editor;
1713 HildonTimeEditorPrivate *priv;
1717 g_assert(GTK_IS_ENTRY(widget));
1718 g_assert(event != NULL);
1719 g_assert(HILDON_IS_TIME_EDITOR(data));
1721 editor = HILDON_TIME_EDITOR(data);
1722 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1723 cursor_pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1725 /* Show error message in case the key pressed is not allowed
1726 (only digits and control characters are allowed )*/
1727 if (!g_unichar_isdigit(event->keyval) && !(event->keyval & 0xF000)) {
1728 g_signal_emit(editor, time_editor_signals[TIME_ERROR], 0, HILDON_DATE_TIME_ERROR_INVALID_CHAR, &r);
1729 hildon_banner_show_information(widget, NULL, _("ckct_ib_illegal_character"));
1733 switch (event->keyval)
1736 /* Return key popups up time picker dialog. Visually it looks as if
1737 the time picker icon was clicked. Before opening the time picker
1738 the fields are first validated and fixed. */
1739 hildon_time_editor_validate (editor, FALSE);
1740 hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), TRUE);
1741 hildon_time_editor_icon_clicked(widget, data);
1742 hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), FALSE);
1746 /* left arrow pressed in the entry. If we are on first position, try to
1747 move to the previous field. */
1748 if (cursor_pos == 0) {
1749 (void) gtk_widget_child_focus(GTK_WIDGET(editor), GTK_DIR_LEFT);
1755 /* right arrow pressed in the entry. If we are on last position, try to
1756 move to the next field. */
1757 if (cursor_pos >= g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(widget)), -1)) {
1758 (void) gtk_widget_child_focus(GTK_WIDGET(editor), GTK_DIR_RIGHT);
1775 convert_to_12h (guint *h, gboolean *am)
1777 g_assert(0 <= *h && *h < 24);
1779 /* 00:00 to 00:59 add 12 hours */
1780 /* 01:00 to 11:59 straight to am */
1781 /* 12:00 to 12:59 straight to pm */
1782 /* 13:00 to 23:59 subtract 12 hours */
1784 if ( *h == 0 ) { *am = TRUE; *h += 12;}
1785 else if ( 1 <= *h && *h < 12 ) { *am = TRUE; }
1786 else if ( 12 <= *h && *h < 13 ) { *am = FALSE; }
1787 else { *am = FALSE; *h -= 12;}
1791 convert_to_24h (guint *h, gboolean am)
1793 if (*h == 12 && am) /* 12 midnight - 12:59 AM subtract 12 hours */
1797 else if (!am && 1 <= *h && *h < 12) /* 1:00 PM - 11:59 AM add 12 hours */
1804 * hildon_time_editor_set_show_hours:
1805 * @editor: The #HildonTimeEditor.
1806 * @enable: Enable or disable showing of hours.
1808 * This function shows or hides the hours field.
1812 void hildon_time_editor_set_show_hours(HildonTimeEditor * editor,
1813 gboolean show_hours)
1815 HildonTimeEditorPrivate *priv;
1817 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1819 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1821 if (show_hours != priv->show_hours) {
1822 priv->show_hours = show_hours;
1824 /* show/hide hours field and its ':' label if the value changed. */
1826 gtk_widget_show(priv->entries[ENTRY_HOURS]);
1827 gtk_widget_show(priv->hm_label);
1829 gtk_widget_hide(priv->entries[ENTRY_HOURS]);
1830 gtk_widget_hide(priv->hm_label);
1833 g_object_notify (G_OBJECT (editor), "show_hours");
1838 * hildon_time_editor_get_show_hours:
1839 * @self: the @HildonTimeEditor widget.
1841 * This function returns a boolean indicating the visibility of
1842 * hours in the @HildonTimeEditor
1844 * Return value: TRUE if hours are visible.
1848 gboolean hildon_time_editor_get_show_hours(HildonTimeEditor *editor)
1850 HildonTimeEditorPrivate *priv;
1852 g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
1853 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1855 return priv->show_hours;
1860 _hildon_time_editor_entry_select_all (GtkWidget *widget)
1862 GDK_THREADS_ENTER ();
1863 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1864 GDK_THREADS_LEAVE ();