/* * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com) * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation. * * 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, see . * */ #include "evolution-config.h" #include #include #include #include #include #include "calendar-config.h" #include "comp-util.h" #include "e-cal-dialogs.h" #include "itip-utils.h" #include "print.h" #include "e-comp-editor-page-general.h" #include "e-comp-editor-page-attachments.h" #include "e-comp-editor-event.h" #include "e-comp-editor-memo.h" #include "e-comp-editor-task.h" #include "e-comp-editor.h" struct _ECompEditorPrivate { EAlertBar *alert_bar; /* not referenced */ EActivityBar *activity_bar; /* not referenced */ GtkNotebook *content; /* not referenced */ EAlert *validation_alert; EShell *shell; GSettings *calendar_settings; ESource *origin_source; icalcomponent *component; guint32 flags; EFocusTracker *focus_tracker; GtkUIManager *ui_manager; GSList *pages; /* ECompEditorPage * */ gulong show_attendees_handler_id; ECompEditorPageGeneral *page_general; /* special page, can be added only once; not referenced */ EActivity *target_client_opening; ECalClient *source_client; ECalClient *target_client; gchar *cal_email_address; gchar *alarm_email_address; gboolean changed; guint updating; gchar *title_suffix; ECompEditorPropertyPart *dtstart_part; ECompEditorPropertyPart *dtend_part; GtkWidget *restore_focus; }; enum { PROP_0, PROP_ALARM_EMAIL_ADDRESS, PROP_CAL_EMAIL_ADDRESS, PROP_CHANGED, PROP_COMPONENT, PROP_FLAGS, PROP_ORIGIN_SOURCE, PROP_SHELL, PROP_SOURCE_CLIENT, PROP_TARGET_CLIENT, PROP_TITLE_SUFFIX }; enum { TIMES_CHANGED, OBJECT_CREATED, EDITOR_CLOSED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static GSList *opened_editors = NULL; static void e_comp_editor_alert_sink_iface_init (EAlertSinkInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ECompEditor, e_comp_editor, GTK_TYPE_WINDOW, G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, e_comp_editor_alert_sink_iface_init) G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)) static void ece_restore_focus (ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (comp_editor->priv->restore_focus) { gtk_widget_grab_focus (comp_editor->priv->restore_focus); if (GTK_IS_ENTRY (comp_editor->priv->restore_focus)) gtk_editable_set_position (GTK_EDITABLE (comp_editor->priv->restore_focus), 0); comp_editor->priv->restore_focus = NULL; } } static void e_comp_editor_enable (ECompEditor *comp_editor, gboolean enable) { GtkActionGroup *group; GtkWidget *current_focus; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); current_focus = gtk_window_get_focus (GTK_WINDOW (comp_editor)); gtk_widget_set_sensitive (GTK_WIDGET (comp_editor->priv->content), enable); group = e_comp_editor_get_action_group (comp_editor, "individual"); gtk_action_group_set_sensitive (group, enable); group = e_comp_editor_get_action_group (comp_editor, "core"); gtk_action_group_set_sensitive (group, enable); group = e_comp_editor_get_action_group (comp_editor, "editable"); gtk_action_group_set_sensitive (group, enable); if (enable) { e_comp_editor_sensitize_widgets (comp_editor); ece_restore_focus (comp_editor); } else { comp_editor->priv->restore_focus = current_focus; } } static void ece_set_attendees_for_delegation (ECalComponent *comp, const gchar *address) { icalproperty *prop; icalparameter *param; icalcomponent *icalcomp; gboolean again; icalcomp = e_cal_component_get_icalcomponent (comp); for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); prop; prop = again ? icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY) : icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { const gchar *attendee = icalproperty_get_attendee (prop); const gchar *delfrom = NULL; again = FALSE; param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER); if (param) delfrom = icalparameter_get_delegatedfrom (param); if (!(g_str_equal (itip_strip_mailto (attendee), address) || ((delfrom && *delfrom) && g_str_equal (itip_strip_mailto (delfrom), address)))) { icalcomponent_remove_property (icalcomp, prop); icalproperty_free (prop); again = TRUE; } } } /* Utility function to get the mime-attachment list from the attachment * bar for sending the comp via itip. The list and its contents must * be freed by the caller. */ static GSList * ece_get_mime_attach_list (ECompEditor *comp_editor) { ECompEditorPage *page_attachments; EAttachmentStore *store; GtkTreeModel *model; GtkTreeIter iter; struct CalMimeAttach *cal_mime_attach; GSList *attach_list = NULL; gboolean valid; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); page_attachments = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS); if (!page_attachments) return NULL; store = e_comp_editor_page_attachments_get_store (E_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments)); if (!store) return NULL; model = GTK_TREE_MODEL (store); valid = gtk_tree_model_get_iter_first (model, &iter); while (valid) { EAttachment *attachment; CamelDataWrapper *wrapper; CamelMimePart *mime_part; CamelStream *stream; GByteArray *byte_array; guchar *buffer = NULL; const gchar *description; const gchar *disposition; gint column_id; column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; gtk_tree_model_get (model, &iter, column_id, &attachment, -1); mime_part = e_attachment_ref_mime_part (attachment); g_object_unref (attachment); valid = gtk_tree_model_iter_next (model, &iter); if (mime_part == NULL) continue; cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach)); wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part)); byte_array = g_byte_array_new (); stream = camel_stream_mem_new_with_byte_array (byte_array); camel_data_wrapper_decode_to_stream_sync ( wrapper, stream, NULL, NULL); buffer = g_memdup (byte_array->data, byte_array->len); camel_mime_part_set_content_id (mime_part, NULL); cal_mime_attach->encoded_data = (gchar *) buffer; cal_mime_attach->length = byte_array->len; cal_mime_attach->filename = g_strdup (camel_mime_part_get_filename (mime_part)); description = camel_mime_part_get_description (mime_part); if (description == NULL || *description == '\0') description = _("attachment"); cal_mime_attach->description = g_strdup (description); cal_mime_attach->content_type = camel_data_wrapper_get_mime_type (wrapper); cal_mime_attach->content_id = g_strdup ( camel_mime_part_get_content_id (mime_part)); disposition = camel_mime_part_get_disposition (mime_part); cal_mime_attach->disposition = (disposition != NULL) && (g_ascii_strcasecmp (disposition, "inline") == 0); attach_list = g_slist_append (attach_list, cal_mime_attach); g_object_unref (mime_part); g_object_unref (stream); } return attach_list; } static void e_comp_editor_set_component (ECompEditor *comp_editor, const icalcomponent *component) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (component != NULL); if (comp_editor->priv->component) icalcomponent_free (comp_editor->priv->component); comp_editor->priv->component = icalcomponent_new_clone ((icalcomponent *) component); g_warn_if_fail (comp_editor->priv->component != NULL); } typedef struct _SaveData { ECompEditor *comp_editor; ECalClient *source_client; ECalClient *target_client; icalcomponent *component; gboolean with_send; gboolean close_after_save; ECalObjModType recur_mod; gboolean success; GError *error; gchar *alert_ident; gchar *alert_arg_0; gboolean object_created; ECalComponentItipMethod first_send; ECalComponentItipMethod second_send; ECalComponent *send_comp; EActivity *send_activity; gboolean strip_alarms; gboolean only_new_attendees; GSList *mime_attach_list; } SaveData; static void save_data_free (SaveData *sd) { if (sd) { e_comp_editor_enable (sd->comp_editor, TRUE); if (sd->success) { if (sd->close_after_save) { g_signal_emit (sd->comp_editor, signals[EDITOR_CLOSED], 0, TRUE, NULL); gtk_widget_destroy (GTK_WIDGET (sd->comp_editor)); } else { e_comp_editor_set_component (sd->comp_editor, sd->component); e_comp_editor_fill_widgets (sd->comp_editor, sd->component); g_clear_object (&sd->comp_editor->priv->source_client); sd->comp_editor->priv->source_client = g_object_ref (sd->target_client); sd->comp_editor->priv->flags = sd->comp_editor->priv->flags & (~E_COMP_EDITOR_FLAG_IS_NEW); e_comp_editor_sensitize_widgets (sd->comp_editor); e_comp_editor_set_changed (sd->comp_editor, FALSE); } } else if (sd->alert_ident) { e_alert_submit ( E_ALERT_SINK (sd->comp_editor), sd->alert_ident, sd->alert_arg_0, sd->error ? sd->error->message : _("Unknown error"), NULL); } if (sd->send_activity && e_activity_get_state (sd->send_activity) != E_ACTIVITY_CANCELLED) e_activity_set_state (sd->send_activity, E_ACTIVITY_COMPLETED); g_clear_object (&sd->comp_editor); g_clear_object (&sd->source_client); g_clear_object (&sd->target_client); g_clear_object (&sd->send_comp); g_clear_object (&sd->send_activity); g_clear_error (&sd->error); if (sd->component) icalcomponent_free (sd->component); g_slist_free_full (sd->mime_attach_list, itip_cal_mime_attach_free); g_free (sd->alert_ident); g_free (sd->alert_arg_0); g_free (sd); } } static gboolean ece_send_process_method (SaveData *sd, ECalComponentItipMethod send_method, ECalComponent *send_comp, ESourceRegistry *registry, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSList *mime_attach_list = NULL; g_return_val_if_fail (sd != NULL, FALSE); g_return_val_if_fail (E_IS_CAL_COMPONENT (send_comp), FALSE); g_return_val_if_fail (send_method != E_CAL_COMPONENT_METHOD_NONE, FALSE); if (e_cal_component_has_attachments (send_comp) && e_client_check_capability (E_CLIENT (sd->target_client), CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) { /* Clone the component with attachments set to CID:... */ GSList *attach_list = NULL; GSList *attach; /* mime_attach_list is freed by itip_send_component() */ mime_attach_list = sd->mime_attach_list; sd->mime_attach_list = NULL; for (attach = mime_attach_list; attach; attach = attach->next) { struct CalMimeAttach *cma = (struct CalMimeAttach *) attach->data; attach_list = g_slist_append ( attach_list, g_strconcat ( "cid:", cma->content_id, NULL)); } if (attach_list) { e_cal_component_set_attachment_list (send_comp, attach_list); g_slist_free_full (attach_list, g_free); } } itip_send_component ( registry, send_method, send_comp, sd->target_client, NULL, mime_attach_list, NULL, sd->strip_alarms, sd->only_new_attendees, FALSE, cancellable, callback, user_data); return TRUE; } static void ecep_second_send_processed_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { SaveData *sd = user_data; g_return_if_fail (sd != NULL); sd->success = itip_send_component_finish (result, &sd->error); save_data_free (sd); } static void ecep_first_send_processed_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { SaveData *sd = user_data; g_return_if_fail (sd != NULL); sd->success = itip_send_component_finish (result, &sd->error); if (!sd->success || sd->second_send == E_CAL_COMPONENT_METHOD_NONE) { save_data_free (sd); } else { sd->success = ece_send_process_method (sd, sd->second_send, sd->send_comp, e_shell_get_registry (sd->comp_editor->priv->shell), e_activity_get_cancellable (sd->send_activity), ecep_second_send_processed_cb, sd); if (!sd->success) save_data_free (sd); } } static void ece_prepare_send_component_done (gpointer ptr) { SaveData *sd = ptr; g_return_if_fail (sd != NULL); g_return_if_fail (E_IS_COMP_EDITOR (sd->comp_editor)); g_return_if_fail (sd->send_activity != NULL); sd->success = ece_send_process_method (sd, sd->first_send, sd->send_comp, e_shell_get_registry (sd->comp_editor->priv->shell), e_activity_get_cancellable (sd->send_activity), ecep_first_send_processed_cb, sd); if (!sd->success) save_data_free (sd); } static void ece_prepare_send_component_thread (EAlertSinkThreadJobData *job_data, gpointer user_data, GCancellable *cancellable, GError **error) { SaveData *sd = user_data; const gchar *alert_ident; ECalComponent *send_comp = NULL; guint32 flags; ESourceRegistry *registry; g_return_if_fail (sd != NULL); g_return_if_fail (E_IS_CAL_CLIENT (sd->target_client)); g_return_if_fail (sd->component != NULL); while (!sd->send_activity) { /* Give the main thread a chance to set this object and give it a 50 milliseconds delay too */ g_thread_yield (); g_usleep (50000); } switch (icalcomponent_isa (sd->component)) { case ICAL_VEVENT_COMPONENT: alert_ident = "calendar:failed-send-event"; break; case ICAL_VJOURNAL_COMPONENT: alert_ident = "calendar:failed-send-memo"; break; case ICAL_VTODO_COMPONENT: alert_ident = "calendar:failed-send-task"; break; default: g_warning ("%s: Cannot send component of kind %d", G_STRFUNC, icalcomponent_isa (sd->component)); sd->success = FALSE; sd->alert_ident = g_strdup ("calendar:failed-send-event"); return; } g_free (sd->alert_ident); sd->alert_ident = g_strdup (alert_ident); e_alert_sink_thread_job_set_alert_ident (job_data, alert_ident); flags = e_comp_editor_get_flags (sd->comp_editor); registry = e_shell_get_registry (sd->comp_editor->priv->shell); if (sd->recur_mod == E_CAL_OBJ_MOD_ALL && e_cal_component_is_instance (sd->send_comp)) { /* Ensure we send the master object, not the instance only */ icalcomponent *icalcomp = NULL; const gchar *uid = NULL; e_cal_component_get_uid (sd->send_comp, &uid); if (e_cal_client_get_object_sync (sd->target_client, uid, NULL, &icalcomp, cancellable, NULL) && icalcomp != NULL) { send_comp = e_cal_component_new_from_icalcomponent (icalcomp); } } if (!send_comp) send_comp = e_cal_component_clone (sd->send_comp); cal_comp_util_copy_new_attendees (send_comp, sd->send_comp); /* The user updates the delegated status to the Organizer, * so remove all other attendees. */ if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) { gchar *address; address = itip_get_comp_attendee (registry, send_comp, sd->target_client); if (address) { ece_set_attendees_for_delegation (send_comp, address); g_free (address); } } g_clear_object (&sd->send_comp); sd->send_comp = send_comp; } static void ece_save_component_done (gpointer ptr) { SaveData *sd = ptr; g_return_if_fail (sd != NULL); g_return_if_fail (E_IS_COMP_EDITOR (sd->comp_editor)); if (sd->success) { ECalComponent *comp; gboolean delegated, is_new_meeting; gboolean only_new_attendees = FALSE; gboolean strip_alarms = TRUE; guint32 flags; if (sd->object_created) g_signal_emit (sd->comp_editor, signals[OBJECT_CREATED], 0, NULL); comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (sd->component)); if (sd->comp_editor->priv->page_general) { GSList *added_attendees; added_attendees = e_comp_editor_page_general_get_added_attendees (sd->comp_editor->priv->page_general); cal_comp_util_set_added_attendees_mails (comp, added_attendees); } flags = e_comp_editor_get_flags (sd->comp_editor); is_new_meeting = (flags & E_COMP_EDITOR_FLAG_WITH_ATTENDEES) == 0 || (flags & E_COMP_EDITOR_FLAG_IS_NEW) != 0; delegated = (flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0 && e_cal_client_check_save_schedules (sd->target_client); if (delegated || (sd->with_send && e_cal_dialogs_send_component ( GTK_WINDOW (sd->comp_editor), sd->target_client, comp, is_new_meeting, &strip_alarms, &only_new_attendees))) { ESourceRegistry *registry; EActivity *activity; registry = e_shell_get_registry (sd->comp_editor->priv->shell); if (delegated) only_new_attendees = FALSE; if ((itip_organizer_is_user (registry, comp, sd->target_client) || itip_sentby_is_user (registry, comp, sd->target_client))) { if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL) sd->first_send = E_CAL_COMPONENT_METHOD_PUBLISH; else sd->first_send = E_CAL_COMPONENT_METHOD_REQUEST; } else { sd->first_send = E_CAL_COMPONENT_METHOD_REQUEST; if ((flags & E_COMP_EDITOR_FLAG_DELEGATE) != 0) sd->second_send = E_CAL_COMPONENT_METHOD_REPLY; } sd->mime_attach_list = ece_get_mime_attach_list (sd->comp_editor); sd->strip_alarms = strip_alarms; sd->only_new_attendees = only_new_attendees; sd->send_comp = comp; sd->success = FALSE; sd->alert_ident = g_strdup ("calendar:failed-send-event"); sd->alert_arg_0 = e_util_get_source_full_name (registry, e_client_get_source (E_CLIENT (sd->target_client))); activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (sd->comp_editor), _("Sending notifications to attendees..."), sd->alert_ident, sd->alert_arg_0, ece_prepare_send_component_thread, sd, ece_prepare_send_component_done); if (activity) e_activity_bar_set_activity (sd->comp_editor->priv->activity_bar, activity); /* The thread is waiting for this to be set first */ sd->send_activity = activity; return; } g_clear_object (&comp); } save_data_free (sd); } static gboolean ece_save_component_attachments_sync (ECalClient *cal_client, icalcomponent *component, GCancellable *cancellable, GError **error) { icalproperty *prop; const gchar *local_store; gchar *target_filename_prefix, *filename_prefix, *tmp; gboolean success = TRUE; g_return_val_if_fail (E_IS_CAL_CLIENT (cal_client), FALSE); g_return_val_if_fail (component != NULL, FALSE); tmp = g_strdup (icalcomponent_get_uid (component)); e_filename_make_safe (tmp); filename_prefix = g_strconcat (tmp, "-", NULL); g_free (tmp); local_store = e_cal_client_get_local_attachment_store (cal_client); if (local_store && *local_store && g_mkdir_with_parents (local_store, 0700) < 0) { g_debug ("%s: Failed to create local store directory '%s'", G_STRFUNC, local_store); } target_filename_prefix = g_build_filename (local_store, filename_prefix, NULL); g_free (filename_prefix); for (prop = icalcomponent_get_first_property (component, ICAL_ATTACH_PROPERTY); prop && success; prop = icalcomponent_get_next_property (component, ICAL_ATTACH_PROPERTY)) { icalattach *attach; gchar *uri = NULL; attach = icalproperty_get_attach (prop); if (!attach) continue; if (icalattach_get_is_url (attach)) { const gchar *data; gsize buf_size; data = icalattach_get_url (attach); buf_size = strlen (data); uri = g_malloc0 (buf_size + 1); icalvalue_decode_ical_string (data, uri, buf_size); } if (uri) { if (g_ascii_strncasecmp (uri, "file://", 7) == 0 && !g_str_has_prefix (uri + 7, target_filename_prefix)) { GFile *source, *destination; gchar *decoded_filename; gchar *target_filename; decoded_filename = g_uri_unescape_string (strrchr (uri, '/') + 1, NULL); target_filename = g_strconcat (target_filename_prefix, decoded_filename, NULL); g_free (decoded_filename); source = g_file_new_for_uri (uri); destination = g_file_new_for_path (target_filename); if (source && destination) { success = g_file_copy (source, destination, G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error); if (success) { g_free (uri); uri = g_file_get_uri (destination); if (uri) { icalattach *new_attach; gsize buf_size; gchar *buf; buf_size = 2 * strlen (uri) + 1; buf = g_malloc0 (buf_size); icalvalue_encode_ical_string (uri, buf, buf_size); new_attach = icalattach_new_from_url (buf); icalproperty_set_attach (prop, new_attach); icalattach_unref (new_attach); g_free (buf); } } } g_clear_object (&source); g_clear_object (&destination); g_free (target_filename); } g_free (uri); } success = success & !g_cancellable_set_error_if_cancelled (cancellable, error); } g_free (target_filename_prefix); return success; } static void ece_gather_tzids_cb (icalparameter *param, gpointer user_data) { GHashTable *tzids = user_data; const gchar *tzid; g_return_if_fail (param != NULL); g_return_if_fail (tzids != NULL); tzid = icalparameter_get_tzid (param); if (tzid && *tzid && g_ascii_strcasecmp (tzid, "UTC") != 0) g_hash_table_insert (tzids, g_strdup (tzid), NULL); } static gboolean ece_save_component_add_timezones_sync (SaveData *sd, GCancellable *cancellable, GError **error) { GHashTable *tzids; GHashTableIter iter; gpointer key, value; gboolean source_is_target; g_return_val_if_fail (sd != NULL, FALSE); g_return_val_if_fail (sd->component != NULL, FALSE); g_return_val_if_fail (sd->target_client != NULL, FALSE); sd->success = TRUE; tzids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); source_is_target = !sd->source_client || e_source_equal (e_client_get_source (E_CLIENT (sd->target_client)), e_client_get_source (E_CLIENT (sd->source_client))); icalcomponent_foreach_tzid (sd->component, ece_gather_tzids_cb, tzids); g_hash_table_iter_init (&iter, tzids); while (sd->success && g_hash_table_iter_next (&iter, &key, &value)) { const gchar *tzid = key; icaltimezone *zone = NULL; GError *local_error = NULL; if (!e_cal_client_get_timezone_sync (source_is_target ? sd->target_client : sd->source_client, tzid, &zone, cancellable, &local_error)) { zone = icaltimezone_get_builtin_timezone_from_tzid (tzid); if (!zone) zone = icaltimezone_get_builtin_timezone (tzid); if (!zone) { g_propagate_error (error, local_error); local_error = NULL; sd->success = FALSE; break; } } sd->success = e_cal_client_add_timezone_sync (sd->target_client, zone, cancellable, error); g_clear_error (&local_error); } g_hash_table_destroy (tzids); return sd->success; } static void ece_save_component_thread (EAlertSinkThreadJobData *job_data, gpointer user_data, GCancellable *cancellable, GError **error) { SaveData *sd = user_data; const gchar *create_alert_ident, *modify_alert_ident, *remove_alert_ident, *get_alert_ident; gchar *orig_uid, *new_uid = NULL; g_return_if_fail (sd != NULL); g_return_if_fail (E_IS_CAL_CLIENT (sd->target_client)); g_return_if_fail (sd->component != NULL); switch (icalcomponent_isa (sd->component)) { case ICAL_VEVENT_COMPONENT: create_alert_ident = "calendar:failed-create-event"; modify_alert_ident = "calendar:failed-modify-event"; remove_alert_ident = "calendar:failed-remove-event"; get_alert_ident = "calendar:failed-get-event"; break; case ICAL_VJOURNAL_COMPONENT: create_alert_ident = "calendar:failed-create-memo"; modify_alert_ident = "calendar:failed-modify-memo"; remove_alert_ident = "calendar:failed-remove-memo"; get_alert_ident = "calendar:failed-get-memo"; break; case ICAL_VTODO_COMPONENT: create_alert_ident = "calendar:failed-create-task"; modify_alert_ident = "calendar:failed-modify-task"; remove_alert_ident = "calendar:failed-remove-task"; get_alert_ident = "calendar:failed-get-task"; break; default: g_warning ("%s: Cannot save component of kind %d", G_STRFUNC, icalcomponent_isa (sd->component)); return; } sd->success = ece_save_component_add_timezones_sync (sd, cancellable, error); if (!sd->success) { e_alert_sink_thread_job_set_alert_ident (job_data, "calendar:failed-add-timezone"); return; } sd->success = ece_save_component_attachments_sync (sd->target_client, sd->component, cancellable, error); if (!sd->success) { e_alert_sink_thread_job_set_alert_ident (job_data, "calendar:failed-save-attachments"); return; } orig_uid = g_strdup (icalcomponent_get_uid (sd->component)); if (cal_comp_is_icalcomp_on_server_sync (sd->component, sd->target_client, cancellable, error)) { ECalComponent *comp; gboolean has_recurrences; e_alert_sink_thread_job_set_alert_ident (job_data, modify_alert_ident); comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (sd->component)); g_return_if_fail (comp != NULL); has_recurrences = e_cal_util_component_has_recurrences (sd->component); if (has_recurrences && sd->recur_mod == E_CAL_OBJ_MOD_ALL) sd->success = comp_util_sanitize_recurrence_master_sync (comp, sd->target_client, cancellable, error); else sd->success = TRUE; if (sd->recur_mod == E_CAL_OBJ_MOD_THIS) { e_cal_component_set_rdate_list (comp, NULL); e_cal_component_set_rrule_list (comp, NULL); e_cal_component_set_exdate_list (comp, NULL); e_cal_component_set_exrule_list (comp, NULL); } sd->success = sd->success && e_cal_client_modify_object_sync ( sd->target_client, e_cal_component_get_icalcomponent (comp), sd->recur_mod, cancellable, error); g_clear_object (&comp); } else { e_alert_sink_thread_job_set_alert_ident (job_data, create_alert_ident); sd->success = e_cal_client_create_object_sync (sd->target_client, sd->component, &new_uid, cancellable, error); if (sd->success) sd->object_created = TRUE; } if (sd->success && sd->source_client && !e_source_equal (e_client_get_source (E_CLIENT (sd->target_client)), e_client_get_source (E_CLIENT (sd->source_client))) && cal_comp_is_icalcomp_on_server_sync (sd->component, sd->source_client, cancellable, NULL)) { ECalObjModType recur_mod = E_CAL_OBJ_MOD_THIS; /* Comp found a new home. Remove it from old one. */ if (e_cal_util_component_is_instance (sd->component) || e_cal_util_component_has_recurrences (sd->component)) recur_mod = E_CAL_OBJ_MOD_ALL; sd->success = e_cal_client_remove_object_sync ( sd->source_client, orig_uid, NULL, recur_mod, cancellable, error); if (!sd->success) { gchar *source_display_name; source_display_name = e_util_get_source_full_name (e_shell_get_registry (sd->comp_editor->priv->shell), e_client_get_source (E_CLIENT (sd->source_client))); e_alert_sink_thread_job_set_alert_ident (job_data, remove_alert_ident); e_alert_sink_thread_job_set_alert_arg_0 (job_data, source_display_name); g_free (source_display_name); } } if (new_uid) { icalcomponent_set_uid (sd->component, new_uid); g_free (new_uid); } g_free (orig_uid); if (sd->success && !sd->close_after_save) { icalcomponent *comp = NULL; gchar *uid, *rid = NULL; uid = g_strdup (icalcomponent_get_uid (sd->component)); if (icalcomponent_get_first_property (sd->component, ICAL_RECURRENCEID_PROPERTY)) { struct icaltimetype ridtt; ridtt = icalcomponent_get_recurrenceid (sd->component); if (icaltime_is_valid_time (ridtt) && !icaltime_is_null_time (ridtt)) { rid = icaltime_as_ical_string_r (ridtt); } } sd->success = e_cal_client_get_object_sync (sd->target_client, uid, rid, &comp, cancellable, error); if (sd->success && comp) { icalcomponent_free (sd->component); sd->component = comp; } else { e_alert_sink_thread_job_set_alert_ident (job_data, get_alert_ident); } g_free (uid); g_free (rid); } } static void ece_save_component (ECompEditor *comp_editor, icalcomponent *component, gboolean with_send, gboolean close_after_save) { EActivity *activity; const gchar *summary; ECalObjModType recur_mod = E_CAL_OBJ_MOD_THIS; SaveData *sd; gchar *source_display_name; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (component != NULL); summary = icalcomponent_get_summary (component); if (!summary || !*summary) { if (!e_cal_dialogs_send_component_prompt_subject (GTK_WINDOW (comp_editor), component)) { return; } } if (e_cal_util_component_is_instance (component)) { if (!e_cal_dialogs_recur_icalcomp (comp_editor->priv->target_client, component, &recur_mod, GTK_WINDOW (comp_editor), FALSE)) { return; } } else if (e_cal_util_component_has_recurrences (component)) { recur_mod = E_CAL_OBJ_MOD_ALL; } e_comp_editor_enable (comp_editor, FALSE); sd = g_new0 (SaveData, 1); sd->comp_editor = g_object_ref (comp_editor); sd->source_client = comp_editor->priv->source_client ? g_object_ref (comp_editor->priv->source_client) : NULL; sd->target_client = g_object_ref (comp_editor->priv->target_client); sd->component = icalcomponent_new_clone (component); sd->with_send = with_send; sd->close_after_save = close_after_save; sd->recur_mod = recur_mod; sd->first_send = E_CAL_COMPONENT_METHOD_NONE; sd->second_send = E_CAL_COMPONENT_METHOD_NONE; sd->success = FALSE; source_display_name = e_util_get_source_full_name (e_shell_get_registry (comp_editor->priv->shell), e_client_get_source (E_CLIENT (sd->target_client))); activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (comp_editor), _("Saving changes..."), "calendar:failed-create-event", source_display_name, ece_save_component_thread, sd, ece_save_component_done); if (activity) e_activity_bar_set_activity (comp_editor->priv->activity_bar, activity); g_clear_object (&activity); g_free (source_display_name); } typedef struct _OpenTargetClientData { ECompEditor *comp_editor; ESource *source; gchar *extension_name; EClient *client; gchar *cal_email_address; gchar *alarm_email_address; gboolean is_target_client_change; EActivity *activity; } OpenTargetClientData; static void open_target_client_data_free (gpointer ptr) { OpenTargetClientData *otc = ptr; if (otc) { if (otc->comp_editor) { if (otc->client) { gboolean previous_changed = e_comp_editor_get_changed (otc->comp_editor); e_comp_editor_set_alarm_email_address (otc->comp_editor, otc->alarm_email_address); e_comp_editor_set_cal_email_address (otc->comp_editor, otc->cal_email_address); e_comp_editor_set_target_client (otc->comp_editor, E_CAL_CLIENT (otc->client)); if (otc->is_target_client_change) e_comp_editor_set_changed (otc->comp_editor, TRUE); else e_comp_editor_set_changed (otc->comp_editor, previous_changed); } if (otc->comp_editor->priv->activity_bar && otc->activity) { if (otc->activity == e_activity_bar_get_activity (otc->comp_editor->priv->activity_bar)) e_activity_bar_set_activity (otc->comp_editor->priv->activity_bar, NULL); if (otc->activity == otc->comp_editor->priv->target_client_opening) g_clear_object (&otc->comp_editor->priv->target_client_opening); } if (otc->source) { EShell *shell; ECredentialsPrompter *credentials_prompter; shell = e_comp_editor_get_shell (otc->comp_editor); credentials_prompter = e_shell_get_credentials_prompter (shell); e_credentials_prompter_set_auto_prompt_disabled_for (credentials_prompter, otc->source, TRUE); } e_comp_editor_sensitize_widgets (otc->comp_editor); } g_clear_object (&otc->comp_editor); g_clear_object (&otc->source); g_clear_object (&otc->client); g_clear_object (&otc->activity); g_free (otc->extension_name); g_free (otc->cal_email_address); g_free (otc->alarm_email_address); g_free (otc); } } static void comp_editor_open_target_client_thread (EAlertSinkThreadJobData *job_data, gpointer user_data, GCancellable *cancellable, GError **error) { OpenTargetClientData *otc = user_data; EClientCache *client_cache; g_return_if_fail (otc != NULL); if (g_cancellable_set_error_if_cancelled (cancellable, error)) return; g_return_if_fail (E_IS_COMP_EDITOR (otc->comp_editor)); g_return_if_fail (E_IS_SOURCE (otc->source)); g_return_if_fail (otc->extension_name != NULL); client_cache = e_shell_get_client_cache (e_comp_editor_get_shell (otc->comp_editor)); otc->client = e_client_cache_get_client_sync (client_cache, otc->source, otc->extension_name, 30, cancellable, error); if (otc->client) { /* Cache some properties which require remote calls */ if (!g_cancellable_is_cancelled (cancellable)) { e_client_get_capabilities (otc->client); } if (!g_cancellable_is_cancelled (cancellable)) { e_client_get_backend_property_sync (otc->client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &otc->cal_email_address, cancellable, error); } if (!g_cancellable_is_cancelled (cancellable)) { e_client_get_backend_property_sync (otc->client, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS, &otc->alarm_email_address, cancellable, error); } if (g_cancellable_is_cancelled (cancellable)) g_clear_object (&otc->client); } } typedef struct _UpdateActivityBarData { ECompEditor *comp_editor; EActivity *activity; } UpdateActivityBarData; static void update_activity_bar_data_free (gpointer ptr) { UpdateActivityBarData *uab = ptr; if (uab) { g_clear_object (&uab->comp_editor); g_clear_object (&uab->activity); g_free (uab); } } static gboolean update_activity_bar_cb (gpointer user_data) { UpdateActivityBarData *uab = user_data; g_return_val_if_fail (uab != NULL, FALSE); g_return_val_if_fail (E_IS_COMP_EDITOR (uab->comp_editor), FALSE); g_return_val_if_fail (E_IS_ACTIVITY (uab->activity), FALSE); if (uab->comp_editor->priv->target_client_opening == uab->activity && e_activity_get_state (uab->activity) != E_ACTIVITY_CANCELLED && e_activity_get_state (uab->activity) != E_ACTIVITY_COMPLETED) { e_activity_bar_set_activity (uab->comp_editor->priv->activity_bar, uab->activity); } return FALSE; } static void e_comp_editor_open_target_client (ECompEditor *comp_editor) { OpenTargetClientData *otc; ESource *source; EActivity *activity; ECredentialsPrompter *credentials_prompter; gchar *source_display_name, *description = NULL, *alert_ident = NULL, *alert_arg_0 = NULL; gboolean is_target_client_change; const gchar *extension_name; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (comp_editor->priv->page_general != NULL); source = e_comp_editor_page_general_ref_selected_source (comp_editor->priv->page_general); if (!source) return; if (comp_editor->priv->target_client && e_client_get_source (E_CLIENT (comp_editor->priv->target_client)) == source) { g_clear_object (&source); return; } if (comp_editor->priv->target_client_opening) { e_activity_cancel (comp_editor->priv->target_client_opening); g_clear_object (&comp_editor->priv->target_client_opening); } is_target_client_change = comp_editor->priv->target_client != NULL; g_clear_object (&comp_editor->priv->target_client); extension_name = e_comp_editor_page_general_get_source_extension_name (comp_editor->priv->page_general); source_display_name = e_util_get_source_full_name ( e_shell_get_registry (e_comp_editor_get_shell (comp_editor)), source); g_return_if_fail (e_util_get_open_source_job_info (extension_name, source_display_name, &description, &alert_ident, &alert_arg_0)); credentials_prompter = e_shell_get_credentials_prompter (e_comp_editor_get_shell (comp_editor)); e_credentials_prompter_set_auto_prompt_disabled_for (credentials_prompter, source, FALSE); otc = g_new0 (OpenTargetClientData, 1); otc->extension_name = g_strdup (extension_name); otc->comp_editor = g_object_ref (comp_editor); otc->source = g_object_ref (source); otc->is_target_client_change = is_target_client_change; activity = e_alert_sink_submit_thread_job ( E_ALERT_SINK (comp_editor), description, alert_ident, alert_arg_0, comp_editor_open_target_client_thread, otc, open_target_client_data_free); otc->activity = g_object_ref (activity); comp_editor->priv->target_client_opening = g_object_ref (activity); /* Close all alerts */ while (e_alert_bar_close_alert (comp_editor->priv->alert_bar)) { ; } if (comp_editor->priv->activity_bar) { UpdateActivityBarData *uab; uab = g_new0 (UpdateActivityBarData, 1); uab->comp_editor = g_object_ref (comp_editor); uab->activity = g_object_ref (activity); /* To avoid UI flickering when the source can be opened quickly */ g_timeout_add_seconds_full (G_PRIORITY_LOW, 1, update_activity_bar_cb, uab, update_activity_bar_data_free); } g_free (description); g_free (alert_ident); g_free (alert_arg_0); g_free (source_display_name); g_clear_object (&source); g_clear_object (&activity); } static void e_comp_editor_update_window_title (ECompEditor *comp_editor) { ECompEditorClass *comp_editor_class; gboolean with_attendees = FALSE; const gchar *format, *title_suffix; gchar *title; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (comp_editor->priv->page_general) with_attendees = e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general); comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor); if (with_attendees) format = comp_editor_class->title_format_with_attendees; else format = comp_editor_class->title_format_without_attendees; title_suffix = e_comp_editor_get_title_suffix (comp_editor); title = g_strdup_printf (format, title_suffix && *title_suffix ? title_suffix : _("No Summary")); gtk_window_set_icon_name (GTK_WINDOW (comp_editor), comp_editor_class->icon_name); gtk_window_set_title (GTK_WINDOW (comp_editor), title); g_free (title); } static void e_comp_editor_close (ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_signal_emit (comp_editor, signals[EDITOR_CLOSED], 0, FALSE, NULL); gtk_widget_destroy (GTK_WIDGET (comp_editor)); } static void e_comp_editor_save_and_close (ECompEditor *comp_editor, gboolean can_close) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (comp_editor->priv->component) { icalcomponent *component = icalcomponent_new_clone (comp_editor->priv->component); if (component && e_comp_editor_fill_component (comp_editor, component)) { ece_save_component (comp_editor, component, TRUE, can_close); icalcomponent_free (component); } } } static GtkResponseType ece_save_component_dialog (ECompEditor *comp_editor) { const icalcomponent *component; GtkWindow *parent; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), GTK_RESPONSE_NO); g_return_val_if_fail (e_comp_editor_get_component (comp_editor) != NULL, GTK_RESPONSE_NO); parent = GTK_WINDOW (comp_editor); component = e_comp_editor_get_component (comp_editor); switch (icalcomponent_isa (component)) { case ICAL_VEVENT_COMPONENT: if (e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general)) return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-meeting", NULL); else return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-appointment", NULL); case ICAL_VTODO_COMPONENT: return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-task", NULL); case ICAL_VJOURNAL_COMPONENT: return e_alert_run_dialog_for_args (parent, "calendar:prompt-save-memo", NULL); default: return GTK_RESPONSE_NO; } } static gboolean e_comp_editor_prompt_and_save_changes (ECompEditor *comp_editor, gboolean with_send) { icalcomponent *component; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); if (!e_comp_editor_get_changed (comp_editor)) return TRUE; switch (ece_save_component_dialog (comp_editor)) { case GTK_RESPONSE_YES: /* Save */ if (e_client_is_readonly (E_CLIENT (comp_editor->priv->target_client))) { e_alert_submit ( E_ALERT_SINK (comp_editor), "calendar:prompt-read-only-cal-editor", e_source_get_display_name ( e_client_get_source (E_CLIENT (comp_editor->priv->target_client))), NULL); /* don't discard changes when selected readonly calendar */ return FALSE; } if (comp_editor->priv->component && e_comp_editor_page_general_get_show_attendees (comp_editor->priv->page_general) && icalcomponent_isa (comp_editor->priv->component) == ICAL_VTODO_COMPONENT && e_client_check_capability (E_CLIENT (comp_editor->priv->target_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) { e_alert_submit ( E_ALERT_SINK (comp_editor), "calendar:prompt-no-task-assignment-editor", e_source_get_display_name ( e_client_get_source (E_CLIENT (comp_editor->priv->target_client))), NULL); return FALSE; } component = icalcomponent_new_clone (comp_editor->priv->component); if (!e_comp_editor_fill_component (comp_editor, component)) { icalcomponent_free (component); return FALSE; } ece_save_component (comp_editor, component, with_send, TRUE); return FALSE; case GTK_RESPONSE_NO: /* Discard */ return TRUE; case GTK_RESPONSE_CANCEL: /* Cancel */ default: return FALSE; } return FALSE; } static void action_close_cb (GtkAction *action, ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (e_comp_editor_prompt_and_save_changes (comp_editor, TRUE)) e_comp_editor_close (comp_editor); } static void action_help_cb (GtkAction *action, ECompEditor *comp_editor) { ECompEditorClass *klass; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); klass = E_COMP_EDITOR_GET_CLASS (comp_editor); g_return_if_fail (klass->help_section != NULL); e_display_help (GTK_WINDOW (comp_editor), klass->help_section); } static void ece_print_or_preview (ECompEditor *comp_editor, GtkPrintOperationAction print_action) { icalcomponent *component; ECalComponent *comp; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (e_comp_editor_get_component (comp_editor) != NULL); component = icalcomponent_new_clone (e_comp_editor_get_component (comp_editor)); if (!e_comp_editor_fill_component (comp_editor, component)) { icalcomponent_free (component); return; } comp = e_cal_component_new_from_icalcomponent (component); g_return_if_fail (comp != NULL); print_comp (comp, e_comp_editor_get_target_client (comp_editor), calendar_config_get_icaltimezone (), calendar_config_get_24_hour_format (), print_action); g_object_unref (comp); } static void action_print_cb (GtkAction *action, ECompEditor *comp_editor) { ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG); } static void action_print_preview_cb (GtkAction *action, ECompEditor *comp_editor) { ece_print_or_preview (comp_editor, GTK_PRINT_OPERATION_ACTION_PREVIEW); } static void action_save_cb (GtkAction *action, ECompEditor *comp_editor) { e_comp_editor_save_and_close (comp_editor, FALSE); } static void action_save_and_close_cb (GtkAction *action, ECompEditor *comp_editor) { e_comp_editor_save_and_close (comp_editor, TRUE); } static gboolean ece_organizer_email_address_is_user (ECompEditor *comp_editor, EClient *client, const gchar *email_address, gboolean is_organizer) { ESourceRegistry *registry; const gchar *cal_email_address; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE); email_address = itip_strip_mailto (email_address); if (!email_address || !*email_address) return FALSE; cal_email_address = e_comp_editor_get_cal_email_address (comp_editor); if (cal_email_address && *cal_email_address && g_ascii_strcasecmp (cal_email_address, email_address) == 0) { return TRUE; } if (is_organizer && e_client_check_capability (client, CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) return FALSE; registry = e_shell_get_registry (e_comp_editor_get_shell (comp_editor)); return itip_address_is_user (registry, email_address); } static gboolean ece_organizer_is_user (ECompEditor *comp_editor, icalcomponent *component, EClient *client) { icalproperty *prop; const gchar *organizer; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); g_return_val_if_fail (component != NULL, FALSE); g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE); prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY); if (!prop || e_client_check_capability (client, CAL_STATIC_CAPABILITY_NO_ORGANIZER)) return FALSE; organizer = itip_strip_mailto (icalproperty_get_organizer (prop)); if (!organizer || !*organizer) return FALSE; return ece_organizer_email_address_is_user (comp_editor, client, organizer, TRUE); } static gboolean ece_sentby_is_user (ECompEditor *comp_editor, icalcomponent *component, EClient *client) { icalproperty *prop; icalparameter *param; const gchar *sentby; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); g_return_val_if_fail (component != NULL, FALSE); g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE); prop = icalcomponent_get_first_property (component, ICAL_ORGANIZER_PROPERTY); if (!prop || e_client_check_capability (client, CAL_STATIC_CAPABILITY_NO_ORGANIZER)) return FALSE; param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); if (!param) return FALSE; sentby = icalparameter_get_sentby (param); return ece_organizer_email_address_is_user (comp_editor, client, sentby, FALSE); } static void ece_emit_times_changed_cb (ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_signal_emit (comp_editor, signals[TIMES_CHANGED], 0, NULL); } static void ece_connect_time_parts (ECompEditor *comp_editor, ECompEditorPropertyPart *dtstart_part, ECompEditorPropertyPart *dtend_part) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); #define update_part(x) G_STMT_START { \ if (x) \ g_object_ref (x); \ if (comp_editor->priv->x) { \ g_signal_handlers_disconnect_by_func (comp_editor->priv->x, G_CALLBACK (ece_emit_times_changed_cb), comp_editor); \ g_clear_object (&comp_editor->priv->x); \ } \ if (x) { \ comp_editor->priv->x = x; \ g_signal_connect_swapped (comp_editor->priv->x, "changed", \ G_CALLBACK (ece_emit_times_changed_cb), comp_editor); \ } \ } G_STMT_END update_part (dtstart_part); update_part (dtend_part); #undef update_part } static void ece_sensitize_widgets (ECompEditor *comp_editor, gboolean force_insensitive) { GtkActionGroup *group; GSList *link; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) { ECompEditorPage *page = link->data; g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page)); if (!E_IS_COMP_EDITOR_PAGE (page)) continue; e_comp_editor_page_sensitize_widgets (page, force_insensitive); } group = e_comp_editor_get_action_group (comp_editor, "individual"); gtk_action_group_set_sensitive (group, !force_insensitive); group = e_comp_editor_get_action_group (comp_editor, "editable"); gtk_action_group_set_sensitive (group, !force_insensitive); } static void ece_fill_widgets (ECompEditor *comp_editor, icalcomponent *component) { GSList *link; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (component != NULL); for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) { ECompEditorPage *page = link->data; g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page)); if (!E_IS_COMP_EDITOR_PAGE (page)) continue; e_comp_editor_page_fill_widgets (page, component); } } static gboolean ece_fill_component (ECompEditor *comp_editor, icalcomponent *component) { GSList *link; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); g_return_val_if_fail (component != NULL, FALSE); for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) { ECompEditorPage *page = link->data; g_warn_if_fail (E_IS_COMP_EDITOR_PAGE (page)); if (!E_IS_COMP_EDITOR_PAGE (page)) continue; if (!e_comp_editor_page_fill_component (page, component)) return FALSE; } return TRUE; } static void comp_editor_realize_cb (ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (comp_editor->priv->component) { e_comp_editor_fill_widgets (comp_editor, comp_editor->priv->component); e_comp_editor_set_changed (comp_editor, FALSE); } e_comp_editor_update_window_title (comp_editor); e_comp_editor_sensitize_widgets (comp_editor); if (comp_editor->priv->page_general && comp_editor->priv->origin_source) { e_comp_editor_page_general_set_selected_source ( comp_editor->priv->page_general, comp_editor->priv->origin_source); e_comp_editor_set_changed (comp_editor, FALSE); } if (comp_editor->priv->page_general) { e_comp_editor_page_general_update_view (comp_editor->priv->page_general); if (!comp_editor->priv->show_attendees_handler_id) { comp_editor->priv->show_attendees_handler_id = e_signal_connect_notify_swapped (comp_editor->priv->page_general, "notify::show-attendees", G_CALLBACK (e_comp_editor_update_window_title), comp_editor); } } if (!comp_editor->priv->target_client) e_comp_editor_open_target_client (comp_editor); } static void comp_editor_unrealize_cb (ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (comp_editor->priv->page_general) { e_signal_disconnect_notify_handler (comp_editor->priv->page_general, &comp_editor->priv->show_attendees_handler_id); } } static gboolean comp_editor_delete_event (GtkWidget *widget, GdkEventAny *event) { ECompEditor *comp_editor; g_return_val_if_fail (E_IS_COMP_EDITOR (widget), FALSE); comp_editor = E_COMP_EDITOR (widget); /* It's disabled when the component is being saved */ if (gtk_widget_get_sensitive (GTK_WIDGET (comp_editor->priv->content))) action_close_cb (NULL, comp_editor); return TRUE; } static gboolean comp_editor_key_press_event (GtkWidget *widget, GdkEventKey *event) { ECompEditor *comp_editor; g_return_val_if_fail (E_IS_COMP_EDITOR (widget), FALSE); comp_editor = E_COMP_EDITOR (widget); if (event->keyval == GDK_KEY_Escape && !e_alert_bar_close_alert (comp_editor->priv->alert_bar)) { GtkAction *action; action = e_comp_editor_get_action (comp_editor, "close"); gtk_action_activate (action); return TRUE; } /* Chain up to parent's method. */ return GTK_WIDGET_CLASS (e_comp_editor_parent_class)->key_press_event (widget, event); } static void comp_editor_selected_source_notify_cb (ECompEditorPageGeneral *page_general, GParamSpec *param, ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR_PAGE_GENERAL (page_general)); g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (comp_editor->priv->page_general == page_general); e_comp_editor_open_target_client (comp_editor); } static void e_comp_editor_submit_alert (EAlertSink *alert_sink, EAlert *alert) { ECompEditor *comp_editor; g_return_if_fail (E_IS_COMP_EDITOR (alert_sink)); g_return_if_fail (E_IS_ALERT (alert)); comp_editor = E_COMP_EDITOR (alert_sink); e_alert_bar_submit_alert (comp_editor->priv->alert_bar, alert); } static void e_comp_editor_set_origin_source (ECompEditor *comp_editor, ESource *origin_source) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (origin_source) g_return_if_fail (E_IS_SOURCE (origin_source)); g_clear_object (&comp_editor->priv->origin_source); if (origin_source) comp_editor->priv->origin_source = g_object_ref (origin_source); } static void e_comp_editor_set_shell (ECompEditor *comp_editor, EShell *shell) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (E_IS_SHELL (shell)); g_clear_object (&comp_editor->priv->shell); comp_editor->priv->shell = g_object_ref (shell); } static void e_comp_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_ALARM_EMAIL_ADDRESS: e_comp_editor_set_alarm_email_address ( E_COMP_EDITOR (object), g_value_get_string (value)); return; case PROP_CAL_EMAIL_ADDRESS: e_comp_editor_set_cal_email_address ( E_COMP_EDITOR (object), g_value_get_string (value)); return; case PROP_CHANGED: e_comp_editor_set_changed ( E_COMP_EDITOR (object), g_value_get_boolean (value)); return; case PROP_COMPONENT: e_comp_editor_set_component ( E_COMP_EDITOR (object), g_value_get_pointer (value)); return; case PROP_FLAGS: e_comp_editor_set_flags ( E_COMP_EDITOR (object), g_value_get_uint (value)); return; case PROP_ORIGIN_SOURCE: e_comp_editor_set_origin_source ( E_COMP_EDITOR (object), g_value_get_object (value)); return; case PROP_SHELL: e_comp_editor_set_shell ( E_COMP_EDITOR (object), g_value_get_object (value)); return; case PROP_SOURCE_CLIENT: e_comp_editor_set_source_client ( E_COMP_EDITOR (object), g_value_get_object (value)); return; case PROP_TARGET_CLIENT: e_comp_editor_set_target_client ( E_COMP_EDITOR (object), g_value_get_object (value)); return; case PROP_TITLE_SUFFIX: e_comp_editor_set_title_suffix ( E_COMP_EDITOR (object), g_value_get_string (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void e_comp_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_ALARM_EMAIL_ADDRESS: g_value_set_string ( value, e_comp_editor_get_alarm_email_address ( E_COMP_EDITOR (object))); return; case PROP_CAL_EMAIL_ADDRESS: g_value_set_string ( value, e_comp_editor_get_cal_email_address ( E_COMP_EDITOR (object))); return; case PROP_CHANGED: g_value_set_boolean ( value, e_comp_editor_get_changed ( E_COMP_EDITOR (object))); return; case PROP_COMPONENT: g_value_set_pointer ( value, e_comp_editor_get_component ( E_COMP_EDITOR (object))); return; case PROP_FLAGS: g_value_set_uint ( value, e_comp_editor_get_flags ( E_COMP_EDITOR (object))); return; case PROP_ORIGIN_SOURCE: g_value_set_object ( value, e_comp_editor_get_origin_source ( E_COMP_EDITOR (object))); return; case PROP_SHELL: g_value_set_object ( value, e_comp_editor_get_shell ( E_COMP_EDITOR (object))); return; case PROP_SOURCE_CLIENT: g_value_set_object ( value, e_comp_editor_get_source_client ( E_COMP_EDITOR (object))); return; case PROP_TARGET_CLIENT: g_value_set_object ( value, e_comp_editor_get_target_client ( E_COMP_EDITOR (object))); return; case PROP_TITLE_SUFFIX: g_value_set_string ( value, e_comp_editor_get_title_suffix ( E_COMP_EDITOR (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void e_comp_editor_constructed (GObject *object) { const gchar *ui = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " \n" " " " " " " " " " " " " " " " " " " ""; GtkActionEntry core_entries[] = { { "close", "window-close", N_("_Close"), "w", N_("Close the current window"), G_CALLBACK (action_close_cb) }, { "copy-clipboard", "edit-copy", N_("_Copy"), "c", N_("Copy the selection"), NULL }, /* Handled by EFocusTracker */ { "cut-clipboard", "edit-cut", N_("Cu_t"), "x", N_("Cut the selection"), NULL }, /* Handled by EFocusTracker */ { "delete-selection", "edit-delete", N_("_Delete"), NULL, N_("Delete the selection"), NULL }, /* Handled by EFocusTracker */ { "help", "help-browser", N_("_Help"), "F1", N_("View help"), G_CALLBACK (action_help_cb) }, { "paste-clipboard", "edit-paste", N_("_Paste"), "v", N_("Paste the clipboard"), NULL }, /* Handled by EFocusTracker */ { "print", "document-print", N_("_Print..."), "p", NULL, G_CALLBACK (action_print_cb) }, { "print-preview", "document-print-preview", N_("Pre_view..."), NULL, NULL, G_CALLBACK (action_print_preview_cb) }, { "select-all", "edit-select-all", N_("Select _All"), "a", N_("Select all text"), NULL }, /* Handled by EFocusTracker */ { "undo", "edit-undo", N_("_Undo"), "z", N_("Undo"), NULL }, /* Handled by EFocusTracker */ { "redo", "edit-redo", N_("_Redo"), "y", N_("Redo"), NULL }, /* Handled by EFocusTracker */ /* Menus */ { "classification-menu", NULL, N_("_Classification"), NULL, NULL, NULL }, { "edit-menu", NULL, N_("_Edit"), NULL, NULL, NULL }, { "file-menu", NULL, N_("_File"), NULL, NULL, NULL }, { "help-menu", NULL, N_("_Help"), NULL, NULL, NULL }, { "insert-menu", NULL, N_("_Insert"), NULL, NULL, NULL }, { "options-menu", NULL, N_("_Options"), NULL, NULL, NULL }, { "view-menu", NULL, N_("_View"), NULL, NULL, NULL } }; GtkActionEntry editable_entries[] = { { "save", "document-save", N_("_Save"), "s", N_("Save current changes"), G_CALLBACK (action_save_cb) }, { "save-and-close", NULL, N_("Save and Close"), NULL, N_("Save current changes and close editor"), G_CALLBACK (action_save_and_close_cb) } }; ECompEditor *comp_editor = E_COMP_EDITOR (object); GtkWidget *widget; GtkBox *vbox; GtkAction *action; GtkActionGroup *action_group; EFocusTracker *focus_tracker; GError *error = NULL; G_OBJECT_CLASS (e_comp_editor_parent_class)->constructed (object); g_signal_connect (comp_editor, "key-press-event", G_CALLBACK (e_util_check_gtk_bindings_in_key_press_event_cb), NULL); comp_editor->priv->calendar_settings = e_util_ref_settings ("org.gnome.evolution.calendar"); comp_editor->priv->ui_manager = gtk_ui_manager_new (); gtk_window_add_accel_group ( GTK_WINDOW (comp_editor), gtk_ui_manager_get_accel_group (comp_editor->priv->ui_manager)); /* Setup Action Groups */ action_group = gtk_action_group_new ("individual"); gtk_action_group_set_translation_domain ( action_group, GETTEXT_PACKAGE); gtk_ui_manager_insert_action_group ( comp_editor->priv->ui_manager, action_group, 0); g_object_unref (action_group); action_group = gtk_action_group_new ("core"); gtk_action_group_set_translation_domain ( action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions ( action_group, core_entries, G_N_ELEMENTS (core_entries), comp_editor); gtk_ui_manager_insert_action_group ( comp_editor->priv->ui_manager, action_group, 0); g_object_unref (action_group); action_group = gtk_action_group_new ("editable"); gtk_action_group_set_translation_domain ( action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions ( action_group, editable_entries, G_N_ELEMENTS (editable_entries), comp_editor); gtk_ui_manager_insert_action_group ( comp_editor->priv->ui_manager, action_group, 0); g_object_unref (action_group); action = gtk_action_group_get_action (action_group, "save-and-close"); if (action) { GtkAction *save_action; GIcon *icon; GIcon *emblemed_icon; GEmblem *emblem; icon = g_themed_icon_new ("window-close"); emblemed_icon = g_themed_icon_new ("document-save"); emblem = g_emblem_new (emblemed_icon); g_object_unref (emblemed_icon); emblemed_icon = g_emblemed_icon_new (icon, emblem); g_object_unref (emblem); g_object_unref (icon); gtk_action_set_gicon (action, emblemed_icon); g_object_unref (emblemed_icon); save_action = gtk_action_group_get_action (action_group, "save"); e_binding_bind_property ( save_action, "sensitive", action, "sensitive", G_BINDING_SYNC_CREATE); } gtk_ui_manager_add_ui_from_string (comp_editor->priv->ui_manager, ui, -1, &error); if (error != NULL) { g_warning ("%s: %s", G_STRFUNC, error->message); g_error_free (error); } widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); g_object_set (G_OBJECT (widget), "hexpand", TRUE, "halign", GTK_ALIGN_FILL, "vexpand", TRUE, "valign", GTK_ALIGN_FILL, NULL); gtk_widget_show (widget); vbox = GTK_BOX (widget); gtk_container_add (GTK_CONTAINER (comp_editor), widget); widget = e_comp_editor_get_managed_widget (comp_editor, "/main-menu"); gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); gtk_widget_set_visible (widget, TRUE); widget = e_comp_editor_get_managed_widget (comp_editor, "/main-toolbar"); gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0); gtk_widget_show (widget); gtk_style_context_add_class ( gtk_widget_get_style_context (widget), GTK_STYLE_CLASS_PRIMARY_TOOLBAR); widget = e_alert_bar_new (); g_object_set (G_OBJECT (widget), "hexpand", FALSE, "halign", GTK_ALIGN_FILL, "vexpand", FALSE, "valign", GTK_ALIGN_START, NULL); comp_editor->priv->alert_bar = E_ALERT_BAR (widget); gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0); widget = e_activity_bar_new (); g_object_set (G_OBJECT (widget), "hexpand", FALSE, "halign", GTK_ALIGN_FILL, "vexpand", FALSE, "valign", GTK_ALIGN_START, NULL); comp_editor->priv->activity_bar = E_ACTIVITY_BAR (widget); gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0); widget = gtk_notebook_new (); g_object_set (G_OBJECT (widget), "hexpand", TRUE, "halign", GTK_ALIGN_FILL, "vexpand", TRUE, "valign", GTK_ALIGN_FILL, "show-tabs", TRUE, "show-border", FALSE, NULL); gtk_widget_show (widget); comp_editor->priv->content = GTK_NOTEBOOK (widget); gtk_box_pack_start (vbox, widget, TRUE, TRUE, 0); /* Configure an EFocusTracker to manage selection actions. */ focus_tracker = e_focus_tracker_new (GTK_WINDOW (comp_editor)); action = e_comp_editor_get_action (comp_editor, "cut-clipboard"); e_focus_tracker_set_cut_clipboard_action (focus_tracker, action); action = e_comp_editor_get_action (comp_editor, "copy-clipboard"); e_focus_tracker_set_copy_clipboard_action (focus_tracker, action); action = e_comp_editor_get_action (comp_editor, "paste-clipboard"); e_focus_tracker_set_paste_clipboard_action (focus_tracker, action); action = e_comp_editor_get_action (comp_editor, "delete-selection"); e_focus_tracker_set_delete_selection_action (focus_tracker, action); action = e_comp_editor_get_action (comp_editor, "select-all"); e_focus_tracker_set_select_all_action (focus_tracker, action); action = e_comp_editor_get_action (comp_editor, "undo"); e_focus_tracker_set_undo_action (focus_tracker, action); action = e_comp_editor_get_action (comp_editor, "redo"); e_focus_tracker_set_redo_action (focus_tracker, action); comp_editor->priv->focus_tracker = focus_tracker; /* Desensitize the "save" action. */ action = e_comp_editor_get_action (comp_editor, "save"); gtk_action_set_sensitive (action, FALSE); e_binding_bind_property (comp_editor, "changed", action, "sensitive", 0); g_signal_connect (comp_editor, "realize", G_CALLBACK (comp_editor_realize_cb), NULL); g_signal_connect (comp_editor, "unrealize", G_CALLBACK (comp_editor_unrealize_cb), NULL); gtk_application_add_window (GTK_APPLICATION (comp_editor->priv->shell), GTK_WINDOW (comp_editor)); e_extensible_load_extensions (E_EXTENSIBLE (comp_editor)); } static void e_comp_editor_dispose (GObject *object) { ECompEditor *comp_editor = E_COMP_EDITOR (object); if (comp_editor->priv->page_general) { g_signal_handlers_disconnect_by_func (comp_editor->priv->page_general, G_CALLBACK (comp_editor_selected_source_notify_cb), comp_editor); comp_editor->priv->page_general = NULL; } if (comp_editor->priv->target_client_opening) { e_activity_cancel (comp_editor->priv->target_client_opening); g_clear_object (&comp_editor->priv->target_client_opening); } g_slist_free_full (comp_editor->priv->pages, g_object_unref); comp_editor->priv->pages = NULL; g_free (comp_editor->priv->alarm_email_address); comp_editor->priv->alarm_email_address = NULL; g_free (comp_editor->priv->cal_email_address); comp_editor->priv->cal_email_address = NULL; g_free (comp_editor->priv->title_suffix); comp_editor->priv->title_suffix = NULL; if (comp_editor->priv->component) { icalcomponent_free (comp_editor->priv->component); comp_editor->priv->component = NULL; } ece_connect_time_parts (comp_editor, NULL, NULL); g_clear_object (&comp_editor->priv->origin_source); g_clear_object (&comp_editor->priv->shell); g_clear_object (&comp_editor->priv->focus_tracker); g_clear_object (&comp_editor->priv->ui_manager); g_clear_object (&comp_editor->priv->source_client); g_clear_object (&comp_editor->priv->target_client); g_clear_object (&comp_editor->priv->calendar_settings); g_clear_object (&comp_editor->priv->validation_alert); comp_editor->priv->activity_bar = NULL; opened_editors = g_slist_remove (opened_editors, comp_editor); G_OBJECT_CLASS (e_comp_editor_parent_class)->dispose (object); } static void e_comp_editor_init (ECompEditor *comp_editor) { comp_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (comp_editor, E_TYPE_COMP_EDITOR, ECompEditorPrivate); } static void e_comp_editor_alert_sink_iface_init (EAlertSinkInterface *iface) { iface->submit_alert = e_comp_editor_submit_alert; } static void e_comp_editor_class_init (ECompEditorClass *klass) { GtkWidgetClass *widget_class; GObjectClass *object_class; g_type_class_add_private (klass, sizeof (ECompEditorPrivate)); klass->sensitize_widgets = ece_sensitize_widgets; klass->fill_widgets = ece_fill_widgets; klass->fill_component = ece_fill_component; widget_class = GTK_WIDGET_CLASS (klass); widget_class->delete_event = comp_editor_delete_event; widget_class->key_press_event = comp_editor_key_press_event; object_class = G_OBJECT_CLASS (klass); object_class->set_property = e_comp_editor_set_property; object_class->get_property = e_comp_editor_get_property; object_class->constructed = e_comp_editor_constructed; object_class->dispose = e_comp_editor_dispose; g_object_class_install_property ( object_class, PROP_ALARM_EMAIL_ADDRESS, g_param_spec_string ( "alarm-email-address", "Alarm Email Address", "Target client's alarm email address", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_CAL_EMAIL_ADDRESS, g_param_spec_string ( "cal-email-address", "Calendar Email Address", "Target client's calendar email address", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_CHANGED, g_param_spec_boolean ( "changed", "Changed", "Whether the editor content changed", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_COMPONENT, g_param_spec_pointer ( "component", "Component", "icalcomponent currently edited", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_FLAGS, g_param_spec_uint ( "flags", "Flags", "Editor flags", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_ORIGIN_SOURCE, g_param_spec_object ( "origin-source", "Origin Source", "ESource of an ECalClient the component is stored in", E_TYPE_SOURCE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_SHELL, g_param_spec_object ( "shell", "Shell", "EShell", E_TYPE_SHELL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_SOURCE_CLIENT, g_param_spec_object ( "source-client", "Source Client", "ECalClient, the source calendar for the component", E_TYPE_CAL_CLIENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_TARGET_CLIENT, g_param_spec_object ( "target-client", "Target Client", "ECalClient currently set as the target calendar for the component", E_TYPE_CAL_CLIENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_TITLE_SUFFIX, g_param_spec_string ( "title-suffix", "Title Suffix", "Window title suffix, usually summary of the component", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); signals[TIMES_CHANGED] = g_signal_new ( "times-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ECompEditorClass, times_changed), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE); signals[OBJECT_CREATED] = g_signal_new ( "object-created", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ECompEditorClass, object_created), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE); signals[EDITOR_CLOSED] = g_signal_new ( "editor-closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ECompEditorClass, editor_closed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } void e_comp_editor_sensitize_widgets (ECompEditor *comp_editor) { ECompEditorClass *comp_editor_class; gboolean force_insensitive; GtkWidget *current_focus; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor); g_return_if_fail (comp_editor_class != NULL); g_return_if_fail (comp_editor_class->sensitize_widgets != NULL); current_focus = gtk_window_get_focus (GTK_WINDOW (comp_editor)); force_insensitive = !comp_editor->priv->component; if (!force_insensitive) { ECalClient *target_client; target_client = e_comp_editor_get_target_client (comp_editor); if (target_client) { EClient *client = E_CLIENT (target_client); if (e_client_is_readonly (client)) { force_insensitive = TRUE; } else { if (!e_cal_util_component_has_organizer (comp_editor->priv->component) || ece_organizer_is_user (comp_editor, comp_editor->priv->component, client) || ece_sentby_is_user (comp_editor, comp_editor->priv->component, client)) { comp_editor->priv->flags = comp_editor->priv->flags | E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER; } else { comp_editor->priv->flags = comp_editor->priv->flags & (~E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER); } } } else { force_insensitive = TRUE; } } comp_editor_class->sensitize_widgets (comp_editor, force_insensitive); if (force_insensitive) comp_editor->priv->restore_focus = current_focus; else ece_restore_focus (comp_editor); } void e_comp_editor_fill_widgets (ECompEditor *comp_editor, icalcomponent *component) { ECompEditorClass *comp_editor_class; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (component != NULL); comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor); g_return_if_fail (comp_editor_class != NULL); g_return_if_fail (comp_editor_class->fill_widgets != NULL); e_comp_editor_set_updating (comp_editor, TRUE); comp_editor_class->fill_widgets (comp_editor, component); e_comp_editor_set_updating (comp_editor, FALSE); } gboolean e_comp_editor_fill_component (ECompEditor *comp_editor, icalcomponent *component) { ECompEditorClass *comp_editor_class; GtkWidget *focused_widget; gboolean is_valid; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); g_return_val_if_fail (component != NULL, FALSE); comp_editor_class = E_COMP_EDITOR_GET_CLASS (comp_editor); g_return_val_if_fail (comp_editor_class != NULL, FALSE); g_return_val_if_fail (comp_editor_class->fill_component != NULL, FALSE); focused_widget = gtk_window_get_focus (GTK_WINDOW (comp_editor)); if (focused_widget) { GtkWidget *parent, *ce_widget = GTK_WIDGET (comp_editor); /* When a cell-renderer is focused and editing the cell content, then unfocus it may mean to free the currently focused widget, thus get the GtkTreeView in such cases. */ parent = focused_widget; while (parent = gtk_widget_get_parent (parent), parent && parent != ce_widget) { if (GTK_IS_TREE_VIEW (parent)) { focused_widget = parent; break; } } /* Save any pending changes */ gtk_window_set_focus (GTK_WINDOW (comp_editor), NULL); } is_valid = comp_editor_class->fill_component (comp_editor, component); if (focused_widget) gtk_window_set_focus (GTK_WINDOW (comp_editor), focused_widget); if (is_valid && comp_editor->priv->validation_alert) { e_alert_response (comp_editor->priv->validation_alert, GTK_RESPONSE_CLOSE); g_clear_object (&comp_editor->priv->validation_alert); } if (is_valid) { ECalClient *target_client; EClient *client = NULL; target_client = e_comp_editor_get_target_client (comp_editor); if (target_client) client = E_CLIENT (target_client); if (!e_cal_util_component_has_organizer (component) || (client && ( ece_organizer_is_user (comp_editor, component, client) || ece_sentby_is_user (comp_editor, component, client)))) { gint sequence; sequence = icalcomponent_get_sequence (component); icalcomponent_set_sequence (component, sequence + 1); } } return is_valid; } void e_comp_editor_set_validation_error (ECompEditor *comp_editor, ECompEditorPage *error_page, GtkWidget *error_widget, const gchar *error_message) { EAlert *alert, *previous_alert; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (error_message != NULL); /* Ignore validation errors when the inner editor is currently updating. */ if (e_comp_editor_get_updating (comp_editor)) return; alert = e_alert_new ("calendar:comp-editor-failed-validate", error_message, NULL); e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert); previous_alert = comp_editor->priv->validation_alert; comp_editor->priv->validation_alert = alert; if (previous_alert) { e_alert_response (previous_alert, GTK_RESPONSE_CLOSE); g_clear_object (&previous_alert); } if (error_page) e_comp_editor_select_page (comp_editor, error_page); if (error_widget) gtk_widget_grab_focus (error_widget); } EShell * e_comp_editor_get_shell (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->shell; } GSettings * e_comp_editor_get_settings (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->calendar_settings; } ESource * e_comp_editor_get_origin_source (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->origin_source; } icalcomponent * e_comp_editor_get_component (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->component; } guint32 e_comp_editor_get_flags (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), 0); return comp_editor->priv->flags; } void e_comp_editor_set_flags (ECompEditor *comp_editor, guint32 flags) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (comp_editor->priv->flags == flags) return; comp_editor->priv->flags = flags; g_object_notify (G_OBJECT (comp_editor), "flags"); } EFocusTracker * e_comp_editor_get_focus_tracker (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->focus_tracker; } GtkUIManager * e_comp_editor_get_ui_manager (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->ui_manager; } GtkAction * e_comp_editor_get_action (ECompEditor *comp_editor, const gchar *action_name) { GtkUIManager *ui_manager; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); g_return_val_if_fail (action_name != NULL, NULL); ui_manager = e_comp_editor_get_ui_manager (comp_editor); return e_lookup_action (ui_manager, action_name); } GtkActionGroup * e_comp_editor_get_action_group (ECompEditor *comp_editor, const gchar *group_name) { GtkUIManager *ui_manager; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); g_return_val_if_fail (group_name != NULL, NULL); ui_manager = e_comp_editor_get_ui_manager (comp_editor); return e_lookup_action_group (ui_manager, group_name); } GtkWidget * e_comp_editor_get_managed_widget (ECompEditor *comp_editor, const gchar *widget_path) { GtkUIManager *ui_manager; GtkWidget *widget; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); g_return_val_if_fail (widget_path != NULL, NULL); ui_manager = e_comp_editor_get_ui_manager (comp_editor); widget = gtk_ui_manager_get_widget (ui_manager, widget_path); g_return_val_if_fail (widget != NULL, NULL); return widget; } const gchar * e_comp_editor_get_alarm_email_address (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->alarm_email_address; } void e_comp_editor_set_alarm_email_address (ECompEditor *comp_editor, const gchar *alarm_email_address) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (g_strcmp0 (alarm_email_address, comp_editor->priv->alarm_email_address) == 0) return; g_free (comp_editor->priv->alarm_email_address); comp_editor->priv->alarm_email_address = g_strdup (alarm_email_address); g_object_notify (G_OBJECT (comp_editor), "alarm-email-address"); } const gchar * e_comp_editor_get_cal_email_address (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->cal_email_address; } void e_comp_editor_set_cal_email_address (ECompEditor *comp_editor, const gchar *cal_email_address) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (g_strcmp0 (cal_email_address, comp_editor->priv->cal_email_address) == 0) return; g_free (comp_editor->priv->cal_email_address); comp_editor->priv->cal_email_address = g_strdup (cal_email_address); g_object_notify (G_OBJECT (comp_editor), "cal-email-address"); } gboolean e_comp_editor_get_changed (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); return comp_editor->priv->changed; } void e_comp_editor_set_changed (ECompEditor *comp_editor, gboolean changed) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if ((changed ? 1 : 0) == (comp_editor->priv->changed ? 1 : 0)) return; comp_editor->priv->changed = changed; g_object_notify (G_OBJECT (comp_editor), "changed"); } void e_comp_editor_ensure_changed (ECompEditor *comp_editor) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); e_comp_editor_set_changed (comp_editor, TRUE); } gboolean e_comp_editor_get_updating (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); return comp_editor->priv->updating > 0; } void e_comp_editor_set_updating (ECompEditor *comp_editor, gboolean updating) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (updating) { comp_editor->priv->updating++; } else if (comp_editor->priv->updating > 0) { comp_editor->priv->updating--; } else { g_warn_if_reached (); } } ECalClient * e_comp_editor_get_source_client (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->source_client; } void e_comp_editor_set_source_client (ECompEditor *comp_editor, ECalClient *client) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (client == comp_editor->priv->source_client) return; if (client) g_object_ref (client); g_clear_object (&comp_editor->priv->source_client); comp_editor->priv->source_client = client; g_object_notify (G_OBJECT (comp_editor), "source-client"); } ECalClient * e_comp_editor_get_target_client (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->target_client; } void e_comp_editor_set_target_client (ECompEditor *comp_editor, ECalClient *client) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (client == comp_editor->priv->target_client) return; if (client) g_object_ref (client); g_clear_object (&comp_editor->priv->target_client); comp_editor->priv->target_client = client; g_object_notify (G_OBJECT (comp_editor), "target-client"); if (client && !comp_editor->priv->source_client && comp_editor->priv->origin_source && e_source_equal (e_client_get_source (E_CLIENT (client)), comp_editor->priv->origin_source)) e_comp_editor_set_source_client (comp_editor, client); e_comp_editor_sensitize_widgets (comp_editor); } const gchar * e_comp_editor_get_title_suffix (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return comp_editor->priv->title_suffix; } void e_comp_editor_set_title_suffix (ECompEditor *comp_editor, const gchar *title_suffix) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (g_strcmp0 (title_suffix, comp_editor->priv->title_suffix) == 0) return; g_free (comp_editor->priv->title_suffix); comp_editor->priv->title_suffix = g_strdup (title_suffix); g_object_notify (G_OBJECT (comp_editor), "title-suffix"); e_comp_editor_update_window_title (comp_editor); } void e_comp_editor_set_time_parts (ECompEditor *comp_editor, ECompEditorPropertyPart *dtstart_part, ECompEditorPropertyPart *dtend_part) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (dtstart_part) g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (dtstart_part)); if (dtend_part) g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (dtend_part)); ece_connect_time_parts (comp_editor, dtstart_part, dtend_part); } void e_comp_editor_get_time_parts (ECompEditor *comp_editor, ECompEditorPropertyPart **out_dtstart_part, ECompEditorPropertyPart **out_dtend_part) { g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); if (out_dtstart_part) *out_dtstart_part = comp_editor->priv->dtstart_part; if (out_dtend_part) *out_dtend_part = comp_editor->priv->dtend_part; } /* This consumes the @page. */ void e_comp_editor_add_page (ECompEditor *comp_editor, const gchar *label, ECompEditorPage *page) { ECompEditor *pages_comp_editor; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (label != NULL); g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page)); pages_comp_editor = e_comp_editor_page_ref_editor (page); if (pages_comp_editor != comp_editor) { g_warn_if_fail (pages_comp_editor == comp_editor); g_clear_object (&pages_comp_editor); return; } g_clear_object (&pages_comp_editor); /* One reference uses the GtkNotebook, the other the pages GSList */ gtk_notebook_append_page (comp_editor->priv->content, GTK_WIDGET (page), gtk_label_new_with_mnemonic (label)); comp_editor->priv->pages = g_slist_append (comp_editor->priv->pages, g_object_ref (page)); g_signal_connect_swapped (page, "changed", G_CALLBACK (e_comp_editor_ensure_changed), comp_editor); if (E_IS_COMP_EDITOR_PAGE_GENERAL (page)) { ECompEditorPageGeneral *page_general; g_return_if_fail (comp_editor->priv->page_general == NULL); page_general = E_COMP_EDITOR_PAGE_GENERAL (page); g_signal_connect (page_general, "notify::selected-source", G_CALLBACK (comp_editor_selected_source_notify_cb), comp_editor); comp_editor->priv->page_general = page_general; if ((comp_editor->priv->flags & E_COMP_EDITOR_FLAG_WITH_ATTENDEES) != 0) { e_comp_editor_page_general_set_show_attendees (page_general, TRUE); } } } /* The returned pointer is owned by the @comp_editor; returns the first instance, in order of the addition. */ ECompEditorPage * e_comp_editor_get_page (ECompEditor *comp_editor, GType page_type) { GSList *link; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); g_return_val_if_fail (g_type_is_a (page_type, E_TYPE_COMP_EDITOR_PAGE), NULL); g_return_val_if_fail (page_type != E_TYPE_COMP_EDITOR_PAGE, NULL); for (link = comp_editor->priv->pages; link; link = g_slist_next (link)) { ECompEditorPage *page = link->data; if (G_TYPE_CHECK_INSTANCE_TYPE (page, page_type)) return page; } return NULL; } /* Free the returned GSList with g_slist_free(), the memebers are owned by the comp_editor */ GSList * e_comp_editor_get_pages (ECompEditor *comp_editor) { g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); return g_slist_copy (comp_editor->priv->pages); } void e_comp_editor_select_page (ECompEditor *comp_editor, ECompEditorPage *page) { gint page_num; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (E_IS_COMP_EDITOR_PAGE (page)); page_num = gtk_notebook_page_num (comp_editor->priv->content, GTK_WIDGET (page)); g_return_if_fail (page_num != -1); gtk_notebook_set_current_page (comp_editor->priv->content, page_num); } /* Unref returned pointer when done with it. */ static EAlert * e_comp_editor_add_alert (ECompEditor *comp_editor, const gchar *alert_id, const gchar *primary_text, const gchar *secondary_text) { EAlert *alert; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), NULL); g_return_val_if_fail (alert_id != NULL, NULL); g_return_val_if_fail (primary_text != NULL || secondary_text != NULL, NULL); alert = e_alert_new (alert_id, primary_text ? primary_text : "", secondary_text ? secondary_text : "", NULL); e_alert_bar_add_alert (comp_editor->priv->alert_bar, alert); return alert; } /* Unref returned pointer when done with it. */ EAlert * e_comp_editor_add_information (ECompEditor *comp_editor, const gchar *primary_text, const gchar *secondary_text) { return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-information", primary_text, secondary_text); } /* Unref returned pointer when done with it. */ EAlert * e_comp_editor_add_warning (ECompEditor *comp_editor, const gchar *primary_text, const gchar *secondary_text) { return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-warning", primary_text, secondary_text); } /* Unref returned pointer when done with it. */ EAlert * e_comp_editor_add_error (ECompEditor *comp_editor, const gchar *primary_text, const gchar *secondary_text) { return e_comp_editor_add_alert (comp_editor, "calendar:comp-editor-error", primary_text, secondary_text); } static gboolean ece_check_start_before_end (ECompEditor *comp_editor, struct icaltimetype *start_tt, struct icaltimetype *end_tt, gboolean adjust_end_time) { struct icaltimetype end_tt_copy; icaltimezone *start_zone, *end_zone; gint duration = -1; gint cmp; if ((e_comp_editor_get_flags (comp_editor) & E_COMP_EDITOR_FLAG_IS_NEW) == 0) { icalcomponent *icomp; icomp = e_comp_editor_get_component (comp_editor); if (icomp && icalcomponent_get_first_property (icomp, ICAL_DTSTART_PROPERTY) && (icalcomponent_get_first_property (icomp, ICAL_DTEND_PROPERTY) || icalcomponent_get_first_property (icomp, ICAL_DUE_PROPERTY))) { struct icaltimetype orig_start, orig_end; orig_start = icalcomponent_get_dtstart (icomp); orig_end = icalcomponent_get_dtend (icomp); if (icaltime_is_valid_time (orig_start) && icaltime_is_valid_time (orig_end)) { duration = icaltime_as_timet (orig_end) - icaltime_as_timet (orig_start); } } } start_zone = (icaltimezone *) start_tt->zone; end_zone = (icaltimezone *) end_tt->zone; /* Convert the end time to the same timezone as the start time. */ end_tt_copy = *end_tt; if (start_zone && end_zone && start_zone != end_zone) icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone); /* Now check if the start time is after the end time. If it is, * we need to modify one of the times. */ cmp = icaltime_compare (*start_tt, end_tt_copy); if (cmp > 0) { if (adjust_end_time) { /* Try to switch only the date */ end_tt->year = start_tt->year; end_tt->month = start_tt->month; end_tt->day = start_tt->day; end_tt_copy = *end_tt; if (start_zone && end_zone && start_zone != end_zone) icaltimezone_convert_time (&end_tt_copy, end_zone, start_zone); if (duration > 0) icaltime_adjust (&end_tt_copy, 0, 0, 0, -duration); if (icaltime_compare (*start_tt, end_tt_copy) >= 0) { *end_tt = *start_tt; if (duration >= 0) { icaltime_adjust (end_tt, 0, 0, 0, duration); } else { /* Modify the end time, to be the start + 1 hour/day. */ icaltime_adjust (end_tt, 0, start_tt->is_date ? 24 : 1, 0, 0); } if (start_zone && end_zone && start_zone != end_zone) icaltimezone_convert_time (end_tt, start_zone, end_zone); } } else { /* Try to switch only the date */ start_tt->year = end_tt->year; start_tt->month = end_tt->month; start_tt->day = end_tt->day; if (icaltime_compare (*start_tt, end_tt_copy) >= 0) { *start_tt = *end_tt; if (duration >= 0) { icaltime_adjust (start_tt, 0, 0, 0, -duration); } else { /* Modify the start time, to be the end - 1 hour/day. */ icaltime_adjust (start_tt, 0, start_tt->is_date ? -24 : -1, 0, 0); } if (start_zone && end_zone && start_zone != end_zone) icaltimezone_convert_time (start_tt, end_zone, start_zone); } } return TRUE; } return FALSE; } void e_comp_editor_ensure_start_before_end (ECompEditor *comp_editor, ECompEditorPropertyPart *start_datetime, ECompEditorPropertyPart *end_datetime, gboolean change_end_datetime) { ECompEditorPropertyPartDatetime *start_dtm, *end_dtm; struct icaltimetype start_tt, end_tt; gboolean set_dtstart = FALSE, set_dtend = FALSE; g_return_if_fail (E_IS_COMP_EDITOR (comp_editor)); g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (start_datetime)); g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (end_datetime)); start_dtm = E_COMP_EDITOR_PROPERTY_PART_DATETIME (start_datetime); end_dtm = E_COMP_EDITOR_PROPERTY_PART_DATETIME (end_datetime); start_tt = e_comp_editor_property_part_datetime_get_value (start_dtm); end_tt = e_comp_editor_property_part_datetime_get_value (end_dtm); if (icaltime_is_null_time (start_tt) || icaltime_is_null_time (end_tt) || !icaltime_is_valid_time (start_tt) || !icaltime_is_valid_time (end_tt)) return; if (start_tt.is_date || end_tt.is_date) { /* All Day Events are simple. We just compare the dates and if * start > end we copy one of them to the other. */ gint cmp; start_tt.is_date = TRUE; end_tt.is_date = TRUE; cmp = icaltime_compare_date_only (start_tt, end_tt); if (cmp > 0) { if (change_end_datetime) { end_tt = start_tt; set_dtend = TRUE; } else { start_tt = end_tt; set_dtstart = TRUE; } } } else { if (ece_check_start_before_end (comp_editor, &start_tt, &end_tt, change_end_datetime)) { if (change_end_datetime) set_dtend = TRUE; else set_dtstart = TRUE; } } if (set_dtstart || set_dtend) { e_comp_editor_set_updating (comp_editor, TRUE); if (set_dtstart) e_comp_editor_property_part_datetime_set_value (start_dtm, start_tt); if (set_dtend) e_comp_editor_property_part_datetime_set_value (end_dtm, end_tt); e_comp_editor_set_updating (comp_editor, FALSE); } } static gboolean e_comp_editor_holds_component (ECompEditor *comp_editor, ESource *origin_source, const icalcomponent *component) { const gchar *component_uid, *editor_uid; gboolean equal; g_return_val_if_fail (E_IS_COMP_EDITOR (comp_editor), FALSE); g_return_val_if_fail (component != NULL, FALSE); if (!origin_source || !comp_editor->priv->origin_source || !e_source_equal (origin_source, comp_editor->priv->origin_source)) return FALSE; component_uid = icalcomponent_get_uid ((icalcomponent *) component); editor_uid = icalcomponent_get_uid (comp_editor->priv->component); if (!component_uid || !editor_uid) return FALSE; equal = g_strcmp0 (component_uid, editor_uid) == 0; if (equal) { struct icaltimetype component_rid, editor_rid; component_rid = icalcomponent_get_recurrenceid ((icalcomponent *) component); editor_rid = icalcomponent_get_recurrenceid (comp_editor->priv->component); if (icaltime_is_null_time (component_rid)) { equal = icaltime_is_null_time (editor_rid); } else if (!icaltime_is_null_time (editor_rid)) { equal = icaltime_compare (component_rid, editor_rid) == 0; } } return equal; } ECompEditor * e_comp_editor_open_for_component (GtkWindow *parent, EShell *shell, ESource *origin_source, const icalcomponent *component, guint32 flags /* bit-or of ECompEditorFlags */) { ECompEditor *comp_editor; GType comp_editor_type; g_return_val_if_fail (E_IS_SHELL (shell), NULL); if (origin_source) g_return_val_if_fail (E_IS_SOURCE (origin_source), NULL); g_return_val_if_fail (component != NULL, NULL); comp_editor = e_comp_editor_find_existing_for (origin_source, component); if (comp_editor) { gtk_window_present (GTK_WINDOW (comp_editor)); return comp_editor; } switch (icalcomponent_isa (component)) { case ICAL_VEVENT_COMPONENT: comp_editor_type = E_TYPE_COMP_EDITOR_EVENT; break; case ICAL_VTODO_COMPONENT: comp_editor_type = E_TYPE_COMP_EDITOR_TASK; break; case ICAL_VJOURNAL_COMPONENT: comp_editor_type = E_TYPE_COMP_EDITOR_MEMO; break; default: g_warn_if_reached (); return NULL; } comp_editor = g_object_new (comp_editor_type, "shell", shell, "origin-source", origin_source, "component", component, "flags", flags, NULL); opened_editors = g_slist_prepend (opened_editors, comp_editor); gtk_widget_show (GTK_WIDGET (comp_editor)); return comp_editor; } ECompEditor * e_comp_editor_find_existing_for (ESource *origin_source, const icalcomponent *component) { ECompEditor *comp_editor; GSList *link; if (origin_source) g_return_val_if_fail (E_IS_SOURCE (origin_source), NULL); g_return_val_if_fail (component != NULL, NULL); for (link = opened_editors; link; link = g_slist_next (link)) { comp_editor = link->data; if (!comp_editor) continue; if (e_comp_editor_holds_component (comp_editor, origin_source, component)) { gtk_window_present (GTK_WINDOW (comp_editor)); return comp_editor; } } return NULL; }