/* * This program 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 of the License, or (at your option) version 3. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Damon Chaplin * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ /* * ECellDateEdit - a subclass of ECellPopup used to show a date with a popup * window to edit it. */ #ifdef HAVE_CONFIG_H #include #endif #include "e-cell-date-edit.h" #include #include #include #include #include #include #include
#include #include /* This depends on ECalendar which is why I didn't put it in gal. */ #include "e-calendar.h" static void e_cell_date_edit_destroy (GtkObject *object); static void e_cell_date_edit_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void e_cell_date_edit_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static gint e_cell_date_edit_do_popup (ECellPopup *ecp, GdkEvent *event, int row, int view_col); static void e_cell_date_edit_set_popup_values (ECellDateEdit *ecde); static void e_cell_date_edit_select_matching_time(ECellDateEdit *ecde, char *time); static void e_cell_date_edit_show_popup (ECellDateEdit *ecde, int row, int view_col); static void e_cell_date_edit_get_popup_pos (ECellDateEdit *ecde, int row, int view_col, gint *x, gint *y, gint *height, gint *width); static void e_cell_date_edit_rebuild_time_list (ECellDateEdit *ecde); static int e_cell_date_edit_key_press (GtkWidget *popup_window, GdkEventKey *event, ECellDateEdit *ecde); static int e_cell_date_edit_button_press (GtkWidget *popup_window, GdkEventButton *event, ECellDateEdit *ecde); static void e_cell_date_edit_on_ok_clicked (GtkWidget *button, ECellDateEdit *ecde); static void e_cell_date_edit_show_time_invalid_warning (ECellDateEdit *ecde); static void e_cell_date_edit_on_now_clicked (GtkWidget *button, ECellDateEdit *ecde); static void e_cell_date_edit_on_none_clicked (GtkWidget *button, ECellDateEdit *ecde); static void e_cell_date_edit_on_today_clicked (GtkWidget *button, ECellDateEdit *ecde); static void e_cell_date_edit_update_cell (ECellDateEdit *ecde, const gchar *text); static void e_cell_date_edit_on_time_selected (GtkTreeSelection *selection, ECellDateEdit *ecde); static void e_cell_date_edit_hide_popup (ECellDateEdit *ecde); /* Our arguments. */ enum { PROP_0, PROP_SHOW_TIME, PROP_SHOW_NOW_BUTTON, PROP_SHOW_TODAY_BUTTON, PROP_ALLOW_NO_DATE_SET, PROP_USE_24_HOUR_FORMAT, PROP_LOWER_HOUR, PROP_UPPER_HOUR }; G_DEFINE_TYPE (ECellDateEdit, e_cell_date_edit, E_CELL_POPUP_TYPE) static void e_cell_date_edit_class_init (ECellDateEditClass *class) { GObjectClass *object_class; GtkObjectClass *gtk_object_class; ECellPopupClass *ecpc; object_class = G_OBJECT_CLASS (class); object_class->get_property = e_cell_date_edit_get_property; object_class->set_property = e_cell_date_edit_set_property; gtk_object_class = GTK_OBJECT_CLASS (class); gtk_object_class->destroy = e_cell_date_edit_destroy; ecpc = E_CELL_POPUP_CLASS (class); ecpc->popup = e_cell_date_edit_do_popup; g_object_class_install_property ( object_class, PROP_SHOW_TIME, g_param_spec_boolean ( "show_time", NULL, NULL, TRUE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_SHOW_NOW_BUTTON, g_param_spec_boolean ( "show_now_button", NULL, NULL, TRUE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_SHOW_TODAY_BUTTON, g_param_spec_boolean ( "show_today_button", NULL, NULL, TRUE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_ALLOW_NO_DATE_SET, g_param_spec_boolean ( "allow_no_date_set", NULL, NULL, TRUE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_USE_24_HOUR_FORMAT, g_param_spec_boolean ( "use_24_hour_format", NULL, NULL, TRUE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_LOWER_HOUR, g_param_spec_int ( "lower_hour", NULL, NULL, G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_UPPER_HOUR, g_param_spec_int ( "upper_hour", NULL, NULL, G_MININT, G_MAXINT, 24, G_PARAM_READWRITE)); } static void e_cell_date_edit_init (ECellDateEdit *ecde) { GtkWidget *frame, *vbox, *hbox, *vbox2; GtkWidget *scrolled_window, *bbox, *tree_view; GtkWidget *now_button, *today_button, *none_button, *ok_button; GtkListStore *store; ecde->lower_hour = 0; ecde->upper_hour = 24; ecde->use_24_hour_format = TRUE; ecde->need_time_list_rebuild = TRUE; ecde->freeze_count = 0; ecde->time_callback = NULL; ecde->time_callback_data = NULL; ecde->time_callback_destroy = NULL; /* We create one popup window for the ECell, since there will only ever be one popup in use at a time. */ ecde->popup_window = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_type_hint (GTK_WINDOW (ecde->popup_window), GDK_WINDOW_TYPE_HINT_COMBO); gtk_window_set_resizable (GTK_WINDOW (ecde->popup_window), TRUE); frame = gtk_frame_new (NULL); gtk_container_add (GTK_CONTAINER (ecde->popup_window), frame); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); gtk_widget_show (frame); vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); hbox = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); ecde->calendar = e_calendar_new (); gnome_canvas_item_set (GNOME_CANVAS_ITEM (E_CALENDAR (ecde->calendar)->calitem), "move_selection_when_moving", FALSE, NULL); gtk_box_pack_start (GTK_BOX (hbox), ecde->calendar, TRUE, TRUE, 0); gtk_widget_show (ecde->calendar); vbox2 = gtk_vbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0); gtk_widget_show (vbox2); ecde->time_entry = gtk_entry_new (); gtk_widget_set_size_request (ecde->time_entry, 50, -1); gtk_box_pack_start (GTK_BOX (vbox2), ecde->time_entry, FALSE, FALSE, 0); gtk_widget_show (ecde->time_entry); scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_box_pack_start (GTK_BOX (vbox2), scrolled_window, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_widget_show (scrolled_window); store = gtk_list_store_new (1, G_TYPE_STRING); tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); g_object_unref (store); gtk_tree_view_append_column ( GTK_TREE_VIEW (tree_view), gtk_tree_view_column_new_with_attributes ("Text", gtk_cell_renderer_text_new (), "text", 0, NULL)); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), tree_view); gtk_container_set_focus_vadjustment (GTK_CONTAINER (tree_view), gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window))); gtk_container_set_focus_hadjustment (GTK_CONTAINER (tree_view), gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled_window))); gtk_widget_show (tree_view); ecde->time_tree_view = tree_view; g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)), "changed", G_CALLBACK (e_cell_date_edit_on_time_selected), ecde); bbox = gtk_hbutton_box_new (); gtk_container_set_border_width (GTK_CONTAINER (bbox), 4); gtk_box_set_spacing (GTK_BOX (bbox), 2); gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0); gtk_widget_show (bbox); now_button = gtk_button_new_with_label (_("Now")); gtk_container_add (GTK_CONTAINER (bbox), now_button); gtk_widget_show (now_button); g_signal_connect((now_button), "clicked", G_CALLBACK (e_cell_date_edit_on_now_clicked), ecde); ecde->now_button = now_button; today_button = gtk_button_new_with_label (_("Today")); gtk_container_add (GTK_CONTAINER (bbox), today_button); gtk_widget_show (today_button); g_signal_connect((today_button), "clicked", G_CALLBACK (e_cell_date_edit_on_today_clicked), ecde); ecde->today_button = today_button; none_button = gtk_button_new_with_label (_("None")); gtk_container_add (GTK_CONTAINER (bbox), none_button); gtk_widget_show (none_button); g_signal_connect((none_button), "clicked", G_CALLBACK (e_cell_date_edit_on_none_clicked), ecde); ecde->none_button = none_button; ok_button = gtk_button_new_with_label (_("OK")); gtk_container_add (GTK_CONTAINER (bbox), ok_button); gtk_widget_show (ok_button); g_signal_connect((ok_button), "clicked", G_CALLBACK (e_cell_date_edit_on_ok_clicked), ecde); g_signal_connect((ecde->popup_window), "key_press_event", G_CALLBACK (e_cell_date_edit_key_press), ecde); g_signal_connect((ecde->popup_window), "button_press_event", G_CALLBACK (e_cell_date_edit_button_press), ecde); } /** * e_cell_date_edit_new: * * Creates a new ECellDateEdit renderer. * * Returns: an ECellDateEdit object. */ ECell * e_cell_date_edit_new (void) { return g_object_new (e_cell_date_edit_get_type (), NULL); } /* * GtkObject::destroy method */ static void e_cell_date_edit_destroy (GtkObject *object) { ECellDateEdit *ecde = E_CELL_DATE_EDIT (object); e_cell_date_edit_set_get_time_callback (ecde, NULL, NULL, NULL); gtk_widget_destroy (ecde->popup_window); ecde->popup_window = NULL; GTK_OBJECT_CLASS (e_cell_date_edit_parent_class)->destroy (object); } static void e_cell_date_edit_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ECellDateEdit *ecde; ecde = E_CELL_DATE_EDIT (object); switch (property_id) { case PROP_SHOW_TIME: g_value_set_boolean (value, GTK_WIDGET_VISIBLE (ecde->time_entry)); return; case PROP_SHOW_NOW_BUTTON: g_value_set_boolean (value, GTK_WIDGET_VISIBLE (ecde->now_button)); return; case PROP_SHOW_TODAY_BUTTON: g_value_set_boolean (value, GTK_WIDGET_VISIBLE (ecde->today_button)); return; case PROP_ALLOW_NO_DATE_SET: g_value_set_boolean (value, GTK_WIDGET_VISIBLE (ecde->none_button)); return; case PROP_USE_24_HOUR_FORMAT: g_value_set_boolean (value, ecde->use_24_hour_format); return; case PROP_LOWER_HOUR: g_value_set_int (value, ecde->lower_hour); return; case PROP_UPPER_HOUR: g_value_set_int (value, ecde->upper_hour); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void e_cell_date_edit_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ECellDateEdit *ecde; gint ivalue; gboolean bvalue; ecde = E_CELL_DATE_EDIT (object); switch (property_id) { case PROP_SHOW_TIME: if (g_value_get_boolean (value)) { gtk_widget_show (ecde->time_entry); gtk_widget_show (ecde->time_tree_view); } else { gtk_widget_hide (ecde->time_entry); gtk_widget_hide (ecde->time_tree_view); } return; case PROP_SHOW_NOW_BUTTON: if (g_value_get_boolean (value)) { gtk_widget_show (ecde->now_button); } else { gtk_widget_hide (ecde->now_button); } return; case PROP_SHOW_TODAY_BUTTON: if (g_value_get_boolean (value)) { gtk_widget_show (ecde->today_button); } else { gtk_widget_hide (ecde->today_button); } return; case PROP_ALLOW_NO_DATE_SET: if (g_value_get_boolean (value)) { gtk_widget_show (ecde->none_button); } else { /* FIXME: What if we have no date set now. */ gtk_widget_hide (ecde->none_button); } return; case PROP_USE_24_HOUR_FORMAT: bvalue = g_value_get_boolean (value); if (ecde->use_24_hour_format != bvalue) { ecde->use_24_hour_format = bvalue; ecde->need_time_list_rebuild = TRUE; } return; case PROP_LOWER_HOUR: ivalue = g_value_get_int (value); ivalue = CLAMP (ivalue, 0, 24); if (ecde->lower_hour != ivalue) { ecde->lower_hour = ivalue; ecde->need_time_list_rebuild = TRUE; } return; case PROP_UPPER_HOUR: ivalue = g_value_get_int (value); ivalue = CLAMP (ivalue, 0, 24); if (ecde->upper_hour != ivalue) { ecde->upper_hour = ivalue; ecde->need_time_list_rebuild = TRUE; } return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static gint e_cell_date_edit_do_popup (ECellPopup *ecp, GdkEvent *event, int row, int view_col) { ECellDateEdit *ecde = E_CELL_DATE_EDIT (ecp); e_cell_date_edit_show_popup (ecde, row, view_col); e_cell_date_edit_set_popup_values (ecde); gtk_grab_add (ecde->popup_window); /* Set the focus to the first widget. */ gtk_widget_grab_focus (ecde->time_entry); gdk_window_focus (ecde->popup_window->window, GDK_CURRENT_TIME); return TRUE; } static void e_cell_date_edit_set_popup_values (ECellDateEdit *ecde) { ECellPopup *ecp = E_CELL_POPUP (ecde); ECellText *ecell_text = E_CELL_TEXT (ecp->child); ECellView *ecv = (ECellView*) ecp->popup_cell_view; ETableItem *eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view); ETableCol *ecol; char *cell_text; ETimeParseStatus status; struct tm date_tm; GDate date; ECalendarItem *calitem; char buffer[64]; gboolean is_date = TRUE; ecol = e_table_header_get_column (eti->header, ecp->popup_view_col); cell_text = e_cell_text_get_text (ecell_text, ecv->e_table_model, ecol->col_idx, ecp->popup_row); /* Try to parse just a date first. If the value is only a date, we use a DATE value. */ status = e_time_parse_date (cell_text, &date_tm); if (status == E_TIME_PARSE_INVALID) { is_date = FALSE; status = e_time_parse_date_and_time (cell_text, &date_tm); } /* If there is no date and time set, or the date is invalid, we clear the selections, else we select the appropriate date & time. */ calitem = E_CALENDAR_ITEM (E_CALENDAR (ecde->calendar)->calitem); if (status == E_TIME_PARSE_NONE || status == E_TIME_PARSE_INVALID) { gtk_entry_set_text (GTK_ENTRY (ecde->time_entry), ""); e_calendar_item_set_selection (calitem, NULL, NULL); gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (ecde->time_tree_view))); } else { if (is_date) { buffer[0] = '\0'; } else { e_time_format_time (&date_tm, ecde->use_24_hour_format, FALSE, buffer, sizeof (buffer)); } gtk_entry_set_text (GTK_ENTRY (ecde->time_entry), buffer); g_date_clear (&date, 1); g_date_set_dmy (&date, date_tm.tm_mday, date_tm.tm_mon + 1, date_tm.tm_year + 1900); e_calendar_item_set_selection (calitem, &date, &date); if (is_date) { gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (ecde->time_tree_view))); } else { e_cell_date_edit_select_matching_time (ecde, buffer); } } e_cell_text_free_text (ecell_text, cell_text); } static void e_cell_date_edit_select_matching_time (ECellDateEdit *ecde, char *time) { gboolean found = FALSE; gboolean valid; GtkTreeSelection *selection; GtkTreeIter iter; GtkTreeModel *model; model = gtk_tree_view_get_model (GTK_TREE_VIEW (ecde->time_tree_view)); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ecde->time_tree_view)); for (valid = gtk_tree_model_get_iter_first (model, &iter); valid && !found; valid = gtk_tree_model_iter_next (model, &iter)) { char *str = NULL; gtk_tree_model_get (model, &iter, 0, &str, -1); if (g_str_equal (str, time)) { GtkTreePath *path = gtk_tree_model_get_path (model, &iter); gtk_tree_view_set_cursor (GTK_TREE_VIEW (ecde->time_tree_view), path, NULL, FALSE); gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (ecde->time_tree_view), path, NULL, FALSE, 0.0, 0.0); gtk_tree_path_free (path); found = TRUE; } g_free (str); } if (!found) { gtk_tree_selection_unselect_all (selection); gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (ecde->time_tree_view), 0, 0); } } static void e_cell_date_edit_show_popup (ECellDateEdit *ecde, int row, int view_col) { gint x, y, width, height; if (ecde->need_time_list_rebuild) e_cell_date_edit_rebuild_time_list (ecde); /* This code is practically copied from GtkCombo. */ e_cell_date_edit_get_popup_pos (ecde, row, view_col, &x, &y, &height, &width); gtk_window_move (GTK_WINDOW (ecde->popup_window), x, y); gtk_widget_set_size_request (ecde->popup_window, width, height); gtk_widget_realize (ecde->popup_window); gdk_window_resize (ecde->popup_window->window, width, height); gtk_widget_show (ecde->popup_window); e_cell_popup_set_shown (E_CELL_POPUP (ecde), TRUE); } /* Calculates the size and position of the popup window (like GtkCombo). */ static void e_cell_date_edit_get_popup_pos (ECellDateEdit *ecde, int row, int view_col, gint *x, gint *y, gint *height, gint *width) { ECellPopup *ecp = E_CELL_POPUP (ecde); ETableItem *eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view); GtkWidget *canvas = GTK_WIDGET (GNOME_CANVAS_ITEM (eti)->canvas); GtkRequisition popup_requisition; gint avail_height, screen_width, column_width, row_height; double x1, y1, wx, wy; gdk_window_get_origin (canvas->window, x, y); x1 = e_table_header_col_diff (eti->header, 0, view_col + 1); y1 = e_table_item_row_diff (eti, 0, row + 1); column_width = e_table_header_col_diff (eti->header, view_col, view_col + 1); row_height = e_table_item_row_diff (eti, row, row + 1); gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (eti), &x1, &y1); gnome_canvas_world_to_window (GNOME_CANVAS (canvas), x1, y1, &wx, &wy); x1 = wx; y1 = wy; *x += x1; /* The ETable positions don't include the grid lines, I think, so we add 1. */ *y += y1 + 1 - (int)((GnomeCanvas *)canvas)->layout.vadjustment->value + ((GnomeCanvas *)canvas)->zoom_yofs; avail_height = gdk_screen_height () - *y; /* We'll use the entire screen width if needed, but we save space for the vertical scrollbar in case we need to show that. */ screen_width = gdk_screen_width (); gtk_widget_size_request (ecde->popup_window, &popup_requisition); /* Calculate the desired width. */ *width = popup_requisition.width; /* Use at least the same width as the column. */ if (*width < column_width) *width = column_width; /* Check if it fits in the available height. */ if (popup_requisition.height > avail_height) { /* It doesn't fit, so we see if we have the minimum space needed. */ if (*y - row_height > avail_height) { /* We don't, so we show the popup above the cell instead of below it. */ avail_height = *y - row_height; *y -= (popup_requisition.height + row_height); if (*y < 0) *y = 0; } } /* We try to line it up with the right edge of the column, but we don't want it to go off the edges of the screen. */ if (*x > screen_width) *x = screen_width; *x -= *width; if (*x < 0) *x = 0; *height = popup_requisition.height; } /* This handles key press events in the popup window. If the Escape key is pressed we hide the popup, and do not change the cell contents. */ static int e_cell_date_edit_key_press (GtkWidget *popup_window, GdkEventKey *event, ECellDateEdit *ecde) { /* If the Escape key is pressed we hide the popup. */ if (event->keyval != GDK_Escape) return FALSE; e_cell_date_edit_hide_popup (ecde); return TRUE; } /* This handles button press events in the popup window. If the button is pressed outside the popup, we hide it and do not change the cell contents. */ static int e_cell_date_edit_button_press (GtkWidget *popup_window, GdkEventButton *event, ECellDateEdit *ecde) { GtkWidget *event_widget; event_widget = gtk_get_event_widget ((GdkEvent*) event); if (gtk_widget_get_toplevel (event_widget) != popup_window) { e_cell_date_edit_hide_popup (ecde); } return TRUE; } /* Clears the time list and rebuilds it using the lower_hour, upper_hour and use_24_hour_format settings. */ static void e_cell_date_edit_rebuild_time_list (ECellDateEdit *ecde) { GtkListStore *store; char buffer[40]; struct tm tmp_tm; gint hour, min; store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (ecde->time_tree_view))); gtk_list_store_clear (store); /* 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 = ecde->lower_hour; hour <= ecde->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 != ecde->upper_hour); min += 30) { GtkTreeIter iter; tmp_tm.tm_hour = hour; tmp_tm.tm_min = min; e_time_format_time (&tmp_tm, ecde->use_24_hour_format, FALSE, buffer, sizeof (buffer)); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, buffer, -1); } } ecde->need_time_list_rebuild = FALSE; } static void e_cell_date_edit_on_ok_clicked (GtkWidget *button, ECellDateEdit *ecde) { ECalendarItem *calitem; GDate start_date, end_date; gboolean day_selected; struct tm date_tm; char buffer[64]; const char *text; ETimeParseStatus status; gboolean is_date = FALSE; calitem = E_CALENDAR_ITEM (E_CALENDAR (ecde->calendar)->calitem); day_selected = e_calendar_item_get_selection (calitem, &start_date, &end_date); text = gtk_entry_get_text (GTK_ENTRY (ecde->time_entry)); status = e_time_parse_time (text, &date_tm); if (status == E_TIME_PARSE_INVALID) { e_cell_date_edit_show_time_invalid_warning (ecde); return; } else if (status == E_TIME_PARSE_NONE) { is_date = TRUE; } if (day_selected) { date_tm.tm_year = g_date_get_year (&start_date) - 1900; date_tm.tm_mon = g_date_get_month (&start_date) - 1; date_tm.tm_mday = g_date_get_day (&start_date); /* We need to call this to set the weekday. */ mktime (&date_tm); e_time_format_date_and_time (&date_tm, ecde->use_24_hour_format, !is_date, FALSE, buffer, sizeof (buffer)); } else { buffer[0] = '\0'; } e_cell_date_edit_update_cell (ecde, buffer); e_cell_date_edit_hide_popup (ecde); } static void e_cell_date_edit_show_time_invalid_warning (ECellDateEdit *ecde) { GtkWidget *dialog; struct tm date_tm; char buffer[64]; /* Create a useful error message showing the correct format. */ date_tm.tm_year = 100; date_tm.tm_mon = 0; date_tm.tm_mday = 1; date_tm.tm_hour = 1; date_tm.tm_min = 30; date_tm.tm_sec = 0; date_tm.tm_isdst = -1; e_time_format_time (&date_tm, ecde->use_24_hour_format, FALSE, buffer, sizeof (buffer)); /* FIXME: Fix transient settings - I'm not sure it works with popup windows. Maybe we need to use a normal window without decorations.*/ dialog = gtk_message_dialog_new ( GTK_WINDOW (ecde->popup_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The time must be in the format: %s"), buffer); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static void e_cell_date_edit_on_now_clicked (GtkWidget *button, ECellDateEdit *ecde) { struct tm tmp_tm; time_t t; char buffer[64]; if (ecde->time_callback) { tmp_tm = (*ecde->time_callback) (ecde, ecde->time_callback_data); } else { t = time (NULL); tmp_tm = *localtime (&t); } e_time_format_date_and_time (&tmp_tm, ecde->use_24_hour_format, TRUE, FALSE, buffer, sizeof (buffer)); e_cell_date_edit_update_cell (ecde, buffer); e_cell_date_edit_hide_popup (ecde); } static void e_cell_date_edit_on_none_clicked (GtkWidget *button, ECellDateEdit *ecde) { e_cell_date_edit_update_cell (ecde, ""); e_cell_date_edit_hide_popup (ecde); } static void e_cell_date_edit_on_today_clicked (GtkWidget *button, ECellDateEdit *ecde) { struct tm tmp_tm; time_t t; char buffer[64]; if (ecde->time_callback) { tmp_tm = (*ecde->time_callback) (ecde, ecde->time_callback_data); } else { t = time (NULL); tmp_tm = *localtime (&t); } tmp_tm.tm_hour = 0; tmp_tm.tm_min = 0; tmp_tm.tm_sec = 0; e_time_format_date_and_time (&tmp_tm, ecde->use_24_hour_format, FALSE, FALSE, buffer, sizeof (buffer)); e_cell_date_edit_update_cell (ecde, buffer); e_cell_date_edit_hide_popup (ecde); } static void e_cell_date_edit_update_cell (ECellDateEdit *ecde, const gchar *text) { ECellPopup *ecp = E_CELL_POPUP (ecde); ECellText *ecell_text = E_CELL_TEXT (ecp->child); ECellView *ecv = (ECellView*) ecp->popup_cell_view; ETableItem *eti = E_TABLE_ITEM (ecv->e_table_item_view); ETableCol *ecol; gchar *old_text; /* Compare the new text with the existing cell contents. */ ecol = e_table_header_get_column (eti->header, ecp->popup_view_col); old_text = e_cell_text_get_text (ecell_text, ecv->e_table_model, ecol->col_idx, ecp->popup_row); /* If they are different, update the cell contents. */ if (strcmp (old_text, text)) { e_cell_text_set_value (ecell_text, ecv->e_table_model, ecol->col_idx, ecp->popup_row, text); e_cell_leave_edit (ecv, ecp->popup_view_col, ecol->col_idx, ecp->popup_row, NULL); } e_cell_text_free_text (ecell_text, old_text); } static void e_cell_date_edit_on_time_selected (GtkTreeSelection *selection, ECellDateEdit *ecde) { gchar *list_item_text = NULL; GtkTreeIter iter; GtkTreeModel *model; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, 0, &list_item_text, -1); g_return_if_fail (list_item_text != NULL); gtk_entry_set_text (GTK_ENTRY (ecde->time_entry), list_item_text); g_free (list_item_text); } static void e_cell_date_edit_hide_popup (ECellDateEdit *ecde) { gtk_grab_remove (ecde->popup_window); gtk_widget_hide (ecde->popup_window); e_cell_popup_set_shown (E_CELL_POPUP (ecde), FALSE); } /* These freeze and thaw the rebuilding of the time list. They are useful when setting several properties which result in rebuilds of the list, e.g. the lower_hour, upper_hour and use_24_hour_format properties. */ void e_cell_date_edit_freeze (ECellDateEdit *ecde) { g_return_if_fail (E_IS_CELL_DATE_EDIT (ecde)); ecde->freeze_count++; } void e_cell_date_edit_thaw (ECellDateEdit *ecde) { g_return_if_fail (E_IS_CELL_DATE_EDIT (ecde)); if (ecde->freeze_count > 0) { ecde->freeze_count--; if (ecde->freeze_count == 0) e_cell_date_edit_rebuild_time_list (ecde); } } /* Sets a callback to use to get the current time. This is useful if the application needs to use its own timezone data rather than rely on the Unix timezone. */ void e_cell_date_edit_set_get_time_callback (ECellDateEdit *ecde, ECellDateEditGetTimeCallback cb, gpointer data, GDestroyNotify destroy) { g_return_if_fail (E_IS_CELL_DATE_EDIT (ecde)); if (ecde->time_callback_data && ecde->time_callback_destroy) (*ecde->time_callback_destroy) (ecde->time_callback_data); ecde->time_callback = cb; ecde->time_callback_data = data; ecde->time_callback_destroy = destroy; } 006-10-02
4-8/+8
* - Update to 4.92miwi2006-09-303-6/+13
* Change my MAINTAINER email to be my @FreeBSD emailxride2006-09-301-1/+1
* Update to 0.25.tobez2006-09-293-4/+8
* - Apply fix to ulong problem. http://bugs.mysql.com/bug.php?id=22227mnag2006-09-273-1/+23
* The update to handle different versions of ICU was not sufficient.girgen2006-09-2716-32/+32
* Add p5-Genezzo 0.64, an extensible database with SQL and DBI.clsung2006-09-275-0/+157
* - Add qdbm-java, slave port to qdbmahze2006-09-264-0/+62
* - Update for java slave portahze2006-09-261-5/+6
* Add qdbm-plus (slave port to qdbm)ahze2006-09-264-0/+119
* - Update to 1.8.71ahze2006-09-263-7/+5
* upgrade to 1.8.0.7ijliao2006-09-252-6/+5
* Reset maintainership of ports assigned to antonio@php.net after numerouslinimon2006-09-2310-10/+10
* - Update to 1.34mnag2006-09-222-4/+4
* Upgrade to JasperReports 1.2.7.thierry2006-09-222-7/+7
* Fix bad file name to the ICU patch.girgen2006-09-227-7/+7
* Add a ad-hoc patch and unbreak.knu2006-09-212-3/+11
* - Update to 0.30clsung2006-09-212-4/+4
* - Update to 1.2.2b1mnag2006-09-212-7/+4
* - Update to 2.9.0rafan2006-09-216-18/+12
* Prepare for ICU 3.6skv2006-09-202-2/+2
* - Chase URL change of the main distribution site [1]sergei2006-09-201-2/+3
* Update the patch to be more agnostic with regards to the version of icu.girgen2006-09-2016-40/+40
* - refine Makefile, pkg-plist [1]clsung2006-09-203-17/+25
* Allow to build port as 'root' if PACKAGE_BUILDING defined.skv2006-09-203-0/+6
* - Add OPTIONS to some freepascal ports. Now, we can install the external libsacm2006-09-197-24/+19
* Add client library for Firebird 2.0skv2006-09-182-0/+38
* Add Firebird 2.0.0 (RC4)skv2006-09-1857-1412/+593
* use proper Makefile variablesijliao2006-09-181-2/+2
* - Update to 0.753rafan2006-09-182-5/+5
* - Update to 0.726rafan2006-09-182-4/+4
* add java-db 3.0.12ijliao2006-09-185-0/+88
* Update to 0.07002lbr2006-09-172-9/+5
* - Fix INDEX buildsem2006-09-171-1/+1
* - Update to 7.5rafan2006-09-172-9/+7
* The GNUstep Database Library 2 (GDL2) is a set of libraries to mapdinoex2006-09-175-0/+219
* BROKEN: Does not compilekris2006-09-173-0/+6
* Drop maintainership.osa2006-09-152-2/+2
* - portlint(1) in PORTREVISIONmnag2006-09-152-1/+23
* DMOZ::ParseRDF is an object-oriented module for parsing DMOZ data intomiwi2006-09-135-0/+36
* Change all my MAINTAINER lines to my new FreeBSD.org address.alexbl2006-09-131-1/+1
* 1: USE_LDCONFIG.vanilla2006-09-131-1/+2
* KDE 3.5.4 / KOffice 1.5.2lofi2006-09-132-5/+5
* Fix pkg-plist.vanilla2006-09-131-1/+2
* Update to 1.13mat2006-09-124-26/+11
* Update to 3.0007skv2006-09-122-4/+4
* - Respects CFLAGSmiwi2006-09-111-2/+2
* BROKEN on amd64: Does not buildkris2006-09-111-1/+7
* Fix patching with WITH_PROC_SCOPE_PTH=yesale2006-09-115-15/+0
* - Update to 1.33mnag2006-09-102-5/+6
* - update to 0.2.8itetcu2006-09-096-8/+8
* Fix for WITH_PROC_SCOPE_PTH=yes.ale2006-09-091-1/+1
* - Update to 1.40rafan2006-09-082-4/+4
* Minor plist fixup: @dirrm => @dirrmtryskv2006-09-081-1/+1
* Add p5-Cache-Memcached-XS 0.01, client library for memcached usingskv2006-09-085-0/+50
* - Update to 2.0.4acm2006-09-0810-21/+51
* - Update to 0.752rafan2006-09-072-5/+5
* - Update to 0.725rafan2006-09-072-4/+4
* - Update to 5.0.24a releaseale2006-09-072-4/+8
* Upgrade to 1.0.4.vanilla2006-09-055-47/+16
* Reset inactive maintainer who has not responded to email.linimon2006-09-051-1/+1
* Remove locale stuff in plist that are in mtree/BSD.x11-4.dist.mezz2006-09-051-2/+0
* Reset inactive maintainer who has not responded to email. Also mark aslinimon2006-09-041-1/+4
* Reset inactive maintainer who has not responded to email.linimon2006-09-041-1/+1
* Upgrade to JasperReports 1.2.6.thierry2006-09-042-7/+7
* - Update to 1.8.70ahze2006-09-033-8/+9
* NO_PACKAGE is not intended use, rather RESTRICTEDkris2006-09-031-1/+1
* NO_CDROM and NO_PACKAGE are superfluouskris2006-09-032-4/+0
* Schedule these broken ports for termination on 2006-12-01kris2006-09-032-0/+4
* - Update to 2.0.5.1miwi2006-09-022-4/+4
* - Update to 0.751clsung2006-09-022-7/+7
* - Update to 0.724clsung2006-09-022-6/+7
* Update to version 1.3.2:edwin2006-09-022-4/+4
* Update two patchlevels at once. This is all fairly routine bugfixitetcu2006-09-014-10/+8
* I cannot reproduce this breakage in my i386 and amd64 environment (andkuriyama2006-08-311-3/+13
* - fix ldconfig problemclsung2006-08-301-2/+2
* - Update to 0.95clsung2006-08-303-6/+6
* Add p5-SQL-ReservedWords 0.7, reserved SQL words by ANSI/ISO.clsung2006-08-305-0/+60
* - now it builds on 4.xclsung2006-08-281-7/+2
* Fix plist.skv2006-08-252-1/+3
* - Update to 1.8.68ahze2006-08-253-5/+5
* - Update to 1.0.5miwi2006-08-244-567/+583
* Update to 1.34mat2006-08-243-8/+4
* Update to 3.0.15skv2006-08-232-6/+6
* The Python SQL Toolkit and Object Relational Mappermiwi2006-08-2311-0/+209
* New port: databases/p5-DBIx-Class-DigestColumns, Automatic digest columnslbr2006-08-235-0/+46
* * Update rails and its related ports to match rails 1.1.6:clsung2006-08-234-9/+23
* Fix build on FreeBSD 4.xfjoe2006-08-211-0/+3
* Update to 0.07001lbr2006-08-202-6/+10
* - distfile has been re-rolled.clsung2006-08-164-6/+8
* Update to 1.2.15.demon2006-08-163-5/+5
* - Add LATEST_LINKmnag2006-08-161-0/+2
* Update to 0.37.1skv2006-08-165-22/+9
* Add p5-Relations-Query 0.93, object for building queries withtobez2006-08-155-0/+43
* Add p5-Relations 0.95, functions to Use with Databases and Queries.tobez2006-08-155-0/+40
* - Define USE_GETTEXT, bump PORTREVISIONrafan2006-08-151-2/+3
* - Remove directories that are not in mtree filerafan2006-08-151-0/+2
* - Update to version 2.8.2.2clsung2006-08-154-14/+14
* - s,INSTALLS_SHLIB,USE_LDCONFIG,gclsung2006-08-1515-15/+15
* Update to 1.52mat2006-08-153-9/+9
* - Update to 0.75clsung2006-08-142-5/+5
* - Update to 0.723clsung2006-08-142-4/+4
* - Update to 3.3.7mnag2006-08-144-10/+10
* psycopg2 is a PostgreSQL database adapter for the Python programming language.miwi2006-08-115-0/+117
* - Fix build with OpenSSLpav2006-08-101-10/+21
* - When using PERL_MODBUILD=yes, don't use RUN_DEPENDS=${BUILD_DEPENDS}lbr2006-08-101-2/+3
* - When using PERL_MODBUILD=yes, don't use RUN_DEPENDS=${BUILD_DEPENDS}lbr2006-08-101-2/+3
* When using PERL_MODBUILD=yes, don't use RUN_DEPENDS=${BUILD_DEPENDS}lbr2006-08-101-3/+3
* - Update to 1.8.66ahze2006-08-093-5/+5
* Update to 2.6.7girgen2006-08-0912-15/+48
* - Change to use MASTER_SITE_RUBYFORGE macro to fix fetch problem.jmelo2006-08-087-7/+14
* - Eliminate a patcherwin2006-08-082-20/+7
* - Update to 0.35clsung2006-08-083-7/+7
* - Update to 3.1.1miwi2006-08-0710-25/+25
* Make setup not to install an egg info file which isn't needed forperky2006-08-072-0/+4
* - Update to 5.0.24pav2006-08-076-21/+13
* - Update to 4.1.21pav2006-08-072-4/+4
* - Use wxgtk2.6-unicode, bump PORTREVISIONrafan2006-08-061-8/+15
* RRD::Simple provides a simple interface to RRDTool's RRDs module. Thismiwi2006-08-054-0/+50
* Add support for DESTDIR part I.erwin2006-08-044-4/+3
* - Update to 2.8.2.1rafan2006-08-044-12/+12
* - Update to 1.8.65ahze2006-08-033-6/+6
* - Update to 0.04clsung2006-08-022-4/+4
* Update to 1.6.3.marcus2006-08-013-5/+5
* - Provide additional mirrors where neededsat2006-07-311-2/+1
* - Fix pkg-plistmiwi2006-07-311-5/+0
* - Fix pkg-plistmiwi2006-07-301-2/+0
* DBIx::Log4perl is a wrapper over DBI which adds logging of your DBI activityerwin2006-07-305-0/+56
* Update to 0.95itetcu2006-07-292-5/+5
* - Added post-extract on Makefiles.units. It remove the svn directories fromacm2006-07-297-0/+7
* Update to 0.03007lbr2006-07-282-4/+4
* - Update to 0.08 [1]erwin2006-07-282-13/+7
* Update to 1.1.0.knu2006-07-2812-48/+24
* Routine update to the latest stable version. The big delta in theitetcu2006-07-284-14/+16
* Set correct maintainer.ale2006-07-271-1/+1
* - Update to 1.8.63ahze2006-07-273-5/+5
* No functional change: just add "-v" to investigate a linkage failurethierry2006-07-271-1/+10
* Update to 0.03006lbr2006-07-262-8/+11
* Add p5-Class-Inflate 0.03, inflate HASH Object from Values in Database.clsung2006-07-255-0/+36
* Adding port databases/ocaml-sqlite3, OCaml bindings for databases/sqlite3.aaron2006-07-259-0/+134
* Update to 0.9.6.0lbr2006-07-254-10/+10
* Update to 0.9.6lbr2006-07-256-10/+10
* Use bsd.wx.mk.fjoe2006-07-251-3/+4
* - Remove bad site:jmelo2006-07-251-1/+0
* - Fix mastersite.jmelo2006-07-252-2/+2
* - Remove bad site:jmelo2006-07-251-2/+1
* - Remove bad site:jmelo2006-07-251-2/+1
* - Remove bad site:jmelo2006-07-251-1/+1
* - The big change on ports of freepascal, now all ports are compiledacm2006-07-2414-15/+178
* - Update to 0.8.1sem2006-07-238-36/+65
* - Update to 0.07000lbr2006-07-233-31/+32
* Add p5-Cache-BDB 0.04, an object caching wrapper around BerkeleyDB.clsung2006-07-235-0/+49
* - Update to 0.742rafan2006-07-233-5/+7
* - Update to 0.722rafan2006-07-232-4/+4
* Update to 0.03005lbr2006-07-202-8/+6
* - Use USE_LDCONFIGgarga2006-07-193-12/+4
* - Update to 1.8.61ahze2006-07-193-5/+5
* - Fix the last commit: --with-db-lib -> --with-db-versionsem2006-07-161-2/+2
* - Fix a bug when databases/ruby-bdb port links only with highest version ofsem2006-07-161-1/+3
* - Update to 0.741rafan2006-07-162-5/+5
* - Update to 0.721rafan2006-07-162-4/+4
* Mark broken: gets coredump on install on pointyhat (all archs).linimon2006-07-151-0/+2
* Update to 0.09mat2006-07-14