2 * This file is part of hildon-libs
4 * Copyright (C) 2005, 2006 Nokia Corporation.
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.
42 #include <gtk/gtkhbox.h>
43 #include <gtk/gtkeventbox.h>
44 #include <gtk/gtkentry.h>
45 #include <gtk/gtkbutton.h>
46 #include <gtk/gtklabel.h>
47 #include <gtk/gtkframe.h>
48 #include <gdk/gdkkeysyms.h>
56 #include <hildon-widgets/hildon-defines.h>
57 #include <hildon-widgets/hildon-time-editor.h>
58 #include <hildon-widgets/hildon-time-picker.h>
59 #include <hildon-widgets/hildon-banner.h>
60 #include <hildon-widgets/hildon-input-mode-hint.h>
61 #include <hildon-widgets/hildon-private.h>
62 #include "hildon-composite-widget.h"
63 #include "hildon-marshalers.h"
64 #include "hildon-libs-enum-types.h"
66 #define _(String) dgettext(PACKAGE, String)
68 #define HILDON_TIME_EDITOR_GET_PRIVATE(obj) \
69 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
70 HILDON_TYPE_TIME_EDITOR, HildonTimeEditorPrivate));
72 #define TICKS(h,m,s) ((h) * 3600 + (m) * 60 + (s))
74 #define TIME_EDITOR_HEIGHT 30
75 #define ICON_PRESSED 4
76 #define ICON_NAME "qgn_widg_timedit"
77 #define ICON_SIZE "timepicker-size"
78 #define MIN_DURATION 0
79 #define MAX_DURATION TICKS(99, 59, 59)
81 /* Default values for properties */
82 #define HILDON_TIME_EDITOR_TICKS_VALUE 0
83 #define HILDON_TIME_EDITOR_DURATION_MODE FALSE
84 #define HILDON_TIME_EDITOR_DURATION_LOWER_VALUE 0
85 #define HILDON_TIME_EDITOR_DURATION_UPPER_VALUE TICKS(99, 59, 59)
87 #define HOURS_MAX_24 23
88 #define HOURS_MAX_12 12
89 #define HOURS_MIN_24 0
90 #define HOURS_MIN_12 1
91 #define MINUTES_MAX 59
92 #define SECONDS_MAX 59
96 static GtkContainerClass *parent_class;
98 typedef struct _HildonTimeEditorPrivate HildonTimeEditorPrivate;
110 /* Indices for h/m/s entries in priv->entries */
125 /* Error codes categories */
133 static guint time_editor_signals[LAST_SIGNAL] = { 0 };
134 static guint hour_errors[NUM_ERROR_CODES] = { MAX_HOURS, MIN_HOURS, EMPTY_HOURS };
135 static guint min_errors[NUM_ERROR_CODES] = { MAX_MINS, MIN_MINS, EMPTY_MINS };
136 static guint sec_errors[NUM_ERROR_CODES] = { MAX_SECS, MIN_SECS, EMPTY_SECS };
138 struct _HildonTimeEditorPrivate {
139 guint ticks; /* Current duration in seconds */
144 GtkWidget *eventbox; /* hold entries */
145 GtkWidget *iconbutton; /* button for icon */
147 GtkWidget *frame; /* frame around the entries */
148 GtkWidget *entries[ENTRY_COUNT]; /* h, m, s entries */
149 GtkWidget *hm_label; /* between hour and minute */
150 GtkWidget *sec_label; /* between minute and second */
151 GtkWidget *ampm_label; /* label for showing am or pm */
153 GtkWidget *error_widget; /* field to highlight in idle */
156 gboolean duration_mode; /* In HildonDurationEditor mode */
157 gboolean show_seconds; /* show seconds */
158 gboolean show_hours; /* show hours */
160 gboolean ampm_pos_after; /* is am/pm shown after others */
161 gboolean clock_24h; /* whether to show a 24h clock */
162 gboolean am; /* TRUE == am, FALSE == pm */
164 guint duration_min; /* duration editor ranges */
165 guint duration_max; /* duration editor ranges */
167 guint highlight_idle;
174 static void hildon_time_editor_class_init (HildonTimeEditorClass *editor_class);
175 static void hildon_time_editor_init (HildonTimeEditor *editor);
177 static void hildon_time_editor_finalize (GObject *obj_self);
179 static void hildon_time_editor_set_property(GObject *object,
184 static void hildon_time_editor_get_property(GObject *object,
189 static void hildon_time_editor_forall(GtkContainer *container,
190 gboolean include_internals,
191 GtkCallback callback,
192 gpointer callback_data);
194 static void hildon_time_editor_destroy(GtkObject * self);
200 static gboolean hildon_time_editor_entry_focusout(GtkWidget *widget,
201 GdkEventFocus *event,
204 static gboolean hildon_time_editor_entry_focusin(GtkWidget *widget,
205 GdkEventFocus *event,
208 static gboolean hildon_time_editor_time_error(HildonTimeEditor *editor,
209 HildonTimeEditorErrorType type);
211 static gboolean hildon_time_editor_ampm_clicked(GtkWidget *widget,
212 GdkEventButton *event,
215 static gboolean hildon_time_editor_icon_clicked(GtkWidget *widget,
218 static gboolean hildon_time_editor_entry_clicked(GtkWidget *widget,
219 GdkEventButton *event,
222 static void hildon_time_editor_size_request(GtkWidget *widget,
223 GtkRequisition *requisition);
225 static void hildon_time_editor_size_allocate(GtkWidget *widget,
226 GtkAllocation *allocation);
228 static gboolean hildon_time_editor_entry_keypress(GtkWidget *widget,
236 static gboolean hildon_time_editor_check_locale(HildonTimeEditor * editor);
239 void hildon_time_editor_tap_and_hold_setup(GtkWidget * widget,
242 GtkWidgetTapAndHoldFlags flags);
244 hildon_time_editor_validate (HildonTimeEditor *editor, gboolean allow_intermediate);
246 static void hildon_time_editor_set_to_current_time (HildonTimeEditor * editor);
252 static void convert_to_12h (guint *h, gboolean *am);
253 static void convert_to_24h (guint *h, gboolean am);
255 static void ticks_to_time (guint ticks,
261 hildon_time_editor_inserted_text (GtkEditable * editable,
263 gint new_text_length,
267 GType hildon_time_editor_get_type(void)
269 static GType editor_type = 0;
272 static const GTypeInfo editor_info = {
273 sizeof(HildonTimeEditorClass),
274 NULL, /* base_init */
275 NULL, /* base_finalize */
276 (GClassInitFunc) hildon_time_editor_class_init,
277 NULL, /* class_finalize */
278 NULL, /* class_data */
279 sizeof(HildonTimeEditor),
281 (GInstanceInitFunc) hildon_time_editor_init,
283 editor_type = g_type_register_static(GTK_TYPE_CONTAINER,
290 static void hildon_time_editor_forall(GtkContainer * container,
291 gboolean include_internals,
292 GtkCallback callback,
293 gpointer callback_data)
295 HildonTimeEditor *editor;
296 HildonTimeEditorPrivate *priv;
298 g_assert(HILDON_IS_TIME_EDITOR(container));
299 g_assert(callback != NULL);
301 editor = HILDON_TIME_EDITOR(container);
302 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
304 if (!include_internals)
307 /* widget that are always shown */
308 (*callback) (priv->iconbutton, callback_data);
309 (*callback) (priv->frame, callback_data);
312 static void hildon_time_editor_destroy(GtkObject * self)
314 HildonTimeEditorPrivate *priv;
316 priv = HILDON_TIME_EDITOR_GET_PRIVATE(self);
318 if (priv->iconbutton) {
319 gtk_widget_unparent(priv->iconbutton);
320 priv->iconbutton = NULL;
323 gtk_widget_unparent(priv->frame);
327 if (GTK_OBJECT_CLASS(parent_class)->destroy)
328 GTK_OBJECT_CLASS(parent_class)->destroy(self);
333 hildon_time_editor_class_init(HildonTimeEditorClass * editor_class)
335 GObjectClass *object_class = G_OBJECT_CLASS(editor_class);
336 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(editor_class);
337 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(editor_class);
339 parent_class = g_type_class_peek_parent(editor_class);
341 g_type_class_add_private(editor_class,
342 sizeof(HildonTimeEditorPrivate));
344 object_class->get_property = hildon_time_editor_get_property;
345 object_class->set_property = hildon_time_editor_set_property;
346 widget_class->size_request = hildon_time_editor_size_request;
347 widget_class->size_allocate = hildon_time_editor_size_allocate;
348 widget_class->tap_and_hold_setup =
349 hildon_time_editor_tap_and_hold_setup;
350 widget_class->focus = hildon_composite_widget_focus;
352 container_class->forall = hildon_time_editor_forall;
353 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_time_editor_destroy;
355 object_class->finalize = hildon_time_editor_finalize;
357 editor_class->time_error = hildon_time_editor_time_error;
359 time_editor_signals[TIME_ERROR] =
360 g_signal_new("time-error",
361 G_OBJECT_CLASS_TYPE(object_class),
363 G_STRUCT_OFFSET(HildonTimeEditorClass, time_error),
364 g_signal_accumulator_true_handled, NULL,
365 _hildon_marshal_BOOLEAN__ENUM,
366 G_TYPE_BOOLEAN, 1, HILDON_TYPE_TIME_EDITOR_ERROR_TYPE);
369 * HildonTimeEditor:ticks:
371 * If editor is in duration mode, contains the duration seconds.
372 * If not, contains seconds since midnight.
374 g_object_class_install_property( object_class, PROP_TICKS,
375 g_param_spec_uint("ticks",
377 "Current value of duration",
379 HILDON_TIME_EDITOR_TICKS_VALUE,
380 G_PARAM_READABLE | G_PARAM_WRITABLE) );
383 * HildonTimeEditor:show_seconds:
385 * Controls whether seconds are shown in the editor
387 g_object_class_install_property( object_class, PROP_SHOW_SECONDS,
388 g_param_spec_boolean("show_seconds",
389 "Show seconds property",
390 "Controls whether the seconds are shown in the editor",
392 G_PARAM_READABLE | G_PARAM_WRITABLE) );
395 * HildonTimeEditor:show_hours:
397 * Controls whether hours are shown in the editor
399 g_object_class_install_property( object_class, PROP_SHOW_HOURS,
400 g_param_spec_boolean("show_hours",
402 "Controls whether the hours field is shown in the editor",
404 G_PARAM_READABLE | G_PARAM_WRITABLE) );
407 * HildonTimeEditor:duration_mode:
409 * Controls whether the TimeEditor is in duration mode
411 g_object_class_install_property( object_class, PROP_DURATION_MODE,
412 g_param_spec_boolean("duration_mode",
414 "Controls whether the TimeEditor is in duration mode",
415 HILDON_TIME_EDITOR_DURATION_MODE,
416 G_PARAM_READABLE | G_PARAM_WRITABLE) );
419 * HildonTimeEditor:duration_min:
421 * Minimum allowed duration value.
423 g_object_class_install_property( object_class, PROP_DURATION_MIN,
424 g_param_spec_uint("duration_min",
425 "Minumum duration value",
426 "Smallest possible duration value",
427 MIN_DURATION, MAX_DURATION,
428 HILDON_TIME_EDITOR_DURATION_LOWER_VALUE,
429 G_PARAM_READABLE | G_PARAM_WRITABLE) );
432 * HildonTimeEditor:duration_max:
434 * Maximum allowed duration value.
436 g_object_class_install_property( object_class, PROP_DURATION_MAX,
437 g_param_spec_uint("duration_max",
438 "Maximum duration value",
439 "Largest possible duration value",
441 HILDON_TIME_EDITOR_DURATION_UPPER_VALUE,
442 G_PARAM_READABLE | G_PARAM_WRITABLE) );
446 void hildon_time_editor_tap_and_hold_setup(GtkWidget * widget,
449 GtkWidgetTapAndHoldFlags flags)
451 HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE(widget);
454 /* Forward this tap_and_hold_setup signal to all our child widgets */
455 for (i = 0; i < ENTRY_COUNT; i++)
457 gtk_widget_tap_and_hold_setup(priv->entries[i], menu, func,
458 GTK_TAP_AND_HOLD_NO_SIGNALS);
460 gtk_widget_tap_and_hold_setup(priv->eventbox, menu, func,
461 GTK_TAP_AND_HOLD_NO_SIGNALS);
462 gtk_widget_tap_and_hold_setup(priv->iconbutton, menu, func,
463 GTK_TAP_AND_HOLD_NONE);
466 static void hildon_time_editor_entry_changed(GtkWidget *widget, gpointer data)
468 g_assert(HILDON_IS_TIME_EDITOR(data));
469 hildon_time_editor_validate(HILDON_TIME_EDITOR(data), TRUE);
472 static void hildon_time_editor_init(HildonTimeEditor * editor)
474 HildonTimeEditorPrivate *priv;
475 GtkWidget *hbox, *icon;
478 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
480 gtk_widget_push_composite_child();
482 /* Setup defaults and create widgets */
484 priv->show_seconds = FALSE;
485 priv->show_hours = TRUE;
486 priv->ampm_pos_after = TRUE;
487 priv->clock_24h = TRUE;
488 priv->duration_mode = FALSE;
489 priv->iconbutton = gtk_button_new();
490 priv->ampm_label = gtk_label_new(NULL);
491 priv->hm_label = gtk_label_new(NULL);
492 priv->sec_label = gtk_label_new(NULL);
493 priv->frame = gtk_frame_new(NULL);
494 priv->eventbox = gtk_event_box_new();
496 icon = gtk_image_new_from_icon_name(ICON_NAME, HILDON_ICON_SIZE_WIDG);
497 hbox = gtk_hbox_new(FALSE, 0);
499 GTK_WIDGET_SET_FLAGS(editor, GTK_NO_WINDOW);
500 GTK_WIDGET_UNSET_FLAGS(priv->iconbutton, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
502 gtk_event_box_set_visible_window(GTK_EVENT_BOX(priv->eventbox), FALSE);
504 gtk_container_set_border_width(GTK_CONTAINER(priv->frame), 0);
506 gtk_container_add(GTK_CONTAINER(priv->iconbutton), icon);
507 gtk_container_add(GTK_CONTAINER(priv->eventbox), priv->ampm_label);
509 /* Create hour, minute and second entries */
510 for (i = 0; i < ENTRY_COUNT; i++)
512 priv->entries[i] = gtk_entry_new();
514 /* No frames for entries, so that they all appear to be inside one long entry */
515 gtk_entry_set_has_frame(GTK_ENTRY(priv->entries[i]), FALSE);
517 /* Set the entries to accept only numeric characters */
518 g_object_set (priv->entries[i], "input-mode",
519 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
521 /* The entry fields all take exactly two characters */
522 gtk_entry_set_max_length (GTK_ENTRY(priv->entries[i]), 2);
523 gtk_entry_set_width_chars (GTK_ENTRY(priv->entries[i]), 2);
525 /* Connect signals */
526 g_signal_connect(priv->entries[i], "button-release-event",
527 G_CALLBACK(hildon_time_editor_entry_clicked), editor);
528 g_signal_connect(priv->entries[i], "focus-in-event",
529 G_CALLBACK(hildon_time_editor_entry_focusin), editor);
530 g_signal_connect(priv->entries[i], "focus-out-event",
531 G_CALLBACK(hildon_time_editor_entry_focusout), editor);
532 g_signal_connect(priv->entries[i], "key-press-event",
533 G_CALLBACK(hildon_time_editor_entry_keypress), editor);
534 g_signal_connect(priv->entries[i], "changed",
535 G_CALLBACK(hildon_time_editor_entry_changed), editor);
537 /* inserted signal sets time */
538 g_signal_connect_after (G_OBJECT(priv->entries[i]), "insert_text",
539 G_CALLBACK (hildon_time_editor_inserted_text),
543 /* clicked signal for am/pm label */
544 g_signal_connect(G_OBJECT(priv->eventbox), "button_press_event",
545 G_CALLBACK(hildon_time_editor_ampm_clicked), editor);
547 /* clicked signal for icon */
548 g_signal_connect(G_OBJECT(priv->iconbutton), "clicked",
549 G_CALLBACK(hildon_time_editor_icon_clicked), editor);
551 /* Set ourself as the parent of all the widgets we created */
552 gtk_widget_set_parent(priv->iconbutton, GTK_WIDGET(editor));
553 gtk_box_pack_start(GTK_BOX(hbox), priv->entries[ENTRY_HOURS], FALSE, FALSE, 0);
554 gtk_box_pack_start(GTK_BOX(hbox), priv->hm_label, FALSE, FALSE, 0);
555 gtk_box_pack_start(GTK_BOX(hbox), priv->entries[ENTRY_MINS], FALSE, FALSE, 0);
556 gtk_box_pack_start(GTK_BOX(hbox), priv->sec_label, FALSE, FALSE, 0);
557 gtk_box_pack_start(GTK_BOX(hbox), priv->entries[ENTRY_SECS], FALSE, FALSE, 0);
558 gtk_box_pack_start(GTK_BOX(hbox), priv->eventbox, FALSE, FALSE, 0);
560 gtk_container_add(GTK_CONTAINER(priv->frame), hbox);
562 /* Show created widgets */
563 gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
564 gtk_widget_show_all(priv->frame);
565 gtk_widget_show_all(priv->iconbutton);
567 /* Update AM/PM and time separators settings from locale */
568 if (!hildon_time_editor_check_locale(editor)) {
569 /* Using 12h clock */
570 priv->clock_24h = FALSE;
572 gtk_widget_hide(priv->eventbox);
575 if (!priv->show_seconds) {
576 gtk_widget_hide(priv->sec_label);
577 gtk_widget_hide(priv->entries[ENTRY_SECS]);
580 /* set the default time to current time. */
581 hildon_time_editor_set_to_current_time (editor);
583 gtk_widget_pop_composite_child();
586 static void hildon_time_editor_set_property (GObject *object,
591 HildonTimeEditor *time_editor = HILDON_TIME_EDITOR(object);
595 hildon_time_editor_set_ticks (time_editor, g_value_get_uint(value));
598 case PROP_SHOW_SECONDS:
599 hildon_time_editor_set_show_seconds (time_editor, g_value_get_boolean(value));
602 case PROP_SHOW_HOURS:
603 hildon_time_editor_set_show_hours (time_editor, g_value_get_boolean(value));
606 case PROP_DURATION_MODE:
607 hildon_time_editor_set_duration_mode (time_editor, g_value_get_boolean(value));
610 case PROP_DURATION_MIN:
611 hildon_time_editor_set_duration_min (time_editor, g_value_get_uint(value));
614 case PROP_DURATION_MAX:
615 hildon_time_editor_set_duration_max (time_editor, g_value_get_uint(value));
619 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
624 static void hildon_time_editor_get_property (GObject *object,
629 HildonTimeEditor *time_editor = HILDON_TIME_EDITOR(object);
634 g_value_set_uint (value, hildon_time_editor_get_ticks (time_editor));
637 case PROP_SHOW_SECONDS:
638 g_value_set_boolean (value, hildon_time_editor_get_show_seconds (time_editor));
641 case PROP_SHOW_HOURS:
642 g_value_set_boolean (value, hildon_time_editor_get_show_hours (time_editor));
645 case PROP_DURATION_MODE:
646 g_value_set_boolean (value, hildon_time_editor_get_duration_mode (time_editor));
649 case PROP_DURATION_MIN:
650 g_value_set_uint (value, hildon_time_editor_get_duration_min (time_editor));
653 case PROP_DURATION_MAX:
654 g_value_set_uint (value, hildon_time_editor_get_duration_max (time_editor));
658 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
664 * hildon_time_editor_new:
666 * This function creates a new time editor.
668 * Returns: pointer to a new #HildonTimeEditor widget
671 GtkWidget *hildon_time_editor_new(void)
673 return GTK_WIDGET(g_object_new(HILDON_TYPE_TIME_EDITOR, NULL));
676 static void hildon_time_editor_finalize(GObject * obj_self)
678 HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE(obj_self);
680 g_free(priv->am_symbol);
681 g_free(priv->pm_symbol);
683 if (priv->highlight_idle)
684 g_source_remove(priv->highlight_idle);
686 if (G_OBJECT_CLASS(parent_class)->finalize)
687 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
691 * _hildon_time_editor_get_time_separators:
692 * @editor: the #HildonTimeEditor
693 * @hm_sep_label: the label that will show the hour:minutes separator
694 * @ms_sep_label: the label that will show the minutes:seconds separator
696 * Gets hour-minute separator and minute-second separator from current
697 * locale and sets then to the labels we set as parameters. Both
698 * parameters can be NULL if you just want to assing one separator.
702 _hildon_time_editor_get_time_separators(GtkLabel *hm_sep_label,
703 GtkLabel *ms_sep_label)
707 GDate locale_test_date;
710 /* Get localized time string */
711 g_date_set_dmy(&locale_test_date, 1, 2, 1970);
712 (void) g_date_strftime(buffer, sizeof(buffer), "%X", &locale_test_date);
714 if (hm_sep_label != NULL)
716 /* Find h-m separator */
718 while (*iter && g_ascii_isdigit(*iter)) iter++;
720 /* Extract h-m separator*/
722 while (*endp && !g_ascii_isdigit(*endp)) endp++;
723 separator = g_strndup(iter, endp - iter);
724 gtk_label_set_label(hm_sep_label, separator);
728 if (ms_sep_label != NULL)
730 /* Find m-s separator */
732 while (*iter && g_ascii_isdigit(*iter)) iter++;
734 /* Extract m-s separator*/
736 while (*endp && !g_ascii_isdigit(*endp)) endp++;
737 separator = g_strndup(iter, endp - iter);
738 gtk_label_set_label(ms_sep_label, separator);
744 /* Convert ticks to H:M:S. Ticks = seconds since 00:00:00. */
745 static void ticks_to_time (guint ticks,
752 *hours = ticks / 3600;
754 *minutes = left / 60;
755 *seconds = left % 60;
759 * hildon_time_editor_set_ticks:
760 * @editor: the #HildonTimeEditor widget
761 * @ticks: the duration to set, in seconds
763 * Sets the current duration in seconds. This means seconds from
764 * midnight, if not in duration mode. In case of any errors, it tries
768 void hildon_time_editor_set_ticks (HildonTimeEditor * editor,
771 HildonTimeEditorPrivate *priv;
775 g_assert(HILDON_IS_TIME_EDITOR(editor));
777 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
779 /* Validate ticks. If it's too low or too high, set it to
780 min/max value for the current mode. */
781 if (priv->duration_mode)
782 priv->ticks = CLAMP(ticks, priv->duration_min, priv->duration_max);
784 /* Check that ticks value is valid. We only need to check that hours
786 ticks_to_time (ticks, &h, &m, &s);
787 if (h > HOURS_MAX_24)
788 ticks = TICKS(HOURS_MAX_24, m, s);
793 /* Get the time in H:M:S. */
794 ticks_to_time (priv->ticks, &h, &m, &s);
796 if (!priv->clock_24h && !priv->duration_mode)
798 /* Convert 24h H:M:S values to 12h mode, and update AM/PM state */
799 convert_to_12h (&h, &priv->am);
802 /* Set H:M:S values to entries. We do not want to invoke validation
803 callbacks (since they can cause new call to this function), so we
804 block signals while setting values. */
805 for (i = 0; i < ENTRY_COUNT; i++)
807 g_signal_handlers_block_by_func(priv->entries[i],
808 (gpointer) hildon_time_editor_entry_changed, editor);
810 g_signal_handlers_block_by_func(priv->entries[i],
811 (gpointer) hildon_time_editor_inserted_text, editor);
813 g_signal_handlers_block_by_func(priv->entries[i],
814 (gpointer) hildon_time_editor_entry_focusout, editor);
818 g_snprintf(str, sizeof(str), "%02u", h);
819 gtk_entry_set_text(GTK_ENTRY(priv->entries[ENTRY_HOURS]), str);
821 g_snprintf(str, sizeof(str), "%02u", m);
822 gtk_entry_set_text(GTK_ENTRY(priv->entries[ENTRY_MINS]), str);
824 g_snprintf(str, sizeof(str), "%02u", s);
825 gtk_entry_set_text(GTK_ENTRY(priv->entries[ENTRY_SECS]), str);
827 for (i = 0; i < ENTRY_COUNT; i++)
829 g_signal_handlers_unblock_by_func(priv->entries[i],
830 (gpointer) hildon_time_editor_entry_changed, editor);
832 g_signal_handlers_unblock_by_func(priv->entries[i],
833 (gpointer) hildon_time_editor_inserted_text, editor);
835 g_signal_handlers_unblock_by_func(priv->entries[i],
836 (gpointer) hildon_time_editor_entry_focusout, editor);
840 /* Update AM/PM label in case we're in 12h mode */
841 gtk_label_set_label(GTK_LABEL(priv->ampm_label),
842 priv->am ? priv->am_symbol : priv->pm_symbol);
844 g_object_notify (G_OBJECT (editor), "ticks");
848 hildon_time_editor_set_to_current_time (HildonTimeEditor * editor)
854 tm = localtime(&now);
857 hildon_time_editor_set_time(editor, tm->tm_hour, tm->tm_min, tm->tm_sec);
861 * hildon_time_editor_get_ticks:
862 * @editor: the #HildonTimeEditor widget
864 * This function returns the current duration, in seconds.
865 * This means seconds from midnight, if not in duration mode.
867 * Returns: current duration in seconds
870 guint hildon_time_editor_get_ticks (HildonTimeEditor * editor)
872 HildonTimeEditorPrivate *priv;
874 g_return_val_if_fail(editor, 0);
875 g_return_val_if_fail(HILDON_IS_TIME_EDITOR(editor), 0);
877 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
879 return (priv->ticks);
883 * hildon_time_editor_set_show_seconds:
884 * @editor: the #HildonTimeEditor
885 * @show_seconds: enable or disable showing of seconds
887 * This function shows or hides the seconds field.
890 void hildon_time_editor_set_show_seconds (HildonTimeEditor * editor,
891 gboolean show_seconds)
893 HildonTimeEditorPrivate *priv;
895 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
897 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
899 if (show_seconds != priv->show_seconds) {
900 priv->show_seconds = show_seconds;
902 /* show/hide seconds field and its ':' label if the value changed. */
904 gtk_widget_show(priv->entries[ENTRY_SECS]);
905 gtk_widget_show(priv->sec_label);
907 gtk_widget_hide(priv->entries[ENTRY_SECS]);
908 gtk_widget_hide(priv->sec_label);
911 g_object_notify (G_OBJECT (editor), "show_seconds");
916 * hildon_time_editor_get_show_seconds:
917 * @editor: the #HildonTimeEditor widget
919 * This function returns a boolean indicating the visibility of
920 * seconds in the #HildonTimeEditor
922 * Returns: TRUE if the seconds are visible
925 gboolean hildon_time_editor_get_show_seconds (HildonTimeEditor * editor)
927 HildonTimeEditorPrivate *priv;
929 g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
930 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
932 return (priv->show_seconds);
936 * hildon_time_editor_set_duration_mode:
937 * @editor: the #HildonTimeEditor
938 * @duration_mode: enable or disable duration editor mode
940 * This function sets the duration editor mode in which the maximum hours
944 void hildon_time_editor_set_duration_mode (HildonTimeEditor * editor,
945 gboolean duration_mode)
947 HildonTimeEditorPrivate *priv;
949 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
951 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
953 if (duration_mode != priv->duration_mode) {
954 priv->duration_mode = duration_mode;
957 /* FIXME: Why do we reset the duration range here?
958 Would change API, so won't touch this for now. */
959 hildon_time_editor_set_duration_range(editor, MIN_DURATION,
961 /* There's no AM/PM label or time picker icon in duration mode.
962 Make sure they're hidden. */
963 gtk_widget_hide(GTK_WIDGET(priv->ampm_label));
964 gtk_widget_hide(GTK_WIDGET(priv->eventbox));
965 gtk_widget_hide(GTK_WIDGET(priv->iconbutton));
966 /* Duration mode has seconds by default. */
967 hildon_time_editor_set_show_seconds(editor, TRUE);
969 /* Make sure AM/PM label and time picker icons are visible if needed */
970 if (!priv->clock_24h)
971 gtk_widget_show(GTK_WIDGET(priv->ampm_label));
973 gtk_widget_show(GTK_WIDGET(priv->eventbox));
974 gtk_widget_show(GTK_WIDGET(priv->iconbutton));
976 /* Reset the ticks to current time. Anything set in duration mode
977 * is bound to be invalid or useless in time mode.
979 hildon_time_editor_set_to_current_time (editor);
982 g_object_notify (G_OBJECT (editor), "duration_mode");
987 * hildon_time_editor_get_duration_mode:
988 * @editor: the #HildonTimeEditor widget
990 * This function returns a boolean indicating whether the #HildonTimeEditor
991 * is in the duration mode.
993 * Returns: TRUE if the #HildonTimeEditor is in duration mode
996 gboolean hildon_time_editor_get_duration_mode (HildonTimeEditor * editor)
998 HildonTimeEditorPrivate *priv;
1000 g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
1001 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1003 return (priv->duration_mode);
1007 * hildon_time_editor_set_duration_min:
1008 * @editor: the #HildonTimeEditor widget
1009 * @duration_min: mimimum allowed duration
1011 * Sets the minimum allowed duration for the duration mode.
1012 * Note: Has no effect in time mode
1015 void hildon_time_editor_set_duration_min (HildonTimeEditor * editor,
1018 HildonTimeEditorPrivate *priv;
1020 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1021 g_return_if_fail(duration_min >= MIN_DURATION);
1023 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1025 if( !priv->duration_mode )
1028 priv->duration_min = duration_min;
1030 /* Clamp the current value to the minimum if necessary */
1031 if (priv->ticks < duration_min)
1033 hildon_time_editor_set_ticks (editor, duration_min);
1036 g_object_notify (G_OBJECT (editor), "duration_min");
1040 * hildon_time_editor_get_duration_min:
1041 * @editor: the #HildonTimeEditor widget
1043 * This function returns the smallest duration the #HildonTimeEditor
1044 * allows in the duration mode.
1046 * Returns: minimum allowed duration in seconds
1049 guint hildon_time_editor_get_duration_min (HildonTimeEditor * editor)
1051 HildonTimeEditorPrivate *priv;
1053 g_return_val_if_fail(HILDON_IS_TIME_EDITOR(editor), 0);
1055 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1057 if( !priv->duration_mode )
1060 return (priv->duration_min);
1064 * hildon_time_editor_set_duration_max:
1065 * @editor: the #HildonTimeEditor widget
1066 * @duration_max: maximum allowed duration in seconds
1068 * Sets the maximum allowed duration in seconds for the duration mode.
1069 * Note: Has no effect in time mode
1072 void hildon_time_editor_set_duration_max (HildonTimeEditor * editor,
1075 HildonTimeEditorPrivate *priv;
1077 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1078 g_return_if_fail(duration_max <= MAX_DURATION);
1080 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1082 if( !priv->duration_mode )
1085 priv->duration_max = duration_max;
1087 /* Clamp the current value to the maximum if necessary */
1088 if (priv->ticks > duration_max)
1090 hildon_time_editor_set_ticks (editor, duration_max);
1093 g_object_notify (G_OBJECT (editor), "duration_max");
1097 * hildon_time_editor_get_duration_max:
1098 * @editor: the #HildonTimeEditor widget
1100 * This function returns the longest duration the #HildonTimeEditor
1101 * allows in the duration mode.
1103 * Returns: maximum allowed duration in seconds
1106 guint hildon_time_editor_get_duration_max (HildonTimeEditor * editor)
1108 HildonTimeEditorPrivate *priv;
1110 g_return_val_if_fail(HILDON_IS_TIME_EDITOR(editor), 0);
1112 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1114 if( !priv->duration_mode )
1117 return (priv->duration_max);
1122 * hildon_time_editor_set_time:
1123 * @editor: the #HildonTimeEditor widget
1128 * This function sets the time on an existing time editor. If the
1129 * time specified by the arguments is invalid, it's fixed.
1130 * The time is assumed to be in 24h format.
1133 void hildon_time_editor_set_time(HildonTimeEditor * editor, guint hours,
1134 guint minutes, guint seconds)
1136 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1138 hildon_time_editor_set_ticks (editor, TICKS(hours, minutes, seconds));
1142 * hildon_time_editor_get_time:
1143 * @editor: the #HildonTimeEditor widget
1148 * Gets the time of the #HildonTimeEditor widget. The time returned is
1149 * always in 24h format.
1152 void hildon_time_editor_get_time(HildonTimeEditor * editor,
1154 guint * minutes, guint * seconds)
1156 HildonTimeEditorPrivate *priv;
1158 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1160 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1162 ticks_to_time (hildon_time_editor_get_ticks (editor),
1163 hours, minutes, seconds);
1167 * hildon_time_editor_set_duration_range:
1168 * @editor: the #HildonTimeEditor widget
1169 * @min_seconds: minimum allowed time in seconds
1170 * @max_seconds: maximum allowed time in seconds
1172 * Sets the duration editor time range of the #HildonTimeEditor widget.
1175 void hildon_time_editor_set_duration_range(HildonTimeEditor * editor,
1179 HildonTimeEditorPrivate *priv;
1182 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1184 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1185 /* Swap values if reversed */
1186 if (min_seconds > max_seconds)
1189 max_seconds = min_seconds;
1193 hildon_time_editor_set_duration_max (editor, max_seconds);
1194 hildon_time_editor_set_duration_min (editor, min_seconds);
1196 if (priv->duration_mode) {
1197 /* Set minimum allowed value for duration editor.
1198 FIXME: Shouldn't it be changed only if it's not in range?
1199 Would change API, so won't touch this for now. */
1200 hildon_time_editor_set_ticks(editor, min_seconds);
1205 * hildon_time_editor_get_duration_range:
1206 * @editor: the #HildonTimeEditor widget
1207 * @min_seconds: pointer to guint
1208 * @max_seconds: pointer to guint
1210 * Gets the duration editor time range of the #HildonTimeEditor widget.
1213 void hildon_time_editor_get_duration_range(HildonTimeEditor * editor,
1214 guint * min_seconds,
1215 guint * max_seconds)
1217 HildonTimeEditorPrivate *priv;
1219 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1221 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1223 *min_seconds = priv->duration_min;
1224 *max_seconds = priv->duration_max;
1227 static gboolean hildon_time_editor_check_locale(HildonTimeEditor * editor)
1229 HildonTimeEditorPrivate *priv;
1231 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1233 /* Update time separator symbols */
1234 _hildon_time_editor_get_time_separators(GTK_LABEL(priv->hm_label), GTK_LABEL(priv->sec_label));
1236 /* Get AM/PM symbols. */
1237 priv->am_symbol = g_strdup(nl_langinfo(AM_STR));
1238 priv->pm_symbol = g_strdup(nl_langinfo(PM_STR));
1240 if (priv->am_symbol[0] == '\0')
1243 /* 12h clock mode. Check if AM/PM should be before or after time.
1244 %p is the AM/PM string, so we assume that if the format string
1245 begins with %p it's in the beginning, and in any other case it's
1246 in the end (although that's not necessarily the case). */
1247 if (strncmp(nl_langinfo(T_FMT_AMPM), "%p", 2) == 0)
1248 priv->ampm_pos_after = FALSE;
1253 static gboolean hildon_time_editor_entry_focusin(GtkWidget * widget,
1254 GdkEventFocus * event,
1257 /* If we were trying to move away from a field with invalid value,
1258 we get moved back to it. Here we want to select the text in the field.
1259 The !button check checks that the entry wasn't focused with a mouse
1262 The selection happens temporarily if we got here with left/right
1263 keys, but it gets immediately unselected within same call due to some
1264 inner entry/clipboard magic. */
1265 if (!GTK_ENTRY(widget)->button)
1266 gtk_editable_select_region(GTK_EDITABLE(widget), 0, 2);
1272 hildon_time_editor_time_error(HildonTimeEditor *editor,
1273 HildonTimeEditorErrorType type)
1278 /* Returns negative if we didn't get value,
1279 * and should stop further validation
1281 static gint validated_conversion(HildonTimeEditorPrivate *priv,
1285 gboolean allow_intermediate,
1287 GString *error_string)
1293 text = gtk_entry_get_text(GTK_ENTRY(field));
1295 if (text && text[0])
1297 /* Try to convert entry text to number */
1298 value = strtol(text, &tail, 10);
1300 /* Check if conversion succeeded */
1304 g_string_printf(error_string, _("ckct_ib_maximum_value"), max);
1305 priv->error_widget = field;
1306 *error_code = MAX_VALUE;
1309 if (value < min && !allow_intermediate) {
1310 g_string_printf(error_string, _("ckct_ib_minimum_value"), min);
1311 priv->error_widget = field;
1312 *error_code = MIN_VALUE;
1318 /* We'll handle failed conversions soon */
1320 else if (allow_intermediate)
1321 return -1; /* Empty field while user is still editing. No error, but
1322 cannot validate either... */
1323 else /* Empty field: show error and set value to minimum allowed */
1325 g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max);
1326 priv->error_widget = field;
1327 *error_code = WITHIN_RANGE;
1331 /* Empty field and not allowed intermediated OR failed conversion */
1332 g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max);
1333 priv->error_widget = field;
1334 *error_code = WITHIN_RANGE;
1339 hildon_time_editor_real_validate(HildonTimeEditor *editor,
1340 gboolean allow_intermediate, GString *error_string)
1342 HildonTimeEditorPrivate *priv;
1343 guint h, m, s, ticks;
1345 guint max_hours, min_hours, max_minutes, min_minutes, max_seconds, min_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 /* Get time components from fields and validate them... */
1368 if (priv->show_hours) {
1369 h = validated_conversion(priv, priv->entries[ENTRY_HOURS], min_hours, max_hours,
1370 allow_intermediate, &error_code, error_string);
1371 if (priv->error_widget == priv->entries[ENTRY_HOURS])
1372 g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, hour_errors[error_code], &r);
1373 if ((gint) h < 0) return;
1376 m = validated_conversion(priv, priv->entries[ENTRY_MINS], MINUTES_MIN, MINUTES_MAX,
1377 allow_intermediate, &error_code, error_string);
1378 if (priv->error_widget == priv->entries[ENTRY_MINS])
1379 g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, min_errors[error_code], &r);
1380 if ((gint) m < 0) return;
1381 if (priv->show_seconds) {
1382 s = validated_conversion(priv, priv->entries[ENTRY_SECS], SECONDS_MIN, SECONDS_MAX,
1383 allow_intermediate, &error_code, error_string);
1384 if (priv->error_widget == priv->entries[ENTRY_SECS])
1385 g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, sec_errors[error_code], &r);
1386 if ((gint) s < 0) return;
1390 /* Ok, we now do separate check that tick count is valid for duration mode */
1391 if (priv->duration_mode)
1393 ticks = TICKS(h, m, s);
1395 if (ticks < priv->duration_min && !allow_intermediate)
1397 g_string_printf(error_string,
1398 _("ckct_ib_min_allowed_duration_hts"),
1399 min_hours, min_minutes, min_seconds);
1400 hildon_time_editor_set_ticks (editor, priv->duration_min);
1401 priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
1402 g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, MIN_DUR, &r);
1405 else if (ticks > priv->duration_max)
1407 g_string_printf(error_string,
1408 _("ckct_ib_max_allowed_duration_hts"),
1409 max_hours, max_minutes, max_seconds);
1410 hildon_time_editor_set_ticks (editor, priv->duration_max);
1411 priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS];
1412 g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, MAX_DUR, &r);
1416 else if (!priv->clock_24h)
1417 convert_to_24h (&h, priv->am);
1419 /* The only case when we do not want to refresh the
1420 time display, is when the user is editing a value
1421 (unless the value was out of bounds and we have to fix it) */
1422 if (!allow_intermediate || priv->error_widget)
1423 hildon_time_editor_set_time (editor, h, m, s);
1426 /* Setting text to entries causes entry to recompute itself
1427 in idle callback, which remove selection. Because of this
1428 we need to do selection in idle as well. */
1429 static gboolean highlight_callback(gpointer data)
1431 HildonTimeEditorPrivate *priv;
1435 GDK_THREADS_ENTER ();
1437 g_assert(HILDON_IS_TIME_EDITOR(data));
1439 priv = HILDON_TIME_EDITOR_GET_PRIVATE(data);
1440 widget = priv->error_widget;
1441 priv->error_widget = NULL;
1442 priv->highlight_idle = 0;
1444 g_assert(GTK_IS_ENTRY(widget));
1446 /* Avoid revalidation because it will issue the date_error signal
1447 twice when there is an empty field. We must block the signal
1448 for all the entries because we do not know where the focus
1450 for (i = 0; i < ENTRY_COUNT; i++)
1451 g_signal_handlers_block_by_func(priv->entries[i],
1452 (gpointer) hildon_time_editor_entry_focusout, data);
1453 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1454 gtk_widget_grab_focus(widget);
1455 for (i = 0; i < ENTRY_COUNT; i++)
1456 g_signal_handlers_unblock_by_func(priv->entries[i],
1457 (gpointer) hildon_time_editor_entry_focusout, data);
1459 GDK_THREADS_LEAVE ();
1464 /* Update ticks from current H:M:S entries. If they're invalid, show an
1465 infoprint and update the fields unless they're empty. */
1467 hildon_time_editor_validate (HildonTimeEditor *editor, gboolean allow_intermediate)
1469 HildonTimeEditorPrivate *priv;
1470 GString *error_message;
1472 g_assert(HILDON_IS_TIME_EDITOR(editor));
1474 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1476 /* if there is already an error we do nothing until it will be managed by the idle */
1477 if (priv->highlight_idle == 0)
1479 error_message = g_string_new(NULL);
1480 hildon_time_editor_real_validate(editor,
1481 allow_intermediate, error_message);
1483 if (priv->error_widget)
1485 hildon_banner_show_information(priv->error_widget, NULL,
1486 error_message->str);
1488 priv->highlight_idle = g_idle_add(highlight_callback, editor);
1491 g_string_free(error_message, TRUE);
1495 /* on inserted text, if entry has two digits, jumps to the next field. */
1497 hildon_time_editor_inserted_text (GtkEditable * editable,
1499 gint new_text_length,
1503 HildonTimeEditor *editor;
1506 HildonTimeEditorPrivate *priv;
1508 entry = GTK_ENTRY(editable);
1509 editor = HILDON_TIME_EDITOR(user_data);
1511 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1513 /* if there is already an error we don't have to do anything */
1514 if (!priv->error_widget)
1517 value = (gchar *) gtk_entry_get_text(entry);
1519 if (strlen(value) == 2)
1521 HildonTimeEditorPrivate *priv;
1523 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1525 if (GTK_WIDGET(editable) == priv->entries[ENTRY_HOURS])
1527 /* we don't want a focusout signal with this grab_focus
1528 because the validation was already done during the
1530 g_signal_handlers_block_by_func(priv->entries[ENTRY_HOURS],
1531 (gpointer) hildon_time_editor_entry_focusout, editor);
1533 gtk_widget_grab_focus(priv->entries[ENTRY_MINS]);
1536 g_signal_handlers_unblock_by_func(priv->entries[ENTRY_HOURS],
1537 (gpointer) hildon_time_editor_entry_focusout, editor);
1540 else if (GTK_WIDGET(editable) == priv->entries[ENTRY_MINS] &&
1541 GTK_WIDGET_VISIBLE (priv->entries[ENTRY_SECS]))
1543 g_signal_handlers_block_by_func(priv->entries[ENTRY_MINS],
1544 (gpointer) hildon_time_editor_entry_focusout, editor);
1546 gtk_widget_grab_focus(priv->entries[ENTRY_SECS]);
1549 g_signal_handlers_unblock_by_func(priv->entries[ENTRY_MINS],
1550 (gpointer) hildon_time_editor_entry_focusout, editor);
1557 static gboolean hildon_time_editor_entry_focusout(GtkWidget * widget,
1558 GdkEventFocus * event,
1561 g_assert(HILDON_IS_TIME_EDITOR(data));
1563 /* Validate the given time and update ticks. */
1564 hildon_time_editor_validate(HILDON_TIME_EDITOR(data), FALSE);
1570 hildon_time_editor_ampm_clicked(GtkWidget * widget,
1571 GdkEventButton * event, gpointer data)
1573 HildonTimeEditor *editor;
1574 HildonTimeEditorPrivate *priv;
1576 g_assert(GTK_IS_WIDGET(widget));
1577 g_assert(HILDON_IS_TIME_EDITOR(data));
1579 editor = HILDON_TIME_EDITOR(data);
1580 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1582 /* First validate the given time and update ticks. */
1583 hildon_time_editor_validate (editor, FALSE);
1585 /* Apply the AM/PM change by moving the current time by 12 hours */
1587 /* 00:00 .. 11:59 -> 12:00 .. 23:59 */
1588 hildon_time_editor_set_ticks (editor, priv->ticks + 12*3600);
1590 /* 12:00 .. 23:59 -> 00:00 .. 11:59 */
1591 hildon_time_editor_set_ticks (editor, priv->ticks - 12*3600);
1597 hildon_time_editor_icon_clicked(GtkWidget * widget, gpointer data)
1599 HildonTimeEditor *editor;
1602 guint h, m, s, result;
1604 g_assert(HILDON_IS_TIME_EDITOR(data));
1606 editor = HILDON_TIME_EDITOR(data);
1608 /* icon is passive in duration editor mode */
1609 if (hildon_time_editor_get_duration_mode(editor))
1612 /* Launch HildonTimePicker dialog */
1613 parent = gtk_widget_get_ancestor(GTK_WIDGET(editor), GTK_TYPE_WINDOW);
1614 picker = hildon_time_picker_new(GTK_WINDOW(parent));
1616 hildon_time_editor_get_time(editor, &h, &m, &s);
1617 hildon_time_picker_set_time(HILDON_TIME_PICKER(picker), h, m);
1619 result = gtk_dialog_run(GTK_DIALOG(picker));
1621 case GTK_RESPONSE_OK:
1622 case GTK_RESPONSE_ACCEPT:
1623 /* Use the selected time */
1624 hildon_time_picker_get_time(HILDON_TIME_PICKER(picker), &h, &m);
1625 hildon_time_editor_set_time(editor, h, m, 0);
1631 gtk_widget_destroy(picker);
1635 static gboolean hildon_time_editor_entry_clicked(GtkWidget * widget,
1636 GdkEventButton * event,
1639 HildonTimeEditor *editor;
1640 HildonTimeEditorPrivate *priv;
1642 editor = HILDON_TIME_EDITOR (data);
1643 priv = HILDON_TIME_EDITOR_GET_PRIVATE (editor);
1645 /* If the focus has been grabbed back before the "clicked"
1646 * signal gets processed, don't highlight the text.
1647 * This happens when input in one H:M:S field is invalid and we're
1648 * trying to move to another field. The focus moves back to the invalid
1651 if (gtk_widget_is_focus (widget))
1652 gtk_editable_select_region(GTK_EDITABLE(widget), 0, 2);
1657 static void hildon_time_editor_size_request(GtkWidget * widget,
1658 GtkRequisition * requisition)
1660 HildonTimeEditor *editor;
1661 HildonTimeEditorPrivate *priv;
1664 editor = HILDON_TIME_EDITOR(widget);
1665 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1667 /* Get frame's size */
1668 gtk_widget_size_request(priv->frame, requisition);
1670 if (GTK_WIDGET_VISIBLE(priv->iconbutton))
1672 gtk_widget_size_request(priv->iconbutton, &req);
1673 /* Reserve space for icon */
1674 requisition->width += req.width + ICON_PRESSED +
1675 HILDON_MARGIN_DEFAULT;
1678 /* FIXME: It's evil to use hardcoded TIME_EDITOR_HEIGHT. For now we'll
1679 want to force this since themes might have varying thickness values
1680 which cause the height to change. */
1681 requisition->height = TIME_EDITOR_HEIGHT;
1684 static void hildon_time_editor_size_allocate(GtkWidget * widget,
1685 GtkAllocation * allocation)
1687 HildonTimeEditorPrivate *priv = HILDON_TIME_EDITOR_GET_PRIVATE(widget);
1688 GtkAllocation alloc;
1689 GtkRequisition req, max_req;
1691 widget->allocation = *allocation;
1692 gtk_widget_get_child_requisition(widget, &max_req);
1694 /* Center horizontally */
1695 alloc.x = allocation->x + MAX(allocation->width - max_req.width, 0) / 2;
1696 /* Center vertically */
1697 alloc.y = allocation->y + MAX(allocation->height - max_req.height, 0) / 2;
1699 /* allocate frame */
1700 gtk_widget_get_child_requisition(priv->frame, &req);
1702 alloc.width = req.width;
1703 alloc.height = max_req.height;
1704 gtk_widget_size_allocate(priv->frame, &alloc);
1707 if (GTK_WIDGET_VISIBLE(priv->iconbutton)) {
1708 gtk_widget_get_child_requisition(priv->iconbutton, &req);
1710 alloc.x += alloc.width + HILDON_MARGIN_DEFAULT;
1711 alloc.width = req.width;
1712 gtk_widget_size_allocate(priv->iconbutton, &alloc);
1715 /* FIXME: ugly way to move labels up. They just don't seem move up
1716 otherwise. This is likely because we force the editor to be
1717 smaller than it otherwise would be. */
1718 alloc = priv->ampm_label->allocation;
1719 alloc.y = allocation->y - 2;
1720 alloc.height = max_req.height + 2;
1721 gtk_widget_size_allocate(priv->ampm_label, &alloc);
1723 alloc = priv->hm_label->allocation;
1724 alloc.y = allocation->y - 2;
1725 alloc.height = max_req.height + 2;
1726 gtk_widget_size_allocate(priv->hm_label, &alloc);
1728 alloc = priv->sec_label->allocation;
1729 alloc.y = allocation->y - 2;
1730 alloc.height = max_req.height + 2;
1731 gtk_widget_size_allocate(priv->sec_label, &alloc);
1734 static gboolean hildon_time_editor_entry_keypress(GtkWidget * widget,
1735 GdkEventKey * event,
1738 HildonTimeEditor *editor;
1739 HildonTimeEditorPrivate *priv;
1742 g_assert(GTK_IS_ENTRY(widget));
1743 g_assert(event != NULL);
1744 g_assert(HILDON_IS_TIME_EDITOR(data));
1746 editor = HILDON_TIME_EDITOR(data);
1747 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1748 cursor_pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1750 switch (event->keyval)
1753 /* Return key popups up time picker dialog. Visually it looks as if
1754 the time picker icon was clicked. Before opening the time picker
1755 the fields are first validated and fixed. */
1756 hildon_time_editor_validate (editor, FALSE);
1757 hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), TRUE);
1758 hildon_time_editor_icon_clicked(widget, data);
1759 hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), FALSE);
1763 /* left arrow pressed in the entry. If we are on first position, try to
1764 move to the previous field. */
1765 if (cursor_pos == 0) {
1766 (void) gtk_widget_child_focus(GTK_WIDGET(editor), GTK_DIR_LEFT);
1772 /* right arrow pressed in the entry. If we are on last position, try to
1773 move to the next field. */
1774 if (cursor_pos >= g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(widget)), -1)) {
1775 (void) gtk_widget_child_focus(GTK_WIDGET(editor), GTK_DIR_RIGHT);
1792 convert_to_12h (guint *h, gboolean *am)
1794 g_assert(0 <= *h && *h < 24);
1796 /* 00:00 to 00:59 add 12 hours */
1797 /* 01:00 to 11:59 straight to am */
1798 /* 12:00 to 12:59 straight to pm */
1799 /* 13:00 to 23:59 subtract 12 hours */
1801 if ( *h == 0 ) { *am = TRUE; *h += 12;}
1802 else if ( 1 <= *h && *h < 12 ) { *am = TRUE; }
1803 else if ( 12 <= *h && *h < 13 ) { *am = FALSE; }
1804 else { *am = FALSE; *h -= 12;}
1808 convert_to_24h (guint *h, gboolean am)
1810 if (*h == 12 && am) /* 12 midnight - 12:59 AM subtract 12 hours */
1814 else if (!am && 1 <= *h && *h < 12) /* 1:00 PM - 11:59 AM add 12 hours */
1821 * hildon_time_editor_set_show_hours:
1822 * @editor: The #HildonTimeEditor.
1823 * @enable: Enable or disable showing of hours.
1825 * This function shows or hides the hours field.
1829 void hildon_time_editor_set_show_hours(HildonTimeEditor * editor,
1830 gboolean show_hours)
1832 HildonTimeEditorPrivate *priv;
1834 g_return_if_fail(HILDON_IS_TIME_EDITOR(editor));
1836 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1838 if (show_hours != priv->show_hours) {
1839 priv->show_hours = show_hours;
1841 /* show/hide hours field and its ':' label if the value changed. */
1843 gtk_widget_show(priv->entries[ENTRY_HOURS]);
1844 gtk_widget_show(priv->hm_label);
1846 gtk_widget_hide(priv->entries[ENTRY_HOURS]);
1847 gtk_widget_hide(priv->hm_label);
1850 g_object_notify (G_OBJECT (editor), "show_hours");
1855 * hildon_time_editor_get_show_hours:
1856 * @self: the @HildonTimeEditor widget.
1858 * This function returns a boolean indicating the visibility of
1859 * hours in the @HildonTimeEditor
1861 * Return value: TRUE if hours are visible.
1865 gboolean hildon_time_editor_get_show_hours(HildonTimeEditor *editor)
1867 HildonTimeEditorPrivate *priv;
1869 g_return_val_if_fail (HILDON_IS_TIME_EDITOR (editor), FALSE);
1870 priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor);
1872 return priv->show_hours;
1876 * Deprecated functions
1880 * hildon_time_editor_show_seconds:
1881 * @editor: the #HildonTimeEditor
1882 * @enable: enable or disable showing of seconds
1884 * This function is deprecated,
1885 * use #hildon_time_editor_set_show_seconds instead.
1887 void hildon_time_editor_show_seconds(HildonTimeEditor * editor,
1890 hildon_time_editor_set_show_seconds (editor, enable);
1893 * hildon_time_editor_enable_duration_mode:
1894 * @editor: the #HildonTimeEditor
1895 * @enable: enable or disable duration editor mode
1897 * This function is deprecated,
1898 * use #hildon_time_editor_set_duration_mode instead.
1900 void hildon_time_editor_enable_duration_mode(HildonTimeEditor * editor,
1903 hildon_time_editor_set_duration_mode (editor, enable);