/* * ECalListView - display calendar events in an ETable. * * 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. * * 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 Lesser General Public License * along with this program; if not, see . * * Authors: * Hans Petter Jansson * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) */ #include "evolution-config.h" #include "e-cal-list-view.h" #include "ea-calendar.h" #include #include #include #include #include #include #include #include "e-cal-model-calendar.h" #include "e-cell-date-edit-text.h" #include "comp-util.h" #include "itip-utils.h" #include "calendar-config.h" #include "misc.h" struct _ECalListViewPrivate { /* The main display table */ ETable *table; /* The last ECalendarViewEvent we returned from e_cal_list_view_get_selected_events(), to be freed */ ECalendarViewEvent *cursor_event; }; enum { PROP_0, PROP_IS_EDITING }; /* The icons to represent the event. */ static const gchar *icon_names[] = { "x-office-calendar", "stock_people", "view-refresh" }; static void e_cal_list_view_dispose (GObject *object); static GList *e_cal_list_view_get_selected_events (ECalendarView *cal_view); static gboolean e_cal_list_view_get_selected_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time); static gboolean e_cal_list_view_get_visible_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time); static gboolean e_cal_list_view_popup_menu (GtkWidget *widget); static gboolean e_cal_list_view_on_table_double_click (GtkWidget *table, gint row, gint col, GdkEvent *event, gpointer data); static gboolean e_cal_list_view_on_table_right_click (GtkWidget *table, gint row, gint col, GdkEvent *event, gpointer data); static gboolean e_cal_list_view_on_table_key_press (ETable *table, gint row, gint col, GdkEvent *event, gpointer data); static gboolean e_cal_list_view_on_table_white_space_event (ETable *table, GdkEvent *event, gpointer data); static void e_cal_list_view_cursor_change_cb (ETable *etable, gint row, gpointer data); G_DEFINE_TYPE (ECalListView, e_cal_list_view, E_TYPE_CALENDAR_VIEW) static void e_cal_list_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ECalListView *eclv = E_CAL_LIST_VIEW (object); switch (property_id) { case PROP_IS_EDITING: g_value_set_boolean (value, e_cal_list_view_is_editing (eclv)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gchar * e_cal_list_view_get_description_text (ECalendarView *cal_view) { ECalModel *model; GString *string; const gchar *format; gint n_rows; gint n_selected; g_return_val_if_fail (E_IS_CAL_LIST_VIEW (cal_view), NULL); model = e_calendar_view_get_model (cal_view); n_rows = e_table_model_row_count (E_TABLE_MODEL (model)); n_selected = e_table_selected_count (e_cal_list_view_get_table (E_CAL_LIST_VIEW (cal_view))); string = g_string_sized_new (64); format = ngettext ("%d appointment", "%d appointments", n_rows); g_string_append_printf (string, format, n_rows); if (n_selected > 0) { format = _("%d selected"); g_string_append_len (string, ", ", 2); g_string_append_printf (string, format, n_selected); } return g_string_free (string, FALSE); } static void e_cal_list_view_class_init (ECalListViewClass *class) { GObjectClass *object_class; GtkWidgetClass *widget_class; ECalendarViewClass *view_class; object_class = (GObjectClass *) class; widget_class = (GtkWidgetClass *) class; view_class = (ECalendarViewClass *) class; g_type_class_add_private (class, sizeof (ECalListViewPrivate)); /* Method override */ object_class->dispose = e_cal_list_view_dispose; object_class->get_property = e_cal_list_view_get_property; widget_class->popup_menu = e_cal_list_view_popup_menu; view_class->get_selected_events = e_cal_list_view_get_selected_events; view_class->get_selected_time_range = e_cal_list_view_get_selected_time_range; view_class->get_visible_time_range = e_cal_list_view_get_visible_time_range; view_class->get_description_text = e_cal_list_view_get_description_text; g_object_class_override_property ( object_class, PROP_IS_EDITING, "is-editing"); } static void e_cal_list_view_init (ECalListView *cal_list_view) { cal_list_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (cal_list_view, E_TYPE_CAL_LIST_VIEW, ECalListViewPrivate); cal_list_view->priv->table = NULL; cal_list_view->priv->cursor_event = NULL; } /* Returns the current time, for the ECellDateEdit items. */ static struct tm get_current_time_cb (ECellDateEdit *ecde, gpointer data) { ECalListView *cal_list_view = data; ICalTimezone *zone; ICalTime *tt; struct tm tmp_tm; zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view)); tt = i_cal_time_new_from_timet_with_zone (time (NULL), FALSE, zone); tmp_tm = e_cal_util_icaltime_to_tm (tt); g_clear_object (&tt); return tmp_tm; } static void e_cal_list_view_table_editing_changed_cb (ETable *table, GParamSpec *param, ECalListView *eclv) { g_return_if_fail (E_IS_CAL_LIST_VIEW (eclv)); g_object_notify (G_OBJECT (eclv), "is-editing"); } static void setup_e_table (ECalListView *cal_list_view) { ECalModel *model; ETableExtras *extras; ETableSpecification *specification; GList *strings; ECell *cell, *popup_cell; GtkWidget *container; GtkWidget *widget; gchar *etspecfile; GError *local_error = NULL; model = e_calendar_view_get_model (E_CALENDAR_VIEW (cal_list_view)); /* Create the header columns */ extras = e_table_extras_new (); cell = e_cell_toggle_new (icon_names, G_N_ELEMENTS (icon_names)); g_object_set (cell, "bg-color-column", E_CAL_MODEL_FIELD_COLOR, NULL); e_table_extras_add_cell (extras, "icon", cell); g_object_unref (cell); e_table_extras_add_icon_name (extras, "icon", "x-office-calendar"); /* Normal string fields */ cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); g_object_set ( cell, "bg_color_column", E_CAL_MODEL_FIELD_COLOR, "strikeout_column", E_CAL_MODEL_FIELD_CANCELLED, NULL); e_table_extras_add_cell (extras, "calstring", cell); g_object_unref (cell); /* Date fields */ cell = e_cell_date_edit_text_new (NULL, GTK_JUSTIFY_LEFT); g_object_set ( cell, "bg_color_column", E_CAL_MODEL_FIELD_COLOR, "strikeout_column", E_CAL_MODEL_FIELD_CANCELLED, NULL); e_binding_bind_property ( model, "timezone", cell, "timezone", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); e_binding_bind_property ( model, "use-24-hour-format", cell, "use-24-hour-format", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); popup_cell = e_cell_date_edit_new (); e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); g_object_unref (cell); e_binding_bind_property ( model, "use-24-hour-format", popup_cell, "use-24-hour-format", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); e_table_extras_add_cell (extras, "dateedit", popup_cell); g_object_unref (popup_cell); gtk_widget_hide (E_CELL_DATE_EDIT (popup_cell)->none_button); e_cell_date_edit_set_get_time_callback ( E_CELL_DATE_EDIT (popup_cell), get_current_time_cb, cal_list_view, NULL); /* Combo fields */ cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); g_object_set ( cell, "bg_color_column", E_CAL_MODEL_FIELD_COLOR, "strikeout_column", E_CAL_MODEL_FIELD_CANCELLED, "editable", FALSE, NULL); popup_cell = e_cell_combo_new (); e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); g_object_unref (cell); strings = NULL; strings = g_list_append (strings, (gchar *) _("Public")); strings = g_list_append (strings, (gchar *) _("Private")); strings = g_list_append (strings, (gchar *) _("Confidential")); e_cell_combo_set_popdown_strings ( E_CELL_COMBO (popup_cell), strings); g_list_free (strings); e_table_extras_add_cell (extras, "classification", popup_cell); g_object_unref (popup_cell); /* Status field. */ cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); g_object_set ( cell, "bg_color_column", E_CAL_MODEL_FIELD_COLOR, "strikeout_column", E_CAL_MODEL_FIELD_CANCELLED, NULL); popup_cell = e_cell_combo_new (); e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); g_object_unref (cell); strings = cal_comp_util_get_status_list_for_kind (e_cal_model_get_component_kind (model)); e_cell_combo_set_popdown_strings ( E_CELL_COMBO (popup_cell), strings); g_list_free (strings); e_table_extras_add_cell (extras, "calstatus", popup_cell); g_object_unref (popup_cell); /* Sorting */ e_table_extras_add_compare ( extras, "date-compare", e_cell_date_edit_compare_cb); e_table_extras_add_compare ( extras, "status-compare", e_cal_model_util_status_compare_cb); /* set proper format component for a default 'date' cell renderer */ cell = e_table_extras_get_cell (extras, "date"); e_cell_date_set_format_component (E_CELL_DATE (cell), "calendar"); /* Create table view */ container = GTK_WIDGET (cal_list_view); widget = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 2, 2); g_object_set (G_OBJECT (widget), "hexpand", TRUE, "vexpand", TRUE, "halign", GTK_ALIGN_FILL, "valign", GTK_ALIGN_FILL, NULL); gtk_widget_show (widget); container = widget; etspecfile = g_build_filename ( EVOLUTION_ETSPECDIR, "e-cal-list-view.etspec", NULL); specification = e_table_specification_new (etspecfile, &local_error); /* Failure here is fatal. */ if (local_error != NULL) { g_error ("%s: %s", etspecfile, local_error->message); g_return_if_reached (); } widget = e_table_new (E_TABLE_MODEL (model), extras, specification); g_object_set (G_OBJECT (widget), "uniform-row-height", TRUE, NULL); gtk_container_add (GTK_CONTAINER (container), widget); cal_list_view->priv->table = E_TABLE (widget); gtk_widget_show (widget); g_object_unref (specification); g_object_unref (extras); g_free (etspecfile); /* Connect signals */ g_signal_connect ( cal_list_view->priv->table, "double_click", G_CALLBACK (e_cal_list_view_on_table_double_click), cal_list_view); g_signal_connect ( cal_list_view->priv->table, "right-click", G_CALLBACK (e_cal_list_view_on_table_right_click), cal_list_view); g_signal_connect ( cal_list_view->priv->table, "key-press", G_CALLBACK (e_cal_list_view_on_table_key_press), cal_list_view); g_signal_connect ( cal_list_view->priv->table, "white-space-event", G_CALLBACK (e_cal_list_view_on_table_white_space_event), cal_list_view); g_signal_connect_after ( cal_list_view->priv->table, "cursor_change", G_CALLBACK (e_cal_list_view_cursor_change_cb), cal_list_view); e_signal_connect_notify_after ( cal_list_view->priv->table, "notify::is-editing", G_CALLBACK (e_cal_list_view_table_editing_changed_cb), cal_list_view); } /** * e_cal_list_view_new: * @Returns: a new #ECalListView. * * Creates a new #ECalListView. **/ ECalendarView * e_cal_list_view_new (ECalModel *model) { ECalendarView *cal_list_view; cal_list_view = g_object_new ( E_TYPE_CAL_LIST_VIEW, "model", model, NULL); setup_e_table (E_CAL_LIST_VIEW (cal_list_view)); return cal_list_view; } static void e_cal_list_view_dispose (GObject *object) { ECalListView *cal_list_view; cal_list_view = E_CAL_LIST_VIEW (object); g_clear_pointer (&cal_list_view->priv->cursor_event, g_free); if (cal_list_view->priv->table) { gtk_widget_destroy (GTK_WIDGET (cal_list_view->priv->table)); cal_list_view->priv->table = NULL; } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_cal_list_view_parent_class)->dispose (object); } static void e_cal_list_view_show_popup_menu (ECalListView *cal_list_view, GdkEvent *event) { e_calendar_view_popup_event (E_CALENDAR_VIEW (cal_list_view), event); } static gboolean e_cal_list_view_popup_menu (GtkWidget *widget) { ECalListView *cal_list_view = E_CAL_LIST_VIEW (widget); e_cal_list_view_show_popup_menu (cal_list_view, NULL); return TRUE; } static void e_cal_list_view_open_at_row (ECalListView *cal_list_view, gint row) { ECalModelComponent *comp_data; g_return_if_fail (E_IS_CAL_LIST_VIEW (cal_list_view)); comp_data = e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (cal_list_view)), row); g_warn_if_fail (comp_data != NULL); if (!comp_data) return; e_calendar_view_edit_appointment (E_CALENDAR_VIEW (cal_list_view), comp_data->client, comp_data->icalcomp, EDIT_EVENT_AUTODETECT); } static gboolean e_cal_list_view_on_table_double_click (GtkWidget *table, gint row, gint col, GdkEvent *event, gpointer data) { e_cal_list_view_open_at_row (data, row); return TRUE; } static gboolean e_cal_list_view_on_table_right_click (GtkWidget *table, gint row, gint col, GdkEvent *event, gpointer data) { ECalListView *cal_list_view = E_CAL_LIST_VIEW (data); e_cal_list_view_show_popup_menu (cal_list_view, event); return TRUE; } static gboolean e_cal_list_view_on_table_key_press (ETable *table, gint row, gint col, GdkEvent *event, gpointer data) { if (event && event->type == GDK_KEY_PRESS && (event->key.keyval == GDK_KEY_Return || event->key.keyval == GDK_KEY_KP_Enter) && (event->key.state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)) == 0 && !e_table_is_editing (table)) { e_cal_list_view_open_at_row (data, row); return TRUE; } return FALSE; } static gboolean e_cal_list_view_on_table_white_space_event (ETable *table, GdkEvent *event, gpointer user_data) { ECalListView *cal_list_view = user_data; guint event_button = 0; g_return_val_if_fail (E_IS_CAL_LIST_VIEW (cal_list_view), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (event->type == GDK_BUTTON_PRESS && gdk_event_get_button (event, &event_button) && event_button == 3) { GtkWidget *table_canvas; table_canvas = GTK_WIDGET (table->table_canvas); if (!gtk_widget_has_focus (table_canvas)) gtk_widget_grab_focus (table_canvas); e_cal_list_view_show_popup_menu (cal_list_view, event); return TRUE; } return FALSE; } static void e_cal_list_view_cursor_change_cb (ETable *etable, gint row, gpointer data) { ECalListView *cal_list_view = E_CAL_LIST_VIEW (data); g_signal_emit_by_name (cal_list_view, "selection_changed"); } static gboolean e_cal_list_view_get_selected_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time) { GList *selected; ICalTimezone *zone; selected = e_calendar_view_get_selected_events (cal_view); if (selected) { ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data; ECalComponent *comp; if (!is_comp_data_valid (event)) return FALSE; comp = e_cal_component_new (); e_cal_component_set_icalcomponent (comp, i_cal_component_clone (event->comp_data->icalcomp)); if (start_time) { ECalComponentDateTime *dt; dt = e_cal_component_get_dtstart (comp); if (dt) { if (e_cal_component_datetime_get_tzid (dt)) { zone = i_cal_component_get_timezone (e_cal_component_get_icalcomponent (comp), e_cal_component_datetime_get_tzid (dt)); } else { zone = NULL; } *start_time = i_cal_time_as_timet_with_zone (e_cal_component_datetime_get_value (dt), zone); } else { *start_time = (time_t) 0; } e_cal_component_datetime_free (dt); } if (end_time) { ECalComponentDateTime *dt; dt = e_cal_component_get_dtend (comp); if (dt) { if (e_cal_component_datetime_get_tzid (dt)) { zone = i_cal_component_get_timezone (e_cal_component_get_icalcomponent (comp), e_cal_component_datetime_get_tzid (dt)); } else { zone = NULL; } *end_time = i_cal_time_as_timet_with_zone (e_cal_component_datetime_get_value (dt), zone); } else { *end_time = (time_t) 0; } e_cal_component_datetime_free (dt); } g_object_unref (comp); g_list_free (selected); return TRUE; } return FALSE; } static GList * e_cal_list_view_get_selected_events (ECalendarView *cal_view) { GList *event_list = NULL; gint cursor_row; g_clear_pointer (&E_CAL_LIST_VIEW (cal_view)->priv->cursor_event, g_free); cursor_row = e_table_get_cursor_row ( E_CAL_LIST_VIEW (cal_view)->priv->table); if (cursor_row >= 0) { ECalendarViewEvent *event; event = E_CAL_LIST_VIEW (cal_view)->priv->cursor_event = g_new0 (ECalendarViewEvent, 1); event->comp_data = e_cal_model_get_component_at ( e_calendar_view_get_model (cal_view), cursor_row); event_list = g_list_prepend (event_list, event); } return event_list; } static gboolean e_cal_list_view_get_visible_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time) { /* No time range */ return FALSE; } ETable * e_cal_list_view_get_table (ECalListView *cal_list_view) { g_return_val_if_fail (E_IS_CAL_LIST_VIEW (cal_list_view), NULL); return cal_list_view->priv->table; } gboolean e_cal_list_view_is_editing (ECalListView *eclv) { g_return_val_if_fail (E_IS_CAL_LIST_VIEW (eclv), FALSE); return eclv->priv->table && e_table_is_editing (eclv->priv->table); }