/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Author : * Rodrigo Moya * * Copyright 2003, Ximian, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU 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 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 */ #include #include #include #include #include #include #include #include "e-util/e-dialog-utils.h" #include "cal-util/cal-util-marshal.h" #include "cal-util/timeutil.h" #include "evolution-activity-client.h" #include "calendar-commands.h" #include "calendar-config.h" #include "comp-util.h" #include "e-cal-model-calendar.h" #include "e-cal-view.h" #include "e-comp-editor-registry.h" #include "itip-utils.h" #include "dialogs/delete-comp.h" #include "dialogs/delete-error.h" #include "dialogs/event-editor.h" #include "dialogs/send-comp.h" #include "dialogs/cancel-comp.h" #include "dialogs/recur-comp.h" #include "print.h" #include "goto.h" #include "ea-calendar.h" /* Used for the status bar messages */ #define EVOLUTION_CALENDAR_PROGRESS_IMAGE "evolution-calendar-mini.png" static GdkPixbuf *progress_icon[2] = { NULL, NULL }; struct _ECalViewPrivate { /* The GnomeCalendar we are associated to */ GnomeCalendar *calendar; /* The calendar model we are monitoring */ ECalModel *model; /* The activity client used to show messages on the status bar. */ EvolutionActivityClient *activity; /* the invisible widget to manage the clipboard selections */ GtkWidget *invisible; gchar *clipboard_selection; /* The popup menu */ EPopupMenu *view_menu; /* The timezone. */ icaltimezone *zone; /* The default category */ char *default_category; }; static void e_cal_view_class_init (ECalViewClass *klass); static void e_cal_view_init (ECalView *cal_view, ECalViewClass *klass); static void e_cal_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void e_cal_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void e_cal_view_destroy (GtkObject *object); static GObjectClass *parent_class = NULL; static GdkAtom clipboard_atom = GDK_NONE; extern ECompEditorRegistry *comp_editor_registry; /* Property IDs */ enum props { PROP_0, PROP_MODEL, }; /* FIXME Why are we emitting these event signals here? Can't the model just be listened to? */ /* Signal IDs */ enum { SELECTION_CHANGED, TIMEZONE_CHANGED, EVENT_CHANGED, EVENT_ADDED, LAST_SIGNAL }; static guint e_cal_view_signals[LAST_SIGNAL] = { 0 }; static void e_cal_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ECalView *cal_view; ECalViewPrivate *priv; cal_view = E_CAL_VIEW (object); priv = cal_view->priv; switch (property_id) { case PROP_MODEL: e_cal_view_set_model (cal_view, E_CAL_MODEL (g_value_get_object (value))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void e_cal_view_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ECalView *cal_view; ECalViewPrivate *priv; cal_view = E_CAL_VIEW (object); priv = cal_view->priv; switch (property_id) { case PROP_MODEL: g_value_set_object (value, e_cal_view_get_model (cal_view)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void e_cal_view_class_init (ECalViewClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); /* Method override */ gobject_class->set_property = e_cal_view_set_property; gobject_class->get_property = e_cal_view_get_property; object_class->destroy = e_cal_view_destroy; klass->selection_changed = NULL; klass->event_changed = NULL; klass->event_added = NULL; klass->get_selected_events = NULL; klass->get_selected_time_range = NULL; klass->set_selected_time_range = NULL; klass->get_visible_time_range = NULL; klass->update_query = NULL; g_object_class_install_property (gobject_class, PROP_MODEL, g_param_spec_object ("model", NULL, NULL, E_TYPE_CAL_MODEL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); /* Create class' signals */ e_cal_view_signals[SELECTION_CHANGED] = g_signal_new ("selection_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ECalViewClass, selection_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); e_cal_view_signals[TIMEZONE_CHANGED] = g_signal_new ("timezone_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ECalViewClass, timezone_changed), NULL, NULL, cal_util_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); e_cal_view_signals[EVENT_CHANGED] = g_signal_new ("event_changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (ECalViewClass, event_changed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); e_cal_view_signals[EVENT_ADDED] = g_signal_new ("event_added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (ECalViewClass, event_added), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /* clipboard atom */ if (!clipboard_atom) clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); /* init the accessibility support for e_day_view */ e_cal_view_a11y_init (); } static void model_changed_cb (ETableModel *etm, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_update_query (cal_view); } static void model_row_changed_cb (ETableModel *etm, int row, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_update_query (cal_view); } static void model_cell_changed_cb (ETableModel *etm, int col, int row, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_update_query (cal_view); } static void model_rows_changed_cb (ETableModel *etm, int row, int count, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_update_query (cal_view); } static void selection_get (GtkWidget *invisible, GtkSelectionData *selection_data, guint info, guint time_stamp, ECalView *cal_view) { if (cal_view->priv->clipboard_selection != NULL) { gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8, cal_view->priv->clipboard_selection, strlen (cal_view->priv->clipboard_selection)); } } static void selection_clear_event (GtkWidget *invisible, GdkEventSelection *event, ECalView *cal_view) { if (cal_view->priv->clipboard_selection != NULL) { g_free (cal_view->priv->clipboard_selection); cal_view->priv->clipboard_selection = NULL; } } void e_cal_view_add_event (ECalView *cal_view, CalClient *client, time_t dtstart, icaltimezone *default_zone, icalcomponent *icalcomp, gboolean in_top_canvas) { CalComponent *comp; struct icaltimetype itime, old_dtstart, old_dtend; time_t tt_start, tt_end, new_dtstart; struct icaldurationtype ic_dur; char *uid; gint start_offset, end_offset; gboolean all_day_event; start_offset = 0; end_offset = 0; old_dtstart = icalcomponent_get_dtstart (icalcomp); tt_start = icaltime_as_timet (old_dtstart); old_dtend = icalcomponent_get_dtend (icalcomp); tt_end = icaltime_as_timet (old_dtend); ic_dur = icaldurationtype_from_int (tt_end - tt_start); if (icaldurationtype_as_int (ic_dur) > 60*60*24) { /* This is a long event */ start_offset = old_dtstart.hour * 60 + old_dtstart.minute; end_offset = old_dtstart.hour * 60 + old_dtend.minute; } if (start_offset == 0 && end_offset == 0 && in_top_canvas) all_day_event = TRUE; else all_day_event = FALSE; if (in_top_canvas) new_dtstart = dtstart + start_offset * 60; else new_dtstart = dtstart; itime = icaltime_from_timet_with_zone (new_dtstart, FALSE, default_zone); if (all_day_event) itime.is_date = TRUE; icalcomponent_set_dtstart (icalcomp, itime); itime = icaltime_add (itime, ic_dur); if (all_day_event) itime.is_date = TRUE; icalcomponent_set_dtend (icalcomp, itime); /* FIXME The new uid stuff can go away once we actually set it in the backend */ uid = cal_component_gen_uid (); comp = cal_component_new (); cal_component_set_icalcomponent ( comp, icalcomponent_new_clone (icalcomp)); cal_component_set_uid (comp, uid); /* FIXME Error handling */ if (cal_client_create_object (client, cal_component_get_icalcomponent (comp), NULL, NULL)) { if (itip_organizer_is_user (comp, client) && send_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)), client, comp, TRUE)) { itip_send_comp (CAL_COMPONENT_METHOD_REQUEST, comp, client, NULL); } } else { g_message (G_STRLOC ": Could not create the object!"); } free (uid); g_object_unref (comp); } static void selection_received (GtkWidget *invisible, GtkSelectionData *selection_data, guint time, ECalView *cal_view) { char *comp_str, *default_tzid; icalcomponent *icalcomp; icalcomponent_kind kind; time_t selected_time_start, selected_time_end; icaltimezone *default_zone; CalClient *client; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (selection_data->length < 0 || selection_data->type != GDK_SELECTION_TYPE_STRING) { return; } comp_str = (char *) selection_data->data; icalcomp = icalparser_parse_string ((const char *) comp_str); if (!icalcomp) return; default_tzid = calendar_config_get_timezone (); client = e_cal_model_get_default_client (cal_view->priv->model); /* FIXME Error checking */ cal_client_get_timezone (client, default_tzid, &default_zone, NULL); /* check the type of the component */ /* FIXME An error dialog if we return? */ kind = icalcomponent_isa (icalcomp); if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT) return; e_cal_view_set_status_message (cal_view, _("Updating objects")); e_cal_view_get_selected_time_range (cal_view, &selected_time_start, &selected_time_end); /* FIXME Timezone handling */ if (kind == ICAL_VCALENDAR_COMPONENT) { icalcomponent_kind child_kind; icalcomponent *subcomp; subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT); while (subcomp) { child_kind = icalcomponent_isa (subcomp); if (child_kind == ICAL_VEVENT_COMPONENT) e_cal_view_add_event (cal_view, client, selected_time_start, default_zone, subcomp, FALSE); else if (child_kind == ICAL_VTIMEZONE_COMPONENT) { icaltimezone *zone; zone = icaltimezone_new (); icaltimezone_set_component (zone, subcomp); cal_client_add_timezone (client, zone, NULL); icaltimezone_free (zone, 1); } subcomp = icalcomponent_get_next_component ( icalcomp, ICAL_ANY_COMPONENT); } icalcomponent_free (icalcomp); } else { e_cal_view_add_event (cal_view, client, selected_time_start, default_zone, icalcomp, FALSE); } e_cal_view_set_status_message (cal_view, NULL); } static void e_cal_view_init (ECalView *cal_view, ECalViewClass *klass) { cal_view->priv = g_new0 (ECalViewPrivate, 1); cal_view->priv->model = (ECalModel *) e_cal_model_calendar_new (); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_changed", G_CALLBACK (model_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_row_changed", G_CALLBACK (model_row_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_cell_changed", G_CALLBACK (model_cell_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_rows_inserted", G_CALLBACK (model_rows_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_rows_deleted", G_CALLBACK (model_rows_changed_cb), cal_view); /* Set up the invisible widget for the clipboard selections */ cal_view->priv->invisible = gtk_invisible_new (); gtk_selection_add_target (cal_view->priv->invisible, clipboard_atom, GDK_SELECTION_TYPE_STRING, 0); g_signal_connect (cal_view->priv->invisible, "selection_get", G_CALLBACK (selection_get), (gpointer) cal_view); g_signal_connect (cal_view->priv->invisible, "selection_clear_event", G_CALLBACK (selection_clear_event), (gpointer) cal_view); g_signal_connect (cal_view->priv->invisible, "selection_received", G_CALLBACK (selection_received), (gpointer) cal_view); cal_view->priv->clipboard_selection = NULL; } static void e_cal_view_destroy (GtkObject *object) { ECalView *cal_view = (ECalView *) object; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (cal_view->priv) { if (cal_view->priv->model) { g_signal_handlers_disconnect_matched (cal_view->priv->model, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, cal_view); g_object_unref (cal_view->priv->model); cal_view->priv->model = NULL; } if (cal_view->priv->activity) { g_object_unref (cal_view->priv->activity); cal_view->priv->activity = NULL; } if (cal_view->priv->invisible) { gtk_widget_destroy (cal_view->priv->invisible); cal_view->priv->invisible = NULL; } if (cal_view->priv->clipboard_selection) { g_free (cal_view->priv->clipboard_selection); cal_view->priv->clipboard_selection = NULL; } if (cal_view->priv->default_category) { g_free (cal_view->priv->default_category); cal_view->priv->default_category = NULL; } g_free (cal_view->priv); cal_view->priv = NULL; } if (GTK_OBJECT_CLASS (parent_class)->destroy) GTK_OBJECT_CLASS (parent_class)->destroy (object); } E_MAKE_TYPE (e_cal_view, "ECalView", ECalView, e_cal_view_class_init, e_cal_view_init, GTK_TYPE_TABLE); GnomeCalendar * e_cal_view_get_calendar (ECalView *cal_view) { g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), NULL); return cal_view->priv->calendar; } void e_cal_view_set_calendar (ECalView *cal_view, GnomeCalendar *calendar) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); cal_view->priv->calendar = calendar; } ECalModel * e_cal_view_get_model (ECalView *cal_view) { g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), NULL); return cal_view->priv->model; } void e_cal_view_set_model (ECalView *cal_view, ECalModel *model) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); g_return_if_fail (E_IS_CAL_MODEL (model)); if (cal_view->priv->model) { g_signal_handlers_disconnect_matched (cal_view->priv->model, G_SIGNAL_MATCH_DATA, 0, 0, 0, NULL, cal_view); g_object_unref (cal_view->priv->model); } cal_view->priv->model = model; g_object_ref (cal_view->priv->model); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_changed", G_CALLBACK (model_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_row_changed", G_CALLBACK (model_row_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_cell_changed", G_CALLBACK (model_cell_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_rows_inserted", G_CALLBACK (model_rows_changed_cb), cal_view); g_signal_connect (G_OBJECT (cal_view->priv->model), "model_rows_deleted", G_CALLBACK (model_rows_changed_cb), cal_view); e_cal_view_update_query (cal_view); } icaltimezone * e_cal_view_get_timezone (ECalView *cal_view) { g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), NULL); return cal_view->priv->zone; } void e_cal_view_set_timezone (ECalView *cal_view, icaltimezone *zone) { icaltimezone *old_zone; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (zone == cal_view->priv->zone) return; old_zone = cal_view->priv->zone; cal_view->priv->zone = zone; g_signal_emit (G_OBJECT (cal_view), e_cal_view_signals[TIMEZONE_CHANGED], 0, old_zone, cal_view->priv->zone); } const char * e_cal_view_get_default_category (ECalView *cal_view) { g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), NULL); return (const char *) cal_view->priv->default_category; } /** * e_cal_view_set_default_category * @cal_view: A calendar view. * @category: Default category name or NULL for no category. * * Sets the default category that will be used when creating new calendar * components from the given calendar view. */ void e_cal_view_set_default_category (ECalView *cal_view, const char *category) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (cal_view->priv->default_category) g_free (cal_view->priv->default_category); cal_view->priv->default_category = g_strdup (category); } void e_cal_view_set_status_message (ECalView *cal_view, const gchar *message) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (!message || !*message) { if (cal_view->priv->activity) { g_object_unref (cal_view->priv->activity); cal_view->priv->activity = NULL; } } else if (!cal_view->priv->activity) { #if 0 int display; #endif char *client_id = g_strdup_printf ("%p", cal_view); if (progress_icon[0] == NULL) progress_icon[0] = gdk_pixbuf_new_from_file (EVOLUTION_IMAGESDIR "/" EVOLUTION_CALENDAR_PROGRESS_IMAGE, NULL); #if 0 cal_view->priv->activity = evolution_activity_client_new ( global_shell_client, client_id, progress_icon, message, TRUE, &display); #endif g_free (client_id); } else evolution_activity_client_update (cal_view->priv->activity, message, -1.0); } GList * e_cal_view_get_selected_events (ECalView *cal_view) { g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), NULL); if (E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->get_selected_events) return E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->get_selected_events (cal_view); return NULL; } void e_cal_view_get_selected_time_range (ECalView *cal_view, time_t *start_time, time_t *end_time) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->get_selected_time_range) { E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->get_selected_time_range ( cal_view, start_time, end_time); } } void e_cal_view_set_selected_time_range (ECalView *cal_view, time_t start_time, time_t end_time) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); if (E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->set_selected_time_range) { E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->set_selected_time_range ( cal_view, start_time, end_time); } } gboolean e_cal_view_get_visible_time_range (ECalView *cal_view, time_t *start_time, time_t *end_time) { g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), FALSE); if (E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->get_visible_time_range) { return E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->get_visible_time_range ( cal_view, start_time, end_time); } return FALSE; } void e_cal_view_update_query (ECalView *cal_view) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); e_cal_view_set_status_message (cal_view, _("Searching")); if (E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->update_query) { E_CAL_VIEW_CLASS (G_OBJECT_GET_CLASS (cal_view))->update_query (cal_view); } e_cal_view_set_status_message (cal_view, NULL); } void e_cal_view_cut_clipboard (ECalView *cal_view) { GList *selected, *l; const char *uid; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; e_cal_view_set_status_message (cal_view, _("Deleting selected objects")); e_cal_view_copy_clipboard (cal_view); for (l = selected; l != NULL; l = l->next) { CalComponent *comp; ECalViewEvent *event = (ECalViewEvent *) l->data; GError *error = NULL; if (!event) continue; comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); if (itip_organizer_is_user (comp, event->comp_data->client) && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)), event->comp_data->client, comp, TRUE)) itip_send_comp (CAL_COMPONENT_METHOD_CANCEL, comp, event->comp_data->client, NULL); cal_component_get_uid (comp, &uid); cal_client_remove_object (event->comp_data->client, uid, &error); delete_error_dialog (error, CAL_COMPONENT_EVENT); g_clear_error (&error); g_object_unref (comp); } e_cal_view_set_status_message (cal_view, NULL); g_list_free (selected); } void e_cal_view_copy_clipboard (ECalView *cal_view) { GList *selected, *l; gchar *comp_str; icalcomponent *vcal_comp; icalcomponent *new_icalcomp; ECalViewEvent *event; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; /* create top-level VCALENDAR component and add VTIMEZONE's */ vcal_comp = cal_util_new_top_level (); for (l = selected; l != NULL; l = l->next) { event = (ECalViewEvent *) l->data; if (event) cal_util_add_timezones_from_component (vcal_comp, event->comp_data->icalcomp); } for (l = selected; l != NULL; l = l->next) { event = (ECalViewEvent *) l->data; new_icalcomp = icalcomponent_new_clone (event->comp_data->icalcomp); icalcomponent_add_component (vcal_comp, new_icalcomp); } /* copy the VCALENDAR to the clipboard */ comp_str = icalcomponent_as_ical_string (vcal_comp); if (cal_view->priv->clipboard_selection != NULL) g_free (cal_view->priv->clipboard_selection); cal_view->priv->clipboard_selection = g_strdup (comp_str); gtk_selection_owner_set (cal_view->priv->invisible, clipboard_atom, GDK_CURRENT_TIME); /* free memory */ icalcomponent_free (vcal_comp); g_list_free (selected); } void e_cal_view_paste_clipboard (ECalView *cal_view) { g_return_if_fail (E_IS_CAL_VIEW (cal_view)); gtk_selection_convert (cal_view->priv->invisible, clipboard_atom, GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME); } static void delete_event (ECalView *cal_view, ECalViewEvent *event) { CalComponent *comp; CalComponentVType vtype; comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); vtype = cal_component_get_vtype (comp); if (delete_component_dialog (comp, FALSE, 1, vtype, GTK_WIDGET (cal_view))) { const char *uid; GError *error = NULL; if (itip_organizer_is_user (comp, event->comp_data->client) && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)), event->comp_data->client, comp, TRUE)) itip_send_comp (CAL_COMPONENT_METHOD_CANCEL, comp, event->comp_data->client, NULL); cal_component_get_uid (comp, &uid); if (!uid || !*uid) { g_object_unref (comp); return; } cal_client_remove_object (event->comp_data->client, uid, &error); delete_error_dialog (error, CAL_COMPONENT_EVENT); g_clear_error (&error); } g_object_unref (comp); } void e_cal_view_delete_selected_event (ECalView *cal_view) { GList *selected; ECalViewEvent *event; selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; event = (ECalViewEvent *) selected->data; if (event) delete_event (cal_view, event); g_list_free (selected); } void e_cal_view_delete_selected_events (ECalView *cal_view) { GList *selected, *l; ECalViewEvent *event; selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; for (l = selected; l != NULL; l = l->next) { event = (ECalViewEvent *) l->data; if (event) delete_event (cal_view, event); } g_list_free (selected); } void e_cal_view_delete_selected_occurrence (ECalView *cal_view) { ECalViewEvent *event; GList *selected; const char *uid; GError *error = NULL; selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; event = (ECalViewEvent *) selected->data; uid = icalcomponent_get_uid (event->comp_data->icalcomp); /* FIXME: use 'rid' argument */ cal_client_remove_object_with_mod (event->comp_data->client, uid, NULL, CALOBJ_MOD_THIS, &error); delete_error_dialog (error, CAL_COMPONENT_EVENT); g_clear_error (&error); /* free memory */ g_list_free (selected); } static void on_new_appointment (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = (ECalView *) user_data; e_cal_view_new_appointment (cal_view); } static void on_new_event (GtkWidget *widget, gpointer user_data) { time_t dtstart, dtend; ECalView *cal_view = (ECalView *) user_data; e_cal_view_get_selected_time_range (cal_view, &dtstart, &dtend); e_cal_view_new_appointment_for (cal_view, dtstart, dtend, TRUE, FALSE); } static void on_new_meeting (GtkWidget *widget, gpointer user_data) { time_t dtstart, dtend; ECalView *cal_view = (ECalView *) user_data; e_cal_view_get_selected_time_range (cal_view, &dtstart, &dtend); e_cal_view_new_appointment_for (cal_view, dtstart, dtend, FALSE, TRUE); } static void on_new_task (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = (ECalView *) user_data; gnome_calendar_new_task (cal_view->priv->calendar); } static void on_goto_date (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); goto_dialog (cal_view->priv->calendar); } static void on_goto_today (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); calendar_goto_today (cal_view->priv->calendar); } static void on_edit_appointment (GtkWidget *widget, gpointer user_data) { GList *selected; ECalView *cal_view = E_CAL_VIEW (user_data); selected = e_cal_view_get_selected_events (cal_view); if (selected) { ECalViewEvent *event = (ECalViewEvent *) selected->data; if (event) e_cal_view_edit_appointment (cal_view, event->comp_data->client, event->comp_data->icalcomp, FALSE); g_list_free (selected); } } static void on_print (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; time_t start; GnomeCalendarViewType view_type; PrintView print_view; cal_view = E_CAL_VIEW (user_data); e_cal_view_get_visible_time_range (cal_view, &start, NULL); view_type = gnome_calendar_get_view (cal_view->priv->calendar); switch (view_type) { case GNOME_CAL_DAY_VIEW : print_view = PRINT_VIEW_DAY; break; case GNOME_CAL_WORK_WEEK_VIEW : case GNOME_CAL_WEEK_VIEW: print_view = PRINT_VIEW_WEEK; break; case GNOME_CAL_MONTH_VIEW: print_view = PRINT_VIEW_MONTH; break; default: g_assert_not_reached (); return; } print_calendar (cal_view->priv->calendar, FALSE, start, print_view); } static void on_save_as (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; GList *selected; char *filename; char *ical_string; FILE *file; ECalViewEvent *event; cal_view = E_CAL_VIEW (user_data); selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; filename = e_file_dialog_save (_("Save as...")); if (filename == NULL) return; event = (ECalViewEvent *) selected->data; ical_string = cal_client_get_component_as_string (event->comp_data->client, event->comp_data->icalcomp); if (ical_string == NULL) { g_warning ("Couldn't convert item to a string"); return; } file = fopen (filename, "w"); if (file == NULL) { g_warning ("Couldn't save item"); return; } fprintf (file, ical_string); g_free (ical_string); fclose (file); g_list_free (selected); } static void on_print_event (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; GList *selected; ECalViewEvent *event; CalComponent *comp; cal_view = E_CAL_VIEW (user_data); selected = e_cal_view_get_selected_events (cal_view); if (!selected) return; event = (ECalViewEvent *) selected->data; comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); print_comp (comp, event->comp_data->client, FALSE); g_object_unref (comp); } static void on_meeting (GtkWidget *widget, gpointer user_data) { GList *selected; ECalView *cal_view = E_CAL_VIEW (user_data); selected = e_cal_view_get_selected_events (cal_view); if (selected) { ECalViewEvent *event = (ECalViewEvent *) selected->data; e_cal_view_edit_appointment (cal_view, event->comp_data->client, event->comp_data->icalcomp, TRUE); g_list_free (selected); } } static void on_forward (GtkWidget *widget, gpointer user_data) { GList *selected; ECalView *cal_view = E_CAL_VIEW (user_data); selected = e_cal_view_get_selected_events (cal_view); if (selected) { CalComponent *comp; ECalViewEvent *event = (ECalViewEvent *) selected->data; comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); itip_send_comp (CAL_COMPONENT_METHOD_PUBLISH, comp, event->comp_data->client, NULL); g_list_free (selected); g_object_unref (comp); } } static void on_publish (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; icaltimezone *utc; time_t start = time (NULL), end; GList *comp_list = NULL, *client_list, *cl; cal_view = E_CAL_VIEW (user_data); utc = icaltimezone_get_utc_timezone (); start = time_day_begin_with_zone (start, utc); end = time_add_week_with_zone (start, 6, utc); client_list = e_cal_model_get_client_list (cal_view->priv->model); for (cl = client_list; cl != NULL; cl = cl->next) { if (cal_client_get_free_busy ((CalClient *) cl->data, NULL, start, end, &comp_list, NULL)) { GList *l; for (l = comp_list; l; l = l->next) { CalComponent *comp = CAL_COMPONENT (l->data); itip_send_comp (CAL_COMPONENT_METHOD_PUBLISH, comp, (CalClient *) cl->data, NULL); g_object_unref (comp); } g_list_free (comp_list); } } g_list_free (client_list); } static void on_settings (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; cal_view = E_CAL_VIEW (user_data); control_util_show_settings (cal_view->priv->calendar); } static void on_delete_appointment (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; cal_view = E_CAL_VIEW (user_data); e_cal_view_delete_selected_event (cal_view); } static void on_delete_occurrence (GtkWidget *widget, gpointer user_data) { ECalView *cal_view; cal_view = E_CAL_VIEW (user_data); e_cal_view_delete_selected_occurrence (cal_view); } static void on_cut (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_cut_clipboard (cal_view); } static void on_copy (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_copy_clipboard (cal_view); } static void on_paste (GtkWidget *widget, gpointer user_data) { ECalView *cal_view = E_CAL_VIEW (user_data); e_cal_view_paste_clipboard (cal_view); } enum { /* * This is used to "flag" events that can not be editted */ MASK_EDITABLE = 1, /* * To disable recurring actions to be displayed */ MASK_RECURRING = 2, /* * To disable actions for non-recurring items to be displayed */ MASK_SINGLE = 4, /* * This is used to when an event is currently being edited * in another window and we want to disable the event * from being edited twice */ MASK_EDITING = 8, /* * This is used to when an event is already a meeting and * we want to disable the schedule meeting command */ MASK_MEETING = 16, /* * To disable cut and copy for meetings the user is not the * organizer of */ MASK_MEETING_ORGANIZER = 32, /* * To disable things not valid for instances */ MASK_INSTANCE = 64 }; static EPopupMenu main_items [] = { E_POPUP_ITEM (N_("New _Appointment..."), GTK_SIGNAL_FUNC (on_new_appointment), MASK_EDITABLE), E_POPUP_ITEM (N_("New All Day _Event"), GTK_SIGNAL_FUNC (on_new_event), MASK_EDITABLE), E_POPUP_ITEM (N_("New Meeting"), GTK_SIGNAL_FUNC (on_new_meeting), MASK_EDITABLE), E_POPUP_ITEM (N_("New Task"), GTK_SIGNAL_FUNC (on_new_task), MASK_EDITABLE), E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("_Print..."), GTK_SIGNAL_FUNC (on_print), 0), E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("_Paste"), GTK_SIGNAL_FUNC (on_paste), MASK_EDITABLE), E_POPUP_SEPARATOR, E_POPUP_SUBMENU (N_("Current View"), NULL, 0), E_POPUP_ITEM (N_("Go to _Today"), GTK_SIGNAL_FUNC (on_goto_today), 0), E_POPUP_ITEM (N_("_Go to Date..."), GTK_SIGNAL_FUNC (on_goto_date), 0), E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("_Publish Free/Busy Information"), GTK_SIGNAL_FUNC (on_publish), 0), E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("_Settings..."), GTK_SIGNAL_FUNC (on_settings), 0), E_POPUP_TERMINATOR }; static EPopupMenu child_items [] = { E_POPUP_ITEM (N_("_Open"), GTK_SIGNAL_FUNC (on_edit_appointment), MASK_EDITING), E_POPUP_ITEM (N_("_Save As..."), GTK_SIGNAL_FUNC (on_save_as), MASK_EDITING), E_POPUP_ITEM (N_("_Print..."), GTK_SIGNAL_FUNC (on_print_event), MASK_EDITING), /* Only show this separator if one of the above is shown. */ E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("C_ut"), GTK_SIGNAL_FUNC (on_cut), MASK_EDITING | MASK_EDITABLE | MASK_MEETING_ORGANIZER), E_POPUP_ITEM (N_("_Copy"), GTK_SIGNAL_FUNC (on_copy), MASK_EDITING | MASK_MEETING_ORGANIZER), E_POPUP_ITEM (N_("_Paste"), GTK_SIGNAL_FUNC (on_paste), MASK_EDITABLE), E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("_Schedule Meeting..."), GTK_SIGNAL_FUNC (on_meeting), MASK_EDITABLE | MASK_EDITING | MASK_MEETING), E_POPUP_ITEM (N_("_Forward as iCalendar..."), GTK_SIGNAL_FUNC (on_forward), MASK_EDITING), E_POPUP_SEPARATOR, E_POPUP_ITEM (N_("_Delete"), GTK_SIGNAL_FUNC (on_delete_appointment), MASK_EDITABLE | MASK_SINGLE | MASK_EDITING), E_POPUP_ITEM (N_("Delete this _Occurrence"), GTK_SIGNAL_FUNC (on_delete_occurrence), MASK_RECURRING | MASK_EDITING | MASK_EDITABLE), E_POPUP_ITEM (N_("Delete _All Occurrences"), GTK_SIGNAL_FUNC (on_delete_appointment), MASK_RECURRING | MASK_EDITING | MASK_EDITABLE), E_POPUP_TERMINATOR }; static void free_view_popup (GtkWidget *widget, gpointer data) { ECalView *cal_view = E_CAL_VIEW (data); if (cal_view->priv->view_menu == NULL) return; gnome_calendar_discard_view_popup (cal_view->priv->calendar, cal_view->priv->view_menu); cal_view->priv->view_menu = NULL; } static void setup_popup_icons (EPopupMenu *context_menu) { gint i; for (i = 0; context_menu[i].name; i++) { GtkWidget *pixmap_widget = NULL; if (!strcmp (context_menu[i].name, _("_Copy"))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("C_ut"))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("_Delete")) || !strcmp (context_menu[i].name, _("Delete this _Occurrence")) || !strcmp (context_menu[i].name, _("Delete _All Occurrences"))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("Go to _Today"))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_HOME, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("_Go to Date..."))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("New _Appointment..."))) pixmap_widget = gtk_image_new_from_file (EVOLUTION_IMAGESDIR "/new_appointment.xpm"); else if (!strcmp (context_menu[i].name, _("New All Day _Event"))) pixmap_widget = gtk_image_new_from_file (EVOLUTION_IMAGESDIR "/new_all_day_event.png"); else if (!strcmp (context_menu[i].name, _("New Meeting"))) pixmap_widget = gtk_image_new_from_file (EVOLUTION_IMAGESDIR "/meeting-request-16.png"); else if (!strcmp (context_menu[i].name, _("New Task"))) pixmap_widget = gtk_image_new_from_file (EVOLUTION_IMAGESDIR "/new_task-16.png"); else if (!strcmp (context_menu[i].name, _("_Open"))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("_Paste"))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_PASTE, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("_Print..."))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_PRINT, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("_Save As..."))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_MENU); else if (!strcmp (context_menu[i].name, _("_Settings..."))) pixmap_widget = gtk_image_new_from_stock (GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU); if (pixmap_widget) gtk_widget_show (pixmap_widget); context_menu[i].pixmap_widget = pixmap_widget; } } GtkMenu * e_cal_view_create_popup_menu (ECalView *cal_view) { GList *selected; EPopupMenu *context_menu; guint32 disable_mask = 0, hide_mask = 0; GtkMenu *popup; CalClient *client = NULL; gboolean read_only = TRUE; g_return_val_if_fail (E_IS_CAL_VIEW (cal_view), NULL); /* get the selection */ selected = e_cal_view_get_selected_events (cal_view); if (selected == NULL) { cal_view->priv->view_menu = gnome_calendar_setup_view_popup (cal_view->priv->calendar); main_items[9].submenu = cal_view->priv->view_menu; context_menu = main_items; client = e_cal_model_get_default_client (cal_view->priv->model); } else { ECalViewEvent *event; context_menu = child_items; event = (ECalViewEvent *) selected->data; if (cal_util_component_has_recurrences (event->comp_data->icalcomp)) hide_mask |= MASK_SINGLE; else hide_mask |= MASK_RECURRING; if (cal_util_component_is_instance (event->comp_data->icalcomp)) hide_mask |= MASK_INSTANCE; if (cal_util_component_has_organizer (event->comp_data->icalcomp)) { CalComponent *comp; disable_mask |= MASK_MEETING; comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp)); if (!itip_organizer_is_user (comp, event->comp_data->client)) disable_mask |= MASK_MEETING_ORGANIZER; g_object_unref (comp); } client = event->comp_data->client; } cal_client_is_read_only (client, &read_only, NULL); if (read_only) disable_mask |= MASK_EDITABLE; setup_popup_icons (context_menu); popup = e_popup_menu_create (context_menu, disable_mask, hide_mask, cal_view); g_signal_connect (popup, "selection-done", G_CALLBACK (free_view_popup), cal_view); return popup; } /** * e_cal_view_new_appointment_for * @cal_view: A calendar view. * @dtstart: A Unix time_t that marks the beginning of the appointment. * @dtend: A Unix time_t that marks the end of the appointment. * @all_day: If TRUE, the dtstart and dtend are expanded to cover * the entire day, and the event is set to TRANSPARENT. * @meeting: Whether the appointment is a meeting or not. * * Opens an event editor dialog for a new appointment. */ void e_cal_view_new_appointment_for (ECalView *cal_view, time_t dtstart, time_t dtend, gboolean all_day, gboolean meeting) { ECalViewPrivate *priv; struct icaltimetype itt; CalComponentDateTime dt; CalComponent *comp; icalcomponent *icalcomp; CalComponentTransparency transparency; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); priv = cal_view->priv; dt.value = &itt; if (all_day) dt.tzid = NULL; else dt.tzid = icaltimezone_get_tzid (priv->zone); icalcomp = e_cal_model_create_component_with_defaults (priv->model); comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomp); /* DTSTART, DTEND */ itt = icaltime_from_timet_with_zone (dtstart, FALSE, priv->zone); if (all_day) { itt.hour = itt.minute = itt.second = 0; itt.is_date = TRUE; } cal_component_set_dtstart (comp, &dt); itt = icaltime_from_timet_with_zone (dtend, FALSE, priv->zone); if (all_day) { /* We round it up to the end of the day, unless it is already set to midnight */ if (itt.hour != 0 || itt.minute != 0 || itt.second != 0) { icaltime_adjust (&itt, 1, 0, 0, 0); } itt.hour = itt.minute = itt.second = 0; itt.is_date = TRUE; } cal_component_set_dtend (comp, &dt); /* TRANSPARENCY */ transparency = all_day ? CAL_COMPONENT_TRANSP_TRANSPARENT : CAL_COMPONENT_TRANSP_OPAQUE; cal_component_set_transparency (comp, transparency); /* CATEGORY */ cal_component_set_categories (comp, priv->default_category); /* edit the object */ cal_component_commit_sequence (comp); e_cal_view_edit_appointment (cal_view, e_cal_model_get_default_client (priv->model), icalcomp, meeting); g_object_unref (comp); } /** * e_cal_view_new_appointment * @cal_view: A calendar view. * * Opens an event editor dialog for a new appointment. The appointment's * start and end times are set to the currently selected time range in * the calendar view. */ void e_cal_view_new_appointment (ECalView *cal_view) { time_t dtstart, dtend; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); e_cal_view_get_selected_time_range (cal_view, &dtstart, &dtend); e_cal_view_new_appointment_for (cal_view, dtstart, dtend, FALSE, FALSE); } /** * e_cal_view_edit_appointment * @cal_view: A calendar view. * @client: Calendar client. * @icalcomp: The object to be edited. * @meeting: Whether the appointment is a meeting or not. * * Opens an editor window to allow the user to edit the selected * object. */ void e_cal_view_edit_appointment (ECalView *cal_view, CalClient *client, icalcomponent *icalcomp, gboolean meeting) { ECalViewPrivate *priv; CompEditor *ce; const char *uid; CalComponent *comp; g_return_if_fail (E_IS_CAL_VIEW (cal_view)); g_return_if_fail (IS_CAL_CLIENT (client)); g_return_if_fail (icalcomp != NULL); priv = cal_view->priv; uid = icalcomponent_get_uid (icalcomp); ce = e_comp_editor_registry_find (comp_editor_registry, uid); if (!ce) { EventEditor *ee; ee = event_editor_new (client); ce = COMP_EDITOR (ee); comp = cal_component_new (); cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp)); comp_editor_edit_comp (ce, comp); if (meeting) event_editor_show_meeting (ee); e_comp_editor_registry_add (comp_editor_registry, ce, FALSE); g_object_unref (comp); } comp_editor_focus (ce); }