diff options
Diffstat (limited to 'calendar/gui/dialogs/comp-editor.c')
-rw-r--r-- | calendar/gui/dialogs/comp-editor.c | 4057 |
1 files changed, 3226 insertions, 831 deletions
diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c index a1bf930579..39ca3a81e1 100644 --- a/calendar/gui/dialogs/comp-editor.c +++ b/calendar/gui/dialogs/comp-editor.c @@ -1,368 +1,668 @@ -/* Evolution calendar - Framework for a calendar component editor dialog - * - * Copyright (C) 2001 Ximian, Inc. - * - * Author: Federico Mena-Quintero <federico@ximian.com> +/* + * Evolution calendar - Framework for a calendar component editor dialog * * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Federico Mena-Quintero <federico@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif +#include <errno.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> +#include <fcntl.h> #include <unistd.h> -#include <glib.h> +#include <glib/gi18n-lib.h> +#include <glib/gstdio.h> +#include <gio/gio.h> #include <gdk/gdkkeysyms.h> -#include <libgnome/gnome-defs.h> -#include <libgnome/gnome-i18n.h> -#include <libgnomeui/gnome-uidefs.h> -#include <libgnomeui/gnome-dialog.h> -#include <libgnomeui/gnome-dialog-util.h> -#include <libgnomeui/gnome-stock.h> -#include <libgnomeui/gnome-window-icon.h> -#include <libgnomeui/gnome-messagebox.h> -#include <bonobo/bonobo-ui-container.h> -#include <bonobo/bonobo-ui-util.h> -#include <gal/widgets/e-unicode.h> -#include <gal/widgets/e-gui-utils.h> -#include <e-util/e-dialog-utils.h> -#include <evolution-shell-component-utils.h> +#include <libebackend/libebackend.h> + +#include <shell/e-shell.h> + #include "../print.h" +#include "../comp-util.h" #include "save-comp.h" #include "delete-comp.h" #include "send-comp.h" #include "changed-comp.h" #include "cancel-comp.h" +#include "recur-comp.h" #include "comp-editor.h" +#include "comp-editor-util.h" +#include "../calendar-config-keys.h" - +#define COMP_EDITOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), TYPE_COMP_EDITOR, CompEditorPrivate)) + +#define d(x) /* Private part of the CompEditor structure */ struct _CompEditorPrivate { + + gpointer shell; /* weak pointer */ + + GSettings *calendar_settings; + + /* EFocusTracker keeps selection actions up-to-date. */ + EFocusTracker *focus_tracker; + + /* Each CompEditor window gets its own GtkWindowGroup, so it + * doesn't block the main window or other CompEditor windows. */ + GtkWindowGroup *window_group; + /* Client to use */ - CalClient *client; + ECalClient *cal_client; + + /* Source client (where comp lives currently) */ + ECalClient *source_client; + + /* View to listen for changes */ + ECalClientView *view; + GCancellable *view_cancellable; /* Calendar object/uid we are editing; this is an internal copy */ - CalComponent *comp; + ECalComponent *comp; /* The pages we have */ GList *pages; - /* UI Component for the dialog */ - BonoboUIComponent *uic; - /* Notebook to hold the pages */ GtkNotebook *notebook; - GtkWidget *filesel; + /* Attachment handling */ + GtkWidget *attachment_view; + + /* Manages menus and toolbars */ + GtkUIManager *ui_manager; + + gchar *summary; + + guint32 attachment_bar_visible : 1; + + /* TODO use this flags for setting all the boolean variables + * below */ + CompEditorFlags flags; + + icaltimezone *zone; + gboolean use_24_hour_format; + + GDateWeekday week_start_day; + + gint work_day_end_hour; + gint work_day_end_minute; + gint work_day_start_hour; + gint work_day_start_minute; gboolean changed; gboolean needs_send; - gboolean existing_org; - gboolean user_org; - - gboolean warned; - - gboolean updating; -}; + gboolean saved; + + CalObjModType mod; - + gboolean existing_org; + gboolean user_org; + gboolean is_group_item; -static void comp_editor_class_init (CompEditorClass *class); -static void comp_editor_init (CompEditor *editor); -static gint comp_editor_key_press_event (GtkWidget *d, GdkEventKey *e); -static void comp_editor_destroy (GtkObject *object); + gboolean warned; +}; -static void real_set_cal_client (CompEditor *editor, CalClient *client); -static void real_edit_comp (CompEditor *editor, CalComponent *comp); -static gboolean real_send_comp (CompEditor *editor, CalComponentItipMethod method); -static gboolean prompt_to_save_changes (CompEditor *editor, gboolean send); -static void delete_comp (CompEditor *editor); -static void close_dialog (CompEditor *editor); +enum { + PROP_0, + PROP_CHANGED, + PROP_CLIENT, + PROP_FLAGS, + PROP_FOCUS_TRACKER, + PROP_SHELL, + PROP_SUMMARY, + PROP_TIMEZONE, + PROP_USE_24_HOUR_FORMAT, + PROP_WEEK_START_DAY, + PROP_WORK_DAY_END_HOUR, + PROP_WORK_DAY_END_MINUTE, + PROP_WORK_DAY_START_HOUR, + PROP_WORK_DAY_START_MINUTE +}; -static void page_changed_cb (GtkObject *obj, gpointer data); -static void page_summary_changed_cb (GtkObject *obj, const char *summary, gpointer data); -static void page_dates_changed_cb (GtkObject *obj, CompEditorPageDates *dates, gpointer data); +static const gchar *ui = +"<ui>" +" <menubar action='main-menu'>" +" <menu action='file-menu'>" +" <menuitem action='save'/>" +" <menuitem action='save-and-close'/>" +" <separator/>" +" <menuitem action='print-preview'/>" +" <menuitem action='print'/>" +" <separator/>" +" <menuitem action='close'/>" +" </menu>" +" <menu action='edit-menu'>" +" <menuitem action='cut-clipboard'/>" +" <menuitem action='copy-clipboard'/>" +" <menuitem action='paste-clipboard'/>" +" <menuitem action='delete-selection'/>" +" <separator/>" +" <menuitem action='select-all'/>" +" </menu>" +" <menu action='view-menu'/>" +" <menu action='insert-menu'>" +" <menuitem action='attach'/>" +" </menu>" +" <menu action='options-menu'/>" +" <menu action='help-menu'>" +" <menuitem action='help'/>" +" </menu>" +" </menubar>" +" <toolbar name='main-toolbar'>" +" <toolitem action='save-and-close'/>\n" +" <toolitem action='save'/>\n" +" <toolitem action='print'/>\n" +" <separator/>" +" <placeholder name='content'/>\n" +" </toolbar>" +"</ui>"; + +static void comp_editor_show_help (CompEditor *editor); + +static void real_edit_comp (CompEditor *editor, + ECalComponent *comp); +static gboolean real_send_comp (CompEditor *editor, + ECalComponentItipMethod method, + gboolean strip_alarms); +static gboolean prompt_and_save_changes (CompEditor *editor, + gboolean send); +static void close_dialog (CompEditor *editor); + +static void page_dates_changed_cb (CompEditor *editor, + CompEditorPageDates *dates, + CompEditorPage *page); + +static void obj_modified_cb (ECalClientView *view, + const GSList *objs, + CompEditor *editor); +static void obj_removed_cb (ECalClientView *view, + const GSList *uids, + CompEditor *editor); + +G_DEFINE_TYPE_WITH_CODE ( + CompEditor, comp_editor, GTK_TYPE_WINDOW, + G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL) + G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)) + +enum { + OBJECT_CREATED, + COMP_CLOSED, + LAST_SIGNAL +}; -static void obj_updated_cb (CalClient *client, const char *uid, gpointer data); -static void obj_removed_cb (CalClient *client, const char *uid, gpointer data); +static guint signals[LAST_SIGNAL]; +static GList *active_editors; -static void save_cmd (GtkWidget *widget, gpointer data); -static void save_close_cmd (GtkWidget *widget, gpointer data); -static void save_as_cmd (GtkWidget *widget, gpointer data); -static void delete_cmd (GtkWidget *widget, gpointer data); -static void print_cmd (GtkWidget *widget, gpointer data); -static void print_preview_cmd (GtkWidget *widget, gpointer data); -static void print_setup_cmd (GtkWidget *widget, gpointer data); -static void close_cmd (GtkWidget *widget, gpointer data); +static void +comp_editor_weak_notify_cb (gpointer unused, + GObject *where_the_object_was) +{ + active_editors = g_list_remove (active_editors, where_the_object_was); +} -static gint delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data); +static void +attachment_store_changed_cb (CompEditor *editor) +{ + /* Mark the editor as changed so it prompts about unsaved + * changes on close */ + comp_editor_set_changed (editor, TRUE); +} -static EPixmap pixmaps [] = +static void +attachment_save_finished (EAttachmentStore *store, + GAsyncResult *result, + gpointer user_data) { - E_PIXMAP ("/menu/File/FileSave", "save-16.png"), - E_PIXMAP ("/menu/File/FileSaveAndClose", "save-16.png"), - E_PIXMAP ("/menu/File/FileSaveAs", "save-as-16.png"), + GtkWidget *dialog; + const gchar *primary_text; + gchar **uris; + GError *error = NULL; - E_PIXMAP ("/menu/File/FileDelete", "evolution-trash-mini.png"), + struct { + gchar **uris; + gboolean done; + GtkWindow *parent; + } *status = user_data; - E_PIXMAP ("/menu/File/FilePrint", "print.xpm"), - E_PIXMAP ("/menu/File/FilePrintPreview", "print-preview.xpm"), + uris = e_attachment_store_save_finish (store, result, &error); - E_PIXMAP ("/Toolbar/FileSaveAndClose", "buttons/save-24.png"), - E_PIXMAP ("/Toolbar/FilePrint", "buttons/print.png"), - E_PIXMAP ("/Toolbar/FileDelete", "buttons/delete-message.png"), + status->uris = uris; + status->done = TRUE; - E_PIXMAP_END -}; + if (uris != NULL) + goto exit; -static BonoboUIVerb verbs [] = { - BONOBO_UI_UNSAFE_VERB ("FileSave", save_cmd), - BONOBO_UI_UNSAFE_VERB ("FileSaveAndClose", save_close_cmd), - BONOBO_UI_UNSAFE_VERB ("FileSaveAs", save_as_cmd), - BONOBO_UI_UNSAFE_VERB ("FileDelete", delete_cmd), - BONOBO_UI_UNSAFE_VERB ("FilePrint", print_cmd), - BONOBO_UI_UNSAFE_VERB ("FilePrintPreview", print_preview_cmd), - BONOBO_UI_UNSAFE_VERB ("FilePrintSetup", print_setup_cmd), - BONOBO_UI_UNSAFE_VERB ("FileClose", close_cmd), - - BONOBO_UI_VERB_END -}; + /* Ignore cancellations. */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + goto exit; -static GtkObjectClass *parent_class; + primary_text = _("Could not save attachments"); - + dialog = gtk_message_dialog_new_with_markup ( + status->parent, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + "<big><b>%s</b></big>", primary_text); -GtkType -comp_editor_get_type (void) -{ - static GtkType comp_editor_type = 0; + gtk_message_dialog_format_secondary_text ( + GTK_MESSAGE_DIALOG (dialog), "%s", error->message); - if (!comp_editor_type) { - static const GtkTypeInfo comp_editor_info = { - "CompEditor", - sizeof (CompEditor), - sizeof (CompEditorClass), - (GtkClassInitFunc) comp_editor_class_init, - (GtkObjectInitFunc) comp_editor_init, - NULL, /* reserved_1 */ - NULL, /* reserved_2 */ - (GtkClassInitFunc) NULL - }; + gtk_dialog_run (GTK_DIALOG (dialog)); - comp_editor_type = gtk_type_unique (BONOBO_TYPE_WINDOW, - &comp_editor_info); - } + gtk_widget_destroy (dialog); + +exit: + if (error != NULL) + g_error_free (error); - return comp_editor_type; + g_object_unref (status->parent); } -/* Class initialization function for the calendar component editor */ -static void -comp_editor_class_init (CompEditorClass *klass) +static GSList * +get_attachment_list (CompEditor *editor) { - GtkObjectClass *object_class; - GtkWidgetClass *widget_class; - - object_class = GTK_OBJECT_CLASS (klass); - widget_class = GTK_WIDGET_CLASS (klass); + EAttachmentStore *store; + EAttachmentView *view; + GFile *destination; + GSList *list = NULL; + const gchar *comp_uid = NULL; + const gchar *local_store; + gchar *filename_prefix, *tmp; + gint ii; + + struct { + gchar **uris; + gboolean done; + GtkWindow *parent; + } status; + + e_cal_component_get_uid (editor->priv->comp, &comp_uid); + g_return_val_if_fail (comp_uid != NULL, NULL); + + status.uris = NULL; + status.done = FALSE; + status.parent = g_object_ref (editor); + + view = E_ATTACHMENT_VIEW (editor->priv->attachment_view); + store = e_attachment_view_get_store (view); + + tmp = g_strdup (comp_uid); + e_filename_make_safe (tmp); + filename_prefix = g_strconcat (tmp, "-", NULL); + g_free (tmp); + + local_store = e_cal_client_get_local_attachment_store (editor->priv->cal_client); + destination = g_file_new_for_path (local_store); + + e_attachment_store_save_async ( + store, destination, filename_prefix, + (GAsyncReadyCallback) attachment_save_finished, &status); + + g_object_unref (destination); + g_free (filename_prefix); + + /* We can't return until we have results, so crank + * the main loop until the callback gets triggered. */ + while (!status.done) + gtk_main_iteration (); + + if (status.uris == NULL) + return NULL; - parent_class = gtk_type_class (BONOBO_TYPE_WINDOW); + /* Transfer the URI strings to the GSList. */ + for (ii = 0; status.uris[ii] != NULL; ii++) { + list = g_slist_prepend (list, status.uris[ii]); + status.uris[ii] = NULL; + } - klass->set_cal_client = real_set_cal_client; - klass->edit_comp = real_edit_comp; - klass->send_comp = real_send_comp; + g_free (status.uris); - widget_class->key_press_event = comp_editor_key_press_event; - object_class->destroy = comp_editor_destroy; + return g_slist_reverse (list); } -/* Creates the basic in the editor */ +/* This sets the focus to the toplevel, so any field being edited is committed. + * FIXME: In future we may also want to check some of the fields are valid, + * e.g. the EDateEdit fields. */ static void -setup_widgets (CompEditor *editor) +commit_all_fields (CompEditor *editor) { - CompEditorPrivate *priv; - BonoboUIContainer *container; - GtkWidget *vbox; - - priv = editor->priv; + gtk_window_set_focus (GTK_WINDOW (editor), NULL); +} - /* Window and basic vbox */ - bonobo_window_construct (BONOBO_WINDOW (editor), - "event-editor", "iCalendar Editor"); - gtk_signal_connect (GTK_OBJECT (editor), "delete_event", - GTK_SIGNAL_FUNC (delete_event_cb), editor); - - priv->uic = bonobo_ui_component_new_default (); - container = bonobo_ui_container_new (); - bonobo_ui_container_set_win (container, BONOBO_WINDOW (editor)); - bonobo_ui_component_set_container (priv->uic, BONOBO_OBJREF (container)); - bonobo_ui_engine_config_set_path (bonobo_window_get_ui_engine (BONOBO_WINDOW (editor)), - "/evolution/UIConf/kvps"); - - bonobo_ui_component_add_verb_list_with_data (priv->uic, verbs, editor); - bonobo_ui_util_set_ui (priv->uic, EVOLUTION_DATADIR, - "evolution-comp-editor.xml", - "evolution-calendar"); - e_pixmaps_update (priv->uic, pixmaps); - - vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL); - gtk_widget_show (vbox); - gtk_container_set_border_width (GTK_CONTAINER (vbox), GNOME_PAD_SMALL); - bonobo_window_set_contents (BONOBO_WINDOW (editor), vbox); - - /* Notebook */ - priv->notebook = GTK_NOTEBOOK (gtk_notebook_new ()); - gtk_widget_show (GTK_WIDGET (priv->notebook)); - gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (priv->notebook), - TRUE, TRUE, 0); -} - -/* Object initialization function for the calendar component editor */ static void -comp_editor_init (CompEditor *editor) +changes_view_ready_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - CompEditorPrivate *priv; + CompEditor *editor = user_data; + ECalClientView *view = NULL; + gboolean success; + GError *error = NULL; - priv = g_new0 (CompEditorPrivate, 1); - editor->priv = priv; + g_return_if_fail (editor != NULL); - setup_widgets (editor); + success = e_cal_client_get_view_finish ( + E_CAL_CLIENT (source_object), result, &view, &error); - priv->pages = NULL; - priv->changed = FALSE; - priv->needs_send = FALSE; - priv->existing_org = FALSE; - priv->user_org = FALSE; - priv->warned = FALSE; -} + if (!success) + view = NULL; + if (view) { + editor->priv->view = view; + g_signal_connect ( + view, "objects_modified", + G_CALLBACK (obj_modified_cb), editor); + g_signal_connect ( + view, "objects_removed", + G_CALLBACK (obj_removed_cb), editor); -static gint -comp_editor_key_press_event (GtkWidget *d, GdkEventKey *e) -{ - if (e->keyval == GDK_Escape) { - if (prompt_to_save_changes (COMP_EDITOR (d), TRUE)) - close_dialog (COMP_EDITOR (d)); - return TRUE; - } + e_cal_client_view_start (view, &error); - if (GTK_WIDGET_CLASS (parent_class)->key_press_event) - return (* GTK_WIDGET_CLASS (parent_class)->key_press_event) (d, e); - - return FALSE; + if (error != NULL) { + g_warning ( + "%s: Failed to start view: %s", + G_STRFUNC, error->message); + g_error_free (error); + } + } else if (error) { + if (!g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ( + "%s: Failed to get view: %s", + G_STRFUNC, error->message); + g_error_free (error); + } } -/* Destroy handler for the calendar component editor */ static void -comp_editor_destroy (GtkObject *object) +listen_for_changes (CompEditor *editor) { - CompEditor *editor; CompEditorPrivate *priv; - GList *l; + const gchar *uid = NULL; - editor = COMP_EDITOR (object); priv = editor->priv; - gtk_signal_disconnect_by_data (GTK_OBJECT (priv->client), editor); + /* Discard change listener */ + if (priv->view_cancellable) { + g_cancellable_cancel (priv->view_cancellable); + g_object_unref (priv->view_cancellable); + priv->view_cancellable = NULL; + } - /* We want to destroy the pages after the widgets get destroyed, - since they have lots of signal handlers connected to the widgets - with the pages as the data. */ - for (l = priv->pages; l != NULL; l = l->next) - gtk_object_unref (GTK_OBJECT (l->data)); + if (priv->view) { + g_signal_handlers_disconnect_matched ( + priv->view, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, editor); - if (priv->comp) { - gtk_object_unref (GTK_OBJECT (priv->comp)); - priv->comp = NULL; + g_object_unref (priv->view); + priv->view = NULL; + } + + /* Listen for changes */ + if (priv->comp) + e_cal_component_get_uid (priv->comp, &uid); + + if (uid) { + gchar *query; + + priv->view_cancellable = g_cancellable_new (); + query = g_strdup_printf ("(uid? \"%s\")", uid); + e_cal_client_get_view ( + priv->source_client, + query, priv->view_cancellable, + changes_view_ready_cb, editor); + g_free (query); } +} + +static void +send_timezone (gpointer key, + gpointer value, + gpointer user_data) +{ + icaltimezone *zone = value; + CompEditor *editor = user_data; + GError *error = NULL; - g_free (priv); - editor->priv = NULL; + e_cal_client_add_timezone_sync (editor->priv->cal_client, zone, NULL, &error); - if (GTK_OBJECT_CLASS (parent_class)->destroy) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); + if (error != NULL) { + g_warning ( + "%s: Failed to add timezone: %s", + G_STRFUNC, error->message); + g_error_free (error); + } } static gboolean save_comp (CompEditor *editor) { CompEditorPrivate *priv; - CalComponent *clone; + CompEditorFlags flags; + ECalComponent *clone; + ESourceRegistry *registry; + EShell *shell; GList *l; - CalClientResult result; + gboolean result; + GError *error = NULL; + GHashTable *timezones; + const gchar *orig_uid = NULL; + gchar *orig_uid_copy; + icalcomponent *icalcomp; priv = editor->priv; if (!priv->changed) return TRUE; - clone = cal_component_clone (priv->comp); + flags = comp_editor_get_flags (editor); + shell = comp_editor_get_shell (editor); + + registry = e_shell_get_registry (shell); + + /* Stop listening because we are about to change things */ + if (priv->view) { + g_signal_handlers_disconnect_matched ( + priv->view, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + editor); + + g_object_unref (priv->view); + priv->view = NULL; + } + + /* Update on the server */ + timezones = g_hash_table_new (g_str_hash, g_str_equal); + + clone = e_cal_component_clone (priv->comp); + comp_editor_copy_new_attendees (clone, priv->comp); for (l = priv->pages; l != NULL; l = l->next) { - if (!comp_editor_page_fill_component (l->data, clone)) { - gtk_object_unref (GTK_OBJECT (clone)); + if (IS_COMP_EDITOR_PAGE (l->data) && + !comp_editor_page_fill_component (l->data, clone)) { + g_object_unref (clone); + g_hash_table_destroy (timezones); comp_editor_show_page (editor, COMP_EDITOR_PAGE (l->data)); return FALSE; } + + /* retrieve all timezones */ + if (IS_COMP_EDITOR_PAGE (l->data)) + comp_editor_page_fill_timezones (l->data, timezones); } - + /* If we are not the organizer, we don't update the sequence number */ - if (!cal_component_has_organizer (clone) || itip_organizer_is_user (clone)) - cal_component_commit_sequence (clone); + if (!e_cal_component_has_organizer (clone) || + itip_organizer_is_user (registry, clone, priv->cal_client) || + itip_sentby_is_user (registry, clone, priv->cal_client)) + e_cal_component_commit_sequence (clone); else - cal_component_abort_sequence (clone); + e_cal_component_abort_sequence (clone); - gtk_object_unref (GTK_OBJECT (priv->comp)); + g_object_unref (priv->comp); priv->comp = clone; - priv->updating = TRUE; + e_cal_component_get_uid (priv->comp, &orig_uid); + /* Make a copy of it, because call of e_cal_create_object() + * rewrites the internal uid. */ + orig_uid_copy = g_strdup (orig_uid); + + /* send timezones */ + g_hash_table_foreach (timezones, (GHFunc) send_timezone, editor); + g_hash_table_destroy (timezones); + + /* Attachments*/ + + e_cal_component_set_attachment_list ( + priv->comp, get_attachment_list (editor)); + icalcomp = e_cal_component_get_icalcomponent (priv->comp); + /* send the component to the server */ + if (!cal_comp_is_on_server (priv->comp, priv->cal_client)) { + gchar *uid = NULL; + result = e_cal_client_create_object_sync ( + priv->cal_client, icalcomp, &uid, NULL, &error); + if (result) { + icalcomponent_set_uid (icalcomp, uid); + g_free (uid); + g_signal_emit_by_name (editor, "object_created"); + } + } else { + gboolean has_recurrences; - result = cal_client_update_object (priv->client, priv->comp); - if (result != CAL_CLIENT_RESULT_SUCCESS) { - GtkWidget *dlg; - char *msg; + has_recurrences = + e_cal_component_has_recurrences (priv->comp); - switch (result) { - case CAL_CLIENT_RESULT_INVALID_OBJECT : - msg = g_strdup (_("Could not update invalid object")); - break; - case CAL_CLIENT_RESULT_NOT_FOUND : - msg = g_strdup (_("Object not found, not updated")); - break; - case CAL_CLIENT_RESULT_PERMISSION_DENIED : - msg = g_strdup (_("You don't have permissions to update this object")); - break; - default : - msg = g_strdup (_("Could not update object")); - break; + if (has_recurrences && priv->mod == CALOBJ_MOD_ALL) + comp_util_sanitize_recurrence_master ( + priv->comp, priv->cal_client); + + if (priv->mod == CALOBJ_MOD_THIS) { + e_cal_component_set_rdate_list (priv->comp, NULL); + e_cal_component_set_rrule_list (priv->comp, NULL); + e_cal_component_set_exdate_list (priv->comp, NULL); + e_cal_component_set_exrule_list (priv->comp, NULL); + } + result = e_cal_client_modify_object_sync ( + priv->cal_client, icalcomp, priv->mod, NULL, &error); + + if (priv->mod == CALOBJ_MOD_THIS) { + if (result && ((flags & COMP_EDITOR_DELEGATE) || + !e_cal_component_has_organizer (clone) || + itip_organizer_is_user (registry, clone, priv->cal_client) || + itip_sentby_is_user (registry, clone, priv->cal_client))) + e_cal_component_commit_sequence (clone); + else + e_cal_component_abort_sequence (clone); + } + } + + /* If the delay delivery is set, the items will not be created in + * the server immediately, so we need not show them in the view. + * They will appear as soon as the server creates it after the + * delay period. */ + if (result && e_cal_component_has_attendees (priv->comp)) { + gboolean delay_set = FALSE; + icalproperty *icalprop; + icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); + while (icalprop) { + const gchar *x_name; + + x_name = icalproperty_get_x_name (icalprop); + if (!strcmp (x_name, "X-EVOLUTION-OPTIONS-DELAY")) { + delay_set = TRUE; + break; + } + + icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); + } + if (delay_set) { + g_free (orig_uid_copy); + return TRUE; } + } + + if (!result) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new ( + NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", (error != NULL) ? error->message : + _("Could not update object")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); - dlg = gnome_error_dialog (msg); - gnome_dialog_run_and_close (GNOME_DIALOG (dlg)); - g_free (msg); + if (error) + g_error_free (error); + + g_free (orig_uid_copy); return FALSE; } else { - priv->changed = FALSE; + if (priv->source_client && + !e_source_equal (e_client_get_source (E_CLIENT (priv->cal_client)), + e_client_get_source (E_CLIENT (priv->source_client))) && + cal_comp_is_on_server (priv->comp, priv->source_client)) { + /* Comp found a new home. Remove it from old one. */ + GError *error = NULL; + + if (e_cal_component_is_instance (priv->comp) || + e_cal_component_has_recurrences (priv->comp)) + e_cal_client_remove_object_sync ( + priv->source_client, orig_uid_copy, + NULL, CALOBJ_MOD_ALL, NULL, &error); + else + e_cal_client_remove_object_sync ( + priv->source_client, + orig_uid_copy, NULL, CALOBJ_MOD_THIS, NULL, &error); + + if (error != NULL) { + g_warning ( + "%s: Failed to remove object: %s", + G_STRFUNC, error->message); + g_error_free (error); + } + + /* Let priv->source_client point to new home, + * so we can move it again this session. */ + g_object_unref (priv->source_client); + priv->source_client = g_object_ref (priv->cal_client); + + listen_for_changes (editor); + } + + comp_editor_set_changed (editor, FALSE); + priv->saved = TRUE; } - priv->updating = FALSE; + g_free (orig_uid_copy); return TRUE; } @@ -371,144 +671,2055 @@ static gboolean save_comp_with_send (CompEditor *editor) { CompEditorPrivate *priv; - gboolean send; + CompEditorFlags flags; + ESourceRegistry *registry; + EShell *shell; + gboolean send, delegated, only_new_attendees = FALSE; + gboolean delegate; + gboolean strip_alarms = TRUE; priv = editor->priv; + flags = comp_editor_get_flags (editor); + shell = comp_editor_get_shell (editor); + + registry = e_shell_get_registry (shell); + send = priv->changed && priv->needs_send; + delegate = flags & COMP_EDITOR_DELEGATE; + + if (delegate) { + icalcomponent *icalcomp = e_cal_component_get_icalcomponent (priv->comp); + icalproperty *icalprop; + + icalprop = icalproperty_new_x ("1"); + icalproperty_set_x_name (icalprop, "X-EVOLUTION-DELEGATED"); + icalcomponent_add_property (icalcomp, icalprop); + } if (!save_comp (editor)) return FALSE; - if (send && send_component_dialog (priv->comp, !priv->existing_org)) { - if (itip_organizer_is_user (priv->comp)) - return comp_editor_send_comp (editor, CAL_COMPONENT_METHOD_REQUEST); - else - return comp_editor_send_comp (editor, CAL_COMPONENT_METHOD_REPLY); - } + delegated = delegate && !e_cal_client_check_save_schedules (priv->cal_client); + if (delegated || (send && send_component_dialog ( + (GtkWindow *) editor, priv->cal_client, priv->comp, + !priv->existing_org, &strip_alarms, !priv->existing_org ? + NULL : &only_new_attendees))) { + if (delegated) + only_new_attendees = FALSE; + + comp_editor_set_flags ( + editor, (comp_editor_get_flags (editor) & + (~COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) | + (only_new_attendees ? + COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY : 0)); + + if ((itip_organizer_is_user (registry, priv->comp, priv->cal_client) || + itip_sentby_is_user (registry, priv->comp, priv->cal_client))) { + if (e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_JOURNAL) + return comp_editor_send_comp ( + editor, E_CAL_COMPONENT_METHOD_PUBLISH, + strip_alarms); + else + return comp_editor_send_comp ( + editor, E_CAL_COMPONENT_METHOD_REQUEST, + strip_alarms); + } else { + if (!comp_editor_send_comp ( + editor, E_CAL_COMPONENT_METHOD_REQUEST, + strip_alarms)) + return FALSE; + + if (delegate) + return comp_editor_send_comp ( + editor, E_CAL_COMPONENT_METHOD_REPLY, + strip_alarms); + } + } return TRUE; } static void -delete_comp (CompEditor *editor) +update_window_border (CompEditor *editor, + const gchar *description) +{ + const gchar *icon_name; + const gchar *format; + gchar *title; + + if (editor->priv->comp == NULL) { + title = g_strdup (_("Edit Appointment")); + icon_name = "x-office-calendar"; + goto exit; + + } else switch (e_cal_component_get_vtype (editor->priv->comp)) { + case E_CAL_COMPONENT_EVENT: + if (editor->priv->is_group_item) + format = _("Meeting - %s"); + else + format = _("Appointment - %s"); + icon_name = "appointment-new"; + break; + + case E_CAL_COMPONENT_TODO: + if (editor->priv->is_group_item) + format = _("Assigned Task - %s"); + else + format = _("Task - %s"); + icon_name = "stock_task"; + break; + + case E_CAL_COMPONENT_JOURNAL: + format = _("Memo - %s"); + icon_name = "stock_insert-note"; + break; + + default: + g_return_if_reached (); + } + + if (description == NULL || *description == '\0') { + ECalComponentText text; + + e_cal_component_get_summary (editor->priv->comp, &text); + description = text.value; + } + + if (description == NULL || *description == '\0') + description = _("No Summary"); + + title = g_strdup_printf (format, description); + +exit: + gtk_window_set_icon_name (GTK_WINDOW (editor), icon_name); + gtk_window_set_title (GTK_WINDOW (editor), title); + + g_free (title); +} + +static void +action_attach_cb (GtkAction *action, + CompEditor *editor) +{ + EAttachmentStore *store; + EAttachmentView *view; + + view = E_ATTACHMENT_VIEW (editor->priv->attachment_view); + store = e_attachment_view_get_store (view); + + e_attachment_store_run_load_dialog (store, GTK_WINDOW (editor)); +} + +static void +action_classification_cb (GtkRadioAction *action, + GtkRadioAction *current, + CompEditor *editor) +{ + comp_editor_set_changed (editor, TRUE); +} + +static void +action_close_cb (GtkAction *action, + CompEditor *editor) +{ + commit_all_fields (editor); + + if (prompt_and_save_changes (editor, TRUE)) + close_dialog (editor); +} + +static void +action_help_cb (GtkAction *action, + CompEditor *editor) +{ + comp_editor_show_help (editor); +} + +static void +action_print_cb (GtkAction *action, + CompEditor *editor) +{ + CompEditorPrivate *priv = editor->priv; + ECalComponent *comp; + GList *l; + icalcomponent *component; + icalcomponent *clone; + icaltimezone *zone; + gboolean use_24_hour_format; + + comp = e_cal_component_new (); + component = e_cal_component_get_icalcomponent (priv->comp); + clone = icalcomponent_new_clone (component); + e_cal_component_set_icalcomponent (comp, clone); + + for (l = priv->pages; l != NULL; l = l->next) + comp_editor_page_fill_component (l->data, comp); + + zone = comp_editor_get_timezone (editor); + use_24_hour_format = comp_editor_get_use_24_hour_format (editor); + + print_comp ( + comp, priv->cal_client, zone, use_24_hour_format, + GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG); + + g_object_unref (comp); +} + +static void +action_print_preview_cb (GtkAction *action, + CompEditor *editor) +{ + CompEditorPrivate *priv = editor->priv; + ECalComponent *comp; + GList *l; + icalcomponent *component; + icalcomponent *clone; + icaltimezone *zone; + gboolean use_24_hour_format; + + comp = e_cal_component_new (); + component = e_cal_component_get_icalcomponent (priv->comp); + clone = icalcomponent_new_clone (component); + e_cal_component_set_icalcomponent (comp, clone); + + for (l = priv->pages; l != NULL; l = l->next) + comp_editor_page_fill_component (l->data, comp); + + zone = comp_editor_get_timezone (editor); + use_24_hour_format = comp_editor_get_use_24_hour_format (editor); + + print_comp ( + comp, priv->cal_client, zone, use_24_hour_format, + GTK_PRINT_OPERATION_ACTION_PREVIEW); + + g_object_unref (comp); +} + +static gboolean +remove_event_dialog (ECalClient *client, + ECalComponent *comp, + GtkWindow *parent) +{ + GtkWidget *dialog; + gboolean ret; + + g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), TRUE); + + dialog = gtk_message_dialog_new ( + parent, 0, GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, "%s", _("Keep original item?")); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + ret = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES; + gtk_widget_destroy (dialog); + + return ret; +} + +static void +save_and_close_editor (CompEditor *editor, + gboolean can_close) +{ + CompEditorPrivate *priv = editor->priv; + EAttachmentStore *store; + EAttachmentView *view; + ECalComponentText text; + gboolean delegated = FALSE; + gboolean correct = FALSE; + ECalComponent *comp; + const gchar *uid = NULL; + + view = E_ATTACHMENT_VIEW (priv->attachment_view); + store = e_attachment_view_get_store (view); + + if (e_attachment_store_get_num_loading (store) > 0) { + gboolean response = 1; + /*FIXME: Cannot use mail functions from calendar!!!! */ +#if 0 + ECalComponentVType vtype = e_cal_component_get_vtype (editor->priv->comp); + + if (vtype == E_CAL_COMPONENT_EVENT) + response = em_utils_prompt_user ( + (GtkWindow *) widget, + NULL, + "calendar:ask-send-event-pending-download", + NULL); + else + response = em_utils_prompt_user ( + (GtkWindow *) widget, + NULL, + "calendar:ask-send-task-pending-download", + NULL); +#endif + if (!response) + return; + } + + if (e_client_is_readonly (E_CLIENT (priv->cal_client))) { + e_alert_submit ( + E_ALERT_SINK (editor), + "calendar:prompt-read-only-cal-editor", + e_source_get_display_name ( + e_client_get_source (E_CLIENT (priv->cal_client))), + NULL); + return; + } + + if ((comp_editor_get_flags (editor) & COMP_EDITOR_IS_ASSIGNED) != 0 + && e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_TODO + && e_client_check_capability (E_CLIENT (priv->cal_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) { + e_alert_submit ( + E_ALERT_SINK (editor), + "calendar:prompt-no-task-assignment-editor", + e_source_get_display_name ( + e_client_get_source (E_CLIENT (priv->cal_client))), + NULL); + return; + } + + commit_all_fields (editor); + if (e_cal_component_has_recurrences (priv->comp)) { + if (!recur_component_dialog ( + priv->cal_client, priv->comp, &priv->mod, + GTK_WINDOW (editor), delegated)) + return; + } else if (e_cal_component_is_instance (priv->comp)) + priv->mod = CALOBJ_MOD_THIS; + + comp = comp_editor_get_current_comp (editor, &correct); + e_cal_component_get_summary (comp, &text); + g_object_unref (comp); + + if (!correct) + return; + + if (!text.value) + if (!send_component_prompt_subject ( + (GtkWindow *) editor, priv->cal_client, priv->comp)) + return; + + gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE); + + if (save_comp_with_send (editor)) { + CompEditorFlags flags; + gboolean delegate; + + flags = comp_editor_get_flags (editor); + delegate = flags & COMP_EDITOR_DELEGATE; + + if (delegate && !remove_event_dialog ( + priv->cal_client, priv->comp, GTK_WINDOW (editor))) { + GError *error = NULL; + + e_cal_component_get_uid (priv->comp, &uid); + + if (e_cal_component_is_instance (priv->comp) || + e_cal_component_has_recurrences (priv->comp)) { + gchar *rid; + rid = e_cal_component_get_recurid_as_string (priv->comp); + e_cal_client_remove_object_sync ( + priv->cal_client, uid, rid, + priv->mod, NULL, &error); + g_free (rid); + } else + e_cal_client_remove_object_sync ( + priv->cal_client, uid, NULL, + CALOBJ_MOD_THIS, NULL, &error); + + g_clear_error (&error); + } + } else + correct = FALSE; + + gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE); + + if (correct) { + if (can_close) + close_dialog (editor); + else { + ECalComponent *comp; + ECalClientSourceType source_type; + icalcomponent *icalcomp = NULL; + const gchar *uid = NULL; + gchar *rid = NULL; + GError *error = NULL; + + comp_editor_set_changed (editor, FALSE); + + /* + * A server can modify the event on save. Considering this, it is needed to fetch the updated + * version of the event from server, updating the component, then user can keep editing the + * event + */ + e_cal_component_get_uid (priv->comp, &uid); + rid = e_cal_component_get_recurid_as_string (priv->comp); + + source_type = e_cal_client_get_source_type (priv->cal_client); + if (!e_cal_client_get_object_sync (priv->cal_client, uid, rid, &icalcomp, NULL, &error)) { + if (error != NULL) { + switch (source_type) { + case (E_CAL_CLIENT_SOURCE_TYPE_TASKS): + g_warning ("Unable to retrieve saved component from the task list, returned error was: %s", error->message); + break; + case (E_CAL_CLIENT_SOURCE_TYPE_MEMOS): + g_warning ("Unable to retrieve saved component from the memo list, returned error was: %s", error->message); + break; + case (E_CAL_CLIENT_SOURCE_TYPE_EVENTS): + default: + g_warning ("Unable to retrieve saved component from the calendar, returned error was: %s", error->message); + break; + } + g_clear_error (&error); + } else { + switch (source_type) { + case (E_CAL_CLIENT_SOURCE_TYPE_TASKS): + g_warning ("Unable to retrieve saved component from the task list"); + break; + case (E_CAL_CLIENT_SOURCE_TYPE_MEMOS): + g_warning ("Unable to retrieve saved component from the memo list"); + break; + case (E_CAL_CLIENT_SOURCE_TYPE_EVENTS): + default: + g_warning ("Unable to retrieve saved component from the calendar"); + break; + } + } + e_notice ( + GTK_WINDOW (editor), + GTK_MESSAGE_ERROR, + _("Unable to synchronize with the server")); + } else { + comp = e_cal_component_new (); + if (e_cal_component_set_icalcomponent (comp, icalcomp)) { + gboolean has_recurrences; + + has_recurrences = e_cal_component_has_recurrences (comp); + + if (has_recurrences && priv->mod == CALOBJ_MOD_ALL) + comp_util_sanitize_recurrence_master (comp, priv->cal_client); + + comp_editor_edit_comp (editor, comp); + } else { + switch (source_type) { + case (E_CAL_CLIENT_SOURCE_TYPE_TASKS): + g_warning ("Unable to update the editor with the retrieved component from the task list"); + break; + case (E_CAL_CLIENT_SOURCE_TYPE_MEMOS): + g_warning ("Unable to update the editor with the retrieved component from the memo list"); + break; + case (E_CAL_CLIENT_SOURCE_TYPE_EVENTS): + default: + g_warning ("Unable to update the editor with the retrieved component from the calendar"); + break; + } + e_notice ( + GTK_WINDOW (editor), + GTK_MESSAGE_ERROR, + _("Unable to synchronize with the server")); + icalcomponent_free (icalcomp); + } + g_object_unref (comp); + } + g_free (rid); + } + } +} + +static void +action_save_cb (GtkAction *action, + CompEditor *editor) +{ + save_and_close_editor (editor, FALSE); +} + +static void +action_save_and_close_cb (GtkAction *action, + CompEditor *editor) +{ + save_and_close_editor (editor, TRUE); +} + +static void +action_view_categories_cb (GtkToggleAction *action, + CompEditor *editor) +{ + CompEditorClass *class; + gboolean active; + + class = COMP_EDITOR_GET_CLASS (editor); + active = gtk_toggle_action_get_active (action); + + if (class->show_categories != NULL) + class->show_categories (editor, active); +} + +static void +action_view_role_cb (GtkToggleAction *action, + CompEditor *editor) +{ + CompEditorClass *class; + gboolean active; + + class = COMP_EDITOR_GET_CLASS (editor); + active = gtk_toggle_action_get_active (action); + + if (class->show_role != NULL) + class->show_role (editor, active); +} + +static void +action_view_rsvp_cb (GtkToggleAction *action, + CompEditor *editor) +{ + CompEditorClass *class; + gboolean active; + + class = COMP_EDITOR_GET_CLASS (editor); + active = gtk_toggle_action_get_active (action); + + if (class->show_rsvp != NULL) + class->show_rsvp (editor, active); +} + +static void +action_view_status_cb (GtkToggleAction *action, + CompEditor *editor) +{ + CompEditorClass *class; + gboolean active; + + class = COMP_EDITOR_GET_CLASS (editor); + active = gtk_toggle_action_get_active (action); + + if (class->show_status != NULL) + class->show_status (editor, active); +} + +static void +action_view_time_zone_cb (GtkToggleAction *action, + CompEditor *editor) +{ + CompEditorClass *class; + gboolean active; + + class = COMP_EDITOR_GET_CLASS (editor); + active = gtk_toggle_action_get_active (action); + + if (class->show_time_zone != NULL) + class->show_time_zone (editor, active); +} + +static void +action_view_type_cb (GtkToggleAction *action, + CompEditor *editor) +{ + CompEditorClass *class; + gboolean active; + + class = COMP_EDITOR_GET_CLASS (editor); + active = gtk_toggle_action_get_active (action); + + if (class->show_type != NULL) + class->show_type (editor, active); +} + +static GtkActionEntry core_entries[] = { + + { "close", + GTK_STOCK_CLOSE, + NULL, + NULL, + N_("Close the current window"), + G_CALLBACK (action_close_cb) }, + + { "copy-clipboard", + GTK_STOCK_COPY, + NULL, + NULL, + N_("Copy the selection"), + NULL }, /* Handled by EFocusTracker */ + + { "cut-clipboard", + GTK_STOCK_CUT, + NULL, + NULL, + N_("Cut the selection"), + NULL }, /* Handled by EFocusTracker */ + + { "delete-selection", + GTK_STOCK_DELETE, + NULL, + NULL, + N_("Delete the selection"), + NULL }, /* Handled by EFocusTracker */ + + { "help", + GTK_STOCK_HELP, + NULL, + NULL, + N_("View help"), + G_CALLBACK (action_help_cb) }, + + { "paste-clipboard", + GTK_STOCK_PASTE, + NULL, + NULL, + N_("Paste the clipboard"), + NULL }, /* Handled by EFocusTracker */ + + { "print", + GTK_STOCK_PRINT, + NULL, + "<Control>p", + NULL, + G_CALLBACK (action_print_cb) }, + + { "print-preview", + GTK_STOCK_PRINT_PREVIEW, + NULL, + NULL, + NULL, + G_CALLBACK (action_print_preview_cb) }, + + { "save", + GTK_STOCK_SAVE, + NULL, + NULL, + 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) }, + + { "select-all", + GTK_STOCK_SELECT_ALL, + NULL, + "<Control>a", + N_("Select all text"), + 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 } +}; + +static GtkActionEntry individual_entries[] = { + + { "attach", + "mail-attachment", + N_("_Attachment..."), + "<Control>m", + N_("Attach a file"), + G_CALLBACK (action_attach_cb) } +}; + +static GtkToggleActionEntry core_toggle_entries[] = { + + { "view-categories", + NULL, + N_("_Categories"), + NULL, + N_("Toggles whether to display categories"), + G_CALLBACK (action_view_categories_cb), + FALSE }, + + { "view-time-zone", + "stock_timezone", + N_("Time _Zone"), + NULL, + N_("Toggles whether the time zone is displayed"), + G_CALLBACK (action_view_time_zone_cb), + FALSE } +}; + +static GtkRadioActionEntry classification_radio_entries[] = { + + { "classify-public", + NULL, + N_("Pu_blic"), + NULL, + N_("Classify as public"), + E_CAL_COMPONENT_CLASS_PUBLIC }, + + { "classify-private", + NULL, + N_("_Private"), + NULL, + N_("Classify as private"), + E_CAL_COMPONENT_CLASS_PRIVATE }, + + { "classify-confidential", + NULL, + N_("_Confidential"), + NULL, + N_("Classify as confidential"), + E_CAL_COMPONENT_CLASS_CONFIDENTIAL } +}; + +static GtkToggleActionEntry coordinated_toggle_entries[] = { + + { "view-role", + NULL, + N_("R_ole Field"), + NULL, + N_("Toggles whether the Role field is displayed"), + G_CALLBACK (action_view_role_cb), + FALSE }, + + { "view-rsvp", + NULL, + N_("_RSVP"), + NULL, + N_("Toggles whether the RSVP field is displayed"), + G_CALLBACK (action_view_rsvp_cb), + FALSE }, + + { "view-status", + NULL, + N_("_Status Field"), + NULL, + N_("Toggles whether the Status field is displayed"), + G_CALLBACK (action_view_status_cb), + FALSE }, + + { "view-type", + NULL, + N_("_Type Field"), + NULL, + N_("Toggles whether the Attendee Type is displayed"), + G_CALLBACK (action_view_type_cb), + FALSE } +}; + +static void +comp_editor_set_shell (CompEditor *editor, + EShell *shell) +{ + g_return_if_fail (E_IS_SHELL (shell)); + g_return_if_fail (editor->priv->shell == NULL); + + editor->priv->shell = shell; + + g_object_add_weak_pointer (G_OBJECT (shell), &editor->priv->shell); +} + +static void +comp_editor_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CHANGED: + comp_editor_set_changed ( + COMP_EDITOR (object), + g_value_get_boolean (value)); + return; + + case PROP_CLIENT: + comp_editor_set_client ( + COMP_EDITOR (object), + g_value_get_object (value)); + return; + + case PROP_FLAGS: + comp_editor_set_flags ( + COMP_EDITOR (object), + g_value_get_int (value)); + return; + + case PROP_SHELL: + comp_editor_set_shell ( + COMP_EDITOR (object), + g_value_get_object (value)); + return; + + case PROP_SUMMARY: + comp_editor_set_summary ( + COMP_EDITOR (object), + g_value_get_string (value)); + return; + + case PROP_TIMEZONE: + comp_editor_set_timezone ( + COMP_EDITOR (object), + g_value_get_pointer (value)); + return; + + case PROP_USE_24_HOUR_FORMAT: + comp_editor_set_use_24_hour_format ( + COMP_EDITOR (object), + g_value_get_boolean (value)); + return; + + case PROP_WEEK_START_DAY: + comp_editor_set_week_start_day ( + COMP_EDITOR (object), + g_value_get_enum (value)); + return; + + case PROP_WORK_DAY_END_HOUR: + comp_editor_set_work_day_end_hour ( + COMP_EDITOR (object), + g_value_get_int (value)); + return; + + case PROP_WORK_DAY_END_MINUTE: + comp_editor_set_work_day_end_minute ( + COMP_EDITOR (object), + g_value_get_int (value)); + return; + + case PROP_WORK_DAY_START_HOUR: + comp_editor_set_work_day_start_hour ( + COMP_EDITOR (object), + g_value_get_int (value)); + return; + + case PROP_WORK_DAY_START_MINUTE: + comp_editor_set_work_day_start_minute ( + COMP_EDITOR (object), + g_value_get_int (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +comp_editor_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CHANGED: + g_value_set_boolean ( + value, comp_editor_get_changed ( + COMP_EDITOR (object))); + return; + + case PROP_CLIENT: + g_value_set_object ( + value, comp_editor_get_client ( + COMP_EDITOR (object))); + return; + + case PROP_FLAGS: + g_value_set_int ( + value, comp_editor_get_flags ( + COMP_EDITOR (object))); + return; + + case PROP_FOCUS_TRACKER: + g_value_set_object ( + value, comp_editor_get_focus_tracker ( + COMP_EDITOR (object))); + return; + + case PROP_SHELL: + g_value_set_object ( + value, comp_editor_get_shell ( + COMP_EDITOR (object))); + return; + + case PROP_SUMMARY: + g_value_set_string ( + value, comp_editor_get_summary ( + COMP_EDITOR (object))); + return; + + case PROP_TIMEZONE: + g_value_set_pointer ( + value, comp_editor_get_timezone ( + COMP_EDITOR (object))); + return; + + case PROP_USE_24_HOUR_FORMAT: + g_value_set_boolean ( + value, comp_editor_get_use_24_hour_format ( + COMP_EDITOR (object))); + return; + + case PROP_WEEK_START_DAY: + g_value_set_enum ( + value, comp_editor_get_week_start_day ( + COMP_EDITOR (object))); + return; + + case PROP_WORK_DAY_END_HOUR: + g_value_set_int ( + value, comp_editor_get_work_day_end_hour ( + COMP_EDITOR (object))); + return; + + case PROP_WORK_DAY_END_MINUTE: + g_value_set_int ( + value, comp_editor_get_work_day_end_minute ( + COMP_EDITOR (object))); + return; + + case PROP_WORK_DAY_START_HOUR: + g_value_set_int ( + value, comp_editor_get_work_day_start_hour ( + COMP_EDITOR (object))); + return; + + case PROP_WORK_DAY_START_MINUTE: + g_value_set_int ( + value, comp_editor_get_work_day_start_minute ( + COMP_EDITOR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +unref_page_cb (gpointer editor_page, + gpointer comp_editor) +{ + if (IS_COMP_EDITOR_PAGE (editor_page)) { + GtkWidget *page_widget; + CompEditorPage *page = COMP_EDITOR_PAGE (editor_page); + CompEditor *editor = COMP_EDITOR (comp_editor); + + g_return_if_fail (page != NULL); + g_return_if_fail (editor != NULL); + + page_widget = comp_editor_page_get_widget (page); + g_signal_handlers_disconnect_matched ( + page_widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page); + } + + g_signal_handlers_disconnect_matched ( + editor_page, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, comp_editor); + g_object_unref (editor_page); +} + +static void +comp_editor_dispose (GObject *object) { CompEditorPrivate *priv; - const char *uid; - priv = editor->priv; + priv = COMP_EDITOR_GET_PRIVATE (object); - cal_component_get_uid (priv->comp, &uid); - priv->updating = TRUE; - cal_client_remove_object (priv->client, uid); - priv->updating = FALSE; - close_dialog (editor); + if (priv->shell != NULL) { + g_object_remove_weak_pointer ( + G_OBJECT (priv->shell), &priv->shell); + priv->shell = NULL; + } + + if (priv->focus_tracker != NULL) { + g_object_unref (priv->focus_tracker); + priv->focus_tracker = NULL; + } + + if (priv->window_group != NULL) { + g_object_unref (priv->window_group); + priv->window_group = NULL; + } + + if (priv->cal_client) { + g_object_unref (priv->cal_client); + priv->cal_client = NULL; + } + + if (priv->source_client) { + g_object_unref (priv->source_client); + priv->source_client = NULL; + } + + if (priv->view_cancellable) { + g_cancellable_cancel (priv->view_cancellable); + g_object_unref (priv->view_cancellable); + priv->view_cancellable = NULL; + } + + if (priv->view) { + g_signal_handlers_disconnect_matched ( + priv->view, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); + g_object_unref (priv->view); + priv->view = NULL; + } + + if (priv->attachment_view) { + EAttachmentStore *store; + + store = e_attachment_view_get_store ( + E_ATTACHMENT_VIEW (priv->attachment_view)); + g_signal_handlers_disconnect_matched ( + store, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); + g_object_unref (priv->attachment_view); + priv->attachment_view = NULL; + } + + /* We want to destroy the pages after the widgets get destroyed, + * since they have lots of signal handlers connected to the widgets + * with the pages as the data. */ + g_list_foreach (priv->pages, (GFunc) unref_page_cb, object); + g_list_free (priv->pages); + priv->pages = NULL; + + if (priv->comp) { + g_object_unref (priv->comp); + priv->comp = NULL; + } + + if (priv->ui_manager != NULL) { + g_object_unref (priv->ui_manager); + priv->ui_manager = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (comp_editor_parent_class)->dispose (object); +} + +static void +comp_editor_finalize (GObject *object) +{ + CompEditorPrivate *priv; + + priv = COMP_EDITOR_GET_PRIVATE (object); + + g_object_unref (priv->calendar_settings); + g_free (priv->summary); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (comp_editor_parent_class)->finalize (object); +} + +static void +comp_editor_constructed (GObject *object) +{ + e_extensible_load_extensions (E_EXTENSIBLE (object)); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (comp_editor_parent_class)->constructed (object); +} + +static void +comp_editor_bind_settings (CompEditor *editor) +{ + GtkAction *action; + + g_return_if_fail (editor != NULL); + + action = comp_editor_get_action (editor, "view-categories"); + g_settings_bind ( + editor->priv->calendar_settings, "editor-show-categories", + action, "active", + G_SETTINGS_BIND_DEFAULT); + + action = comp_editor_get_action (editor, "view-role"); + g_settings_bind ( + editor->priv->calendar_settings, "editor-show-role", + action, "active", + G_SETTINGS_BIND_DEFAULT); + + action = comp_editor_get_action (editor, "view-rsvp"); + g_settings_bind ( + editor->priv->calendar_settings, "editor-show-rsvp", + action, "active", + G_SETTINGS_BIND_DEFAULT); + + action = comp_editor_get_action (editor, "view-status"); + g_settings_bind ( + editor->priv->calendar_settings, "editor-show-status", + action, "active", + G_SETTINGS_BIND_DEFAULT); + + action = comp_editor_get_action (editor, "view-time-zone"); + g_settings_bind ( + editor->priv->calendar_settings, "editor-show-timezone", + action, "active", + G_SETTINGS_BIND_DEFAULT); + + action = comp_editor_get_action (editor, "view-type"); + g_settings_bind ( + editor->priv->calendar_settings, "editor-show-type", + action, "active", + G_SETTINGS_BIND_DEFAULT); +} + +static gboolean +comp_editor_delete_event (GtkWidget *widget, + GdkEventAny *event) +{ + CompEditor *editor; + + editor = COMP_EDITOR (widget); + + commit_all_fields (editor); + + if (prompt_and_save_changes (editor, TRUE)) + close_dialog (editor); + + return TRUE; +} + +static gboolean +comp_editor_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + CompEditor *editor; + + editor = COMP_EDITOR (widget); + + if (event->keyval == GDK_KEY_Escape) { + commit_all_fields (editor); + + if (prompt_and_save_changes (editor, TRUE)) + close_dialog (editor); + + return TRUE; + } + + /* Chain up to parent's key_press_event() method. */ + return GTK_WIDGET_CLASS (comp_editor_parent_class)-> + key_press_event (widget, event); +} + +static gboolean +comp_editor_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + CompEditorPrivate *priv; + EAttachmentView *view; + + priv = COMP_EDITOR_GET_PRIVATE (widget); + view = E_ATTACHMENT_VIEW (priv->attachment_view); + + return e_attachment_view_drag_motion (view, context, x, y, time); +} + +static void +comp_editor_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection, + guint info, + guint time) +{ + CompEditorPrivate *priv; + EAttachmentView *view; + + priv = COMP_EDITOR_GET_PRIVATE (widget); + view = E_ATTACHMENT_VIEW (priv->attachment_view); + + /* Forward the data to the attachment view. Note that calling + * e_attachment_view_drag_data_received() will not work because + * that function only handles the case where all the other drag + * handlers have failed. */ + e_attachment_paned_drag_data_received ( + E_ATTACHMENT_PANED (view), + context, x, y, selection, info, time); +} + +static void +comp_editor_class_init (CompEditorClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + g_type_class_add_private (class, sizeof (CompEditorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = comp_editor_set_property; + object_class->get_property = comp_editor_get_property; + object_class->dispose = comp_editor_dispose; + object_class->finalize = comp_editor_finalize; + object_class->constructed = comp_editor_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->delete_event = comp_editor_delete_event; + widget_class->key_press_event = comp_editor_key_press_event; + widget_class->drag_motion = comp_editor_drag_motion; + widget_class->drag_data_received = comp_editor_drag_data_received; + + class->help_section = "memos-usage"; + class->edit_comp = real_edit_comp; + class->send_comp = real_send_comp; + class->object_created = NULL; + + g_object_class_install_property ( + object_class, + PROP_CHANGED, + g_param_spec_boolean ( + "changed", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_CLIENT, + g_param_spec_object ( + "client", + NULL, + NULL, + E_TYPE_CAL_CLIENT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + /* FIXME: Use a proper flags type instead of int. */ + g_object_class_install_property ( + object_class, + PROP_FLAGS, + g_param_spec_int ( + "flags", + NULL, + NULL, + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_FOCUS_TRACKER, + g_param_spec_object ( + "focus-tracker", + NULL, + NULL, + E_TYPE_FOCUS_TRACKER, + G_PARAM_READABLE)); + + g_object_class_install_property ( + object_class, + PROP_SHELL, + g_param_spec_object ( + "shell", + NULL, + NULL, + E_TYPE_SHELL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property ( + object_class, + PROP_SUMMARY, + g_param_spec_string ( + "summary", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_TIMEZONE, + g_param_spec_pointer ( + "timezone", + "Time Zone", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_USE_24_HOUR_FORMAT, + g_param_spec_boolean ( + "use-24-hour-format", + "Use 24-hour Format", + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_WEEK_START_DAY, + g_param_spec_enum ( + "week-start-day", + "Week Start Day", + NULL, + E_TYPE_DATE_WEEKDAY, + G_DATE_MONDAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_WORK_DAY_END_HOUR, + g_param_spec_int ( + "work-day-end-hour", + "Work Day End Hour", + NULL, + 0, + 23, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_WORK_DAY_END_MINUTE, + g_param_spec_int ( + "work-day-end-minute", + "Work Day End Minute", + NULL, + 0, + 59, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_WORK_DAY_START_HOUR, + g_param_spec_int ( + "work-day-start-hour", + "Work Day Start Hour", + NULL, + 0, + 23, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_WORK_DAY_START_MINUTE, + g_param_spec_int ( + "work-day-start-minute", + "Work Day Start Minute", + NULL, + 0, + 59, + 0, + G_PARAM_READWRITE)); + + signals[OBJECT_CREATED] = g_signal_new ( + "object_created", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CompEditorClass, object_created), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[COMP_CLOSED] = g_signal_new ( + "comp_closed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CompEditorClass, comp_closed), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} + +static void +comp_editor_init (CompEditor *editor) +{ + CompEditorPrivate *priv; + EAttachmentView *view; + EAttachmentStore *store; + EFocusTracker *focus_tracker; + GdkDragAction drag_actions; + GtkTargetList *target_list; + GtkTargetEntry *targets; + GtkActionGroup *action_group; + GtkActionGroup *action_group_2; + GtkAction *action; + GtkWidget *container; + GtkWidget *widget; + GtkWindow *window; + EShell *shell; + gboolean express_mode; + gint n_targets; + GError *error = NULL; + + /* FIXME We already have a 'shell' property. Move stuff + * that depends on it to a constructed() method. */ + shell = e_shell_get_default (); + express_mode = e_shell_get_express_mode (shell); + + editor->priv = priv = COMP_EDITOR_GET_PRIVATE (editor); + + g_object_weak_ref ( + G_OBJECT (editor), (GWeakNotify) + comp_editor_weak_notify_cb, NULL); + + active_editors = g_list_prepend (active_editors, editor); + + priv->calendar_settings = g_settings_new ("org.gnome.evolution.calendar"); + + /* Each editor window gets its own window group. */ + window = GTK_WINDOW (editor); + priv->window_group = gtk_window_group_new (); + gtk_window_group_add_window (priv->window_group, window); + + priv->pages = NULL; + priv->changed = FALSE; + priv->needs_send = FALSE; + priv->mod = CALOBJ_MOD_ALL; + priv->existing_org = FALSE; + priv->user_org = FALSE; + priv->warned = FALSE; + priv->is_group_item = FALSE; + priv->saved = FALSE; + + priv->ui_manager = gtk_ui_manager_new (); + + gtk_window_add_accel_group ( + GTK_WINDOW (editor), + gtk_ui_manager_get_accel_group (priv->ui_manager)); + + /* Setup Action Groups */ + + 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), editor); + gtk_action_group_add_toggle_actions ( + action_group, core_toggle_entries, + G_N_ELEMENTS (core_toggle_entries), editor); + gtk_ui_manager_insert_action_group ( + 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 (GTK_STOCK_CLOSE); + emblemed_icon = g_themed_icon_new (GTK_STOCK_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"); + g_object_bind_property ( + save_action, "sensitive", + action, "sensitive", + G_BINDING_SYNC_CREATE); + } + + action_group = gtk_action_group_new ("individual"); + gtk_action_group_set_translation_domain ( + action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions ( + action_group, individual_entries, + G_N_ELEMENTS (individual_entries), editor); + gtk_action_group_add_radio_actions ( + action_group, classification_radio_entries, + G_N_ELEMENTS (classification_radio_entries), + E_CAL_COMPONENT_CLASS_PUBLIC, + G_CALLBACK (action_classification_cb), editor); + gtk_ui_manager_insert_action_group ( + 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_ui_manager_insert_action_group ( + priv->ui_manager, action_group, 0); + g_object_unref (action_group); + + action_group = gtk_action_group_new ("coordinated"); + gtk_action_group_set_translation_domain ( + action_group, GETTEXT_PACKAGE); + gtk_action_group_add_toggle_actions ( + action_group, coordinated_toggle_entries, + G_N_ELEMENTS (coordinated_toggle_entries), editor); + gtk_ui_manager_insert_action_group ( + priv->ui_manager, action_group, 0); + g_object_unref (action_group); + + /* Configure an EFocusTracker to manage selection actions. */ + + focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor)); + + action = comp_editor_get_action (editor, "cut-clipboard"); + e_focus_tracker_set_cut_clipboard_action (focus_tracker, action); + + action = comp_editor_get_action (editor, "copy-clipboard"); + e_focus_tracker_set_copy_clipboard_action (focus_tracker, action); + + action = comp_editor_get_action (editor, "paste-clipboard"); + e_focus_tracker_set_paste_clipboard_action (focus_tracker, action); + + action = comp_editor_get_action (editor, "delete-selection"); + e_focus_tracker_set_delete_selection_action (focus_tracker, action); + + action = comp_editor_get_action (editor, "select-all"); + e_focus_tracker_set_select_all_action (focus_tracker, action); + + priv->focus_tracker = focus_tracker; + + /* Fine Tuning */ + + action = comp_editor_get_action (editor, "attach"); + g_object_set (action, "short-label", _("Attach"), NULL); + + /* Desensitize the "save" action. */ + action = comp_editor_get_action (editor, "save"); + gtk_action_set_sensitive (action, FALSE); + + gtk_ui_manager_add_ui_from_string (priv->ui_manager, ui, -1, &error); + if (error != NULL) { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + /* Setup Widgets */ + + container = GTK_WIDGET (editor); + + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + container = widget; + + widget = comp_editor_get_managed_widget (editor, "/main-menu"); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + gtk_widget_set_visible (widget, TRUE); + + widget = comp_editor_get_managed_widget (editor, "/main-toolbar"); + gtk_box_pack_start (GTK_BOX (container), 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_attachment_paned_new (); + e_attachment_paned_set_resize_toplevel ( + E_ATTACHMENT_PANED (widget), TRUE); + gtk_container_set_border_width (GTK_CONTAINER (widget), 6); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->attachment_view = g_object_ref (widget); + gtk_widget_show (widget); + + if (express_mode) { + widget = e_attachment_paned_get_view_combo ( + E_ATTACHMENT_PANED (widget)); + gtk_widget_hide (widget); + } + + container = e_attachment_paned_get_content_area ( + E_ATTACHMENT_PANED (priv->attachment_view)); + + widget = gtk_notebook_new (); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), express_mode); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->notebook = GTK_NOTEBOOK (widget); + gtk_widget_show (widget); + + /* Drag-and-Drop Support */ + + view = E_ATTACHMENT_VIEW (priv->attachment_view); + target_list = e_attachment_view_get_target_list (view); + drag_actions = e_attachment_view_get_drag_actions (view); + + targets = gtk_target_table_new_from_list (target_list, &n_targets); + + gtk_drag_dest_set ( + GTK_WIDGET (editor), GTK_DEST_DEFAULT_ALL, + targets, n_targets, drag_actions); + + gtk_target_table_free (targets, n_targets); + + gtk_window_set_type_hint ( + GTK_WINDOW (editor), GDK_WINDOW_TYPE_HINT_NORMAL); + + action_group = comp_editor_get_action_group (editor, "individual"); + action_group_2 = e_attachment_view_get_action_group (view, "editable"); + + g_object_bind_property ( + action_group, "sensitive", + action_group_2, "sensitive", + G_BINDING_SYNC_CREATE); + + /* Listen for attachment store changes. */ + + store = e_attachment_view_get_store (view); + + g_signal_connect_swapped ( + store, "row-deleted", + G_CALLBACK (attachment_store_changed_cb), editor); + + g_signal_connect_swapped ( + store, "row-inserted", + G_CALLBACK (attachment_store_changed_cb), editor); + + comp_editor_bind_settings (editor); + + gtk_application_add_window ( + GTK_APPLICATION (shell), GTK_WINDOW (editor)); } static gboolean -prompt_to_save_changes (CompEditor *editor, gboolean send) +prompt_and_save_changes (CompEditor *editor, + gboolean send) { CompEditorPrivate *priv; + gboolean correct = FALSE; + ECalComponent *comp; + ECalComponentText text; priv = editor->priv; if (!priv->changed) return TRUE; - switch (save_component_dialog (GTK_WINDOW (editor))) { - case 0: /* Save */ + switch (save_component_dialog (GTK_WINDOW (editor), priv->comp)) { + case GTK_RESPONSE_YES: /* Save */ + if (e_client_is_readonly (E_CLIENT (priv->cal_client))) { + e_alert_submit ( + E_ALERT_SINK (editor), + "calendar:prompt-read-only-cal-editor", + e_source_get_display_name ( + e_client_get_source (E_CLIENT (priv->cal_client))), + NULL); + /* don't discard changes when selected readonly calendar */ + return FALSE; + } + + if ((comp_editor_get_flags (editor) & COMP_EDITOR_IS_ASSIGNED) != 0 + && e_cal_component_get_vtype (priv->comp) == E_CAL_COMPONENT_TODO + && e_client_check_capability (E_CLIENT (priv->cal_client), CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT)) { + e_alert_submit ( + E_ALERT_SINK (editor), + "calendar:prompt-no-task-assignment-editor", + e_source_get_display_name ( + e_client_get_source (E_CLIENT (priv->cal_client))), + NULL); + return FALSE; + } + + comp = comp_editor_get_current_comp (editor, &correct); + e_cal_component_get_summary (comp, &text); + g_object_unref (comp); + + if (!correct) + return FALSE; + + if (!text.value) + if (!send_component_prompt_subject ( + (GtkWindow *) editor, priv->cal_client, priv->comp)) + return FALSE; + + if (e_cal_component_is_instance (priv->comp)) + if (!recur_component_dialog ( + priv->cal_client, priv->comp, &priv->mod, + GTK_WINDOW (editor), FALSE)) + return FALSE; + if (send && save_comp_with_send (editor)) return TRUE; else if (!send && save_comp (editor)) return TRUE; else return FALSE; - case 1: /* Discard */ + case GTK_RESPONSE_NO: /* Discard */ return TRUE; - case 2: /* Cancel */ + case GTK_RESPONSE_CANCEL: /* Cancel */ default: return FALSE; } } -/* This sets the focus to the toplevel, so any field being edited is committed. - FIXME: In future we may also want to check some of the fields are valid, - e.g. the EDateEdit fields. */ +/* Menu callbacks */ + static void -commit_all_fields (CompEditor *editor) +comp_editor_show_help (CompEditor *editor) { - CompEditorPrivate *priv; + CompEditorClass *class; - priv = editor->priv; + class = COMP_EDITOR_GET_CLASS (editor); + g_return_if_fail (class->help_section != NULL); - gtk_window_set_focus (GTK_WINDOW (editor), NULL); + e_display_help (GTK_WINDOW (editor), class->help_section); } /* Closes the dialog box and emits the appropriate signals */ static void close_dialog (CompEditor *editor) { - CompEditorPrivate *priv; + CompEditorPrivate *priv = editor->priv; - priv = editor->priv; + g_signal_emit_by_name (editor, "comp_closed", priv->saved); + + /* FIXME Unfortunately we do this here because otherwise corba + * calls happen during destruction and we might get a change + * notification back when we are in an inconsistent state */ + if (priv->view) + g_signal_handlers_disconnect_matched ( + priv->view, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, editor); - gtk_object_destroy (GTK_OBJECT (editor)); + gtk_widget_destroy (GTK_WIDGET (editor)); } - +static gint +comp_editor_compare (CompEditor *editor_a, + const gchar *uid_b) +{ + const gchar *uid_a = NULL; + + e_cal_component_get_uid (editor_a->priv->comp, &uid_a); + + return g_strcmp0 (uid_a, uid_b); +} void -comp_editor_set_existing_org (CompEditor *editor, gboolean existing_org) +comp_editor_set_existing_org (CompEditor *editor, + gboolean existing_org) { - CompEditorPrivate *priv; - - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); - priv = editor->priv; - - priv->existing_org = existing_org; + editor->priv->existing_org = existing_org; } gboolean comp_editor_get_existing_org (CompEditor *editor) { - CompEditorPrivate *priv; + g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); + + return editor->priv->existing_org; +} + +void +comp_editor_set_user_org (CompEditor *editor, + gboolean user_org) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); - g_return_val_if_fail (editor != NULL, FALSE); + editor->priv->user_org = user_org; +} + +gboolean +comp_editor_get_user_org (CompEditor *editor) +{ g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); - priv = editor->priv; + return editor->priv->user_org; +} - return priv->existing_org; +void +comp_editor_set_group_item (CompEditor *editor, + gboolean group_item) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + editor->priv->is_group_item = group_item; +} + +gboolean +comp_editor_get_group_item (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); + + return editor->priv->is_group_item; } void -comp_editor_set_user_org (CompEditor *editor, gboolean user_org) +comp_editor_set_classification (CompEditor *editor, + ECalComponentClassification classification) { - CompEditorPrivate *priv; + GtkAction *action; - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); - priv = editor->priv; + switch (classification) { + case E_CAL_COMPONENT_CLASS_PUBLIC: + case E_CAL_COMPONENT_CLASS_PRIVATE: + case E_CAL_COMPONENT_CLASS_CONFIDENTIAL: + break; + default: + classification = E_CAL_COMPONENT_CLASS_PUBLIC; + break; + } - priv->user_org = user_org; + action = comp_editor_get_action (editor, "classify-public"); + gtk_radio_action_set_current_value ( + GTK_RADIO_ACTION (action), classification); } -gboolean -comp_editor_get_user_org (CompEditor *editor) +ECalComponentClassification +comp_editor_get_classification (CompEditor *editor) { - CompEditorPrivate *priv; + GtkAction *action; + + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); + + action = comp_editor_get_action (editor, "classify-public"); + return gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action)); +} + +EShell * +comp_editor_get_shell (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + + return editor->priv->shell; +} + +void +comp_editor_set_summary (CompEditor *editor, + const gchar *summary) +{ + gboolean show_warning; + + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (g_strcmp0 (editor->priv->summary, summary) == 0) + return; + + g_free (editor->priv->summary); + editor->priv->summary = g_strdup (summary); + + show_warning = + !editor->priv->warned && + !(editor->priv->flags & COMP_EDITOR_DELEGATE) && + editor->priv->existing_org && + !editor->priv->user_org && !(editor->priv->flags & COMP_EDITOR_NEW_ITEM); + + if (show_warning) { + e_notice ( + editor->priv->notebook, GTK_MESSAGE_INFO, + _("Changes made to this item may be " + "discarded if an update arrives")); + editor->priv->warned = TRUE; + } + + update_window_border (editor, summary); + + g_object_notify (G_OBJECT (editor), "summary"); +} + +const gchar * +comp_editor_get_summary (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + + return editor->priv->summary; +} + +icaltimezone * +comp_editor_get_timezone (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + + return editor->priv->zone; +} + +void +comp_editor_set_timezone (CompEditor *editor, + icaltimezone *zone) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->zone == zone) + return; + + editor->priv->zone = zone; - g_return_val_if_fail (editor != NULL, FALSE); + g_object_notify (G_OBJECT (editor), "timezone"); +} + +gboolean +comp_editor_get_use_24_hour_format (CompEditor *editor) +{ g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); - priv = editor->priv; + return editor->priv->use_24_hour_format; +} + +void +comp_editor_set_use_24_hour_format (CompEditor *editor, + gboolean use_24_hour_format) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->use_24_hour_format == use_24_hour_format) + return; + + editor->priv->use_24_hour_format = use_24_hour_format; + + g_object_notify (G_OBJECT (editor), "use-24-hour-format"); +} + +GDateWeekday +comp_editor_get_week_start_day (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); - return priv->user_org; + return editor->priv->week_start_day; } +void +comp_editor_set_week_start_day (CompEditor *editor, + GDateWeekday week_start_day) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + g_return_if_fail (g_date_valid_weekday (week_start_day)); + + if (week_start_day == editor->priv->week_start_day) + return; + + editor->priv->week_start_day = week_start_day; + + g_object_notify (G_OBJECT (editor), "week-start-day"); +} + +gint +comp_editor_get_work_day_end_hour (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); + + return editor->priv->work_day_end_hour; +} + +void +comp_editor_set_work_day_end_hour (CompEditor *editor, + gint work_day_end_hour) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->work_day_end_hour == work_day_end_hour) + return; + + editor->priv->work_day_end_hour = work_day_end_hour; + + g_object_notify (G_OBJECT (editor), "work-day-end-hour"); +} + +gint +comp_editor_get_work_day_end_minute (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); + + return editor->priv->work_day_end_minute; +} + +void +comp_editor_set_work_day_end_minute (CompEditor *editor, + gint work_day_end_minute) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->work_day_end_minute == work_day_end_minute) + return; + + editor->priv->work_day_end_minute = work_day_end_minute; + + g_object_notify (G_OBJECT (editor), "work-day-end-minute"); +} + +gint +comp_editor_get_work_day_start_hour (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); + + return editor->priv->work_day_start_hour; +} + +void +comp_editor_set_work_day_start_hour (CompEditor *editor, + gint work_day_start_hour) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->work_day_start_hour == work_day_start_hour) + return; + + editor->priv->work_day_start_hour = work_day_start_hour; + + g_object_notify (G_OBJECT (editor), "work-day-start-hour"); +} + +gint +comp_editor_get_work_day_start_minute (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); + + return editor->priv->work_day_start_minute; +} + +void +comp_editor_set_work_day_start_minute (CompEditor *editor, + gint work_day_start_minute) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->work_day_start_minute == work_day_start_minute) + return; + + editor->priv->work_day_start_minute = work_day_start_minute; + + g_object_notify (G_OBJECT (editor), "work-day-start-minute"); +} /** * comp_editor_set_changed: @@ -518,13 +2729,41 @@ comp_editor_get_user_org (CompEditor *editor) * Set the dialog changed state to the given value **/ void -comp_editor_set_changed (CompEditor *editor, gboolean changed) +comp_editor_set_changed (CompEditor *editor, + gboolean changed) { - CompEditorPrivate *priv; + GtkAction *action; + gboolean show_warning; - priv = editor->priv; + g_return_if_fail (IS_COMP_EDITOR (editor)); - priv->changed = changed; + /* always process below changes, because other parts of + * the editor listen for 'changed' notifications to update + * its widgets, thus do it even the value actually didn't change + */ + if (editor->priv->changed != changed) { + editor->priv->changed = changed; + + action = comp_editor_get_action (editor, "save"); + g_return_if_fail (action != NULL); + gtk_action_set_sensitive (action, changed); + } + + show_warning = + changed && !editor->priv->warned && + !(editor->priv->flags & COMP_EDITOR_DELEGATE) && + editor->priv->existing_org && !editor->priv->user_org + && !(editor->priv->flags & COMP_EDITOR_NEW_ITEM); + + if (show_warning) { + e_notice ( + editor->priv->notebook, GTK_MESSAGE_INFO, + _("Changes made to this item may be " + "discarded if an update arrives")); + editor->priv->warned = TRUE; + } + + g_object_notify (G_OBJECT (editor), "changed"); } /** @@ -539,11 +2778,107 @@ comp_editor_set_changed (CompEditor *editor, gboolean changed) gboolean comp_editor_get_changed (CompEditor *editor) { - CompEditorPrivate *priv; + g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); - priv = editor->priv; + return editor->priv->changed; +} + +EFocusTracker * +comp_editor_get_focus_tracker (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + + return editor->priv->focus_tracker; +} + +void +comp_editor_set_flags (CompEditor *editor, + CompEditorFlags flags) +{ + g_return_if_fail (IS_COMP_EDITOR (editor)); + + if (editor->priv->flags == flags) + return; + + editor->priv->flags = flags; + editor->priv->user_org = flags & COMP_EDITOR_USER_ORG; + + g_object_notify (G_OBJECT (editor), "flags"); +} + +CompEditorFlags +comp_editor_get_flags (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), 0); + + return editor->priv->flags; +} + +GtkUIManager * +comp_editor_get_ui_manager (CompEditor *editor) +{ + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); - return priv->changed; + return editor->priv->ui_manager; +} + +GtkAction * +comp_editor_get_action (CompEditor *editor, + const gchar *action_name) +{ + GtkUIManager *ui_manager; + + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + g_return_val_if_fail (action_name != NULL, NULL); + + ui_manager = comp_editor_get_ui_manager (editor); + + return e_lookup_action (ui_manager, action_name); +} + +GtkActionGroup * +comp_editor_get_action_group (CompEditor *editor, + const gchar *group_name) +{ + GtkUIManager *ui_manager; + + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + g_return_val_if_fail (group_name != NULL, NULL); + + ui_manager = comp_editor_get_ui_manager (editor); + + return e_lookup_action_group (ui_manager, group_name); +} + +GtkWidget * +comp_editor_get_managed_widget (CompEditor *editor, + const gchar *widget_path) +{ + GtkUIManager *ui_manager; + GtkWidget *widget; + + g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); + g_return_val_if_fail (widget_path != NULL, NULL); + + ui_manager = comp_editor_get_ui_manager (editor); + widget = gtk_ui_manager_get_widget (ui_manager, widget_path); + g_return_val_if_fail (widget != NULL, NULL); + + return widget; +} + +CompEditor * +comp_editor_find_instance (const gchar *uid) +{ + GList *link; + + g_return_val_if_fail (uid != NULL, NULL); + + link = g_list_find_custom ( + active_editors, uid, + (GCompareFunc) comp_editor_compare); + + return (link != NULL) ? link->data : NULL; } /** @@ -554,13 +2889,12 @@ comp_editor_get_changed (CompEditor *editor) * Set the dialog needs send state to the given value **/ void -comp_editor_set_needs_send (CompEditor *editor, gboolean needs_send) +comp_editor_set_needs_send (CompEditor *editor, + gboolean needs_send) { - CompEditorPrivate *priv; - - priv = editor->priv; + g_return_if_fail (IS_COMP_EDITOR (editor)); - priv->needs_send = needs_send; + editor->priv->needs_send = needs_send; } /** @@ -575,15 +2909,14 @@ comp_editor_set_needs_send (CompEditor *editor, gboolean needs_send) gboolean comp_editor_get_needs_send (CompEditor *editor) { - CompEditorPrivate *priv; - - priv = editor->priv; + g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); - return priv->needs_send; + return editor->priv->needs_send; } -static void page_mapped_cb (GtkWidget *page_widget, - CompEditorPage *page) +static void +page_mapped_cb (GtkWidget *page_widget, + CompEditorPage *page) { GtkWidget *toplevel; @@ -592,13 +2925,15 @@ static void page_mapped_cb (GtkWidget *page_widget, return; if (page->accel_group) { - gtk_window_add_accel_group (GTK_WINDOW (toplevel), - page->accel_group); + gtk_window_add_accel_group ( + GTK_WINDOW (toplevel), + page->accel_group); } } -static void page_unmapped_cb (GtkWidget *page_widget, - CompEditorPage *page) +static void +page_unmapped_cb (GtkWidget *page_widget, + CompEditorPage *page) { GtkWidget *toplevel; @@ -607,72 +2942,127 @@ static void page_unmapped_cb (GtkWidget *page_widget, return; if (page->accel_group) { - gtk_window_remove_accel_group (GTK_WINDOW (toplevel), - page->accel_group); + gtk_window_remove_accel_group (GTK_WINDOW (toplevel), page->accel_group); + } +} + +/** + * comp_editor_append_widget: + * @editor: A component editor + * @page: A component editor page + * @label: Label of the page. Should be NULL if add is FALSE. + * @add: Add's the page into the notebook if TRUE + * + * Appends a page to the notebook if add is TRUE else + * just adds it to the list of pages. + **/ +void +comp_editor_append_widget (CompEditor *editor, + GtkWidget *page, + const gchar *label, + gboolean add) +{ + CompEditorPrivate *priv; + GtkWidget *label_widget = NULL; + + g_return_if_fail (IS_COMP_EDITOR (editor)); + + priv = editor->priv; + + g_object_ref (page); + + if (label) + label_widget = gtk_label_new_with_mnemonic (label); + + priv->pages = g_list_append (priv->pages, page); + + if (add) { + gtk_notebook_append_page (priv->notebook, page, label_widget); + gtk_container_child_set ( + GTK_CONTAINER (priv->notebook), page, + "tab-fill", FALSE, "tab-expand", FALSE, NULL); } + + /* Listen for when the page is mapped/unmapped so we can + * install/uninstall the appropriate GtkAccelGroup. + g_signal_connect ( + page, "map", + G_CALLBACK (page_mapped_cb), page); + g_signal_connect ( + page, "unmap", + G_CALLBACK (page_unmapped_cb), page); + */ + } /** * comp_editor_append_page: * @editor: A component editor * @page: A component editor page - * @label: Label of the page + * @label: Label of the page. Should be NULL if add is FALSE. + * @add: Add's the page into the notebook if TRUE * - * Appends a page to the editor notebook with the given label + * Appends a page to the notebook if add is TRUE else + * just adds it to the list of pages. **/ void comp_editor_append_page (CompEditor *editor, - CompEditorPage *page, - const char *label) + CompEditorPage *page, + const gchar *label, + gboolean add) { CompEditorPrivate *priv; GtkWidget *page_widget; - GtkWidget *label_widget; + GtkWidget *label_widget = NULL; gboolean is_first_page; - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); - g_return_if_fail (page != NULL); g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); - g_return_if_fail (label != NULL); priv = editor->priv; - gtk_object_ref (GTK_OBJECT (page)); + g_object_ref (page); /* If we are editing something, fill the widgets with current info */ if (priv->comp != NULL) { - CalComponent *comp; + ECalComponent *comp; - comp = comp_editor_get_current_comp (editor); + comp = comp_editor_get_current_comp (editor, NULL); comp_editor_page_fill_widgets (page, comp); - gtk_object_unref (GTK_OBJECT (comp)); + g_object_unref (comp); } page_widget = comp_editor_page_get_widget (page); - g_assert (page_widget != NULL); + g_return_if_fail (page_widget != NULL); - label_widget = gtk_label_new (label); + if (label) + label_widget = gtk_label_new_with_mnemonic (label); is_first_page = (priv->pages == NULL); priv->pages = g_list_append (priv->pages, page); - gtk_notebook_append_page (priv->notebook, page_widget, label_widget); + + if (add) { + gtk_notebook_append_page ( + priv->notebook, page_widget, label_widget); + gtk_container_child_set ( + GTK_CONTAINER (priv->notebook), page_widget, + "tab-fill", FALSE, "tab-expand", FALSE, NULL); + } /* Listen for things happening on the page */ - gtk_signal_connect (GTK_OBJECT (page), "changed", - GTK_SIGNAL_FUNC (page_changed_cb), editor); - gtk_signal_connect (GTK_OBJECT (page), "summary_changed", - GTK_SIGNAL_FUNC (page_summary_changed_cb), editor); - gtk_signal_connect (GTK_OBJECT (page), "dates_changed", - GTK_SIGNAL_FUNC (page_dates_changed_cb), editor); + g_signal_connect_swapped ( + page, "dates_changed", + G_CALLBACK (page_dates_changed_cb), editor); /* Listen for when the page is mapped/unmapped so we can - install/uninstall the appropriate GtkAccelGroup. */ - gtk_signal_connect (GTK_OBJECT (page_widget), "map", - GTK_SIGNAL_FUNC (page_mapped_cb), page); - gtk_signal_connect (GTK_OBJECT (page_widget), "unmap", - GTK_SIGNAL_FUNC (page_unmapped_cb), page); + * install/uninstall the appropriate GtkAccelGroup. */ + g_signal_connect ( + page_widget, "map", + G_CALLBACK (page_mapped_cb), page); + g_signal_connect ( + page_widget, "unmap", + G_CALLBACK (page_unmapped_cb), page); /* The first page is the main page of the editor, so we ask it to focus * its main widget. @@ -689,15 +3079,14 @@ comp_editor_append_page (CompEditor *editor, * Removes the page from the component editor **/ void -comp_editor_remove_page (CompEditor *editor, CompEditorPage *page) +comp_editor_remove_page (CompEditor *editor, + CompEditorPage *page) { CompEditorPrivate *priv; GtkWidget *page_widget; gint page_num; - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); - g_return_if_fail (page != NULL); g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); priv = editor->priv; @@ -706,15 +3095,17 @@ comp_editor_remove_page (CompEditor *editor, CompEditorPage *page) page_num = gtk_notebook_page_num (priv->notebook, page_widget); if (page_num == -1) return; - + /* Disconnect all the signals added in append_page(). */ - gtk_signal_disconnect_by_data (GTK_OBJECT (page), editor); - gtk_signal_disconnect_by_data (GTK_OBJECT (page_widget), page); + g_signal_handlers_disconnect_matched ( + page, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor); + g_signal_handlers_disconnect_matched ( + page_widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, page); gtk_notebook_remove_page (priv->notebook, page_num); priv->pages = g_list_remove (priv->pages, page); - gtk_object_unref (GTK_OBJECT (page)); + g_object_unref (page); } /** @@ -725,259 +3116,412 @@ comp_editor_remove_page (CompEditor *editor, CompEditorPage *page) * **/ void -comp_editor_show_page (CompEditor *editor, CompEditorPage *page) +comp_editor_show_page (CompEditor *editor, + CompEditorPage *page) { CompEditorPrivate *priv; GtkWidget *page_widget; gint page_num; - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); - g_return_if_fail (page != NULL); g_return_if_fail (IS_COMP_EDITOR_PAGE (page)); priv = editor->priv; page_widget = comp_editor_page_get_widget (page); page_num = gtk_notebook_page_num (priv->notebook, page_widget); - gtk_notebook_set_page (priv->notebook, page_num); + gtk_notebook_set_current_page (priv->notebook, page_num); } /** - * comp_editor_set_cal_client: + * comp_editor_set_client: * @editor: A component editor - * @client: The calendar client to use + * @cal_client: The calendar client to use * * Sets the calendar client used by the editor to update components **/ void -comp_editor_set_cal_client (CompEditor *editor, CalClient *client) +comp_editor_set_client (CompEditor *editor, + ECalClient *cal_client) { - CompEditorClass *klass; - - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); + g_return_if_fail (cal_client == NULL || E_IS_CAL_CLIENT (cal_client)); + + if (editor->priv->cal_client == cal_client) + return; + + if (cal_client != NULL) + g_object_ref (cal_client); - klass = COMP_EDITOR_CLASS (GTK_OBJECT (editor)->klass); + if (editor->priv->cal_client != NULL) + g_object_unref (editor->priv->cal_client); - if (klass->set_cal_client) - klass->set_cal_client (editor, client); + editor->priv->cal_client = cal_client; + + if (editor->priv->source_client == NULL && cal_client != NULL) + editor->priv->source_client = g_object_ref (cal_client); + + g_object_notify (G_OBJECT (editor), "client"); } /** - * comp_editor_get_cal_client: + * comp_editor_get_client: * @editor: A component editor * * Returns the calendar client of the editor * * Return value: The calendar client of the editor **/ -CalClient * -comp_editor_get_cal_client (CompEditor *editor) +ECalClient * +comp_editor_get_client (CompEditor *editor) { - CompEditorPrivate *priv; - - g_return_val_if_fail (editor != NULL, NULL); g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); - priv = editor->priv; - - return priv->client; + return editor->priv->cal_client; } -/* Creates an appropriate title for the event editor dialog */ -static char * -make_title_from_comp (CalComponent *comp) +static void +attachment_loaded_cb (EAttachment *attachment, + GAsyncResult *result, + GtkWindow *parent) { - char *title; - const char *type_string; - CalComponentVType type; - CalComponentText text; - - if (!comp) - return g_strdup (_("Edit Appointment")); + GFileInfo *file_info; + const gchar *display_name; + const gchar *uid; + gchar *new_name; + + /* Prior to 2.27.2, attachment files were named: + * + * <component-uid> '-' <actual-filename> + * ------------------------------------- + * (one long filename) + * + * Here we fix the display name if this form is detected so we + * don't show the component UID in the user interface. If the + * user saves changes in the editor, the attachment will be + * written to disk as: + * + * <component-uid> / <actual-filename> + * --------------- ----------------- + * (directory) (original name) + * + * So this is a lazy migration from the old form to the new. + */ - type = cal_component_get_vtype (comp); - switch (type) { - case CAL_COMPONENT_EVENT: - type_string = _("Appointment - %s"); - break; - case CAL_COMPONENT_TODO: - type_string = _("Task - %s"); - break; - case CAL_COMPONENT_JOURNAL: - type_string = _("Journal entry - %s"); - break; - default: - g_message ("make_title_from_comp(): Cannot handle object of type %d", type); - return NULL; + file_info = e_attachment_ref_file_info (attachment); + if (file_info == NULL) { + /* failed to load an attachment file */ + e_attachment_load_handle_error (attachment, result, parent); + return; } - cal_component_get_summary (comp, &text); - if (text.value) { - char *summary; - summary = e_utf8_to_locale_string (text.value); - title = g_strdup_printf (type_string, summary); - g_free (summary); - } else - title = g_strdup_printf (type_string, _("No summary")); - - return title; -} + display_name = g_file_info_get_display_name (file_info); + uid = g_object_get_data (G_OBJECT (attachment), "uid"); -static const char * -make_icon_from_comp (CalComponent *comp) -{ - CalComponentVType type; - - if (!comp) - return EVOLUTION_ICONSDIR "/evolution-calendar-mini.png"; - - type = cal_component_get_vtype (comp); - switch (type) { - case CAL_COMPONENT_EVENT: - return EVOLUTION_ICONSDIR "/buttons/new_appointment.png"; - break; - case CAL_COMPONENT_TODO: - return EVOLUTION_ICONSDIR "/buttons/new_task.png"; - break; - default: - return EVOLUTION_ICONSDIR "/evolution-calendar-mini.png"; + if (g_str_has_prefix (display_name, uid)) { + new_name = g_strdup (display_name + strlen (uid) + 1); + g_file_info_set_display_name (file_info, new_name); + g_object_notify (G_OBJECT (attachment), "file-info"); + g_free (new_name); } -} -/* Sets the event editor's window title from a calendar component */ -static void -set_title_from_comp (CompEditor *editor) -{ - CompEditorPrivate *priv; - char *title; + g_object_unref (file_info); - priv = editor->priv; - title = make_title_from_comp (priv->comp); - gtk_window_set_title (GTK_WINDOW (editor), title); - g_free (title); + e_attachment_load_handle_error (attachment, result, parent); } static void -set_icon_from_comp (CompEditor *editor) +set_attachment_list (CompEditor *editor, + GSList *uri_list) { - CompEditorPrivate *priv; - const char *file; + EAttachmentStore *store; + EAttachmentView *view; + const gchar *uid = NULL; + GSList *iter; + + view = E_ATTACHMENT_VIEW (editor->priv->attachment_view); + store = e_attachment_view_get_store (view); + + if (e_attachment_store_get_num_attachments (store) > 0) { + /* To prevent repopulating the + * bar due to redraw functions in fill_widget. + * Assumes it can be set only once. + */ + return; + } - priv = editor->priv; - file = make_icon_from_comp (priv->comp); - gnome_window_icon_set_from_file (GTK_WINDOW (editor), file); + /* XXX What an awkward API this is. Takes a return location + * for a constant string instead of just returning it. */ + e_cal_component_get_uid (editor->priv->comp, &uid); + + for (iter = uri_list; iter != NULL; iter = iter->next) { + EAttachment *attachment; + + attachment = e_attachment_new_for_uri (iter->data); + e_attachment_store_add_attachment (store, attachment); + g_object_set_data_full ( + G_OBJECT (attachment), + "uid", g_strdup (uid), + (GDestroyNotify) g_free); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + attachment_loaded_cb, editor); + g_object_unref (attachment); + } } static void fill_widgets (CompEditor *editor) { + EAttachmentStore *store; + EAttachmentView *view; CompEditorPrivate *priv; - GList *l; + GtkAction *action; + GList *iter; + + view = E_ATTACHMENT_VIEW (editor->priv->attachment_view); + store = e_attachment_view_get_store (view); priv = editor->priv; - for (l = priv->pages; l != NULL; l = l->next) - comp_editor_page_fill_widgets (l->data, priv->comp); + /*Check if attachments are available here and set them*/ + if (e_cal_component_has_attachments (priv->comp)) { + GSList *attachment_list = NULL; + e_cal_component_get_attachment_list (priv->comp, &attachment_list); + g_signal_handlers_block_by_func ( + store, G_CALLBACK (attachment_store_changed_cb), + editor); + set_attachment_list (editor, attachment_list); + g_signal_handlers_unblock_by_func ( + store, G_CALLBACK (attachment_store_changed_cb), + editor); + g_slist_foreach (attachment_list, (GFunc) g_free, NULL); + g_slist_free (attachment_list); + } + + action = comp_editor_get_action (editor, "classify-public"); + g_signal_handlers_block_by_func ( + action, G_CALLBACK (action_classification_cb), editor); + + for (iter = priv->pages; iter != NULL; iter = iter->next) { + if (IS_COMP_EDITOR_PAGE (iter->data)) + comp_editor_page_fill_widgets (iter->data, priv->comp); + } + + g_signal_handlers_unblock_by_func ( + action, G_CALLBACK (action_classification_cb), editor); } static void -real_set_cal_client (CompEditor *editor, CalClient *client) +real_edit_comp (CompEditor *editor, + ECalComponent *comp) { CompEditorPrivate *priv; - GList *elem; - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); priv = editor->priv; - if (client == priv->client) - return; - - if (client) { - g_return_if_fail (IS_CAL_CLIENT (client)); - g_return_if_fail (cal_client_get_load_state (client) == - CAL_CLIENT_LOAD_LOADED); - gtk_object_ref (GTK_OBJECT (client)); + if (priv->comp) { + g_object_unref (priv->comp); + priv->comp = NULL; } - if (priv->client) { - gtk_signal_disconnect_by_data (GTK_OBJECT (priv->client), - editor); - gtk_object_unref (GTK_OBJECT (priv->client)); + if (comp) { + priv->comp = e_cal_component_clone (comp); + comp_editor_copy_new_attendees (priv->comp, comp); } - priv->client = client; + priv->existing_org = e_cal_component_has_organizer (comp); + priv->warned = FALSE; + + update_window_border (editor, NULL); - /* Pass the client to any pages that need it. */ - for (elem = priv->pages; elem; elem = elem->next) - comp_editor_page_set_cal_client (elem->data, client); + fill_widgets (editor); - gtk_signal_connect (GTK_OBJECT (priv->client), "obj_updated", - GTK_SIGNAL_FUNC (obj_updated_cb), editor); + comp_editor_set_changed (editor, FALSE); - gtk_signal_connect (GTK_OBJECT (priv->client), "obj_removed", - GTK_SIGNAL_FUNC (obj_removed_cb), editor); + listen_for_changes (editor); } +/* TODO These functions should be available in e-cal-component.c */ static void -real_edit_comp (CompEditor *editor, CalComponent *comp) +set_attendees_for_delegation (ECalComponent *comp, + const gchar *address, + ECalComponentItipMethod method) { - CompEditorPrivate *priv; - - g_return_if_fail (editor != NULL); - g_return_if_fail (IS_COMP_EDITOR (editor)); + icalproperty *prop; + icalparameter *param; + icalcomponent *icalcomp; + + icalcomp = e_cal_component_get_icalcomponent (comp); + + for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); + prop; + prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { + const gchar *attendee = icalproperty_get_attendee (prop); + const gchar *delfrom = NULL; + + 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); + } - priv = editor->priv; + } - if (priv->comp) { - gtk_object_unref (GTK_OBJECT (priv->comp)); - priv->comp = NULL; +} + +static void +get_users_from_memo_comp (ECalComponent *comp, + GSList **users) +{ + icalcomponent *icalcomp; + icalproperty *icalprop; + const gchar *attendees = NULL; + gchar **emails, **iter; + + icalcomp = e_cal_component_get_icalcomponent (comp); + + for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); + icalprop != NULL; + icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { + const gchar *x_name; + + x_name = icalproperty_get_x_name (icalprop); + + if (g_str_equal (x_name, "X-EVOLUTION-RECIPIENTS")) + break; } - if (comp) - priv->comp = cal_component_clone (comp); + if (icalprop) { + attendees = icalproperty_get_x (icalprop); + emails = g_strsplit (attendees, ";", -1); - priv->existing_org = cal_component_has_organizer (comp); - priv->user_org = itip_organizer_is_user (comp); - priv->warned = FALSE; - - set_title_from_comp (editor); - set_icon_from_comp (editor); - fill_widgets (editor); + iter = emails; + while (*iter) { + *users = g_slist_append (*users, g_strdup (*iter)); + iter++; + } + g_strfreev (emails); + } } - static gboolean -real_send_comp (CompEditor *editor, CalComponentItipMethod method) +real_send_comp (CompEditor *editor, + ECalComponentItipMethod method, + gboolean strip_alarms) { CompEditorPrivate *priv; - CalComponent *tmp_comp; - - g_return_val_if_fail (editor != NULL, FALSE); + CompEditorFlags flags; + EShell *shell; + ESourceRegistry *registry; + ECalComponent *send_comp = NULL; + gchar *address = NULL; + GSList *users = NULL; + g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); priv = editor->priv; - if (itip_send_comp (method, priv->comp, priv->client, NULL)) { - tmp_comp = priv->comp; - gtk_object_ref (GTK_OBJECT (tmp_comp)); - comp_editor_edit_comp (editor, tmp_comp); - gtk_object_unref (GTK_OBJECT (tmp_comp)); - - comp_editor_set_changed (editor, TRUE); - save_comp (editor); + flags = comp_editor_get_flags (editor); + shell = comp_editor_get_shell (editor); - return TRUE; + registry = e_shell_get_registry (shell); + + if (priv->mod == CALOBJ_MOD_ALL && e_cal_component_is_instance (priv->comp)) { + /* Ensure we send the master object, not the instance only */ + icalcomponent *icalcomp = NULL; + const gchar *uid = NULL; + + e_cal_component_get_uid (priv->comp, &uid); + if (e_cal_client_get_object_sync (priv->cal_client, uid, NULL, &icalcomp, NULL, NULL) && icalcomp) { + send_comp = e_cal_component_new (); + if (!e_cal_component_set_icalcomponent (send_comp, icalcomp)) { + icalcomponent_free (icalcomp); + g_object_unref (send_comp); + send_comp = NULL; + } + } + } + + if (!send_comp) + send_comp = e_cal_component_clone (priv->comp); + + comp_editor_copy_new_attendees (send_comp, priv->comp); + + if (e_cal_component_get_vtype (send_comp) == E_CAL_COMPONENT_JOURNAL) + get_users_from_memo_comp (send_comp, &users); + + /* The user updates the delegated status to the Organizer, + * so remove all other attendees. */ + if (flags & COMP_EDITOR_DELEGATE) { + address = itip_get_comp_attendee ( + registry, send_comp, priv->cal_client); + + if (address) + set_attendees_for_delegation (send_comp, address, method); + } + + if (!e_cal_component_has_attachments (priv->comp) || + e_client_check_capability ( + E_CLIENT (priv->cal_client), + CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) { + if (itip_send_comp ( + registry, method, send_comp, priv->cal_client, + NULL, NULL, users, strip_alarms, + priv->flags & COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) { + g_object_unref (send_comp); + return TRUE; + } + } else { + /* Clone the component with attachments set to CID:... */ + GSList *attach_list = NULL; + GSList *mime_attach_list, *attach; + + /* mime_attach_list is freed by itip_send_comp */ + mime_attach_list = comp_editor_get_mime_attach_list (editor); + + 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_foreach (attach_list, (GFunc) g_free, NULL); + g_slist_free (attach_list); + } + + if (itip_send_comp ( + registry, method, send_comp, priv->cal_client, + NULL, mime_attach_list, users, strip_alarms, + priv->flags & COMP_EDITOR_SEND_TO_NEW_ATTENDEES_ONLY)) { + gboolean saved = save_comp (editor); + + g_object_unref (send_comp); + + if (!saved) + comp_editor_set_changed (editor, TRUE); + + return saved; + } } + g_object_unref (send_comp); + g_free (address); comp_editor_set_changed (editor, TRUE); return FALSE; -} +} /** * comp_editor_edit_comp: @@ -987,52 +3531,61 @@ real_send_comp (CompEditor *editor, CalComponentItipMethod method) * Starts the editor editing the given component **/ void -comp_editor_edit_comp (CompEditor *editor, CalComponent *comp) +comp_editor_edit_comp (CompEditor *editor, + ECalComponent *comp) { - CompEditorClass *klass; + CompEditorClass *class; - g_return_if_fail (editor != NULL); g_return_if_fail (IS_COMP_EDITOR (editor)); - g_return_if_fail (comp != NULL); - g_return_if_fail (IS_CAL_COMPONENT (comp)); + g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - klass = COMP_EDITOR_CLASS (GTK_OBJECT (editor)->klass); + class = COMP_EDITOR_GET_CLASS (editor); - if (klass->edit_comp) - klass->edit_comp (editor, comp); + if (class->edit_comp) + class->edit_comp (editor, comp); } -CalComponent * +ECalComponent * comp_editor_get_comp (CompEditor *editor) { - CompEditorPrivate *priv; - - g_return_val_if_fail (editor != NULL, NULL); g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); - priv = editor->priv; - - return priv->comp; + return editor->priv->comp; } -CalComponent * -comp_editor_get_current_comp (CompEditor *editor) +/** + * comp_editor_get_current_comp + * @editor: a #CompEditor + * @correct: Set this no non-%NULL if you are interested to know if all + * pages reported success when filling component. + * + * Returns: Newly allocated component, should be unref-ed by g_object_unref(). + **/ +ECalComponent * +comp_editor_get_current_comp (CompEditor *editor, + gboolean *correct) { CompEditorPrivate *priv; - CalComponent *comp; + ECalComponent *comp; GList *l; + gboolean all_ok = TRUE; - g_return_val_if_fail (editor != NULL, NULL); g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL); priv = editor->priv; - comp = cal_component_clone (priv->comp); + comp = e_cal_component_clone (priv->comp); + comp_editor_copy_new_attendees (comp, priv->comp); if (priv->changed) { - for (l = priv->pages; l != NULL; l = l->next) - comp_editor_page_fill_component (l->data, comp); + for (l = priv->pages; l != NULL; l = l->next) { + if (IS_COMP_EDITOR_PAGE (l->data)) + all_ok = comp_editor_page_fill_component (l->data, comp) && all_ok; + } } + if (correct) + *correct = all_ok; + return comp; } @@ -1043,9 +3596,10 @@ comp_editor_get_current_comp (CompEditor *editor) * **/ gboolean -comp_editor_save_comp (CompEditor *editor, gboolean send) +comp_editor_save_comp (CompEditor *editor, + gboolean send) { - return prompt_to_save_changes (editor, send); + return prompt_and_save_changes (editor, send); } /** @@ -1057,7 +3611,24 @@ comp_editor_save_comp (CompEditor *editor, gboolean send) void comp_editor_delete_comp (CompEditor *editor) { - delete_comp (editor); + CompEditorPrivate *priv; + const gchar *uid; + + g_return_if_fail (IS_COMP_EDITOR (editor)); + + priv = editor->priv; + + e_cal_component_get_uid (priv->comp, &uid); + if (e_cal_component_is_instance (priv->comp) || + e_cal_component_has_recurrences (priv->comp)) + e_cal_client_remove_object_sync ( + priv->cal_client, uid, NULL, + CALOBJ_MOD_ALL, NULL, NULL); + else + e_cal_client_remove_object_sync ( + priv->cal_client, uid, NULL, + CALOBJ_MOD_THIS, NULL, NULL); + close_dialog (editor); } /** @@ -1068,17 +3639,18 @@ comp_editor_delete_comp (CompEditor *editor) * **/ gboolean -comp_editor_send_comp (CompEditor *editor, CalComponentItipMethod method) +comp_editor_send_comp (CompEditor *editor, + ECalComponentItipMethod method, + gboolean strip_alarms) { - CompEditorClass *klass; + CompEditorClass *class; - g_return_val_if_fail (editor != NULL, FALSE); g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); - klass = COMP_EDITOR_CLASS (GTK_OBJECT (editor)->klass); + class = COMP_EDITOR_GET_CLASS (editor); - if (klass->send_comp) - return klass->send_comp (editor, method); + if (class->send_comp) + return class->send_comp (editor, method, strip_alarms); return FALSE; } @@ -1087,344 +3659,167 @@ gboolean comp_editor_close (CompEditor *editor) { gboolean close; - - g_return_val_if_fail (editor != NULL, FALSE); + g_return_val_if_fail (IS_COMP_EDITOR (editor), FALSE); commit_all_fields (editor); - - close = prompt_to_save_changes (editor, TRUE); + + close = prompt_and_save_changes (editor, TRUE); if (close) close_dialog (editor); return close; } -/** - * comp_editor_merge_ui: - * @editor: - * @filename: - * @verbs: - * - * - **/ -void -comp_editor_merge_ui (CompEditor *editor, - const char *filename, - BonoboUIVerb *verbs, - EPixmap *component_pixmaps) -{ - CompEditorPrivate *priv; - - g_return_if_fail (editor != NULL); - g_return_if_fail (IS_COMP_EDITOR (editor)); - - priv = editor->priv; - - bonobo_ui_util_set_ui (priv->uic, EVOLUTION_DATADIR, filename, "evolution-calendar"); - bonobo_ui_component_add_verb_list_with_data (priv->uic, verbs, editor); - - if (component_pixmaps != NULL) - e_pixmaps_update (priv->uic, component_pixmaps); -} - -/** - * comp_editor_set_ui_prop: - * @editor: - * @path: - * @attr: - * @val: - * - * - **/ -void -comp_editor_set_ui_prop (CompEditor *editor, - const char *path, - const char *attr, - const char *val) -{ - CompEditorPrivate *priv; - - g_return_if_fail (editor != NULL); - g_return_if_fail (IS_COMP_EDITOR (editor)); - - priv = editor->priv; - - bonobo_ui_component_set_prop (priv->uic, path, attr, val, NULL); -} - - -/* Brings attention to a window by raising it and giving it focus */ -static void -raise_and_focus (GtkWidget *widget) -{ - g_assert (GTK_WIDGET_REALIZED (widget)); - gdk_window_show (widget->window); - gtk_widget_grab_focus (widget); -} - -/** - * comp_editor_focus: - * @editor: A component editor - * - * Brings the editor window to the front and gives it focus - **/ -void -comp_editor_focus (CompEditor *editor) -{ - CompEditorPrivate *priv; - - g_return_if_fail (editor != NULL); - g_return_if_fail (IS_COMP_EDITOR (editor)); - - priv = editor->priv; - - gtk_widget_show (GTK_WIDGET (editor)); - raise_and_focus (GTK_WIDGET (editor)); -} - -/* Menu Commands */ -static void -save_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - - commit_all_fields (editor); - - save_comp_with_send (editor); -} - -static void -save_close_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - - commit_all_fields (editor); - - if (save_comp_with_send (editor)) - close_dialog (editor); -} - -static void -save_as_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; - char *filename; - char *ical_string; - FILE *file; - - priv = editor->priv; - - commit_all_fields (editor); - - filename = e_file_dialog_save (_("Save as...")); - if (filename == NULL) - return; - - ical_string = cal_client_get_component_as_string (priv->client, priv->comp); - if (ical_string == NULL) { - g_warning ("Couldn't convert item to a string"); - return; - } - - file = fopen (filename, "w"); - if (file == NULL) { - g_warning ("Couldn't save item"); - return; - } - - fprintf (file, ical_string); - g_free (ical_string); - fclose (file); -} - -static void -delete_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; - CalComponentVType vtype; - - priv = editor->priv; - - vtype = cal_component_get_vtype (priv->comp); - - if (delete_component_dialog (priv->comp, FALSE, 1, vtype, GTK_WIDGET (editor))) { - if (itip_organizer_is_user (priv->comp) - && cancel_component_dialog (priv->comp, TRUE)) - itip_send_comp (CAL_COMPONENT_METHOD_CANCEL, priv->comp, priv->client, NULL); - - delete_comp (editor); - } -} - -static void -print_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CalComponent *comp; - - commit_all_fields (editor); - - comp = comp_editor_get_current_comp (editor); - print_comp (comp, editor->priv->client, FALSE); - gtk_object_unref (GTK_OBJECT (comp)); -} - -static void -print_preview_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CalComponent *comp; - - commit_all_fields (editor); - - comp = comp_editor_get_current_comp (editor); - print_comp (comp, editor->priv->client, TRUE); - gtk_object_unref (GTK_OBJECT (comp)); -} - -static void -print_setup_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; - - priv = editor->priv; - - print_setup (); -} - -static void -close_cmd (GtkWidget *widget, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - - commit_all_fields (editor); - - if (prompt_to_save_changes (editor, TRUE)) - close_dialog (editor); -} - -static void -page_changed_cb (GtkObject *obj, gpointer data) +/* 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. + */ +GSList * +comp_editor_get_mime_attach_list (CompEditor *editor) { - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; + EAttachmentStore *store; + EAttachmentView *view; + GtkTreeModel *model; + GtkTreeIter iter; + struct CalMimeAttach *cal_mime_attach; + GSList *attach_list = NULL; + gboolean valid; + + view = E_ATTACHMENT_VIEW (editor->priv->attachment_view); + store = e_attachment_view_get_store (view); + + 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 = g_strdup ( + 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); - priv = editor->priv; - - priv->changed = TRUE; - - if (!priv->warned && priv->existing_org && !priv->user_org) { - e_notice (NULL, GNOME_MESSAGE_BOX_INFO, - _("Changes made to this item may be discarded if an update arrives via email")); - priv->warned = TRUE; } - -} -/* Page signal callbacks */ -static void -page_summary_changed_cb (GtkObject *obj, const char *summary, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; - GList *l; - - priv = editor->priv; - - for (l = priv->pages; l != NULL; l = l->next) - if (obj != l->data) - comp_editor_page_set_summary (l->data, summary); - - priv->changed = TRUE; - - if (!priv->warned && priv->existing_org && !priv->user_org) { - e_notice (NULL, GNOME_MESSAGE_BOX_INFO, - _("Changes made to this item may be discarded if an update arrives via email")); - priv->warned = TRUE; - } + return attach_list; } static void -page_dates_changed_cb (GtkObject *obj, - CompEditorPageDates *dates, - gpointer data) +page_dates_changed_cb (CompEditor *editor, + CompEditorPageDates *dates, + CompEditorPage *page) { - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; + CompEditorPrivate *priv = editor->priv; GList *l; - priv = editor->priv; - for (l = priv->pages; l != NULL; l = l->next) - if (obj != l->data) + if (page != (CompEditorPage *) l->data && IS_COMP_EDITOR_PAGE (l->data)) comp_editor_page_set_dates (l->data, dates); - priv->changed = TRUE; - - if (!priv->warned && priv->existing_org && !priv->user_org) { - e_notice (NULL, GNOME_MESSAGE_BOX_INFO, - _("Changes made to this item may be discarded if an update arrives via email")); + if (!priv->warned && priv->existing_org && !priv->user_org && + !(editor->priv->flags & COMP_EDITOR_NEW_ITEM)) { + e_notice ( + priv->notebook, GTK_MESSAGE_INFO, + _("Changes made to this item may be discarded " + "if an update arrives")); priv->warned = TRUE; } } static void -obj_updated_cb (CalClient *client, const char *uid, gpointer data) +obj_modified_cb (ECalClientView *view, + const GSList *objects, + CompEditor *editor) { - CompEditor *editor = COMP_EDITOR (data); CompEditorPrivate *priv; - CalComponent *comp = NULL; - CalClientGetStatus status; - const char *edit_uid; + ECalComponent *comp = NULL; priv = editor->priv; - cal_component_get_uid (priv->comp, &edit_uid); - - if (!strcmp (uid, edit_uid) && !priv->updating) { - if (changed_component_dialog (priv->comp, FALSE, priv->changed)) { - status = cal_client_get_object (priv->client, uid, &comp); - if (status == CAL_CLIENT_GET_SUCCESS) { - comp_editor_edit_comp (editor, comp); - gtk_object_unref (GTK_OBJECT (comp)); - } else { - GtkWidget *dlg; - - dlg = gnome_error_dialog (_("Unable to obtain current version!")); - gnome_dialog_run_and_close (GNOME_DIALOG (dlg)); - } + /* We queried based on a specific UID so we definitely changed */ + if (changed_component_dialog ( + (GtkWindow *) editor, priv->comp, FALSE, priv->changed)) { + icalcomponent *icalcomp = icalcomponent_new_clone (objects->data); + + comp = e_cal_component_new (); + if (e_cal_component_set_icalcomponent (comp, icalcomp)) { + comp_editor_edit_comp (editor, comp); + } else { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new ( + NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + _("Unable to use current version!")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + icalcomponent_free (icalcomp); } - } -} - -static void -obj_removed_cb (CalClient *client, const char *uid, gpointer data) -{ - CompEditor *editor = COMP_EDITOR (data); - CompEditorPrivate *priv; - const char *edit_uid; - priv = editor->priv; - - cal_component_get_uid (priv->comp, &edit_uid); - - if (!strcmp (uid, edit_uid) && !priv->updating) { - if (changed_component_dialog (priv->comp, TRUE, priv->changed)) - close_dialog (editor); + g_object_unref (comp); } } -static gint -delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data) +static void +obj_removed_cb (ECalClientView *view, + const GSList *uids, + CompEditor *editor) { - CompEditor *editor = COMP_EDITOR (data); + CompEditorPrivate *priv = editor->priv; - if (prompt_to_save_changes (editor, TRUE)) + if (changed_component_dialog ( + GTK_WINDOW (editor), priv->comp, TRUE, priv->changed)) close_dialog (editor); - - return TRUE; } |