diff --git a/src/calendar/gui/e-comp-editor.c b/src/calendar/gui/e-comp-editor.c
index 4c01d1d..0f00f58 100644
--- a/src/calendar/gui/e-comp-editor.c
+++ b/src/calendar/gui/e-comp-editor.c
@@ -2610,6 +2610,7 @@ 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);
@@ -2619,8 +2620,30 @@ e_comp_editor_fill_component (ECompEditor *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);
diff --git a/src/calendar/gui/e-comp-editor.c.crash-empty-attendee b/src/calendar/gui/e-comp-editor.c.crash-empty-attendee
new file mode 100644
index 0000000..4c01d1d
--- /dev/null
+++ b/src/calendar/gui/e-comp-editor.c.crash-empty-attendee
@@ -0,0 +1,3428 @@
+/*
+ * 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;
+ 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);
+
+ is_valid = comp_editor_class->fill_component (comp_editor, component);
+
+ 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;
+}
diff --git a/src/modules/calendar/e-cal-shell-content.c b/src/modules/calendar/e-cal-shell-content.c
index 87ed166..69c4e72 100644
--- a/src/modules/calendar/e-cal-shell-content.c
+++ b/src/modules/calendar/e-cal-shell-content.c
@@ -847,9 +847,9 @@ cal_shell_content_get_attendee_prop (icalcomponent *icalcomp,
while (prop != NULL) {
const gchar *attendee;
- attendee = icalproperty_get_attendee (prop);
+ attendee = itip_strip_mailto (icalproperty_get_attendee (prop));
- if (g_str_equal (itip_strip_mailto (attendee), address))
+ if (attendee && g_ascii_strcasecmp (attendee, address) == 0)
return prop;
prop = icalcomponent_get_next_property (
diff --git a/src/modules/calendar/e-cal-shell-content.c.crash-empty-attendee b/src/modules/calendar/e-cal-shell-content.c.crash-empty-attendee
new file mode 100644
index 0000000..87ed166
--- /dev/null
+++ b/src/modules/calendar/e-cal-shell-content.c.crash-empty-attendee
@@ -0,0 +1,2338 @@
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2014 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 Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see .
+ */
+
+#include "evolution-config.h"
+
+#include
+#include
+#include
+
+#include "calendar/gui/calendar-config.h"
+#include "calendar/gui/calendar-view.h"
+#include "calendar/gui/comp-util.h"
+#include "calendar/gui/e-cal-list-view.h"
+#include "calendar/gui/e-cal-model-calendar.h"
+#include "calendar/gui/e-cal-model-memos.h"
+#include "calendar/gui/e-cal-model-tasks.h"
+#include "calendar/gui/e-calendar-view.h"
+#include "calendar/gui/e-day-view.h"
+#include "calendar/gui/e-month-view.h"
+#include "calendar/gui/e-week-view.h"
+#include "calendar/gui/itip-utils.h"
+#include "calendar/gui/tag-calendar.h"
+
+#include "e-cal-base-shell-sidebar.h"
+#include "e-cal-shell-view-private.h"
+#include "e-cal-shell-content.h"
+
+#define E_CAL_SHELL_CONTENT_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentPrivate))
+
+struct _ECalShellContentPrivate {
+ GtkWidget *hpaned;
+ GtkWidget *vpaned;
+
+ GtkWidget *calendar_notebook;
+ GtkWidget *task_table;
+ ECalModel *task_model;
+ ECalDataModel *task_data_model;
+
+ GtkWidget *memo_table;
+ ECalModel *memo_model;
+ ECalDataModel *memo_data_model;
+
+ ETagCalendar *tag_calendar;
+ gulong datepicker_selection_changed_id;
+ gulong datepicker_range_moved_id;
+
+ ECalViewKind current_view;
+ ECalendarView *views[E_CAL_VIEW_KIND_LAST];
+ GDate view_start, view_end;
+ guint32 view_start_range_day_offset;
+ GDate last_range_start; /* because "date-range-changed" can be emit with no real change */
+
+ time_t previous_selected_start_time;
+ time_t previous_selected_end_time;
+
+ gulong current_view_id_changed_id;
+};
+
+enum {
+ PROP_0,
+ PROP_CALENDAR_NOTEBOOK,
+ PROP_MEMO_TABLE,
+ PROP_TASK_TABLE,
+ PROP_CURRENT_VIEW_ID,
+ PROP_CURRENT_VIEW
+};
+
+/* Used to indicate who has the focus within the calendar view. */
+typedef enum {
+ FOCUS_CALENDAR_NOTEBOOK,
+ FOCUS_MEMO_TABLE,
+ FOCUS_TASK_TABLE,
+ FOCUS_OTHER
+} FocusLocation;
+
+G_DEFINE_DYNAMIC_TYPE (ECalShellContent, e_cal_shell_content, E_TYPE_CAL_BASE_SHELL_CONTENT)
+
+static time_t
+convert_to_local_zone (time_t tm,
+ icaltimezone *from_zone)
+{
+ struct icaltimetype tt;
+
+ tt = icaltime_from_timet_with_zone (tm, FALSE, from_zone);
+ return icaltime_as_timet (tt);
+}
+
+static void
+cal_shell_content_update_model_and_current_view_times (ECalShellContent *cal_shell_content,
+ ECalModel *model,
+ ECalendarItem *calitem,
+ time_t view_start_tt,
+ time_t view_end_tt,
+ const GDate *view_start,
+ const GDate *view_end)
+{
+ ECalendarView *current_view;
+ EDayView *day_view = NULL;
+ gint day_view_selection_start_day = -1, day_view_selection_end_day = -1;
+ gint day_view_selection_start_row = -1, day_view_selection_end_row = -1;
+ gdouble day_view_scrollbar_position = 0.0;
+ gint syy, smm, sdd, eyy, emm, edd;
+ time_t visible_range_start, visible_range_end;
+ gboolean filters_updated = FALSE;
+ icaltimezone *zone;
+ gchar *cal_filter;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+ g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
+
+ current_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
+ g_return_if_fail (current_view != NULL);
+
+ zone = e_cal_model_get_timezone (model);
+ cal_filter = e_cal_data_model_dup_filter (e_cal_model_get_data_model (model));
+
+ if (E_IS_DAY_VIEW (current_view)) {
+ GtkAdjustment *adjustment;
+
+ day_view = E_DAY_VIEW (current_view);
+ day_view_selection_start_day = day_view->selection_start_day;
+ day_view_selection_end_day = day_view->selection_end_day;
+ day_view_selection_start_row = day_view->selection_start_row;
+ day_view_selection_end_row = day_view->selection_end_row;
+
+ adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
+ day_view_scrollbar_position = gtk_adjustment_get_value (adjustment);
+ }
+
+ g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id);
+ g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
+
+ visible_range_start = view_start_tt;
+ visible_range_end = view_end_tt;
+
+ e_calendar_view_precalc_visible_time_range (current_view, view_start_tt, view_end_tt, &visible_range_start, &visible_range_end);
+ if (view_start_tt != visible_range_start || view_end_tt != visible_range_end) {
+ time_t cmp_range_start = convert_to_local_zone (visible_range_start, zone);
+ time_t cmp_range_end = convert_to_local_zone (visible_range_end, zone);
+
+ if (view_start_tt != cmp_range_start ||
+ view_end_tt != cmp_range_end - 1) {
+ /* Calendar views update their inner time range during e_cal_model_set_time_range() call,
+ while they can change it if needed (like a clamp of a week view with a week start day
+ not being Monday */
+ GDate new_view_start, new_view_end;
+
+ /* Midnight means the next day, which is not desired here */
+ cmp_range_end--;
+ visible_range_end--;
+
+ /* These times are in the correct zone already */
+ time_to_gdate_with_zone (&new_view_start, cmp_range_start, NULL);
+ time_to_gdate_with_zone (&new_view_end, cmp_range_end, NULL);
+
+ e_calendar_item_set_selection (calitem, &new_view_start, &new_view_end);
+ e_cal_shell_content_update_filters (cal_shell_content, cal_filter, visible_range_start, visible_range_end);
+ e_calendar_view_set_selected_time_range (current_view, cmp_range_start, cmp_range_start);
+ filters_updated = TRUE;
+ view_start_tt = cmp_range_start;
+ view_end_tt = cmp_range_end;
+ }
+ }
+
+ if (!filters_updated) {
+ e_calendar_item_set_selection (calitem, view_start, view_end);
+ e_cal_shell_content_update_filters (cal_shell_content, cal_filter, view_start_tt, view_end_tt);
+ e_calendar_view_set_selected_time_range (current_view, view_start_tt, view_start_tt);
+ }
+
+ if (day_view && day_view_selection_start_day != -1 && day_view_selection_end_day != -1 &&
+ day_view_selection_start_row != -1 && day_view_selection_end_row != -1) {
+ GtkAdjustment *adjustment;
+
+ day_view->selection_start_day = day_view_selection_start_day;
+ day_view->selection_end_day = day_view_selection_end_day;
+ day_view->selection_start_row = day_view_selection_start_row;
+ day_view->selection_end_row = day_view_selection_end_row;
+
+ /* This is better than e_day_view_ensure_rows_visible(), because it keeps both
+ selection and the exact scrollbar position in the main canvas, which may not
+ always correspond to each other. */
+ adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (day_view->main_canvas));
+ gtk_adjustment_set_value (adjustment, day_view_scrollbar_position);
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (current_view));
+
+ g_free (cal_filter);
+
+ g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id);
+ g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
+
+ if (e_calendar_item_get_date_range (calitem, &syy, &smm, &sdd, &eyy, &emm, &edd)) {
+ GDate range_start;
+
+ g_date_set_dmy (&range_start, sdd, smm + 1, syy);
+
+ cal_shell_content->priv->view_start_range_day_offset =
+ g_date_get_julian (&cal_shell_content->priv->view_start) - g_date_get_julian (&range_start);
+ }
+}
+
+static void
+e_cal_shell_content_change_view (ECalShellContent *cal_shell_content,
+ ECalViewKind to_view,
+ const GDate *view_start,
+ const GDate *view_end,
+ gboolean force_change)
+{
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ ECalendar *calendar;
+ ECalModel *model;
+ icaltimezone *zone;
+ time_t view_start_tt, view_end_tt;
+ gboolean view_changed = FALSE;
+ gint selected_days;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (to_view >= E_CAL_VIEW_KIND_DAY && to_view < E_CAL_VIEW_KIND_LAST);
+ g_return_if_fail (view_start != NULL);
+ g_return_if_fail (g_date_valid (view_start));
+ g_return_if_fail (view_end != NULL);
+ g_return_if_fail (g_date_valid (view_end));
+
+ shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+
+ calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+ g_return_if_fail (E_IS_CALENDAR (calendar));
+
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ zone = e_cal_model_get_timezone (model);
+ view_start_tt = cal_comp_gdate_to_timet (view_start, zone);
+ view_end_tt = cal_comp_gdate_to_timet (view_end, zone);
+
+ if (to_view != cal_shell_content->priv->current_view) {
+ g_signal_handler_block (cal_shell_content, cal_shell_content->priv->current_view_id_changed_id);
+ e_cal_shell_content_set_current_view_id (cal_shell_content, to_view);
+ g_signal_handler_unblock (cal_shell_content, cal_shell_content->priv->current_view_id_changed_id);
+ view_changed = TRUE;
+ }
+
+ selected_days = g_date_get_julian (view_end) - g_date_get_julian (view_start) + 1;
+
+ if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY) {
+ EDayView *day_view;
+
+ day_view = E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY]);
+ e_day_view_set_days_shown (day_view, selected_days);
+ } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH) {
+ EWeekView *month_view;
+
+ month_view = E_WEEK_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH]);
+ e_week_view_set_weeks_shown (month_view, selected_days / 7);
+ }
+
+ if (!force_change &&
+ g_date_valid (&cal_shell_content->priv->view_start) &&
+ g_date_valid (&cal_shell_content->priv->view_end) &&
+ g_date_compare (&cal_shell_content->priv->view_start, view_start) == 0 &&
+ g_date_compare (&cal_shell_content->priv->view_end, view_end) == 0) {
+ ECalendarItem *calitem = e_calendar_get_item (calendar);
+
+ if (view_changed)
+ cal_shell_content_update_model_and_current_view_times (
+ cal_shell_content, model, calitem, view_start_tt, view_end_tt, view_start, view_end);
+
+ g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id);
+ g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
+
+ e_calendar_item_set_selection (calitem, view_start, view_end);
+
+ g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id);
+ g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_selection_changed_id);
+
+ return;
+ }
+
+ cal_shell_content->priv->view_start = *view_start;
+ cal_shell_content->priv->view_end = *view_end;
+
+ cal_shell_content_update_model_and_current_view_times (
+ cal_shell_content, model, e_calendar_get_item (calendar), view_start_tt, view_end_tt, view_start, view_end);
+}
+
+static void
+cal_shell_content_clamp_for_whole_weeks (GDateWeekday week_start_day,
+ GDate *sel_start,
+ GDate *sel_end,
+ gboolean saturday_as_sunday)
+{
+ GDateWeekday wday;
+ guint32 julian_start, julian_end;
+
+ g_return_if_fail (sel_start != NULL);
+ g_return_if_fail (sel_end != NULL);
+
+ wday = g_date_get_weekday (sel_start);
+
+ /* This is because the month/week view doesn't split weekends */
+ if (saturday_as_sunday && wday == G_DATE_SATURDAY && week_start_day == G_DATE_SUNDAY)
+ wday = G_DATE_SUNDAY;
+
+ if (week_start_day > wday) {
+ g_date_subtract_days (sel_start, wday);
+ wday = g_date_get_weekday (sel_start);
+ }
+
+ if (week_start_day < wday)
+ g_date_subtract_days (sel_start, wday - week_start_day);
+
+ julian_start = g_date_get_julian (sel_start);
+ julian_end = g_date_get_julian (sel_end);
+
+ if (((julian_end - julian_start + 1) % 7) != 0)
+ g_date_add_days (sel_end, 7 - ((julian_end - julian_start + 1) % 7));
+
+ julian_end = g_date_get_julian (sel_end);
+
+ /* Can show only up to 6 weeks */
+ if ((julian_end - julian_start + 1) / 7 > 6) {
+ *sel_end = *sel_start;
+ g_date_add_days (sel_end, (7 * 6) - 1);
+ }
+
+ if (g_date_compare (sel_start, sel_end) == 0)
+ g_date_add_days (sel_end, 6);
+}
+
+static gboolean
+cal_shell_content_weekday_within (GDateWeekday start_wday,
+ GDateWeekday end_wday,
+ GDateWeekday test_wday)
+{
+ gint ii;
+
+ if (start_wday <= end_wday)
+ return start_wday <= test_wday && test_wday <= end_wday;
+
+ for (ii = 0; ii < 7; ii++) {
+ if (start_wday == test_wday)
+ return TRUE;
+
+ if (start_wday == end_wday)
+ break;
+
+ start_wday = e_weekday_get_next (start_wday);
+ }
+
+ return FALSE;
+}
+
+static void
+cal_shell_content_change_selection_in_current_view (ECalShellContent *cal_shell_content,
+ time_t sel_start_tt,
+ time_t sel_end_tt,
+ icaltimezone *zone)
+{
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY &&
+ cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) {
+ ECalendarView *view;
+
+ view = cal_shell_content->priv->views[cal_shell_content->priv->current_view];
+
+ /* Preserve selected time (change only date) for these views */
+ if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY ||
+ cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK) {
+ time_t current_sel_start = (time_t) -1, current_sel_end = (time_t) -1;
+
+ if (e_calendar_view_get_selected_time_range (view, ¤t_sel_start, ¤t_sel_end)) {
+ current_sel_start = icaltime_as_timet_with_zone (icaltime_from_timet_with_zone (current_sel_start, 0, zone), NULL);
+ current_sel_end = icaltime_as_timet_with_zone (icaltime_from_timet_with_zone (current_sel_end, 0, zone), NULL);
+
+ sel_start_tt += current_sel_start % (24 * 60 * 60);
+ sel_end_tt += current_sel_end % (24 * 60 * 60);
+ }
+ }
+
+ e_calendar_view_set_selected_time_range (view, sel_start_tt, sel_end_tt);
+ }
+}
+
+static void
+cal_shell_content_datepicker_selection_changed_cb (ECalendarItem *calitem,
+ ECalShellContent *cal_shell_content)
+{
+ GDate sel_start, sel_end;
+ guint32 selected_days, start_julian, end_julian;
+ icaltimezone *zone;
+ time_t sel_start_tt, sel_end_tt;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
+
+ g_date_clear (&sel_start, 1);
+ g_date_clear (&sel_end, 1);
+
+ e_calendar_item_get_selection (calitem, &sel_start, &sel_end);
+
+ start_julian = g_date_get_julian (&sel_start);
+ end_julian = g_date_get_julian (&sel_end);
+
+ g_return_if_fail (start_julian <= end_julian);
+
+ if (g_date_compare (&cal_shell_content->priv->view_start, &sel_start) == 0 &&
+ g_date_compare (&cal_shell_content->priv->view_end, &sel_end) == 0) {
+ /* No change in the selection range */
+ return;
+ }
+
+ zone = e_cal_model_get_timezone (e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)));
+ sel_start_tt = cal_comp_gdate_to_timet (&sel_start, zone);
+ sel_end_tt = cal_comp_gdate_to_timet (&sel_end, zone);
+
+ selected_days = end_julian - start_julian + 1;
+ if (selected_days == 1) {
+ GDateWeekday sel_start_wday, sel_end_wday, cur_start_wday, cur_end_wday;
+
+ /* Clicked inside currently selected view range; do not do anything,
+ just make sure the days are selected again */
+ if (g_date_compare (&cal_shell_content->priv->view_start, &sel_start) <= 0 &&
+ g_date_compare (&sel_start, &cal_shell_content->priv->view_end) <= 0) {
+ sel_start = cal_shell_content->priv->view_start;
+ sel_end = cal_shell_content->priv->view_end;
+
+ e_calendar_item_set_selection (calitem, &sel_start, &sel_end);
+
+ cal_shell_content_change_selection_in_current_view (cal_shell_content, sel_start_tt, sel_end_tt, zone);
+ return;
+ }
+
+ sel_start_wday = g_date_get_weekday (&sel_start);
+ sel_end_wday = g_date_get_weekday (&sel_end);
+ cur_start_wday = g_date_get_weekday (&cal_shell_content->priv->view_start);
+ cur_end_wday = g_date_get_weekday (&cal_shell_content->priv->view_end);
+
+ if ((cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK ||
+ (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY &&
+ e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) != 1)) &&
+ cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_start_wday)) {
+ if (cur_start_wday < sel_start_wday) {
+ g_date_subtract_days (&sel_start, sel_start_wday - cur_start_wday);
+ } else if (cur_start_wday > sel_start_wday) {
+ g_date_subtract_days (&sel_start, 7 - (cur_start_wday - sel_start_wday));
+ }
+ sel_end = sel_start;
+ if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY)
+ g_date_add_days (&sel_end, e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) - 1);
+ else
+ g_date_add_days (&sel_end, e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK])) - 1);
+
+ e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, FALSE);
+ } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WEEK &&
+ cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_start_wday) &&
+ cal_shell_content_weekday_within (cur_start_wday, cur_end_wday, sel_end_wday)) {
+ if (cur_start_wday < sel_start_wday)
+ g_date_subtract_days (&sel_start, sel_start_wday - cur_start_wday);
+ sel_end = sel_start;
+ cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, TRUE);
+
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE);
+ } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH ||
+ cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST) {
+ /* whole month */
+ g_date_set_day (&sel_start, 1);
+ sel_end = sel_start;
+ g_date_set_day (&sel_end, g_date_get_days_in_month (g_date_get_month (&sel_start), g_date_get_year (&sel_start)) - 1);
+ cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH);
+
+ e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, FALSE);
+ } else if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK) {
+ cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, TRUE);
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE);
+ } else {
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE);
+ }
+
+ cal_shell_content_change_selection_in_current_view (cal_shell_content, sel_start_tt, sel_end_tt, zone);
+ } else if (selected_days < 7) {
+ GDateWeekday first_work_wday;
+
+ first_work_wday = e_cal_model_get_work_day_first (e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content)));
+
+ if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WORKWEEK &&
+ first_work_wday == g_date_get_weekday (&sel_start) &&
+ selected_days == e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK])))
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WORKWEEK, &sel_start, &sel_end, FALSE);
+ else
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE);
+ } else if (selected_days == 7) {
+ GDateWeekday sel_start_wday;
+
+ sel_start_wday = g_date_get_weekday (&sel_start);
+
+ if (sel_start_wday == calitem->week_start_day &&
+ cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_DAY &&
+ e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY])) == 7) {
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_end, FALSE);
+ } else {
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_WEEK, &sel_start, &sel_end, FALSE);
+ }
+ } else {
+ if (cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_LIST) {
+ /* whole month */
+ g_date_set_day (&sel_start, 1);
+ sel_end = sel_start;
+ g_date_set_day (&sel_end, g_date_get_days_in_month (g_date_get_month (&sel_start), g_date_get_year (&sel_start)));
+ cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end, FALSE);
+
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_LIST, &sel_start, &sel_end, FALSE);
+ } else {
+ cal_shell_content_clamp_for_whole_weeks (calitem->week_start_day, &sel_start, &sel_end,
+ cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH || cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_WEEK);
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_MONTH, &sel_start, &sel_end, FALSE);
+ }
+ }
+}
+
+static void
+cal_shell_content_datepicker_range_moved_cb (ECalendarItem *calitem,
+ ECalShellContent *cal_shell_content)
+{
+ gint start_year, start_month, start_day, end_year, end_month, end_day;
+ GDate sel_start_date, sel_end_date, range_start_date;
+
+ g_return_if_fail (E_IS_CALENDAR_ITEM (calitem));
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ if (!e_calendar_item_get_date_range (calitem, &start_year, &start_month, &start_day, &end_year, &end_month, &end_day))
+ return;
+
+ g_date_set_dmy (&range_start_date, start_day, start_month + 1, start_year);
+
+ if (g_date_valid (&cal_shell_content->priv->last_range_start) &&
+ g_date_compare (&cal_shell_content->priv->last_range_start, &range_start_date) == 0) {
+ return;
+ }
+
+ cal_shell_content->priv->last_range_start = range_start_date;
+
+ g_date_clear (&sel_start_date, 1);
+ g_date_clear (&sel_end_date, 1);
+
+ if (cal_shell_content->priv->view_start_range_day_offset == (guint32) -1) {
+ sel_start_date = cal_shell_content->priv->view_start;
+ sel_end_date = cal_shell_content->priv->view_end;
+ cal_shell_content->priv->view_start_range_day_offset =
+ g_date_get_julian (&cal_shell_content->priv->view_start) - g_date_get_julian (&range_start_date);
+ } else {
+ gint view_days;
+
+ view_days = g_date_get_julian (&cal_shell_content->priv->view_end) - g_date_get_julian (&cal_shell_content->priv->view_start);
+
+ sel_start_date = range_start_date;
+ g_date_add_days (&sel_start_date, cal_shell_content->priv->view_start_range_day_offset);
+
+ sel_end_date = sel_start_date;
+ g_date_add_days (&sel_end_date, view_days);
+ }
+
+ g_signal_handler_block (calitem, cal_shell_content->priv->datepicker_range_moved_id);
+
+ e_calendar_item_set_selection (calitem, &sel_start_date, &sel_end_date);
+
+ g_signal_handler_unblock (calitem, cal_shell_content->priv->datepicker_range_moved_id);
+}
+
+static gboolean
+cal_shell_content_datepicker_button_press_cb (ECalendar *calendar,
+ GdkEvent *event,
+ ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), FALSE);
+
+ if (!event)
+ return FALSE;
+
+ if (event->type == GDK_2BUTTON_PRESS) {
+ ECalendarItem *calitem = e_calendar_get_item (calendar);
+ GDate sel_start, sel_end;
+
+ g_date_clear (&sel_start, 1);
+ g_date_clear (&sel_end, 1);
+
+ e_calendar_item_get_selection (calitem, &sel_start, &sel_end);
+
+ /* Switch to a day view on a double-click */
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &sel_start, &sel_start, FALSE);
+ }
+
+ return FALSE;
+}
+
+static void
+cal_shell_content_current_view_id_changed_cb (ECalShellContent *cal_shell_content)
+{
+ GDate sel_start, sel_end;
+ GDateWeekday work_day_first, week_start_day;
+ ECalModel *model;
+ gint ii;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ work_day_first = e_cal_model_get_work_day_first (model);
+ week_start_day = e_cal_model_get_week_start_day (model);
+
+ if (cal_shell_content->priv->previous_selected_start_time != -1 &&
+ cal_shell_content->priv->previous_selected_end_time != -1) {
+ icaltimezone *zone;
+
+ zone = e_cal_model_get_timezone (model);
+ time_to_gdate_with_zone (&sel_start, cal_shell_content->priv->previous_selected_start_time, zone);
+ time_to_gdate_with_zone (&sel_end, cal_shell_content->priv->previous_selected_end_time, zone);
+ } else {
+ sel_start = cal_shell_content->priv->view_start;
+ sel_end = cal_shell_content->priv->view_end;
+ }
+
+ switch (cal_shell_content->priv->current_view) {
+ case E_CAL_VIEW_KIND_DAY:
+ /* Left the start & end being the current view start */
+ sel_end = sel_start;
+ break;
+ case E_CAL_VIEW_KIND_WORKWEEK:
+ cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, FALSE);
+ ii = 0;
+ while (g_date_get_weekday (&sel_start) != work_day_first && ii < 7) {
+ g_date_add_days (&sel_start, 1);
+ ii++;
+ }
+
+ sel_end = sel_start;
+ g_date_add_days (&sel_end, e_day_view_get_days_shown (E_DAY_VIEW (cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK])) - 1);
+ break;
+ case E_CAL_VIEW_KIND_WEEK:
+ sel_end = sel_start;
+ cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, TRUE);
+ break;
+ case E_CAL_VIEW_KIND_MONTH:
+ case E_CAL_VIEW_KIND_LIST:
+ if (g_date_get_day (&sel_start) != 1 &&
+ (g_date_get_julian (&sel_end) - g_date_get_julian (&sel_start) + 1) / 7 >= 3 &&
+ g_date_get_month (&sel_start) != g_date_get_month (&sel_end)) {
+ g_date_set_day (&sel_start, 1);
+ g_date_add_months (&sel_start, 1);
+ } else {
+ g_date_set_day (&sel_start, 1);
+ }
+ sel_end = sel_start;
+ g_date_add_months (&sel_end, 1);
+ g_date_subtract_days (&sel_end, 1);
+ cal_shell_content_clamp_for_whole_weeks (week_start_day, &sel_start, &sel_end, cal_shell_content->priv->current_view == E_CAL_VIEW_KIND_MONTH);
+ break;
+ default:
+ g_warn_if_reached ();
+ return;
+ }
+
+ /* Ensure a change */
+ e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &sel_start, &sel_end, TRUE);
+
+ /* Try to preserve selection between the views */
+ if (cal_shell_content->priv->previous_selected_start_time != -1 &&
+ cal_shell_content->priv->previous_selected_end_time != -1) {
+ if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY &&
+ cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) {
+ ECalendarView *cal_view = cal_shell_content->priv->views[cal_shell_content->priv->current_view];
+
+ e_calendar_view_set_selected_time_range (cal_view,
+ cal_shell_content->priv->previous_selected_start_time,
+ cal_shell_content->priv->previous_selected_end_time);
+ }
+ }
+
+ cal_shell_content->priv->previous_selected_start_time = -1;
+ cal_shell_content->priv->previous_selected_end_time = -1;
+}
+
+static void
+cal_shell_content_display_view_cb (ECalShellContent *cal_shell_content,
+ GalView *gal_view)
+{
+ ECalViewKind view_kind;
+ GType gal_view_type;
+
+ gal_view_type = G_OBJECT_TYPE (gal_view);
+
+ if (gal_view_type == GAL_TYPE_VIEW_ETABLE) {
+ ECalendarView *calendar_view;
+
+ view_kind = E_CAL_VIEW_KIND_LIST;
+ calendar_view = cal_shell_content->priv->views[view_kind];
+ gal_view_etable_attach_table (
+ GAL_VIEW_ETABLE (gal_view),
+ E_CAL_LIST_VIEW (calendar_view)->table);
+
+ } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_DAY) {
+ view_kind = E_CAL_VIEW_KIND_DAY;
+
+ } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_WORK_WEEK) {
+ view_kind = E_CAL_VIEW_KIND_WORKWEEK;
+
+ } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_WEEK) {
+ view_kind = E_CAL_VIEW_KIND_WEEK;
+
+ } else if (gal_view_type == GAL_TYPE_VIEW_CALENDAR_MONTH) {
+ view_kind = E_CAL_VIEW_KIND_MONTH;
+
+ } else {
+ g_return_if_reached ();
+ }
+
+ e_cal_shell_content_set_current_view_id (cal_shell_content, view_kind);
+}
+
+static void
+cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content)
+{
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ GSettings *settings;
+ GtkWidget *paned;
+ const gchar *key;
+ const gchar *view_id;
+
+ settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+ paned = cal_shell_content->priv->hpaned;
+
+ shell_content = E_SHELL_CONTENT (cal_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ view_id = e_shell_view_get_view_id (shell_view);
+
+ if (view_id != NULL && strcmp (view_id, "Month_View") == 0)
+ key = "month-hpane-position";
+ else
+ key = "hpane-position";
+
+ g_settings_unbind (paned, "hposition");
+
+ g_settings_bind (
+ settings, key,
+ paned, "hposition",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_object_unref (settings);
+}
+
+static void
+cal_shell_content_is_editing_changed_cb (gpointer cal_view_tasks_memos_table,
+ GParamSpec *param,
+ EShellView *shell_view)
+{
+ g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
+
+ e_shell_view_update_actions (shell_view);
+}
+
+static gchar *
+cal_shell_content_get_pad_state_filename (EShellContent *shell_content,
+ ETable *table)
+{
+ EShellBackend *shell_backend;
+ EShellView *shell_view;
+ const gchar *config_dir, *nick = NULL;
+
+ g_return_val_if_fail (shell_content != NULL, NULL);
+ g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL);
+ g_return_val_if_fail (table != NULL, NULL);
+ g_return_val_if_fail (E_IS_TABLE (table), NULL);
+
+ if (E_IS_TASK_TABLE (table))
+ nick = "TaskPad";
+ else if (E_IS_MEMO_TABLE (table))
+ nick = "MemoPad";
+
+ g_return_val_if_fail (nick != NULL, NULL);
+
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ config_dir = e_shell_backend_get_config_dir (shell_backend);
+
+ return g_build_filename (config_dir, nick, NULL);
+}
+
+static void
+cal_shell_content_save_table_state (EShellContent *shell_content,
+ ETable *table)
+{
+ gchar *filename;
+
+ filename = cal_shell_content_get_pad_state_filename (
+ shell_content, table);
+ g_return_if_fail (filename != NULL);
+
+ e_table_save_state (table, filename);
+ g_free (filename);
+}
+
+static void
+cal_shell_content_load_table_state (EShellContent *shell_content,
+ ETable *table)
+{
+ gchar *filename;
+
+ filename = cal_shell_content_get_pad_state_filename (shell_content, table);
+ g_return_if_fail (filename != NULL);
+
+ e_table_load_state (table, filename);
+ g_free (filename);
+}
+
+static icalproperty *
+cal_shell_content_get_attendee_prop (icalcomponent *icalcomp,
+ const gchar *address)
+{
+ icalproperty *prop;
+
+ if (address == NULL || *address == '\0')
+ return NULL;
+
+ prop = icalcomponent_get_first_property (
+ icalcomp, ICAL_ATTENDEE_PROPERTY);
+
+ while (prop != NULL) {
+ const gchar *attendee;
+
+ attendee = icalproperty_get_attendee (prop);
+
+ if (g_str_equal (itip_strip_mailto (attendee), address))
+ return prop;
+
+ prop = icalcomponent_get_next_property (
+ icalcomp, ICAL_ATTENDEE_PROPERTY);
+ }
+
+ return NULL;
+}
+
+static gboolean
+cal_shell_content_icalcomp_is_delegated (icalcomponent *icalcomp,
+ const gchar *user_email)
+{
+ icalproperty *prop;
+ icalparameter *param;
+ const gchar *delto = NULL;
+ gboolean is_delegated = FALSE;
+
+ prop = cal_shell_content_get_attendee_prop (icalcomp, user_email);
+
+ if (prop != NULL) {
+ param = icalproperty_get_first_parameter (
+ prop, ICAL_DELEGATEDTO_PARAMETER);
+ if (param != NULL) {
+ delto = icalparameter_get_delegatedto (param);
+ delto = itip_strip_mailto (delto);
+ }
+ } else
+ return FALSE;
+
+ prop = cal_shell_content_get_attendee_prop (icalcomp, delto);
+
+ if (prop != NULL) {
+ const gchar *delfrom = NULL;
+ icalparameter_partstat status = ICAL_PARTSTAT_NONE;
+
+ param = icalproperty_get_first_parameter (
+ prop, ICAL_DELEGATEDFROM_PARAMETER);
+ if (param != NULL) {
+ delfrom = icalparameter_get_delegatedfrom (param);
+ delfrom = itip_strip_mailto (delfrom);
+ }
+ param = icalproperty_get_first_parameter (
+ prop, ICAL_PARTSTAT_PARAMETER);
+ if (param != NULL)
+ status = icalparameter_get_partstat (param);
+ is_delegated =
+ (status != ICAL_PARTSTAT_DECLINED) &&
+ (g_strcmp0 (delfrom, user_email) == 0);
+ }
+
+ return is_delegated;
+}
+
+static guint32
+cal_shell_content_check_state (EShellContent *shell_content)
+{
+ EShell *shell;
+ EShellView *shell_view;
+ EShellBackend *shell_backend;
+ ESourceRegistry *registry;
+ ECalShellContent *cal_shell_content;
+ ECalendarView *calendar_view;
+ gboolean selection_is_editable = FALSE;
+ gboolean selection_is_instance = FALSE;
+ gboolean selection_is_meeting = FALSE;
+ gboolean selection_is_organizer = FALSE;
+ gboolean selection_is_recurring = FALSE;
+ gboolean selection_can_delegate = FALSE;
+ guint32 state = 0;
+ GList *selected;
+ GList *link;
+ guint n_selected;
+
+ cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
+
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_backend = e_shell_view_get_shell_backend (shell_view);
+ shell = e_shell_backend_get_shell (shell_backend);
+ registry = e_shell_get_registry (shell);
+
+ calendar_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
+
+ selected = e_calendar_view_get_selected_events (calendar_view);
+ n_selected = g_list_length (selected);
+
+ /* If we have a selection, assume it's
+ * editable until we learn otherwise. */
+ if (n_selected > 0)
+ selection_is_editable = TRUE;
+
+ for (link = selected; link != NULL; link = g_list_next (link)) {
+ ECalendarViewEvent *event = link->data;
+ ECalClient *client;
+ ECalComponent *comp;
+ gchar *user_email;
+ icalcomponent *icalcomp;
+ const gchar *capability;
+ gboolean cap_delegate_supported;
+ gboolean cap_delegate_to_many;
+ gboolean icalcomp_is_delegated;
+ gboolean read_only;
+
+ if (!is_comp_data_valid (event))
+ continue;
+
+ client = event->comp_data->client;
+ icalcomp = event->comp_data->icalcomp;
+
+ read_only = e_client_is_readonly (E_CLIENT (client));
+ selection_is_editable &= !read_only;
+
+ selection_is_instance |=
+ e_cal_util_component_is_instance (icalcomp);
+
+ selection_is_meeting =
+ (n_selected == 1) &&
+ e_cal_util_component_has_attendee (icalcomp);
+
+ selection_is_recurring |=
+ e_cal_util_component_is_instance (icalcomp) ||
+ e_cal_util_component_has_recurrences (icalcomp);
+
+ /* XXX The rest of this is rather expensive and
+ * only applies if a single event is selected,
+ * so continue with the loop iteration if the
+ * rest of this is not applicable. */
+ if (n_selected > 1)
+ continue;
+
+ /* XXX This probably belongs in comp-util.c. */
+
+ comp = e_cal_component_new ();
+ e_cal_component_set_icalcomponent (
+ comp, icalcomponent_new_clone (icalcomp));
+ user_email = itip_get_comp_attendee (
+ registry, comp, client);
+
+ selection_is_organizer =
+ e_cal_util_component_has_organizer (icalcomp) &&
+ itip_organizer_is_user (registry, comp, client);
+
+ capability = CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED;
+ cap_delegate_supported =
+ e_client_check_capability (
+ E_CLIENT (client), capability);
+
+ capability = CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY;
+ cap_delegate_to_many =
+ e_client_check_capability (
+ E_CLIENT (client), capability);
+
+ icalcomp_is_delegated =
+ (user_email != NULL) &&
+ cal_shell_content_icalcomp_is_delegated (
+ icalcomp, user_email);
+
+ selection_can_delegate =
+ cap_delegate_supported &&
+ (cap_delegate_to_many ||
+ (!selection_is_organizer &&
+ !icalcomp_is_delegated));
+
+ g_free (user_email);
+ g_object_unref (comp);
+ }
+
+ g_list_free (selected);
+
+ if (n_selected == 1)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_SINGLE;
+ if (n_selected > 1)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_MULTIPLE;
+ if (selection_is_editable)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_EDITABLE;
+ if (selection_is_instance)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_INSTANCE;
+ if (selection_is_meeting)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_MEETING;
+ if (selection_is_organizer)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_ORGANIZER;
+ if (selection_is_recurring)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_IS_RECURRING;
+ if (selection_can_delegate)
+ state |= E_CAL_BASE_SHELL_CONTENT_SELECTION_CAN_DELEGATE;
+
+ return state;
+}
+
+static void
+cal_shell_content_focus_search_results (EShellContent *shell_content)
+{
+ ECalendarView *calendar_view;
+
+ calendar_view = e_cal_shell_content_get_current_calendar_view (E_CAL_SHELL_CONTENT (shell_content));
+
+ gtk_widget_grab_focus (GTK_WIDGET (calendar_view));
+}
+
+static time_t
+cal_shell_content_get_default_time (ECalModel *model,
+ gpointer user_data)
+{
+ ECalShellContent *cal_shell_content = user_data;
+ icaltimezone *zone;
+
+ g_return_val_if_fail (model != NULL, 0);
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), 0);
+
+ if (e_cal_shell_content_get_current_view_id (cal_shell_content) != E_CAL_VIEW_KIND_LIST) {
+ ECalendarView *cal_view;
+ time_t selected_start = (time_t) 0, selected_end = (time_t) 0;
+
+ cal_view = e_cal_shell_content_get_current_calendar_view (cal_shell_content);
+
+ if (cal_view && e_calendar_view_get_selected_time_range (cal_view, &selected_start, &selected_end))
+ return selected_start;
+ }
+
+ zone = e_cal_model_get_timezone (model);
+
+ return icaltime_as_timet_with_zone (icaltime_current_time_with_zone (zone), zone);
+}
+
+static void
+update_adjustment (ECalShellContent *cal_shell_content,
+ GtkAdjustment *adjustment,
+ EWeekView *week_view,
+ gboolean move_by_week)
+{
+ GDate start_date, end_date;
+ GDate first_day_shown;
+ ECalModel *model;
+ gint week_offset;
+ struct icaltimetype start_tt = icaltime_null_time ();
+ time_t lower;
+ guint32 old_first_day_julian, new_first_day_julian;
+ icaltimezone *timezone;
+ gdouble value;
+
+ e_week_view_get_first_day_shown (week_view, &first_day_shown);
+
+ /* If we don't have a valid date set yet, just return. */
+ if (!g_date_valid (&first_day_shown))
+ return;
+
+ value = gtk_adjustment_get_value (adjustment);
+
+ /* Determine the first date shown. */
+ start_date = week_view->base_date;
+ week_offset = floor (value + 0.5);
+ g_date_add_days (&start_date, week_offset * 7);
+
+ /* Convert the old & new first days shown to julian values. */
+ old_first_day_julian = g_date_get_julian (&first_day_shown);
+ new_first_day_julian = g_date_get_julian (&start_date);
+
+ /* If we are already showing the date, just return. */
+ if (old_first_day_julian == new_first_day_julian)
+ return;
+
+ /* Convert it to a time_t. */
+ start_tt.year = g_date_get_year (&start_date);
+ start_tt.month = g_date_get_month (&start_date);
+ start_tt.day = g_date_get_day (&start_date);
+
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ timezone = e_cal_model_get_timezone (model);
+ lower = icaltime_as_timet_with_zone (start_tt, timezone);
+
+ end_date = start_date;
+ if (move_by_week) {
+ g_date_add_days (&end_date, 7 - 1);
+ } else {
+ g_date_add_days (&end_date, 7 * e_week_view_get_weeks_shown (week_view) - 1);
+ }
+
+ e_week_view_set_update_base_date (week_view, FALSE);
+ e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &start_date, &end_date, FALSE);
+ e_calendar_view_set_selected_time_range (E_CALENDAR_VIEW (week_view), lower, lower);
+ e_week_view_set_update_base_date (week_view, TRUE);
+}
+
+static void
+week_view_adjustment_changed_cb (GtkAdjustment *adjustment,
+ ECalShellContent *cal_shell_content)
+{
+ ECalendarView *view;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ view = cal_shell_content->priv->views[E_CAL_VIEW_KIND_WEEK];
+ update_adjustment (cal_shell_content, adjustment, E_WEEK_VIEW (view), TRUE);
+}
+
+static void
+month_view_adjustment_changed_cb (GtkAdjustment *adjustment,
+ ECalShellContent *cal_shell_content)
+{
+ ECalendarView *view;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ view = cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH];
+ update_adjustment (cal_shell_content, adjustment, E_WEEK_VIEW (view), FALSE);
+}
+
+static void
+cal_shell_content_notify_work_day_cb (ECalModel *model,
+ GParamSpec *param,
+ ECalShellContent *cal_shell_content)
+{
+ GDateWeekday work_day_first, work_day_last;
+
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ if (cal_shell_content->priv->current_view != E_CAL_VIEW_KIND_WORKWEEK)
+ return;
+
+ work_day_first = e_cal_model_get_work_day_first (model);
+ work_day_last = e_cal_model_get_work_day_last (model);
+
+ if (work_day_first == g_date_get_weekday (&cal_shell_content->priv->view_start) &&
+ work_day_last == g_date_get_weekday (&cal_shell_content->priv->view_end))
+ return;
+
+ cal_shell_content->priv->previous_selected_start_time = -1;
+ cal_shell_content->priv->previous_selected_end_time = -1;
+
+ /* This makes sure that the selection in the datepicker corresponds
+ to the time range used in the Work Week view */
+ cal_shell_content_current_view_id_changed_cb (cal_shell_content);
+}
+
+static void
+cal_shell_content_notify_week_start_day_cb (ECalModel *model,
+ GParamSpec *param,
+ ECalShellContent *cal_shell_content)
+{
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ cal_shell_content->priv->previous_selected_start_time = -1;
+ cal_shell_content->priv->previous_selected_end_time = -1;
+
+ /* This makes sure that the selection in the datepicker corresponds
+ to the time range used in the current view */
+ cal_shell_content_current_view_id_changed_cb (cal_shell_content);
+}
+
+static void
+cal_shell_content_move_view_range_cb (ECalendarView *cal_view,
+ ECalendarViewMoveType move_type,
+ gint64 exact_date,
+ ECalShellContent *cal_shell_content)
+{
+ g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ if (!cal_view->in_focus)
+ return;
+
+ e_cal_shell_content_move_view_range (cal_shell_content, move_type, (time_t) exact_date);
+}
+
+static void
+cal_shell_content_foreign_client_opened_cb (ECalBaseShellSidebar *cal_base_shell_sidebar,
+ ECalClient *client,
+ ECalModel *model)
+{
+ g_return_if_fail (E_IS_CAL_CLIENT (client));
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+
+ e_cal_data_model_add_client (e_cal_model_get_data_model (model), client);
+}
+
+static void
+cal_shell_content_foreign_client_closed_cb (ECalBaseShellSidebar *cal_base_shell_sidebar,
+ ESource *source,
+ ECalModel *model)
+{
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+
+ e_cal_data_model_remove_client (e_cal_model_get_data_model (model), e_source_get_uid (source));
+}
+
+static void
+cal_shell_content_setup_foreign_sources (EShellWindow *shell_window,
+ const gchar *view_name,
+ const gchar *extension_name,
+ ECalModel *model)
+{
+ EShellSidebar *foreign_sidebar;
+ EShellContent *foreign_content;
+ EShellView *foreign_view;
+ ECalModel *foreign_model;
+ gboolean is_new_view;
+
+ g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+
+ is_new_view = e_shell_window_peek_shell_view (shell_window, view_name) == NULL;
+
+ foreign_view = e_shell_window_get_shell_view (shell_window, view_name);
+ g_return_if_fail (E_IS_SHELL_VIEW (foreign_view));
+
+ foreign_sidebar = e_shell_view_get_shell_sidebar (foreign_view);
+ g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar));
+
+ if (is_new_view) {
+ /* Preselect default source, when the view was not created yet */
+ ESourceSelector *source_selector;
+ ESourceRegistry *registry;
+ ESource *source;
+
+ source_selector = e_cal_base_shell_sidebar_get_selector (E_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar));
+ registry = e_source_selector_get_registry (source_selector);
+ source = e_source_registry_ref_default_for_extension_name (registry, extension_name);
+
+ if (source)
+ e_source_selector_set_primary_selection (source_selector, source);
+
+ g_clear_object (&source);
+ }
+
+ g_signal_connect_object (foreign_sidebar, "client-opened",
+ G_CALLBACK (cal_shell_content_foreign_client_opened_cb), model, 0);
+ g_signal_connect_object (foreign_sidebar, "client-closed",
+ G_CALLBACK (cal_shell_content_foreign_client_closed_cb), model, 0);
+
+ foreign_content = e_shell_view_get_shell_content (foreign_view);
+ foreign_model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (foreign_content));
+
+ e_binding_bind_property (
+ foreign_model, "default-source-uid",
+ model, "default-source-uid",
+ G_BINDING_SYNC_CREATE);
+
+ g_signal_connect_object (model, "row-appended",
+ G_CALLBACK (e_cal_base_shell_view_model_row_appended), foreign_view, G_CONNECT_SWAPPED);
+
+ /* This makes sure that the local models for memos and tasks
+ in the calendar view get populated with the same sources
+ as those in the respective views. */
+
+ e_cal_base_shell_sidebar_ensure_sources_open (E_CAL_BASE_SHELL_SIDEBAR (foreign_sidebar));
+}
+
+static void
+cal_shell_content_view_created (ECalBaseShellContent *cal_base_shell_content)
+{
+ ECalShellContent *cal_shell_content;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShellSidebar *shell_sidebar;
+ GalViewInstance *view_instance;
+ ECalendar *calendar;
+ ECalModel *model;
+ ECalDataModel *data_model;
+ GDate date;
+ time_t today;
+
+ cal_shell_content = E_CAL_SHELL_CONTENT (cal_base_shell_content);
+ cal_shell_content->priv->current_view = E_CAL_VIEW_KIND_DAY;
+
+ today = time (NULL);
+ g_date_clear (&date, 1);
+ g_date_set_time_t (&date, today);
+
+ shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+
+ calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+ g_return_if_fail (E_IS_CALENDAR (calendar));
+
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ e_calendar_item_set_selection (e_calendar_get_item (calendar), &date, &date);
+ e_cal_model_set_time_range (model, today, today);
+
+ /* Show everything known by default in the task and memo pads */
+ e_cal_model_set_time_range (cal_shell_content->priv->memo_model, 0, 0);
+ e_cal_model_set_time_range (cal_shell_content->priv->task_model, 0, 0);
+
+ cal_shell_content->priv->datepicker_selection_changed_id =
+ g_signal_connect (e_calendar_get_item (calendar), "selection-changed",
+ G_CALLBACK (cal_shell_content_datepicker_selection_changed_cb), cal_shell_content);
+ cal_shell_content->priv->datepicker_range_moved_id =
+ g_signal_connect (e_calendar_get_item (calendar), "date-range-moved",
+ G_CALLBACK (cal_shell_content_datepicker_range_moved_cb), cal_shell_content);
+
+ g_signal_connect_after (calendar, "button-press-event",
+ G_CALLBACK (cal_shell_content_datepicker_button_press_cb), cal_shell_content);
+
+ data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ cal_shell_content->priv->tag_calendar = e_tag_calendar_new (calendar);
+ e_tag_calendar_subscribe (cal_shell_content->priv->tag_calendar, data_model);
+
+ /* Intentionally not using e_signal_connect_notify() here, no need to filter
+ out "false" notifications, it's dealt with them in another way */
+ cal_shell_content->priv->current_view_id_changed_id = g_signal_connect (
+ cal_shell_content, "notify::current-view-id",
+ G_CALLBACK (cal_shell_content_current_view_id_changed_cb), NULL);
+
+ /* List of selected Task/Memo sources is taken from respective views,
+ which are loaded if necessary. */
+ cal_shell_content_setup_foreign_sources (shell_window, "memos", E_SOURCE_EXTENSION_MEMO_LIST,
+ cal_shell_content->priv->memo_model);
+
+ cal_shell_content_setup_foreign_sources (shell_window, "tasks", E_SOURCE_EXTENSION_TASK_LIST,
+ cal_shell_content->priv->task_model);
+
+ /* Finally load the view instance */
+ view_instance = e_shell_view_get_view_instance (shell_view);
+ gal_view_instance_load (view_instance);
+
+ /* Keep the toolbar view buttons in sync with the calendar. */
+ e_binding_bind_property (
+ cal_shell_content, "current-view-id",
+ ACTION (CALENDAR_VIEW_DAY), "current-value",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-monday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-tuesday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-wednesday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-thursday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-friday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-saturday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::work-day-sunday",
+ G_CALLBACK (cal_shell_content_notify_work_day_cb), cal_shell_content);
+
+ e_signal_connect_notify (
+ model, "notify::week-start-day",
+ G_CALLBACK (cal_shell_content_notify_week_start_day_cb), cal_shell_content);
+}
+
+static void
+e_cal_shell_content_create_calendar_views (ECalShellContent *cal_shell_content)
+{
+ EShellView *shell_view;
+ ECalModel *model;
+ ECalendarView *calendar_view;
+ GtkAdjustment *adjustment;
+ time_t today;
+ gint ii;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (cal_shell_content->priv->calendar_notebook != NULL);
+ g_return_if_fail (cal_shell_content->priv->views[0] == NULL);
+
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+
+ /* Day View */
+ calendar_view = e_day_view_new (model);
+ cal_shell_content->priv->views[E_CAL_VIEW_KIND_DAY] = calendar_view;
+ g_object_ref_sink (calendar_view);
+
+ /* Work Week View */
+ calendar_view = e_day_view_new (model);
+ e_day_view_set_work_week_view (E_DAY_VIEW (calendar_view), TRUE);
+ e_day_view_set_days_shown (E_DAY_VIEW (calendar_view), 5);
+ cal_shell_content->priv->views[E_CAL_VIEW_KIND_WORKWEEK] = calendar_view;
+ g_object_ref_sink (calendar_view);
+
+ /* Week View */
+ calendar_view = e_week_view_new (model);
+ cal_shell_content->priv->views[E_CAL_VIEW_KIND_WEEK] = calendar_view;
+ g_object_ref_sink (calendar_view);
+
+ adjustment = gtk_range_get_adjustment (
+ GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar));
+ g_signal_connect (
+ adjustment, "value-changed",
+ G_CALLBACK (week_view_adjustment_changed_cb), cal_shell_content);
+
+ /* Month View */
+ calendar_view = e_month_view_new (model);
+ e_week_view_set_multi_week_view (E_WEEK_VIEW (calendar_view), TRUE);
+ e_week_view_set_weeks_shown (E_WEEK_VIEW (calendar_view), 6);
+ cal_shell_content->priv->views[E_CAL_VIEW_KIND_MONTH] = calendar_view;
+ g_object_ref_sink (calendar_view);
+
+ adjustment = gtk_range_get_adjustment (
+ GTK_RANGE (E_WEEK_VIEW (calendar_view)->vscrollbar));
+ g_signal_connect (
+ adjustment, "value-changed",
+ G_CALLBACK (month_view_adjustment_changed_cb), cal_shell_content);
+
+ /* List View */
+ calendar_view = e_cal_list_view_new (model);
+ cal_shell_content->priv->views[E_CAL_VIEW_KIND_LIST] = calendar_view;
+ g_object_ref_sink (calendar_view);
+
+ shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
+ today = time (NULL);
+
+ for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
+ calendar_view = cal_shell_content->priv->views[ii];
+
+ calendar_view->in_focus = ii == cal_shell_content->priv->current_view;
+
+ e_calendar_view_set_selected_time_range (calendar_view, today, today);
+
+ e_signal_connect_notify (
+ calendar_view, "notify::is-editing",
+ G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view);
+
+ g_signal_connect (
+ calendar_view, "move-view-range",
+ G_CALLBACK (cal_shell_content_move_view_range_cb), cal_shell_content);
+
+ gtk_notebook_append_page (
+ GTK_NOTEBOOK (cal_shell_content->priv->calendar_notebook),
+ GTK_WIDGET (calendar_view), NULL);
+ gtk_widget_show (GTK_WIDGET (calendar_view));
+ }
+}
+
+static void
+cal_shell_content_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CURRENT_VIEW_ID:
+ e_cal_shell_content_set_current_view_id (E_CAL_SHELL_CONTENT (object),
+ g_value_get_int (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_shell_content_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CALENDAR_NOTEBOOK:
+ g_value_set_object (
+ value, e_cal_shell_content_get_calendar_notebook (
+ E_CAL_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_MEMO_TABLE:
+ g_value_set_object (
+ value, e_cal_shell_content_get_memo_table (
+ E_CAL_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_TASK_TABLE:
+ g_value_set_object (
+ value, e_cal_shell_content_get_task_table (
+ E_CAL_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_CURRENT_VIEW_ID:
+ g_value_set_int (value,
+ e_cal_shell_content_get_current_view_id (E_CAL_SHELL_CONTENT (object)));
+ return;
+
+ case PROP_CURRENT_VIEW:
+ g_value_set_object (value,
+ e_cal_shell_content_get_current_calendar_view (E_CAL_SHELL_CONTENT (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_shell_content_dispose (GObject *object)
+{
+ ECalShellContent *cal_shell_content = E_CAL_SHELL_CONTENT (object);
+ gint ii;
+
+ if (cal_shell_content->priv->task_data_model) {
+ e_cal_data_model_set_disposing (cal_shell_content->priv->task_data_model, TRUE);
+ e_cal_data_model_unsubscribe (cal_shell_content->priv->task_data_model,
+ E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->task_model));
+ }
+
+ if (cal_shell_content->priv->memo_data_model) {
+ e_cal_data_model_set_disposing (cal_shell_content->priv->memo_data_model, TRUE);
+ e_cal_data_model_unsubscribe (cal_shell_content->priv->memo_data_model,
+ E_CAL_DATA_MODEL_SUBSCRIBER (cal_shell_content->priv->memo_model));
+ }
+
+ if (cal_shell_content->priv->tag_calendar) {
+ ECalDataModel *data_model;
+
+ data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ e_cal_data_model_set_disposing (data_model, TRUE);
+ e_tag_calendar_unsubscribe (cal_shell_content->priv->tag_calendar, data_model);
+ g_clear_object (&cal_shell_content->priv->tag_calendar);
+ }
+
+ for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
+ g_clear_object (&(cal_shell_content->priv->views[ii]));
+ }
+
+ g_clear_object (&cal_shell_content->priv->hpaned);
+ g_clear_object (&cal_shell_content->priv->vpaned);
+ g_clear_object (&cal_shell_content->priv->calendar_notebook);
+ g_clear_object (&cal_shell_content->priv->task_table);
+ g_clear_object (&cal_shell_content->priv->task_model);
+ g_clear_object (&cal_shell_content->priv->task_data_model);
+ g_clear_object (&cal_shell_content->priv->memo_table);
+ g_clear_object (&cal_shell_content->priv->memo_model);
+ g_clear_object (&cal_shell_content->priv->memo_data_model);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_cal_shell_content_parent_class)->dispose (object);
+}
+
+static void
+cal_shell_content_constructed (GObject *object)
+{
+ ECalShellContent *cal_shell_content;
+ EShellContent *shell_content;
+ EShellView *shell_view;
+ EShellWindow *shell_window;
+ EShell *shell;
+ GalViewInstance *view_instance;
+ GSettings *settings;
+ GtkWidget *container;
+ GtkWidget *widget;
+ gchar *markup;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_cal_shell_content_parent_class)->constructed (object);
+
+ cal_shell_content = E_CAL_SHELL_CONTENT (object);
+ shell_content = E_SHELL_CONTENT (cal_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ shell_window = e_shell_view_get_shell_window (shell_view);
+ shell = e_shell_window_get_shell (shell_window);
+
+ cal_shell_content->priv->memo_data_model =
+ e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ cal_shell_content->priv->memo_model =
+ e_cal_model_memos_new (cal_shell_content->priv->memo_data_model, e_shell_get_registry (shell), shell);
+
+ cal_shell_content->priv->task_data_model =
+ e_cal_base_shell_content_create_new_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ cal_shell_content->priv->task_model =
+ e_cal_model_tasks_new (cal_shell_content->priv->task_data_model, e_shell_get_registry (shell), shell);
+
+ e_binding_bind_property (
+ cal_shell_content->priv->memo_model, "timezone",
+ cal_shell_content->priv->memo_data_model, "timezone",
+ G_BINDING_SYNC_CREATE);
+
+ e_binding_bind_property (
+ cal_shell_content->priv->task_model, "timezone",
+ cal_shell_content->priv->task_data_model, "timezone",
+ G_BINDING_SYNC_CREATE);
+
+ /* Build content widgets. */
+
+ container = GTK_WIDGET (object);
+
+ widget = e_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ cal_shell_content->priv->hpaned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = cal_shell_content->priv->hpaned;
+
+ widget = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
+ gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
+ cal_shell_content->priv->calendar_notebook = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ widget = e_paned_new (GTK_ORIENTATION_VERTICAL);
+ e_paned_set_fixed_resize (E_PANED (widget), FALSE);
+ gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, TRUE);
+ cal_shell_content->priv->vpaned = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ container = cal_shell_content->priv->calendar_notebook;
+
+ e_cal_shell_content_create_calendar_views (cal_shell_content);
+
+ e_binding_bind_property (
+ cal_shell_content, "current-view-id",
+ cal_shell_content->priv->calendar_notebook, "page",
+ G_BINDING_SYNC_CREATE);
+
+ container = cal_shell_content->priv->vpaned;
+
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new (NULL);
+ markup = g_strdup_printf ("%s", _("Tasks"));
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_task_table_new (shell_view, cal_shell_content->priv->task_model);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ cal_shell_content->priv->task_table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ cal_shell_content_load_table_state (shell_content, E_TABLE (widget));
+
+ g_signal_connect_swapped (
+ widget, "open-component",
+ G_CALLBACK (e_cal_shell_view_taskpad_open_task),
+ shell_view);
+
+ e_signal_connect_notify (
+ widget, "notify::is-editing",
+ G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view);
+
+ container = cal_shell_content->priv->vpaned;
+
+ widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, TRUE);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_label_new (NULL);
+ markup = g_strdup_printf ("%s", _("Memos"));
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+ g_free (markup);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = e_memo_table_new (shell_view, cal_shell_content->priv->memo_model);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ cal_shell_content->priv->memo_table = g_object_ref (widget);
+ gtk_widget_show (widget);
+
+ cal_shell_content_load_table_state (shell_content, E_TABLE (widget));
+
+ e_cal_model_set_default_time_func (cal_shell_content->priv->memo_model, cal_shell_content_get_default_time, cal_shell_content);
+
+ g_signal_connect_swapped (
+ widget, "open-component",
+ G_CALLBACK (e_cal_shell_view_memopad_open_memo),
+ shell_view);
+
+ e_signal_connect_notify (
+ widget, "notify::is-editing",
+ G_CALLBACK (cal_shell_content_is_editing_changed_cb), shell_view);
+
+ /* Prepare the view instance. */
+
+ view_instance = e_shell_view_new_view_instance (shell_view, NULL);
+ g_signal_connect_swapped (
+ view_instance, "display-view",
+ G_CALLBACK (cal_shell_content_display_view_cb),
+ object);
+ /* Actual load happens at cal_shell_content_view_created() */
+ e_shell_view_set_view_instance (shell_view, view_instance);
+ g_object_unref (view_instance);
+
+ e_signal_connect_notify_swapped (
+ shell_view, "notify::view-id",
+ G_CALLBACK (cal_shell_content_notify_view_id_cb),
+ cal_shell_content);
+
+ settings = e_util_ref_settings ("org.gnome.evolution.calendar");
+
+ g_settings_bind (
+ settings, "tag-vpane-position",
+ cal_shell_content->priv->vpaned, "proportion",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_object_unref (settings);
+
+ /* Cannot access shell sidebar here, thus rely on cal_shell_content_view_created()
+ with exact widget settings which require it */
+}
+
+static void
+e_cal_shell_content_class_init (ECalShellContentClass *class)
+{
+ GObjectClass *object_class;
+ EShellContentClass *shell_content_class;
+ ECalBaseShellContentClass *cal_base_shell_content_class;
+
+ g_type_class_add_private (class, sizeof (ECalShellContentPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = cal_shell_content_set_property;
+ object_class->get_property = cal_shell_content_get_property;
+ object_class->dispose = cal_shell_content_dispose;
+ object_class->constructed = cal_shell_content_constructed;
+
+ shell_content_class = E_SHELL_CONTENT_CLASS (class);
+ shell_content_class->check_state = cal_shell_content_check_state;
+ shell_content_class->focus_search_results = cal_shell_content_focus_search_results;
+
+ cal_base_shell_content_class = E_CAL_BASE_SHELL_CONTENT_CLASS (class);
+ cal_base_shell_content_class->new_cal_model = e_cal_model_calendar_new;
+ cal_base_shell_content_class->view_created = cal_shell_content_view_created;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CALENDAR_NOTEBOOK,
+ g_param_spec_object (
+ "calendar-notebook",
+ NULL,
+ NULL,
+ GTK_TYPE_NOTEBOOK,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MEMO_TABLE,
+ g_param_spec_object (
+ "memo-table",
+ NULL,
+ NULL,
+ E_TYPE_MEMO_TABLE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_TASK_TABLE,
+ g_param_spec_object (
+ "task-table",
+ NULL,
+ NULL,
+ E_TYPE_TASK_TABLE,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURRENT_VIEW_ID,
+ g_param_spec_int (
+ "current-view-id",
+ "Current Calendar View ID",
+ NULL,
+ E_CAL_VIEW_KIND_DAY,
+ E_CAL_VIEW_KIND_LAST - 1,
+ E_CAL_VIEW_KIND_DAY,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_CURRENT_VIEW,
+ g_param_spec_object (
+ "current-view",
+ "Current Calendar View",
+ NULL,
+ E_TYPE_CALENDAR_VIEW,
+ G_PARAM_READABLE));
+}
+
+static void
+e_cal_shell_content_class_finalize (ECalShellContentClass *class)
+{
+}
+
+static void
+e_cal_shell_content_init (ECalShellContent *cal_shell_content)
+{
+ time_t now;
+
+ cal_shell_content->priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (cal_shell_content);
+ g_date_clear (&cal_shell_content->priv->view_start, 1);
+ g_date_clear (&cal_shell_content->priv->view_end, 1);
+ g_date_clear (&cal_shell_content->priv->last_range_start, 1);
+
+ now = time (NULL);
+ g_date_set_time_t (&cal_shell_content->priv->view_start, now);
+ g_date_set_time_t (&cal_shell_content->priv->view_end, now);
+
+ cal_shell_content->priv->view_start_range_day_offset = (guint32) -1;
+ cal_shell_content->priv->previous_selected_start_time = -1;
+ cal_shell_content->priv->previous_selected_end_time = -1;
+}
+
+void
+e_cal_shell_content_type_register (GTypeModule *type_module)
+{
+ /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+ * function, so we have to wrap it with a public function in
+ * order to register types from a separate compilation unit. */
+ e_cal_shell_content_register_type (type_module);
+}
+
+GtkWidget *
+e_cal_shell_content_new (EShellView *shell_view)
+{
+ g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
+
+ return g_object_new (
+ E_TYPE_CAL_SHELL_CONTENT,
+ "shell-view", shell_view, NULL);
+}
+
+GtkNotebook *
+e_cal_shell_content_get_calendar_notebook (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return GTK_NOTEBOOK (cal_shell_content->priv->calendar_notebook);
+}
+
+EMemoTable *
+e_cal_shell_content_get_memo_table (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return E_MEMO_TABLE (cal_shell_content->priv->memo_table);
+}
+
+ETaskTable *
+e_cal_shell_content_get_task_table (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return E_TASK_TABLE (cal_shell_content->priv->task_table);
+}
+
+EShellSearchbar *
+e_cal_shell_content_get_searchbar (ECalShellContent *cal_shell_content)
+{
+ EShellView *shell_view;
+ EShellContent *shell_content;
+ GtkWidget *widget;
+
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ shell_content = E_SHELL_CONTENT (cal_shell_content);
+ shell_view = e_shell_content_get_shell_view (shell_content);
+ widget = e_shell_view_get_searchbar (shell_view);
+
+ return E_SHELL_SEARCHBAR (widget);
+}
+
+static void
+cal_shell_content_resubscribe (ECalendarView *cal_view,
+ ECalModel *model)
+{
+ ECalDataModel *data_model;
+ ECalDataModelSubscriber *subscriber;
+ time_t range_start, range_end;
+ gboolean is_tasks_or_memos;
+
+ g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+
+ data_model = e_cal_model_get_data_model (model);
+ subscriber = E_CAL_DATA_MODEL_SUBSCRIBER (model);
+ is_tasks_or_memos = e_cal_model_get_component_kind (model) == ICAL_VJOURNAL_COMPONENT ||
+ e_cal_model_get_component_kind (model) == ICAL_VTODO_COMPONENT;
+
+ if ((!is_tasks_or_memos && e_calendar_view_get_visible_time_range (cal_view, &range_start, &range_end)) ||
+ e_cal_data_model_get_subscriber_range (data_model, subscriber, &range_start, &range_end)) {
+ e_cal_data_model_unsubscribe (data_model, subscriber);
+ e_cal_model_remove_all_objects (model);
+
+ if (is_tasks_or_memos)
+ e_cal_data_model_subscribe (data_model, subscriber, range_start, range_end);
+ }
+}
+
+void
+e_cal_shell_content_set_current_view_id (ECalShellContent *cal_shell_content,
+ ECalViewKind view_kind)
+{
+ time_t start_time = -1, end_time = -1;
+ gint ii;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (view_kind >= E_CAL_VIEW_KIND_DAY && view_kind < E_CAL_VIEW_KIND_LAST);
+
+ if (cal_shell_content->priv->current_view == view_kind)
+ return;
+
+ if (cal_shell_content->priv->current_view >= E_CAL_VIEW_KIND_DAY &&
+ cal_shell_content->priv->current_view < E_CAL_VIEW_KIND_LAST) {
+ ECalendarView *cal_view = cal_shell_content->priv->views[cal_shell_content->priv->current_view];
+
+ if (!e_calendar_view_get_selected_time_range (cal_view, &start_time, &end_time)) {
+ start_time = -1;
+ end_time = -1;
+ }
+ }
+
+ cal_shell_content->priv->previous_selected_start_time = start_time;
+ cal_shell_content->priv->previous_selected_end_time = end_time;
+
+ for (ii = 0; ii < E_CAL_VIEW_KIND_LAST; ii++) {
+ ECalendarView *cal_view = cal_shell_content->priv->views[ii];
+ gboolean in_focus = ii == view_kind;
+ gboolean focus_changed;
+
+ if (!cal_view) {
+ g_warn_if_reached ();
+ continue;
+ }
+
+ focus_changed = (cal_view->in_focus ? 1 : 0) != (in_focus ? 1 : 0);
+
+ cal_view->in_focus = in_focus;
+
+ if (focus_changed && in_focus) {
+ /* Currently focused view changed. Any events within the common time
+ range are not shown in the newly focused view, thus make sure it'll
+ contain all what it should have. */
+ ECalModel *model;
+
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+
+ /* This may not cause any queries to backends with events,
+ because the time range should be always within the one
+ shown in the date picker. */
+ cal_shell_content_resubscribe (cal_view, model);
+
+ if (cal_shell_content->priv->task_table) {
+ ETaskTable *task_table;
+
+ task_table = E_TASK_TABLE (cal_shell_content->priv->task_table);
+ cal_shell_content_resubscribe (cal_view, e_task_table_get_model (task_table));
+ }
+
+ if (cal_shell_content->priv->memo_table) {
+ EMemoTable *memo_table;
+
+ memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table);
+ cal_shell_content_resubscribe (cal_view, e_memo_table_get_model (memo_table));
+ }
+ }
+ }
+
+ cal_shell_content->priv->current_view = view_kind;
+
+ g_object_notify (G_OBJECT (cal_shell_content), "current-view-id");
+
+ gtk_widget_queue_draw (GTK_WIDGET (cal_shell_content->priv->views[cal_shell_content->priv->current_view]));
+}
+
+ECalViewKind
+e_cal_shell_content_get_current_view_id (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), E_CAL_VIEW_KIND_LAST);
+
+ return cal_shell_content->priv->current_view;
+}
+
+ECalendarView *
+e_cal_shell_content_get_calendar_view (ECalShellContent *cal_shell_content,
+ ECalViewKind view_kind)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+ g_return_val_if_fail (view_kind >= E_CAL_VIEW_KIND_DAY && view_kind < E_CAL_VIEW_KIND_LAST, NULL);
+
+ return cal_shell_content->priv->views[view_kind];
+}
+
+ECalendarView *
+e_cal_shell_content_get_current_calendar_view (ECalShellContent *cal_shell_content)
+{
+ g_return_val_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
+
+ return e_cal_shell_content_get_calendar_view (cal_shell_content,
+ e_cal_shell_content_get_current_view_id (cal_shell_content));
+}
+
+void
+e_cal_shell_content_save_state (ECalShellContent *cal_shell_content)
+{
+ ECalShellContentPrivate *priv;
+
+ g_return_if_fail (cal_shell_content != NULL);
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ priv = cal_shell_content->priv;
+
+ if (priv->task_table != NULL)
+ cal_shell_content_save_table_state (
+ E_SHELL_CONTENT (cal_shell_content),
+ E_TABLE (priv->task_table));
+
+ if (priv->memo_table != NULL)
+ cal_shell_content_save_table_state (
+ E_SHELL_CONTENT (cal_shell_content),
+ E_TABLE (priv->memo_table));
+}
+
+void
+e_cal_shell_content_get_current_range (ECalShellContent *cal_shell_content,
+ time_t *range_start,
+ time_t *range_end)
+{
+ icaltimezone *zone;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (range_start != NULL);
+ g_return_if_fail (range_end != NULL);
+
+ zone = e_cal_data_model_get_timezone (e_cal_base_shell_content_get_data_model (
+ E_CAL_BASE_SHELL_CONTENT (cal_shell_content)));
+
+ *range_start = cal_comp_gdate_to_timet (&(cal_shell_content->priv->view_start), zone);
+ *range_end = cal_comp_gdate_to_timet (&(cal_shell_content->priv->view_end), zone);
+}
+
+void
+e_cal_shell_content_get_current_range_dates (ECalShellContent *cal_shell_content,
+ GDate *range_start,
+ GDate *range_end)
+{
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (range_start != NULL);
+ g_return_if_fail (range_end != NULL);
+
+ *range_start = cal_shell_content->priv->view_start;
+ *range_end = cal_shell_content->priv->view_end;
+}
+
+static void
+cal_shell_content_move_view_range_relative (ECalShellContent *cal_shell_content,
+ gint direction)
+{
+ GDate start, end;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+ g_return_if_fail (direction != 0);
+
+ start = cal_shell_content->priv->view_start;
+ end = cal_shell_content->priv->view_end;
+
+ switch (cal_shell_content->priv->current_view) {
+ case E_CAL_VIEW_KIND_DAY:
+ if (direction > 0) {
+ g_date_add_days (&start, direction);
+ g_date_add_days (&end, direction);
+ } else {
+ g_date_subtract_days (&start, direction * -1);
+ g_date_subtract_days (&end, direction * -1);
+ }
+ break;
+ case E_CAL_VIEW_KIND_WORKWEEK:
+ case E_CAL_VIEW_KIND_WEEK:
+ if (direction > 0) {
+ g_date_add_days (&start, direction * 7);
+ g_date_add_days (&end, direction * 7);
+ } else {
+ g_date_subtract_days (&start, direction * -7);
+ g_date_subtract_days (&end, direction * -7);
+ }
+ break;
+ case E_CAL_VIEW_KIND_MONTH:
+ case E_CAL_VIEW_KIND_LIST:
+ if (g_date_get_day (&start) != 1) {
+ g_date_add_months (&start, 1);
+ g_date_set_day (&start, 1);
+ }
+ if (direction > 0)
+ g_date_add_months (&start, direction);
+ else
+ g_date_subtract_months (&start, direction * -1);
+ end = start;
+ g_date_set_day (&end, g_date_get_days_in_month (g_date_get_month (&start), g_date_get_year (&start)));
+ g_date_add_days (&end, 6);
+ break;
+ case E_CAL_VIEW_KIND_LAST:
+ return;
+ }
+
+ e_cal_shell_content_change_view (cal_shell_content, cal_shell_content->priv->current_view, &start, &end, FALSE);
+}
+
+void
+e_cal_shell_content_move_view_range (ECalShellContent *cal_shell_content,
+ ECalendarViewMoveType move_type,
+ time_t exact_date)
+{
+ ECalendar *calendar;
+ ECalDataModel *data_model;
+ EShellSidebar *shell_sidebar;
+ EShellView *shell_view;
+ struct icaltimetype tt;
+ icaltimezone *zone;
+ GDate date;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ shell_view = e_shell_content_get_shell_view (E_SHELL_CONTENT (cal_shell_content));
+ shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
+ g_return_if_fail (E_IS_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+
+ calendar = e_cal_base_shell_sidebar_get_date_navigator (E_CAL_BASE_SHELL_SIDEBAR (shell_sidebar));
+ g_return_if_fail (E_IS_CALENDAR (calendar));
+ g_return_if_fail (e_calendar_get_item (calendar) != NULL);
+
+ data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ zone = e_cal_data_model_get_timezone (data_model);
+
+ switch (move_type) {
+ case E_CALENDAR_VIEW_MOVE_PREVIOUS:
+ cal_shell_content_move_view_range_relative (cal_shell_content, -1);
+ break;
+ case E_CALENDAR_VIEW_MOVE_NEXT:
+ cal_shell_content_move_view_range_relative (cal_shell_content, +1);
+ break;
+ case E_CALENDAR_VIEW_MOVE_TO_TODAY:
+ tt = icaltime_current_time_with_zone (zone);
+ g_date_set_dmy (&date, tt.day, tt.month, tt.year);
+ /* one-day selection takes care of the view range move with left view kind */
+ e_calendar_item_set_selection (e_calendar_get_item (calendar), &date, &date);
+ break;
+ case E_CALENDAR_VIEW_MOVE_TO_EXACT_DAY:
+ time_to_gdate_with_zone (&date, exact_date, zone);
+ e_cal_shell_content_change_view (cal_shell_content, E_CAL_VIEW_KIND_DAY, &date, &date, FALSE);
+ break;
+ }
+}
+
+static void
+cal_shell_content_update_model_filter (ECalDataModel *data_model,
+ ECalModel *model,
+ const gchar *filter,
+ time_t range_start,
+ time_t range_end)
+{
+ time_t tmp_start, tmp_end;
+
+ g_return_if_fail (E_IS_CAL_DATA_MODEL (data_model));
+ g_return_if_fail (E_IS_CAL_MODEL (model));
+
+ e_cal_data_model_freeze_views_update (data_model);
+ if (filter != NULL)
+ e_cal_data_model_set_filter (data_model, filter);
+ e_cal_model_set_time_range (model, range_start, range_end);
+
+ if (!e_cal_data_model_get_subscriber_range (data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model), &tmp_start, &tmp_end)) {
+ e_cal_data_model_subscribe (data_model, E_CAL_DATA_MODEL_SUBSCRIBER (model), range_start, range_end);
+ }
+
+ e_cal_data_model_thaw_views_update (data_model);
+}
+
+void
+e_cal_shell_content_update_filters (ECalShellContent *cal_shell_content,
+ const gchar *cal_filter,
+ time_t start_range,
+ time_t end_range)
+{
+ ECalDataModel *data_model;
+ ECalModel *model;
+
+ g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
+
+ if (!cal_filter)
+ return;
+
+ data_model = e_cal_base_shell_content_get_data_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+ model = e_cal_base_shell_content_get_model (E_CAL_BASE_SHELL_CONTENT (cal_shell_content));
+
+ cal_shell_content_update_model_filter (data_model, model, cal_filter, start_range, end_range);
+
+ if (cal_shell_content->priv->task_table) {
+ ETaskTable *task_table;
+ gchar *hide_completed_tasks_sexp;
+
+ /* Set the query on the task pad. */
+
+ task_table = E_TASK_TABLE (cal_shell_content->priv->task_table);
+ model = e_task_table_get_model (task_table);
+ data_model = e_cal_model_get_data_model (model);
+
+ hide_completed_tasks_sexp = calendar_config_get_hide_completed_tasks_sexp (FALSE);
+
+ if (hide_completed_tasks_sexp != NULL) {
+ if (*cal_filter) {
+ gchar *filter;
+
+ filter = g_strdup_printf ("(and %s %s)", hide_completed_tasks_sexp, cal_filter);
+ cal_shell_content_update_model_filter (data_model, model, filter, 0, 0);
+ g_free (filter);
+ } else {
+ cal_shell_content_update_model_filter (data_model, model, hide_completed_tasks_sexp, 0, 0);
+ }
+ } else {
+ cal_shell_content_update_model_filter (data_model, model, *cal_filter ? cal_filter : "#t", 0, 0);
+ }
+
+ g_free (hide_completed_tasks_sexp);
+ }
+
+ if (cal_shell_content->priv->memo_table) {
+ EMemoTable *memo_table;
+
+ /* Set the query on the memo pad. */
+
+ memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table);
+ model = e_memo_table_get_model (memo_table);
+ data_model = e_cal_model_get_data_model (model);
+
+ if (start_range != 0 && end_range != 0) {
+ icaltimezone *zone;
+ const gchar *default_tzloc = NULL;
+ time_t end = end_range;
+ gchar *filter;
+ gchar *iso_start;
+ gchar *iso_end;
+
+ zone = e_cal_data_model_get_timezone (data_model);
+ if (zone && zone != icaltimezone_get_utc_timezone ())
+ default_tzloc = icaltimezone_get_location (zone);
+ if (!default_tzloc)
+ default_tzloc = "";
+
+ if (start_range != (time_t) 0 && end_range != (time_t) 0) {
+ end = time_day_end_with_zone (end_range, zone);
+ }
+
+ iso_start = isodate_from_time_t (start_range);
+ iso_end = isodate_from_time_t (end);
+
+ filter = g_strdup_printf (
+ "(and (or (not (has-start?)) "
+ "(occur-in-time-range? (make-time \"%s\") "
+ "(make-time \"%s\") \"%s\")) %s)",
+ iso_start, iso_end, default_tzloc, cal_filter);
+
+ cal_shell_content_update_model_filter (data_model, model, filter, 0, 0);
+
+ g_free (filter);
+ g_free (iso_start);
+ g_free (iso_end);
+ } else {
+ cal_shell_content_update_model_filter (data_model, model, *cal_filter ? cal_filter : "#t", 0, 0);
+ }
+ }
+}