/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Author : * Damon Chaplin * * Copyright 2000, Helix Code, Inc. * * Based on the GnomeDateEdit, part of the Gnome Library. * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ /* * EDateEdit - a widget based on GnomeDateEdit to provide a date & optional * time field with popups for entering a date. */ /* We need this for strptime. */ #define _XOPEN_SOURCE 4 #include #include #include #include #include #include #include #include "e-dateedit.h" #include "e-calendar.h" #include #include #include #include #include #include #include #include #include struct _EDateEditPrivate { GtkWidget *date_entry; GtkWidget *date_button; GtkWidget *space; GtkWidget *time_combo; GtkWidget *cal_popup; GtkWidget *calendar; GtkWidget *now_button; GtkWidget *today_button; GtkWidget *none_button; gboolean show_time; gboolean use_24_hour_format; gint lower_hour; gint upper_hour; }; enum { DATE_CHANGED, TIME_CHANGED, LAST_SIGNAL }; static gint date_edit_signals [LAST_SIGNAL] = { 0 }; static void e_date_edit_class_init (EDateEditClass *class); static void e_date_edit_init (EDateEdit *dedit); static void create_children (EDateEdit *dedit); static void e_date_edit_destroy (GtkObject *object); static void e_date_edit_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); static void on_date_button_clicked (GtkWidget *widget, EDateEdit *dedit); static void position_date_popup (EDateEdit *dedit); static void on_date_popup_none_button_clicked (GtkWidget *button, EDateEdit *dedit); static void on_date_popup_today_button_clicked (GtkWidget *button, EDateEdit *dedit); static void on_date_popup_now_button_clicked (GtkWidget *button, EDateEdit *dedit); static gint on_date_popup_delete_event (GtkWidget *widget, gpointer data); static gint on_date_popup_key_press (GtkWidget *widget, GdkEventKey *event, gpointer data); static gint on_date_popup_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data); static void on_date_popup_date_selected (ECalendarItem *calitem, EDateEdit *dedit); static void hide_date_popup (EDateEdit *dedit); static void rebuild_time_popup (EDateEdit *dedit); static void enable_time_combo (EDateEdit *dedit); static void disable_time_combo (EDateEdit *dedit); static gboolean date_is_none (char *date_text); static gboolean e_date_edit_parse_date (EDateEdit *dedit, char *date_text, struct tm *date_tm); static GtkHBoxClass *parent_class; /** * e_date_edit_get_type: * * Returns the GtkType for the EDateEdit widget */ guint e_date_edit_get_type (void) { static guint date_edit_type = 0; if (!date_edit_type){ GtkTypeInfo date_edit_info = { "EDateEdit", sizeof (EDateEdit), sizeof (EDateEditClass), (GtkClassInitFunc) e_date_edit_class_init, (GtkObjectInitFunc) e_date_edit_init, NULL, NULL, }; date_edit_type = gtk_type_unique (gtk_hbox_get_type (), &date_edit_info); } return date_edit_type; } static void e_date_edit_class_init (EDateEditClass *class) { GtkObjectClass *object_class = (GtkObjectClass *) class; GtkContainerClass *container_class = (GtkContainerClass *) class; object_class = (GtkObjectClass*) class; parent_class = gtk_type_class (gtk_hbox_get_type ()); date_edit_signals [TIME_CHANGED] = gtk_signal_new ("time_changed", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (EDateEditClass, time_changed), gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); date_edit_signals [DATE_CHANGED] = gtk_signal_new ("date_changed", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (EDateEditClass, date_changed), gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, date_edit_signals, LAST_SIGNAL); container_class->forall = e_date_edit_forall; object_class->destroy = e_date_edit_destroy; class->date_changed = NULL; class->time_changed = NULL; } static void e_date_edit_init (EDateEdit *dedit) { EDateEditPrivate *priv; dedit->_priv = priv = g_new0 (EDateEditPrivate, 1); priv->show_time = TRUE; priv->use_24_hour_format = TRUE; priv->lower_hour = 0; priv->upper_hour = 24; } /** * e_date_edit_new: * * Description: Creates a new #EDateEdit widget which can be used * to provide an easy to use way for entering dates and times. * * Returns: a new #EDateEdit widget. */ GtkWidget * e_date_edit_new (void) { EDateEdit *dedit; dedit = gtk_type_new (e_date_edit_get_type ()); create_children (dedit); e_date_edit_set_time (dedit, 0); return GTK_WIDGET (dedit); } static void create_children (EDateEdit *dedit) { EDateEditPrivate *priv; ECalendar *calendar; GtkWidget *frame, *arrow; GtkWidget *vbox, *bbox; priv = dedit->_priv; priv->date_entry = gtk_entry_new (); gtk_widget_set_usize (priv->date_entry, 90, 0); gtk_box_pack_start (GTK_BOX (dedit), priv->date_entry, FALSE, TRUE, 0); gtk_widget_show (priv->date_entry); priv->date_button = gtk_button_new (); gtk_signal_connect (GTK_OBJECT (priv->date_button), "clicked", GTK_SIGNAL_FUNC (on_date_button_clicked), dedit); gtk_box_pack_start (GTK_BOX (dedit), priv->date_button, FALSE, FALSE, 0); arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT); gtk_container_add (GTK_CONTAINER (priv->date_button), arrow); gtk_widget_show (arrow); gtk_widget_show (priv->date_button); /* This is just to create a space between the date & time parts. */ priv->space = gtk_drawing_area_new (); gtk_box_pack_start (GTK_BOX (dedit), priv->space, FALSE, FALSE, 2); priv->time_combo = gtk_combo_new (); gtk_widget_set_usize (GTK_COMBO (priv->time_combo)->entry, 90, 0); gtk_box_pack_start (GTK_BOX (dedit), priv->time_combo, FALSE, TRUE, 0); rebuild_time_popup (dedit); if (priv->show_time) { gtk_widget_show (priv->space); gtk_widget_show (priv->time_combo); } priv->cal_popup = gtk_window_new (GTK_WINDOW_POPUP); gtk_widget_set_events (priv->cal_popup, gtk_widget_get_events (priv->cal_popup) | GDK_KEY_PRESS_MASK); gtk_signal_connect (GTK_OBJECT (priv->cal_popup), "delete_event", (GtkSignalFunc) on_date_popup_delete_event, dedit); gtk_signal_connect (GTK_OBJECT (priv->cal_popup), "key_press_event", (GtkSignalFunc) on_date_popup_key_press, dedit); gtk_signal_connect (GTK_OBJECT (priv->cal_popup), "button_press_event", (GtkSignalFunc) on_date_popup_button_press, dedit); gtk_window_set_policy (GTK_WINDOW (priv->cal_popup), FALSE, FALSE, TRUE); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); gtk_container_add (GTK_CONTAINER (priv->cal_popup), frame); gtk_widget_show (frame); vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); priv->calendar = e_calendar_new (); calendar = E_CALENDAR (priv->calendar); /*e_calendar_set_buttons (calendar, TRUE, TRUE);*/ gnome_canvas_item_set (GNOME_CANVAS_ITEM (calendar->calitem), "maximum_days_selected", 1, "move_selection_when_moving", FALSE, NULL); gtk_signal_connect (GTK_OBJECT (calendar->calitem), "selection_changed", GTK_SIGNAL_FUNC (on_date_popup_date_selected), dedit); gtk_box_pack_start (GTK_BOX (vbox), priv->calendar, FALSE, FALSE, 0); gtk_widget_show (priv->calendar); bbox = gtk_hbutton_box_new (); gtk_container_set_border_width (GTK_CONTAINER (bbox), 4); gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 2); gtk_button_box_set_child_ipadding (GTK_BUTTON_BOX (bbox), 2, 0); gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), 0, 0); gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); gtk_widget_show (bbox); priv->now_button = gtk_button_new_with_label (_("Now")); gtk_container_add (GTK_CONTAINER (bbox), priv->now_button); gtk_widget_show (priv->now_button); gtk_signal_connect (GTK_OBJECT (priv->now_button), "clicked", GTK_SIGNAL_FUNC (on_date_popup_now_button_clicked), dedit); priv->today_button = gtk_button_new_with_label (_("Today")); gtk_container_add (GTK_CONTAINER (bbox), priv->today_button); gtk_widget_show (priv->today_button); gtk_signal_connect (GTK_OBJECT (priv->today_button), "clicked", GTK_SIGNAL_FUNC (on_date_popup_today_button_clicked), dedit); priv->none_button = gtk_button_new_with_label (_("None")); gtk_container_add (GTK_CONTAINER (bbox), priv->none_button); gtk_signal_connect (GTK_OBJECT (priv->none_button), "clicked", GTK_SIGNAL_FUNC (on_date_popup_none_button_clicked), dedit); } static void e_date_edit_destroy (GtkObject *object) { EDateEdit *dedit; g_return_if_fail (E_IS_DATE_EDIT (object)); dedit = E_DATE_EDIT (object); gtk_widget_destroy (dedit->_priv->cal_popup); gtk_widget_unref (dedit->_priv->cal_popup); g_free(dedit->_priv); dedit->_priv = NULL; if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } static void e_date_edit_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { g_return_if_fail (E_IS_DATE_EDIT (container)); g_return_if_fail (callback != NULL); /* Let GtkBox handle things only if the internal widgets need to be * poked. */ if (include_internals) if (GTK_CONTAINER_CLASS (parent_class)->forall) (* GTK_CONTAINER_CLASS (parent_class)->forall) (container, include_internals, callback, callback_data); } /* The arrow button beside the date field has been clicked, so we show the popup with the ECalendar in. */ static void on_date_button_clicked (GtkWidget *widget, EDateEdit *dedit) { EDateEditPrivate *priv; ECalendar *calendar; struct tm mtm; gchar *date_text; GDate selected_day; gboolean clear_selection = FALSE; priv = dedit->_priv; calendar = E_CALENDAR (priv->calendar); date_text = gtk_entry_get_text (GTK_ENTRY (dedit->_priv->date_entry)); if (date_is_none (date_text) || !e_date_edit_parse_date (dedit, date_text, &mtm)) clear_selection = TRUE; if (clear_selection) { e_calendar_item_set_selection (calendar->calitem, NULL, NULL); } else { g_date_clear (&selected_day, 1); g_date_set_dmy (&selected_day, mtm.tm_mday, mtm.tm_mon + 1, mtm.tm_year + 1900); e_calendar_item_set_selection (calendar->calitem, &selected_day, NULL); } /* FIXME: Hack. Change ECalendarItem so it doesn't queue signal emissions. */ calendar->calitem->selection_changed = FALSE; position_date_popup (dedit); gtk_widget_realize (dedit->_priv->cal_popup); gtk_widget_show (dedit->_priv->cal_popup); gtk_widget_grab_focus (dedit->_priv->cal_popup); gtk_grab_add (dedit->_priv->cal_popup); gdk_pointer_grab (dedit->_priv->cal_popup->window, TRUE, (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK), NULL, NULL, GDK_CURRENT_TIME); } /* This positions the date popup below and to the left of the arrow button, just before it is shown. */ static void position_date_popup (EDateEdit *dedit) { gint x, y; gint bwidth, bheight; GtkRequisition req; gint screen_width, screen_height; gtk_widget_size_request (dedit->_priv->cal_popup, &req); gdk_window_get_origin (dedit->_priv->date_button->window, &x, &y); gdk_window_get_size (dedit->_priv->date_button->window, &bwidth, &bheight); screen_width = gdk_screen_width (); screen_height = gdk_screen_height (); x += bwidth - req.width; y += bheight; x = CLAMP (x, 0, MAX (0, screen_width - req.width)); y = CLAMP (y, 0, MAX (0, screen_height - req.height)); gtk_widget_set_uposition (dedit->_priv->cal_popup, x, y); } /* A date has been selected in the date popup, so we set the date field and hide the popup. */ static void on_date_popup_date_selected (ECalendarItem *calitem, EDateEdit *dedit) { GDate start_date, end_date; struct tm tmp_tm; char buffer [40]; if (!e_calendar_item_get_selection (calitem, &start_date, &end_date)) return; g_date_to_struct_tm (&start_date, &tmp_tm); /* This is a strftime() format for a short date. %m = month, %d = day of month, %Y = year (all digits). */ strftime (buffer, sizeof (buffer), _("%m/%d/%Y"), &tmp_tm); gtk_entry_set_text (GTK_ENTRY (dedit->_priv->date_entry), buffer); enable_time_combo (dedit); gtk_signal_emit (GTK_OBJECT (dedit), date_edit_signals [DATE_CHANGED]); hide_date_popup (dedit); } static void on_date_popup_now_button_clicked (GtkWidget *button, EDateEdit *dedit) { e_date_edit_set_time (dedit, time (NULL)); gtk_signal_emit (GTK_OBJECT (dedit), date_edit_signals [DATE_CHANGED]); hide_date_popup (dedit); } static void on_date_popup_today_button_clicked (GtkWidget *button, EDateEdit *dedit) { struct tm *tmp_tm; time_t t; char buffer [40]; t = time (NULL); tmp_tm = localtime (&t); /* This is a strftime() format for a short date. %m = month, %d = day of month, %Y = year (all digits). */ strftime (buffer, sizeof (buffer), _("%m/%d/%Y"), tmp_tm); gtk_entry_set_text (GTK_ENTRY (dedit->_priv->date_entry), buffer); enable_time_combo (dedit); gtk_signal_emit (GTK_OBJECT (dedit), date_edit_signals [DATE_CHANGED]); hide_date_popup (dedit); } static void on_date_popup_none_button_clicked (GtkWidget *button, EDateEdit *dedit) { e_date_edit_set_time (dedit, -1); gtk_signal_emit (GTK_OBJECT (dedit), date_edit_signals [DATE_CHANGED]); hide_date_popup (dedit); } /* A key has been pressed while the date popup is showing. If it is the Escape key we hide the popup. */ static gint on_date_popup_key_press (GtkWidget *widget, GdkEventKey *event, gpointer data) { EDateEdit *dedit; if (event->keyval != GDK_Escape) return FALSE; dedit = data; gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event"); hide_date_popup (dedit); return TRUE; } /* A mouse button has been pressed while the date popup is showing. Any button press events used to select days etc. in the popup will have have been handled elsewhere, so here we just hide the popup. (This function is yanked from gtkcombo.c) */ static gint on_date_popup_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { EDateEdit *dedit; GtkWidget *child; dedit = data; child = gtk_get_event_widget ((GdkEvent *) event); /* We don't ask for button press events on the grab widget, so * if an event is reported directly to the grab widget, it must * be on a window outside the application (and thus we remove * the popup window). Otherwise, we check if the widget is a child * of the grab widget, and only remove the popup window if it * is not. */ if (child != widget) { while (child) { if (child == widget) return FALSE; child = child->parent; } } hide_date_popup (dedit); return TRUE; } /* A delete event has been received for the date popup, so we hide it and return TRUE so it doesn't get destroyed. */ static gint on_date_popup_delete_event (GtkWidget *widget, gpointer data) { EDateEdit *dedit; dedit = data; hide_date_popup (dedit); return TRUE; } /* Hides the date popup, removing any grabs. */ static void hide_date_popup (EDateEdit *dedit) { gtk_widget_hide (dedit->_priv->cal_popup); gtk_grab_remove (dedit->_priv->cal_popup); gdk_pointer_ungrab (GDK_CURRENT_TIME); } /** * e_date_edit_get_time: * @dedit: The EDateEdit widget * * Returns the time entered in the EDateEdit widget, or -1 if the date is not * set or -2 if the date can't be parsed. */ time_t e_date_edit_get_time (EDateEdit *dedit) { EDateEditPrivate *priv; struct tm date_tm = { 0 }, time_tm = { 0 }; char *date_text, *time_text, *format; g_return_val_if_fail (E_IS_DATE_EDIT (dedit), -1); priv = dedit->_priv; date_text = gtk_entry_get_text (GTK_ENTRY (priv->date_entry)); if (date_is_none (date_text)) return -1; if (!e_date_edit_parse_date (dedit, date_text, &date_tm)) return -2; if (dedit->_priv->show_time) { time_text = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (priv->time_combo)->entry)); if (priv->use_24_hour_format) format = "%H:%M"; else format = "%I:%M %p"; if (!strptime (time_text, format, &time_tm)) return -2; date_tm.tm_hour = time_tm.tm_hour; date_tm.tm_min = time_tm.tm_min; } date_tm.tm_isdst = -1; return mktime (&date_tm); } static gboolean e_date_edit_parse_date (EDateEdit *dedit, gchar *date_text, struct tm *date_tm) { struct tm *tmp_tm; time_t t; /* This is a stpftime() format for a short date. %m = month, %d = day of month, %Y = year (all digits). */ if (!strptime (date_text, _("%m/%d/%Y"), date_tm)) return FALSE; /* If the user entered a 2-digit year we use the current century. */ if (date_tm->tm_year < 0) { t = time (NULL); tmp_tm = localtime (&t); /* This should convert it into a value from 0 to 99. */ date_tm->tm_year += 1900; /* Now add on the century. */ date_tm->tm_year += tmp_tm->tm_year - (tmp_tm->tm_year % 100); } return TRUE; } /** * e_date_edit_set_time: * @dedit: the EDateEdit widget * @the_time: The time and date that should be set on the widget * * Description: Changes the displayed date and time in the EDateEdit * widget to be the one represented by @the_time. If @the_time is 0 * then current time is used. */ void e_date_edit_set_time (EDateEdit *dedit, time_t the_time) { EDateEditPrivate *priv; struct tm *mytm; char buffer[40], *format; g_return_if_fail (E_IS_DATE_EDIT (dedit)); priv = dedit->_priv; if (the_time == -1) { gtk_entry_set_text (GTK_ENTRY (priv->date_entry), _("None")); disable_time_combo (dedit); return; } enable_time_combo (dedit); if (the_time == 0) the_time = time (NULL); mytm = localtime (&the_time); /* Set the date */ /* This is a strftime() format for a short date. %m = month, %d = day of month, %Y = year (all digits). */ strftime (buffer, sizeof (buffer), _("%m/%d/%Y"), mytm); gtk_entry_set_text (GTK_ENTRY (priv->date_entry), buffer); /* Set the time */ if (priv->use_24_hour_format) format = "%H:%M"; else format = "%I:%M %p"; strftime (buffer, sizeof (buffer), format, mytm); gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (priv->time_combo)->entry), buffer); } /** * e_date_edit_get_show_time: * @dedit: an #EDateEdit widget * @Returns: Whether the time field is shown. * * Description: Returns TRUE if the time field is currently shown. */ gboolean e_date_edit_get_show_time (EDateEdit *dedit) { g_return_val_if_fail (E_IS_DATE_EDIT (dedit), TRUE); return dedit->_priv->show_time; } /** * e_date_edit_set_show_time: * @dedit: an #EDateEdit widget * @show_time: TRUE if the time field should be shown. * * Description: Specifies whether the time field should be shown. The time * field would be hidden if only a date needed to be entered. */ void e_date_edit_set_show_time (EDateEdit *dedit, gboolean show_time) { EDateEditPrivate *priv; g_return_if_fail (E_IS_DATE_EDIT (dedit)); priv = dedit->_priv; if (priv->show_time == show_time) return; priv->show_time = show_time; if (show_time) { gtk_widget_show (priv->space); gtk_widget_show (priv->time_combo); } else { gtk_widget_hide (priv->space); gtk_widget_hide (priv->time_combo); gtk_widget_hide (priv->now_button); } } /** * e_date_edit_get_week_start_day: * @dedit: an #EDateEdit widget * @Returns: the week start day, from 0 (Sunday) to 6 (Saturday). * * Description: Returns the week start day currently used in the calendar * popup. */ gint e_date_edit_get_week_start_day (EDateEdit *dedit) { gint week_start_day; g_return_val_if_fail (E_IS_DATE_EDIT (dedit), 1); gtk_object_get (GTK_OBJECT (E_CALENDAR (dedit->_priv->calendar)->calitem), "week_start_day", &week_start_day, NULL); return week_start_day; } /** * e_date_edit_set_week_start_day: * @dedit: an #EDateEdit widget * @week_start_day: the week start day, from 0 (Sunday) to 6 (Saturday). * * Description: Sets the week start day to use in the calendar popup. */ void e_date_edit_set_week_start_day (EDateEdit *dedit, gint week_start_day) { g_return_if_fail (E_IS_DATE_EDIT (dedit)); gnome_canvas_item_set (GNOME_CANVAS_ITEM (E_CALENDAR (dedit->_priv->calendar)->calitem), "week_start_day", week_start_day, NULL); } /* Whether we show week numbers in the date popup. */ gboolean e_date_edit_get_show_week_numbers (EDateEdit *dedit) { gboolean show_week_numbers; g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE); gtk_object_get (GTK_OBJECT (E_CALENDAR (dedit->_priv->calendar)->calitem), "show_week_numbers", &show_week_numbers, NULL); return show_week_numbers; } void e_date_edit_set_show_week_numbers (EDateEdit *dedit, gboolean show_week_numbers) { g_return_if_fail (E_IS_DATE_EDIT (dedit)); gnome_canvas_item_set (GNOME_CANVAS_ITEM (E_CALENDAR (dedit->_priv->calendar)->calitem), "show_week_numbers", show_week_numbers, NULL); } /* Whether we use 24 hour format in the time field & popup. */ gboolean e_date_edit_get_use_24_hour_format (EDateEdit *dedit) { g_return_val_if_fail (E_IS_DATE_EDIT (dedit), TRUE); return dedit->_priv->use_24_hour_format; } void e_date_edit_set_use_24_hour_format (EDateEdit *dedit, gboolean use_24_hour_format) { g_return_if_fail (E_IS_DATE_EDIT (dedit)); if (dedit->_priv->use_24_hour_format == use_24_hour_format) return; dedit->_priv->use_24_hour_format = use_24_hour_format; rebuild_time_popup (dedit); } /* Whether we allow the date to be set to 'None'. e_date_edit_get_time() will return (time_t) -1 in this case. */ gboolean e_date_edit_get_allow_no_date_set (EDateEdit *dedit) { g_return_val_if_fail (E_IS_DATE_EDIT (dedit), FALSE); return GTK_WIDGET_VISIBLE (dedit->_priv->none_button); } void e_date_edit_set_allow_no_date_set (EDateEdit *dedit, gboolean allow_no_date_set) { g_return_if_fail (E_IS_DATE_EDIT (dedit)); if (allow_no_date_set) { gtk_widget_show (dedit->_priv->none_button); } else { gtk_widget_hide (dedit->_priv->none_button); /* If currently set to 'None' set to the current time. */ if (e_date_edit_get_time (dedit) == -1) e_date_edit_set_time (dedit, time (NULL)); } } /* The range of time to show in the time combo popup. */ void e_date_edit_get_time_popup_range (EDateEdit *dedit, gint *lower_hour, gint *upper_hour) { g_return_if_fail (E_IS_DATE_EDIT (dedit)); *lower_hour = dedit->_priv->lower_hour; *upper_hour = dedit->_priv->upper_hour; } void e_date_edit_set_time_popup_range (EDateEdit *dedit, gint lower_hour, gint upper_hour) { g_return_if_fail (E_IS_DATE_EDIT (dedit)); if (dedit->_priv->lower_hour == lower_hour && dedit->_priv->upper_hour == upper_hour) return; dedit->_priv->lower_hour = lower_hour; dedit->_priv->upper_hour = upper_hour; rebuild_time_popup (dedit); } /* Clears the time popup and rebuilds it using the lower_hour, upper_hour and use_24_hour_format settings. */ static void rebuild_time_popup (EDateEdit *dedit) { EDateEditPrivate *priv; GtkList *list; GtkWidget *listitem; char buffer[40], *format; struct tm tmp_tm; gint hour, min; priv = dedit->_priv; list = GTK_LIST (GTK_COMBO (priv->time_combo)->list); gtk_list_clear_items (list, 0, -1); /* Fill the struct tm with some sane values. */ tmp_tm.tm_year = 2000; tmp_tm.tm_mon = 0; tmp_tm.tm_mday = 1; tmp_tm.tm_sec = 0; tmp_tm.tm_isdst = 0; for (hour = priv->lower_hour; hour <= priv->upper_hour; hour++) { /* We don't want to display midnight at the end, since that is really in the next day. */ if (hour == 24) break; /* We want to finish on upper_hour, with min == 0. */ for (min = 0; min == 0 || (min < 60 && hour != priv->upper_hour); min += 30) { tmp_tm.tm_hour = hour; tmp_tm.tm_min = min; if (priv->use_24_hour_format) format = "%H:%M"; else format = "%I:%M %p"; strftime (buffer, sizeof (buffer), format, &tmp_tm); listitem = gtk_list_item_new_with_label (buffer); gtk_widget_show (listitem); gtk_container_add (GTK_CONTAINER (list), listitem); } } } /* Makes the time field & popup sensitive so the user can set the time. */ static void enable_time_combo (EDateEdit *dedit) { gtk_widget_set_sensitive (dedit->_priv->time_combo, TRUE); } /* Makes the time field & popup insensitive and clears it. This is used when the date has been set to "None". */ static void disable_time_combo (EDateEdit *dedit) { gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (dedit->_priv->time_combo)->entry), ""); gtk_widget_set_sensitive (dedit->_priv->time_combo, FALSE); } /* Returns TRUE if the string is empty or is "None" in the current locale. It ignores whitespace. */ static gboolean date_is_none (char *date_text) { char *pos, *none_string; pos = date_text; while (isspace (*pos)) pos++; none_string = _("None"); if (*pos == '\0' || !strncmp (pos, none_string, strlen (none_string))) return TRUE; return FALSE; }