X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hildon-widgets%2Fhildon-time-editor.c;h=7e1e6b7d1082276dd3edfaad6617dd847bf90d87;hb=16826dd971dae515057efa0b8b52b9405cc3d23f;hp=11423d1a4545b3439ee0c246f6325ef146a85b7c;hpb=f1322b158d2ca72700505d20d7a2039e3a3e2d7b;p=hildon diff --git a/hildon-widgets/hildon-time-editor.c b/hildon-widgets/hildon-time-editor.c index 11423d1..7e1e6b7 100644 --- a/hildon-widgets/hildon-time-editor.c +++ b/hildon-widgets/hildon-time-editor.c @@ -1,14 +1,14 @@ /* * This file is part of hildon-libs * - * Copyright (C) 2005 Nokia Corporation. + * Copyright (C) 2005, 2006 Nokia Corporation. * - * Contact: Luc Pionchon + * Contact: Michael Dominic Kostrzewa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. + * as published by the Free Software Foundation; version 2.1 of + * the License. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of @@ -58,7 +58,10 @@ #include #include #include +#include #include "hildon-composite-widget.h" +#include "hildon-marshalers.h" +#include "hildon-libs-enum-types.h" #define _(String) dgettext(PACKAGE, String) @@ -104,6 +107,7 @@ enum PROP_SHOW_HOURS }; +/* Indices for h/m/s entries in priv->entries */ enum { ENTRY_HOURS, ENTRY_MINS, @@ -112,15 +116,31 @@ enum { ENTRY_COUNT }; +/* Signals */ +enum { + TIME_ERROR, + LAST_SIGNAL +}; + +/* Error codes categories */ +enum { + MAX_VALUE, + MIN_VALUE, + WITHIN_RANGE, + NUM_ERROR_CODES +}; + +static guint time_editor_signals[LAST_SIGNAL] = { 0 }; +static guint hour_errors[NUM_ERROR_CODES] = { MAX_HOURS, MIN_HOURS, EMPTY_HOURS }; +static guint min_errors[NUM_ERROR_CODES] = { MAX_MINS, MIN_MINS, EMPTY_MINS }; +static guint sec_errors[NUM_ERROR_CODES] = { MAX_SECS, MIN_SECS, EMPTY_SECS }; + struct _HildonTimeEditorPrivate { guint ticks; /* Current duration in seconds */ gchar *am_symbol; gchar *pm_symbol; - gchar *hm_sep_symbol; /* hours/minutes separator */ - gchar *ms_sep_symbol; /* minutes/seconds separator */ - GtkWidget *eventbox; /* hold entries */ GtkWidget *iconbutton; /* button for icon */ @@ -185,6 +205,9 @@ static gboolean hildon_time_editor_entry_focusin(GtkWidget *widget, GdkEventFocus *event, gpointer data); +static gboolean hildon_time_editor_time_error(HildonTimeEditor *editor, + HildonTimeEditorErrorType type); + static gboolean hildon_time_editor_ampm_clicked(GtkWidget *widget, GdkEventButton *event, gpointer data); @@ -226,14 +249,20 @@ static void hildon_time_editor_set_to_current_time (HildonTimeEditor * editor); * Utility functions */ -static void convert_to_12h (guint *h, guint *m, guint *s, gboolean *am); -static void convert_to_24h (guint *h, guint *m, guint *s, gboolean am); +static void convert_to_12h (guint *h, gboolean *am); +static void convert_to_24h (guint *h, gboolean am); static void ticks_to_time (guint ticks, guint *hours, guint *minutes, guint *seconds); +static void +hildon_time_editor_inserted_text (GtkEditable * editable, + gchar * new_text, + gint new_text_length, + gint * position, + gpointer user_data); GType hildon_time_editor_get_type(void) { @@ -325,6 +354,17 @@ hildon_time_editor_class_init(HildonTimeEditorClass * editor_class) object_class->finalize = hildon_time_editor_finalize; + editor_class->time_error = hildon_time_editor_time_error; + + time_editor_signals[TIME_ERROR] = + g_signal_new("time-error", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(HildonTimeEditorClass, time_error), + g_signal_accumulator_true_handled, NULL, + _hildon_marshal_BOOLEAN__ENUM, + G_TYPE_BOOLEAN, 1, HILDON_TYPE_TIME_EDITOR_ERROR_TYPE); + /** * HildonTimeEditor:ticks: * @@ -493,6 +533,11 @@ static void hildon_time_editor_init(HildonTimeEditor * editor) G_CALLBACK(hildon_time_editor_entry_keypress), editor); g_signal_connect(priv->entries[i], "changed", G_CALLBACK(hildon_time_editor_entry_changed), editor); + + /* inserted signal sets time */ + g_signal_connect_after (G_OBJECT(priv->entries[i]), "insert_text", + G_CALLBACK (hildon_time_editor_inserted_text), + editor); } /* clicked signal for am/pm label */ @@ -526,10 +571,6 @@ static void hildon_time_editor_init(HildonTimeEditor * editor) } else { gtk_widget_hide(priv->eventbox); } - - /* Set time separators labels */ - gtk_label_set_label(GTK_LABEL(priv->hm_label), priv->hm_sep_symbol); /* priv-> set by ..._chech_locale()*/ - gtk_label_set_label(GTK_LABEL(priv->sec_label), priv->ms_sep_symbol); if (!priv->show_seconds) { gtk_widget_hide(priv->sec_label); @@ -639,12 +680,6 @@ static void hildon_time_editor_finalize(GObject * obj_self) g_free(priv->am_symbol); g_free(priv->pm_symbol); - if (priv->hm_sep_symbol) - g_free(priv->hm_sep_symbol); - - if (priv->ms_sep_symbol) - g_free(priv->ms_sep_symbol); - if (priv->highlight_idle) g_source_remove(priv->highlight_idle); @@ -653,36 +688,57 @@ static void hildon_time_editor_finalize(GObject * obj_self) } /** - * Gets hour-minute separator and minute-second separator - * from current locale + * _hildon_time_editor_get_time_separators: + * @editor: the #HildonTimeEditor + * @hm_sep_label: the label that will show the hour:minutes separator + * @ms_sep_label: the label that will show the minutes:seconds separator + * + * Gets hour-minute separator and minute-second separator from current + * locale and sets then to the labels we set as parameters. Both + * parameters can be NULL if you just want to assing one separator. + * */ -static void get_time_separators(HildonTimeEditorPrivate *priv) +void +_hildon_time_editor_get_time_separators(GtkLabel *hm_sep_label, + GtkLabel *ms_sep_label) { gchar buffer[256]; + gchar *separator; GDate locale_test_date; gchar *iter, *endp; /* Get localized time string */ g_date_set_dmy(&locale_test_date, 1, 2, 1970); (void) g_date_strftime(buffer, sizeof(buffer), "%X", &locale_test_date); - - /* Find h-m separator */ - iter = buffer; - while (*iter && g_ascii_isdigit(*iter)) iter++; + + if (hm_sep_label != NULL) + { + /* Find h-m separator */ + iter = buffer; + while (*iter && g_ascii_isdigit(*iter)) iter++; - /* Extract h-m separator*/ - endp = iter; - while (*endp && !g_ascii_isdigit(*endp)) endp++; - priv->hm_sep_symbol = g_strndup(iter, endp - iter); - - /* Find m-s separator */ - iter = endp; - while (*iter && g_ascii_isdigit(*iter)) iter++; + /* Extract h-m separator*/ + endp = iter; + while (*endp && !g_ascii_isdigit(*endp)) endp++; + separator = g_strndup(iter, endp - iter); + gtk_label_set_label(hm_sep_label, separator); + g_free(separator); + } + + if (ms_sep_label != NULL) + { + /* Find m-s separator */ + iter = endp; + while (*iter && g_ascii_isdigit(*iter)) iter++; - /* Extract m-s separator*/ - endp = iter; - while (*endp && !g_ascii_isdigit(*endp)) endp++; - priv->ms_sep_symbol = g_strndup(iter, endp - iter); + /* Extract m-s separator*/ + endp = iter; + while (*endp && !g_ascii_isdigit(*endp)) endp++; + separator = g_strndup(iter, endp - iter); + gtk_label_set_label(ms_sep_label, separator); + g_free(separator); + } + } /* Convert ticks to H:M:S. Ticks = seconds since 00:00:00. */ @@ -740,7 +796,7 @@ void hildon_time_editor_set_ticks (HildonTimeEditor * editor, if (!priv->clock_24h && !priv->duration_mode) { /* Convert 24h H:M:S values to 12h mode, and update AM/PM state */ - convert_to_12h (&h, &m, &s, &priv->am); + convert_to_12h (&h, &priv->am); } /* Set H:M:S values to entries. We do not want to invoke validation @@ -750,6 +806,13 @@ void hildon_time_editor_set_ticks (HildonTimeEditor * editor, { g_signal_handlers_block_by_func(priv->entries[i], (gpointer) hildon_time_editor_entry_changed, editor); + + g_signal_handlers_block_by_func(priv->entries[i], + (gpointer) hildon_time_editor_inserted_text, editor); + + g_signal_handlers_block_by_func(priv->entries[i], + (gpointer) hildon_time_editor_entry_focusout, editor); + } g_snprintf(str, sizeof(str), "%02u", h); @@ -765,7 +828,14 @@ void hildon_time_editor_set_ticks (HildonTimeEditor * editor, { g_signal_handlers_unblock_by_func(priv->entries[i], (gpointer) hildon_time_editor_entry_changed, editor); - } + + g_signal_handlers_unblock_by_func(priv->entries[i], + (gpointer) hildon_time_editor_inserted_text, editor); + + g_signal_handlers_unblock_by_func(priv->entries[i], + (gpointer) hildon_time_editor_entry_focusout, editor); + + } /* Update AM/PM label in case we're in 12h mode */ gtk_label_set_label(GTK_LABEL(priv->ampm_label), @@ -1161,11 +1231,11 @@ static gboolean hildon_time_editor_check_locale(HildonTimeEditor * editor) priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor); /* Update time separator symbols */ - get_time_separators(priv); + _hildon_time_editor_get_time_separators(GTK_LABEL(priv->hm_label), GTK_LABEL(priv->sec_label)); - /* Get AM/PM symbols. We want to show them in lowercase. */ - priv->am_symbol = g_ascii_strdown(nl_langinfo(AM_STR), -1); - priv->pm_symbol = g_ascii_strdown(nl_langinfo(PM_STR), -1); + /* Get AM/PM symbols. */ + priv->am_symbol = g_strdup(nl_langinfo(AM_STR)); + priv->pm_symbol = g_strdup(nl_langinfo(PM_STR)); if (priv->am_symbol[0] == '\0') return TRUE; @@ -1198,6 +1268,13 @@ static gboolean hildon_time_editor_entry_focusin(GtkWidget * widget, return FALSE; } +static gboolean +hildon_time_editor_time_error(HildonTimeEditor *editor, + HildonTimeEditorErrorType type) +{ + return TRUE; +} + /* Returns negative if we didn't get value, * and should stop further validation */ @@ -1206,6 +1283,7 @@ static gint validated_conversion(HildonTimeEditorPrivate *priv, gint min, gint max, gboolean allow_intermediate, + guint *error_code, GString *error_string) { const gchar *text; @@ -1225,11 +1303,13 @@ static gint validated_conversion(HildonTimeEditorPrivate *priv, if (value > max) { g_string_printf(error_string, _("ckct_ib_maximum_value"), max); priv->error_widget = field; + *error_code = MAX_VALUE; return max; } if (value < min && !allow_intermediate) { g_string_printf(error_string, _("ckct_ib_minimum_value"), min); priv->error_widget = field; + *error_code = MIN_VALUE; return min; } @@ -1240,10 +1320,18 @@ static gint validated_conversion(HildonTimeEditorPrivate *priv, else if (allow_intermediate) return -1; /* Empty field while user is still editing. No error, but cannot validate either... */ + else /* Empty field: show error and set value to minimum allowed */ + { + g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max); + priv->error_widget = field; + *error_code = WITHIN_RANGE; + return min; + } /* Empty field and not allowed intermediated OR failed conversion */ g_string_printf(error_string, _("ckct_ib_set_a_value_within_range"), min, max); priv->error_widget = field; + *error_code = WITHIN_RANGE; return -1; } @@ -1253,7 +1341,9 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor, { HildonTimeEditorPrivate *priv; guint h, m, s, ticks; + guint error_code; guint max_hours, min_hours, max_minutes, min_minutes, max_seconds, min_seconds; + gboolean r; g_assert(HILDON_IS_TIME_EDITOR(editor)); @@ -1277,16 +1367,22 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor, /* Get time components from fields and validate them... */ if (priv->show_hours) { h = validated_conversion(priv, priv->entries[ENTRY_HOURS], min_hours, max_hours, - allow_intermediate, error_string); + allow_intermediate, &error_code, error_string); + if (priv->error_widget == priv->entries[ENTRY_HOURS]) + g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, hour_errors[error_code], &r); if ((gint) h < 0) return; } else h = 0; m = validated_conversion(priv, priv->entries[ENTRY_MINS], MINUTES_MIN, MINUTES_MAX, - allow_intermediate, error_string); + allow_intermediate, &error_code, error_string); + if (priv->error_widget == priv->entries[ENTRY_MINS]) + g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, min_errors[error_code], &r); if ((gint) m < 0) return; if (priv->show_seconds) { s = validated_conversion(priv, priv->entries[ENTRY_SECS], SECONDS_MIN, SECONDS_MAX, - allow_intermediate, error_string); + allow_intermediate, &error_code, error_string); + if (priv->error_widget == priv->entries[ENTRY_SECS]) + g_signal_emit (editor, time_editor_signals [TIME_ERROR], 0, sec_errors[error_code], &r); if ((gint) s < 0) return; } else s = 0; @@ -1303,6 +1399,7 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor, min_hours, min_minutes, min_seconds); hildon_time_editor_set_ticks (editor, priv->duration_min); priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS]; + g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, MIN_DUR, &r); return; } else if (ticks > priv->duration_max) @@ -1312,11 +1409,12 @@ hildon_time_editor_real_validate(HildonTimeEditor *editor, max_hours, max_minutes, max_seconds); hildon_time_editor_set_ticks (editor, priv->duration_max); priv->error_widget = priv->show_hours ? priv->entries[ENTRY_HOURS] : priv->entries[ENTRY_MINS]; + g_signal_emit (editor, time_editor_signals[TIME_ERROR], 0, MAX_DUR, &r); return; } } else if (!priv->clock_24h) - convert_to_24h (&h, &m, &s, priv->am); + convert_to_24h (&h, priv->am); /* The only case when we do not want to refresh the time display, is when the user is editing a value @@ -1332,7 +1430,10 @@ static gboolean highlight_callback(gpointer data) { HildonTimeEditorPrivate *priv; GtkWidget *widget; + gint i; + GDK_THREADS_ENTER (); + g_assert(HILDON_IS_TIME_EDITOR(data)); priv = HILDON_TIME_EDITOR_GET_PRIVATE(data); @@ -1342,10 +1443,20 @@ static gboolean highlight_callback(gpointer data) g_assert(GTK_IS_ENTRY(widget)); - /* Grabbing focus can cause re-validation, priv->error widget - can be set to something else, including NULL */ + /* Avoid revalidation because it will issue the date_error signal + twice when there is an empty field. We must block the signal + for all the entries because we do not know where the focus + comes from */ + for (i = 0; i < ENTRY_COUNT; i++) + g_signal_handlers_block_by_func(priv->entries[i], + (gpointer) hildon_time_editor_entry_focusout, data); gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1); gtk_widget_grab_focus(widget); + for (i = 0; i < ENTRY_COUNT; i++) + g_signal_handlers_unblock_by_func(priv->entries[i], + (gpointer) hildon_time_editor_entry_focusout, data); + + GDK_THREADS_LEAVE (); return FALSE; } @@ -1361,19 +1472,69 @@ hildon_time_editor_validate (HildonTimeEditor *editor, gboolean allow_intermedia g_assert(HILDON_IS_TIME_EDITOR(editor)); priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor); - priv->error_widget = NULL; - - error_message = g_string_new(NULL); - hildon_time_editor_real_validate(editor, - allow_intermediate, error_message); - if (priv->error_widget) { - hildon_banner_show_information(priv->error_widget, NULL, - error_message->str); - if (priv->highlight_idle == 0) + /* if there is already an error we do nothing until it will be managed by the idle */ + if (priv->highlight_idle == 0) + { + error_message = g_string_new(NULL); + hildon_time_editor_real_validate(editor, + allow_intermediate, error_message); + + if (priv->error_widget) + { + hildon_banner_show_information(priv->error_widget, NULL, + error_message->str); + priv->highlight_idle = g_idle_add(highlight_callback, editor); - } - g_string_free(error_message, TRUE); + } + + g_string_free(error_message, TRUE); + } +} + +/* on inserted text, if entry has two digits, jumps to the next field. */ +static void +hildon_time_editor_inserted_text (GtkEditable * editable, + gchar * new_text, + gint new_text_length, + gint * position, + gpointer user_data) +{ + HildonTimeEditor *editor; + GtkEntry *entry; + gchar *value; + HildonTimeEditorPrivate *priv; + + entry = GTK_ENTRY(editable); + editor = HILDON_TIME_EDITOR(user_data); + + priv = HILDON_TIME_EDITOR_GET_PRIVATE(editor); + + /* if there is already an error we don't have to do anything */ + if (!priv->error_widget) + { + + value = (gchar *) gtk_entry_get_text(entry); + + if (strlen(value) == 2) + { + if (GTK_WIDGET(editable) == priv->entries[ENTRY_HOURS]) + { + /* We already checked the input in changed signal, but + * now we will re-check it again in focus-out we + * intermediate flag set to FALSE */ + gtk_widget_grab_focus(priv->entries[ENTRY_MINS]); + *position = -1; + } + else if (GTK_WIDGET(editable) == priv->entries[ENTRY_MINS] && + GTK_WIDGET_VISIBLE (priv->entries[ENTRY_SECS])) + { + /* See above */ + gtk_widget_grab_focus(priv->entries[ENTRY_SECS]); + *position = -1; + } + } + } } static gboolean hildon_time_editor_entry_focusout(GtkWidget * widget, @@ -1576,9 +1737,9 @@ static gboolean hildon_time_editor_entry_keypress(GtkWidget * widget, the time picker icon was clicked. Before opening the time picker the fields are first validated and fixed. */ hildon_time_editor_validate (editor, FALSE); - _gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), TRUE); + hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), TRUE); hildon_time_editor_icon_clicked(widget, data); - _gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), FALSE); + hildon_gtk_button_set_depressed(GTK_BUTTON(priv->iconbutton), FALSE); return TRUE; case GDK_Left: @@ -1611,7 +1772,7 @@ static gboolean hildon_time_editor_entry_keypress(GtkWidget * widget, */ static void -convert_to_12h (guint *h, guint *m, guint *s, gboolean *am) +convert_to_12h (guint *h, gboolean *am) { g_assert(0 <= *h && *h < 24); @@ -1627,7 +1788,7 @@ convert_to_12h (guint *h, guint *m, guint *s, gboolean *am) } static void -convert_to_24h (guint *h, guint *m, guint *s, gboolean am) +convert_to_24h (guint *h, gboolean am) { if (*h == 12 && am) /* 12 midnight - 12:59 AM subtract 12 hours */ { @@ -1646,6 +1807,7 @@ convert_to_24h (guint *h, guint *m, guint *s, gboolean am) * * This function shows or hides the hours field. * + * Since: 0.12.4 **/ void hildon_time_editor_set_show_hours(HildonTimeEditor * editor, gboolean show_hours) @@ -1680,6 +1842,8 @@ void hildon_time_editor_set_show_hours(HildonTimeEditor * editor, * hours in the @HildonTimeEditor * * Return value: TRUE if hours are visible. + * + * Since: 0.12.4-1 **/ gboolean hildon_time_editor_get_show_hours(HildonTimeEditor *editor) {