diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2009-08-30 13:37:36 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2009-08-30 13:40:49 +0800 |
commit | cfb9c32b6657165e4d5e11aa7b47804f679a61f8 (patch) | |
tree | 1f9c8954df7a357b5dc20a13ac82bf31c1112083 /modules/calendar | |
parent | fefeb30f58447f2fa7bcbee16dbe68a9333ce89d (diff) | |
parent | 0f7f4cfe38b3c4cd83efbe9922ae15c5aee00317 (diff) | |
download | gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.tar gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.tar.gz gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.tar.bz2 gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.tar.lz gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.tar.xz gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.tar.zst gsoc2013-evolution-cfb9c32b6657165e4d5e11aa7b47804f679a61f8.zip |
Merge commit 'origin/kill-bonobo'
Diffstat (limited to 'modules/calendar')
54 files changed, 20338 insertions, 0 deletions
diff --git a/modules/calendar/Makefile.am b/modules/calendar/Makefile.am new file mode 100644 index 0000000000..490d42a05e --- /dev/null +++ b/modules/calendar/Makefile.am @@ -0,0 +1,84 @@ +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"calendar-modules\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ + -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ + $(EVOLUTION_CALENDAR_CFLAGS) + +module_LTLIBRARIES = \ + libevolution-module-calendar.la + +libevolution_module_calendar_la_SOURCES = \ + evolution-module-calendar.c \ + e-cal-attachment-handler.c \ + e-cal-attachment-handler.h \ + e-cal-config-hook.c \ + e-cal-config-hook.h \ + e-cal-event-hook.c \ + e-cal-event-hook.h \ + e-cal-shell-backend.c \ + e-cal-shell-backend.h \ + e-cal-shell-content.c \ + e-cal-shell-content.h \ + e-cal-shell-migrate.c \ + e-cal-shell-migrate.h \ + e-cal-shell-settings.c \ + e-cal-shell-settings.h \ + e-cal-shell-sidebar.c \ + e-cal-shell-sidebar.h \ + e-cal-shell-view.c \ + e-cal-shell-view.h \ + e-cal-shell-view-actions.c \ + e-cal-shell-view-actions.h \ + e-cal-shell-view-memopad.c \ + e-cal-shell-view-private.c \ + e-cal-shell-view-private.h \ + e-cal-shell-view-taskpad.c \ + e-memo-shell-backend.c \ + e-memo-shell-backend.h \ + e-memo-shell-content.c \ + e-memo-shell-content.h \ + e-memo-shell-migrate.c \ + e-memo-shell-migrate.h \ + e-memo-shell-sidebar.c \ + e-memo-shell-sidebar.h \ + e-memo-shell-view.c \ + e-memo-shell-view.h \ + e-memo-shell-view-actions.c \ + e-memo-shell-view-actions.h \ + e-memo-shell-view-private.c \ + e-memo-shell-view-private.h \ + e-task-shell-backend.c \ + e-task-shell-backend.h \ + e-task-shell-content.c \ + e-task-shell-content.h \ + e-task-shell-migrate.c \ + e-task-shell-migrate.h \ + e-task-shell-sidebar.c \ + e-task-shell-sidebar.h \ + e-task-shell-view.c \ + e-task-shell-view.h \ + e-task-shell-view-actions.c \ + e-task-shell-view-actions.h \ + e-task-shell-view-private.c \ + e-task-shell-view-private.h + +libevolution_module_calendar_la_LIBADD = \ + $(top_builddir)/shell/libeshell.la \ + $(top_builddir)/calendar/gui/libevolution-calendar.la \ + $(top_builddir)/calendar/importers/libevolution-calendar-importers.la \ + $(top_builddir)/mail/libevolution-mail.la \ + $(top_builddir)/addressbook/gui/contact-editor/libecontacteditor.la \ + $(top_builddir)/addressbook/gui/contact-list-editor/libecontactlisteditor.la \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/filter/libfilter.la \ + $(top_builddir)/widgets/menus/libmenus.la \ + $(top_builddir)/widgets/misc/libemiscwidgets.la \ + $(top_builddir)/widgets/table/libetable.la \ + $(CAMEL_LIBS) \ + $(EVOLUTION_CALENDAR_LIBS) + +libevolution_module_calendar_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) + +-include $(top_srcdir)/git.mk diff --git a/modules/calendar/e-cal-attachment-handler.c b/modules/calendar/e-cal-attachment-handler.c new file mode 100644 index 0000000000..dd95cc5d08 --- /dev/null +++ b/modules/calendar/e-cal-attachment-handler.c @@ -0,0 +1,512 @@ +/* + * e-cal-attachment-handler.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-attachment-handler.h" + +#include <glib/gi18n.h> +#include <libical/ical.h> +#include <libecal/e-cal.h> +#include <camel/camel-stream-mem.h> +#include <libedataserverui/e-source-selector.h> + +#include "calendar/common/authentication.h" + +#define E_CAL_ATTACHMENT_HANDLER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CAL_ATTACHMENT_HANDLER, ECalAttachmentHandlerPrivate)) + +typedef struct _ImportContext ImportContext; + +struct _ECalAttachmentHandlerPrivate { + gint placeholder; +}; + +struct _ImportContext { + ECal *client; + icalcomponent *component; + ECalSourceType source_type; +}; + +static gpointer parent_class; +static GType cal_attachment_handler_type; + +static const gchar *ui = +"<ui>" +" <popup name='context'>" +" <placeholder name='custom-actions'>" +" <menuitem action='import-to-calendar'/>" +" <menuitem action='import-to-tasks'/>" +" </placeholder>" +" </popup>" +"</ui>"; + +static icalcomponent * +attachment_handler_get_component (EAttachment *attachment) +{ + CamelDataWrapper *wrapper; + CamelMimePart *mime_part; + CamelStream *stream; + GByteArray *buffer; + icalcomponent *component; + const gchar *key = "__icalcomponent__"; + + component = g_object_get_data (G_OBJECT (attachment), key); + if (component != NULL) + return component; + + mime_part = e_attachment_get_mime_part (attachment); + if (!CAMEL_IS_MIME_PART (mime_part)) + return NULL; + + buffer = g_byte_array_new (); + stream = camel_stream_mem_new (); + camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), buffer); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + camel_data_wrapper_decode_to_stream (wrapper, stream); + camel_object_unref (stream); + + component = e_cal_util_parse_ics_string ((gchar *) buffer->data); + + g_byte_array_free (buffer, TRUE); + + if (component == NULL) + return NULL; + + g_object_set_data_full ( + G_OBJECT (attachment), key, component, + (GDestroyNotify) icalcomponent_free); + + return component; +} + +static gboolean +attachment_handler_update_objects (ECal *client, + icalcomponent *component) +{ + icalcomponent_kind kind; + icalcomponent *vcalendar; + gboolean success; + + kind = icalcomponent_isa (component); + + switch (kind) { + case ICAL_VTODO_COMPONENT: + case ICAL_VEVENT_COMPONENT: + vcalendar = e_cal_util_new_top_level (); + if (icalcomponent_get_method (component) == ICAL_METHOD_CANCEL) + icalcomponent_set_method (vcalendar, ICAL_METHOD_CANCEL); + else + icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH); + icalcomponent_add_component ( + vcalendar, icalcomponent_new_clone (component)); + break; + + case ICAL_VCALENDAR_COMPONENT: + vcalendar = icalcomponent_new_clone (component); + if (!icalcomponent_get_first_property (vcalendar, ICAL_METHOD_PROPERTY)) + icalcomponent_set_method (vcalendar, ICAL_METHOD_PUBLISH); + break; + + default: + return FALSE; + } + + success = e_cal_receive_objects (client, vcalendar, NULL); + + icalcomponent_free (vcalendar); + + return success; +} + +static void +attachment_handler_import_event (ECal *client, + ECalendarStatus status, + EAttachment *attachment) +{ + icalcomponent *component; + icalcomponent *subcomponent; + icalcompiter iter; + + /* FIXME Notify the user somehow. */ + g_return_if_fail (status == E_CALENDAR_STATUS_OK); + + component = attachment_handler_get_component (attachment); + g_return_if_fail (component != NULL); + + iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT); + + while ((subcomponent = icalcompiter_deref (&iter)) != NULL) { + icalcomponent_kind kind; + + kind = icalcomponent_isa (subcomponent); + icalcompiter_next (&iter); + + if (kind == ICAL_VEVENT_COMPONENT) + continue; + + if (kind == ICAL_VTIMEZONE_COMPONENT) + continue; + + icalcomponent_remove_component (component, subcomponent); + icalcomponent_free (subcomponent); + } + + /* XXX Do something with the return value. */ + attachment_handler_update_objects (client, component); + + g_object_unref (attachment); + g_object_unref (client); +} + +static void +attachment_handler_import_todo (ECal *client, + ECalendarStatus status, + EAttachment *attachment) +{ + icalcomponent *component; + icalcomponent *subcomponent; + icalcompiter iter; + + /* FIXME Notify the user somehow. */ + g_return_if_fail (status == E_CALENDAR_STATUS_OK); + + component = attachment_handler_get_component (attachment); + g_return_if_fail (component != NULL); + + iter = icalcomponent_begin_component (component, ICAL_ANY_COMPONENT); + + while ((subcomponent = icalcompiter_deref (&iter)) != NULL) { + icalcomponent_kind kind; + + kind = icalcomponent_isa (subcomponent); + icalcompiter_next (&iter); + + if (kind == ICAL_VTODO_COMPONENT) + continue; + + if (kind == ICAL_VTIMEZONE_COMPONENT) + continue; + + icalcomponent_remove_component (component, subcomponent); + icalcomponent_free (subcomponent); + } + + /* XXX Do something with the return value. */ + attachment_handler_update_objects (client, component); + + g_object_unref (attachment); + g_object_unref (client); +} + +static void +attachment_handler_row_activated_cb (GtkDialog *dialog) +{ + gtk_dialog_response (dialog, GTK_RESPONSE_OK); +} + +static void +attachment_handler_run_dialog (GtkWindow *parent, + EAttachment *attachment, + ECalSourceType source_type, + const gchar *title) +{ + GtkWidget *dialog; + GtkWidget *container; + GtkWidget *widget; + GCallback callback; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + ECal *client; + icalcomponent *component; + GError *error = NULL; + + component = attachment_handler_get_component (attachment); + g_return_if_fail (component != NULL); + + e_cal_get_sources (&source_list, source_type, &error); + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + source = e_source_list_peek_source_any (source_list); + g_return_if_fail (source != NULL); + + dialog = gtk_dialog_new_with_buttons ( + title, parent, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + + widget = gtk_button_new_with_mnemonic (_("I_mport")); + gtk_button_set_image ( + GTK_BUTTON (widget), gtk_image_new_from_icon_name ( + "stock_mail-import", GTK_ICON_SIZE_MENU)); + gtk_dialog_add_action_widget ( + GTK_DIALOG (dialog), widget, GTK_RESPONSE_OK); + gtk_widget_show (widget); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400); + + container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + container = widget; + + widget = e_source_selector_new (source_list); + selector = E_SOURCE_SELECTOR (widget); + e_source_selector_set_primary_selection (selector, source); + e_source_selector_show_selection (selector, FALSE); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + g_signal_connect_swapped ( + widget, "row-activated", + G_CALLBACK (attachment_handler_row_activated_cb), dialog); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) + goto exit; + + source = e_source_selector_peek_primary_selection (selector); + if (source == NULL) + goto exit; + + client = auth_new_cal_from_source (source, source_type); + if (client == NULL) + goto exit; + + if (source_type == E_CAL_SOURCE_TYPE_EVENT) + callback = G_CALLBACK (attachment_handler_import_event); + else if (source_type == E_CAL_SOURCE_TYPE_TODO) + callback = G_CALLBACK (attachment_handler_import_todo); + else + goto exit; + + g_object_ref (attachment); + g_signal_connect (client, "cal-opened", callback, attachment); + e_cal_open_async (client, FALSE); + +exit: + gtk_widget_destroy (dialog); +} + +static void +attachment_handler_import_to_calendar (GtkAction *action, + EAttachmentHandler *handler) +{ + EAttachment *attachment; + EAttachmentView *view; + GList *selected; + gpointer parent; + + view = e_attachment_handler_get_view (handler); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + attachment = E_ATTACHMENT (selected->data); + + attachment_handler_run_dialog ( + parent, attachment, + E_CAL_SOURCE_TYPE_EVENT, + _("Select a Calendar")); + + g_object_unref (attachment); + g_list_free (selected); +} + +static void +attachment_handler_import_to_tasks (GtkAction *action, + EAttachmentHandler *handler) +{ + EAttachment *attachment; + EAttachmentView *view; + GList *selected; + gpointer parent; + + view = e_attachment_handler_get_view (handler); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + attachment = E_ATTACHMENT (selected->data); + + attachment_handler_run_dialog ( + parent, attachment, + E_CAL_SOURCE_TYPE_TODO, + _("Select a Task List")); + + g_object_unref (attachment); + g_list_free (selected); +} + +static GtkActionEntry standard_entries[] = { + + { "import-to-calendar", + "stock_mail-import", + N_("I_mport to Calendar"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_import_to_calendar) }, + + { "import-to-tasks", + "stock_mail-import", + N_("I_mport to Tasks"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_import_to_tasks) } +}; + +static void +cal_attachment_handler_update_actions (EAttachmentView *view) +{ + EAttachment *attachment; + GtkAction *action; + GList *selected; + icalcomponent *component; + icalcomponent *subcomponent; + icalcomponent_kind kind; + gboolean is_vevent = FALSE; + gboolean is_vtodo = FALSE; + + selected = e_attachment_view_get_selected_attachments (view); + + if (g_list_length (selected) != 1) + goto exit; + + attachment = E_ATTACHMENT (selected->data); + component = attachment_handler_get_component (attachment); + + if (component == NULL) + goto exit; + + subcomponent = icalcomponent_get_inner (component); + + if (subcomponent == NULL) + goto exit; + + kind = icalcomponent_isa (subcomponent); + is_vevent = (kind == ICAL_VEVENT_COMPONENT); + is_vtodo = (kind == ICAL_VTODO_COMPONENT); + +exit: + action = e_attachment_view_get_action (view, "import-to-calendar"); + gtk_action_set_visible (action, is_vevent); + + action = e_attachment_view_get_action (view, "import-to-tasks"); + gtk_action_set_visible (action, is_vtodo); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +cal_attachment_handler_constructed (GObject *object) +{ + EAttachmentHandler *handler; + EAttachmentView *view; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; + GError *error = NULL; + + handler = E_ATTACHMENT_HANDLER (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + view = e_attachment_handler_get_view (handler); + + action_group = e_attachment_view_add_action_group (view, "calendar"); + gtk_action_group_add_actions ( + action_group, standard_entries, + G_N_ELEMENTS (standard_entries), handler); + + ui_manager = e_attachment_view_get_ui_manager (view); + gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); + + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_signal_connect ( + view, "update_actions", + G_CALLBACK (cal_attachment_handler_update_actions), + NULL); +} + +static void +cal_attachment_handler_class_init (ECalAttachmentHandlerClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ECalAttachmentHandlerPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = cal_attachment_handler_constructed; +} + +static void +cal_attachment_handler_init (ECalAttachmentHandler *handler) +{ + handler->priv = E_CAL_ATTACHMENT_HANDLER_GET_PRIVATE (handler); +} + +GType +e_cal_attachment_handler_get_type (void) +{ + return cal_attachment_handler_type; +} + +void +e_cal_attachment_handler_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (ECalAttachmentHandlerClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_attachment_handler_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ECalAttachmentHandler), + 0, /* n_preallocs */ + (GInstanceInitFunc) cal_attachment_handler_init, + NULL /* value_table */ + }; + + cal_attachment_handler_type = g_type_module_register_type ( + type_module, E_TYPE_ATTACHMENT_HANDLER, + "ECalAttachmentHandler", &type_info, 0); +} diff --git a/modules/calendar/e-cal-attachment-handler.h b/modules/calendar/e-cal-attachment-handler.h new file mode 100644 index 0000000000..b792fbf765 --- /dev/null +++ b/modules/calendar/e-cal-attachment-handler.h @@ -0,0 +1,67 @@ +/* + * e-cal-attachment-handler.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_ATTACHMENT_HANDLER_H +#define E_CAL_ATTACHMENT_HANDLER_H + +#include <misc/e-attachment-handler.h> + +/* Standard GObject macros */ +#define E_TYPE_CAL_ATTACHMENT_HANDLER \ + (e_cal_attachment_handler_get_type ()) +#define E_CAL_ATTACHMENT_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CAL_ATTACHMENT_HANDLER, ECalAttachmentHandler)) +#define E_CAL_ATTACHMENT_HANDLER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CAL_ATTACHMENT_HANDLER, ECalAttachmentHandlerClass)) +#define E_IS_CAL_ATTACHMENT_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CAL_ATTACHMENT_HANDLER)) +#define E_IS_CAL_ATTACHMENT_HANDLER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CAL_ATTACHMENT_HANDLER)) +#define E_CAL_ATTACHMENT_HANDLER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CAL_ATTACHMENT_HANDLER, ECalAttachmentHandlerClass)) + +G_BEGIN_DECLS + +typedef struct _ECalAttachmentHandler ECalAttachmentHandler; +typedef struct _ECalAttachmentHandlerClass ECalAttachmentHandlerClass; +typedef struct _ECalAttachmentHandlerPrivate ECalAttachmentHandlerPrivate; + +struct _ECalAttachmentHandler { + EAttachmentHandler parent; + ECalAttachmentHandlerPrivate *priv; +}; + +struct _ECalAttachmentHandlerClass { + EAttachmentHandlerClass parent_class; +}; + +GType e_cal_attachment_handler_get_type (void); +void e_cal_attachment_handler_register_type + (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_CAL_ATTACHMENT_HANDLER_H */ diff --git a/modules/calendar/e-cal-config-hook.c b/modules/calendar/e-cal-config-hook.c new file mode 100644 index 0000000000..4a0522460c --- /dev/null +++ b/modules/calendar/e-cal-config-hook.c @@ -0,0 +1,68 @@ +/* + * e-cal-config-hook.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-config-hook.h" + +#include "e-util/e-config.h" +#include "calendar/gui/e-cal-config.h" + +static const EConfigHookTargetMask no_masks[] = { + { NULL } +}; + +static const EConfigHookTargetMap targets[] = { + { "source", EC_CONFIG_TARGET_SOURCE, no_masks }, + { "prefs", EC_CONFIG_TARGET_PREFS, no_masks }, + { NULL } +}; + +static void +cal_config_hook_class_init (EPluginHookClass *class) +{ + gint ii; + + class->id = "org.gnome.evolution.calendar.config:1.0"; + + for (ii = 0; targets[ii].type != NULL; ii++) + e_config_hook_class_add_target_map ( + (EConfigHookClass *) class, &targets[ii]); +} + +void +e_cal_config_hook_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EConfigHookClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_config_hook_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EConfigHook), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + NULL /* value_table */ + }; + + g_type_module_register_type ( + type_module, e_config_hook_get_type (), + "ECalConfigHook", &type_info, 0); +} diff --git a/modules/calendar/e-cal-config-hook.h b/modules/calendar/e-cal-config-hook.h new file mode 100644 index 0000000000..a22ec56bbc --- /dev/null +++ b/modules/calendar/e-cal-config-hook.h @@ -0,0 +1,33 @@ +/* + * e-cal-config-hook.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_CONFIG_HOOK_H +#define E_CAL_CONFIG_HOOK_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +void e_cal_config_hook_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_CAL_CONFIG_HOOK_H */ diff --git a/modules/calendar/e-cal-event-hook.c b/modules/calendar/e-cal-event-hook.c new file mode 100644 index 0000000000..b263727107 --- /dev/null +++ b/modules/calendar/e-cal-event-hook.c @@ -0,0 +1,72 @@ +/* + * e-cal-event-hook.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-event-hook.h" + +#include "e-util/e-event.h" +#include "calendar/gui/e-cal-event.h" + +static const EEventHookTargetMask masks[] = { + { "migration", E_CAL_EVENT_MODULE_MIGRATION }, + { NULL } +}; + +static const EEventHookTargetMap targets[] = { + { "module", E_CAL_EVENT_TARGET_BACKEND, masks }, + { NULL } +}; + +static void +cal_event_hook_class_init (EPluginHookClass *class) +{ + EEventHookClass *event_hook_class; + gint ii; + + event_hook_class = (EEventHookClass *) class; + event_hook_class->event = (EEvent *) e_cal_event_peek (); + + class->id = "org.gnome.evolution.calendar.events:1.0"; + + for (ii = 0; targets[ii].type != NULL; ii++) + e_event_hook_class_add_target_map ( + (EEventHookClass *) class, &targets[ii]); +} + +void +e_cal_event_hook_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EEventHookClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_event_hook_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EEventHook), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + NULL /* value_table */ + }; + + g_type_module_register_type ( + type_module, e_event_hook_get_type (), + "ECalEventHook", &type_info, 0); +} diff --git a/modules/calendar/e-cal-event-hook.h b/modules/calendar/e-cal-event-hook.h new file mode 100644 index 0000000000..9dde31f900 --- /dev/null +++ b/modules/calendar/e-cal-event-hook.h @@ -0,0 +1,33 @@ +/* + * e-cal-event-hook.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_EVENT_HOOK_H +#define E_CAL_EVENT_HOOK_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +void e_cal_event_hook_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_CAL_EVENT_HOOK_H */ diff --git a/modules/calendar/e-cal-shell-backend.c b/modules/calendar/e-cal-shell-backend.c new file mode 100644 index 0000000000..5149574323 --- /dev/null +++ b/modules/calendar/e-cal-shell-backend.c @@ -0,0 +1,854 @@ +/* + * e-cal-shell-backend.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-backend.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal.h> +#include <libecal/e-cal-time-util.h> +#include <libedataserver/e-url.h> +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-group.h> + +#include "e-util/e-import.h" +#include "shell/e-shell.h" +#include "shell/e-shell-backend.h" +#include "shell/e-shell-window.h" +#include "widgets/misc/e-preferences-window.h" + +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/comp-util.h" +#include "calendar/gui/dialogs/cal-prefs-dialog.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/event-editor.h" +#include "calendar/importers/evolution-calendar-importer.h" + +#include "e-cal-shell-migrate.h" +#include "e-cal-shell-settings.h" +#include "e-cal-shell-view.h" + +#define E_CAL_SHELL_BACKEND_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendPrivate)) + +#define CONTACTS_BASE_URI "contacts://" +#define WEATHER_BASE_URI "weather://" +#define WEB_BASE_URI "webcal://" +#define PERSONAL_RELATIVE_URI "system" + +struct _ECalShellBackendPrivate { + ESourceList *source_list; +}; + +enum { + PROP_0, + PROP_SOURCE_LIST +}; + +static gpointer parent_class; +static GType cal_shell_backend_type; + +static void +cal_shell_backend_ensure_sources (EShellBackend *shell_backend) +{ + /* XXX This is basically the same algorithm across all backends. + * Maybe we could somehow integrate this into EShellBackend? */ + + ECalShellBackendPrivate *priv; + ESourceGroup *on_this_computer; + ESourceGroup *on_the_web; + ESourceGroup *contacts; + ESourceGroup *weather; + ESource *birthdays; + ESource *personal; + EShell *shell; + EShellSettings *shell_settings; + GSList *groups, *iter; + const gchar *data_dir; + const gchar *name; + gchar *base_uri; + gchar *filename; + gchar *property; + + on_this_computer = NULL; + on_the_web = NULL; + contacts = NULL; + weather = NULL; + birthdays = NULL; + personal = NULL; + + priv = E_CAL_SHELL_BACKEND_GET_PRIVATE (shell_backend); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + if (!e_cal_get_sources (&priv->source_list, E_CAL_SOURCE_TYPE_EVENT, NULL)) { + g_warning ("Could not get calendar sources from GConf!"); + return; + } + + data_dir = e_shell_backend_get_data_dir (shell_backend); + filename = g_build_filename (data_dir, "local", NULL); + base_uri = g_filename_to_uri (filename, NULL, NULL); + g_free (filename); + + groups = e_source_list_peek_groups (priv->source_list); + for (iter = groups; iter != NULL; iter = iter->next) { + ESourceGroup *source_group = iter->data; + const gchar *group_base_uri; + + group_base_uri = e_source_group_peek_base_uri (source_group); + + /* Compare only "file://" part. if the user's home + * changes, we do not want to create another group. */ + if (on_this_computer == NULL && + strncmp (base_uri, group_base_uri, 7) == 0) + on_this_computer = source_group; + + else if (on_the_web == NULL && + strcmp (WEB_BASE_URI, group_base_uri) == 0) + on_the_web = source_group; + + else if (contacts == NULL && + strcmp (CONTACTS_BASE_URI, group_base_uri) == 0) + contacts = source_group; + + else if (weather == NULL && + strcmp (WEATHER_BASE_URI, group_base_uri) == 0) + weather = source_group; + } + + name = _("On This Computer"); + + if (on_this_computer != NULL) { + GSList *sources; + const gchar *group_base_uri; + + /* Force the group name to the current locale. */ + e_source_group_set_name (on_this_computer, name); + + sources = e_source_group_peek_sources (on_this_computer); + group_base_uri = e_source_group_peek_base_uri (on_this_computer); + + /* Make sure this group includes a "Personal" source. */ + for (iter = sources; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + const gchar *relative_uri; + + relative_uri = e_source_peek_relative_uri (source); + if (relative_uri == NULL) + continue; + + if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0) + continue; + + personal = source; + break; + } + + /* Make sure we have the correct base URI. This can + * change when the user's home directory changes. */ + if (strcmp (base_uri, group_base_uri) != 0) { + e_source_group_set_base_uri ( + on_this_computer, base_uri); + + /* XXX We shouldn't need this sync call here as + * set_base_uri() results in synching to GConf, + * but that happens in an idle loop and too late + * to prevent the user from seeing a "Cannot + * Open ... because of invalid URI" error. */ + e_source_list_sync (priv->source_list, NULL); + } + + } else { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, base_uri); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } + + name = _("Personal"); + + if (personal == NULL) { + ESource *source; + GSList *selected; + gchar *primary; + + source = e_source_new (name, PERSONAL_RELATIVE_URI); + e_source_group_add_source (on_this_computer, source, -1); + g_object_unref (source); + + primary = e_shell_settings_get_string ( + shell_settings, "cal-primary-calendar"); + + selected = calendar_config_get_calendars_selected (); + + if (primary == NULL && selected == NULL) { + const gchar *uid; + + uid = e_source_peek_uid (source); + selected = g_slist_prepend (NULL, g_strdup (uid)); + + e_shell_settings_set_string ( + shell_settings, "cal-primary-calendar", uid); + calendar_config_set_calendars_selected (selected); + } + + g_slist_foreach (selected, (GFunc) g_free, NULL); + g_slist_free (selected); + g_free (primary); + } else { + /* Force the source name to the current locale. */ + e_source_set_name (personal, name); + } + + name = _("On The Web"); + + if (on_the_web == NULL) { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, WEB_BASE_URI); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } else { + /* Force the group name to the current locale. */ + e_source_group_set_name (on_the_web, name); + } + + name = _("Contacts"); + + if (contacts != NULL) { + GSList *sources; + + /* Force the group name to the current locale. */ + e_source_group_set_name (contacts, name); + + sources = e_source_group_peek_sources (contacts); + + if (sources != NULL) { + GSList *trash; + + /* There is only one source under Contacts. */ + birthdays = E_SOURCE (sources->data); + sources = g_slist_next (sources); + + /* Delete any other sources in this group. + * Earlier versions allowed you to create + * additional sources under Contacts. */ + trash = g_slist_copy (sources); + while (trash != NULL) { + ESource *source = trash->data; + e_source_group_remove_source (contacts, source); + trash = g_slist_delete_link (trash, trash); + } + + } + } else { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, CONTACTS_BASE_URI); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + + /* This is now a borrowed reference. */ + contacts = source_group; + } + + /* XXX e_source_group_get_property() returns a newly-allocated + * string when it could just as easily return a const string. + * Unfortunately, fixing that would break the API. */ + property = e_source_group_get_property (contacts, "create_source"); + if (property == NULL) + e_source_group_set_property (contacts, "create_source", "no"); + g_free (property); + + name = _("Birthdays & Anniversaries"); + + if (birthdays == NULL) { + ESource *source; + const gchar *name; + + name = _("Birthdays & Anniversaries"); + source = e_source_new (name, "/"); + e_source_group_add_source (contacts, source, -1); + g_object_unref (source); + + /* This is now a borrowed reference. */ + birthdays = source; + } else { + /* Force the source name to the current locale. */ + e_source_set_name (birthdays, name); + } + + if (e_source_get_property (birthdays, "delete") == NULL) + e_source_set_property (birthdays, "delete", "no"); + + if (e_source_peek_color_spec (birthdays) == NULL) + e_source_set_color_spec (birthdays, "#DDBECE"); + + name = _("Weather"); + + if (weather == NULL) { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, WEATHER_BASE_URI); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } else { + /* Force the group name to the current locale. */ + e_source_group_set_name (weather, name); + } + + g_free (base_uri); +} + +static void +cal_shell_backend_event_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + flags |= COMP_EDITOR_USER_ORG; + + editor = event_editor_new (cal, shell, flags); + comp = cal_comp_event_new_with_current_time (cal, FALSE); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +cal_shell_backend_event_all_day_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + flags |= COMP_EDITOR_USER_ORG; + + editor = event_editor_new (cal, shell, flags); + comp = cal_comp_event_new_with_current_time (cal, TRUE); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +cal_shell_backend_event_meeting_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + flags |= COMP_EDITOR_USER_ORG; + flags |= COMP_EDITOR_MEETING; + + editor = event_editor_new (cal, shell, flags); + comp = cal_comp_event_new_with_current_time (cal, FALSE); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +action_event_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + ECal *cal = NULL; + ECalSourceType source_type; + ESourceList *source_list; + EShellSettings *shell_settings; + EShell *shell; + const gchar *action_name; + gchar *uid; + + /* This callback is used for both appointments and meetings. */ + + source_type = E_CAL_SOURCE_TYPE_EVENT; + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_warning ("Could not get calendar sources from GConf!"); + return; + } + + uid = e_shell_settings_get_string ( + shell_settings, "cal-primary-calendar"); + + if (uid != NULL) { + ESource *source; + + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source != NULL) + cal = auth_new_cal_from_source (source, source_type); + g_free (uid); + } + + if (cal == NULL) + cal = auth_new_cal_from_default (source_type); + + g_return_if_fail (cal != NULL); + + /* Connect the appropriate signal handler. */ + action_name = gtk_action_get_name (action); + if (strcmp (action_name, "event-all-day-new") == 0) + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (cal_shell_backend_event_all_day_new_cb), + shell); + else if (strcmp (action_name, "event-meeting-new") == 0) + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (cal_shell_backend_event_meeting_new_cb), + shell); + else + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (cal_shell_backend_event_new_cb), + shell); + + e_cal_open_async (cal, FALSE); +} + +static void +action_calendar_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + calendar_setup_new_calendar (GTK_WINDOW (shell_window)); +} + +static GtkActionEntry item_entries[] = { + + { "event-new", + "appointment-new", + NC_("New", "_Appointment"), + "<Shift><Control>a", + N_("Create a new appointment"), + G_CALLBACK (action_event_new_cb) }, + + { "event-all-day-new", + "stock_new-24h-appointment", + NC_("New", "All Day A_ppointment"), + NULL, + N_("Create a new all-day appointment"), + G_CALLBACK (action_event_new_cb) }, + + { "event-meeting-new", + "stock_new-meeting", + NC_("New", "M_eeting"), + "<Shift><Control>e", + N_("Create a new meeting request"), + G_CALLBACK (action_event_new_cb) } +}; + +static GtkActionEntry source_entries[] = { + + { "calendar-new", + "x-office-calendar", + NC_("New", "Cale_ndar"), + NULL, + N_("Create a new calendar"), + G_CALLBACK (action_calendar_new_cb) } +}; + +static void +cal_shell_backend_init_importers (void) +{ + EImportClass *import_class; + EImportImporter *importer; + + import_class = g_type_class_ref (e_import_get_type ()); + + importer = gnome_calendar_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = ical_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = vcal_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); +} + +static void +cal_shell_backend_init_preferences (EShell *shell) +{ + GtkWidget *preferences_window; + + preferences_window = e_shell_get_preferences_window (shell); + + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "calendar-and-tasks", + "preferences-calendar-and-tasks", + _("Calendar and Tasks"), + calendar_prefs_dialog_new (shell), + 600); +} + +static gboolean +cal_shell_backend_handle_uri_cb (EShellBackend *shell_backend, + const gchar *uri) +{ + EShell *shell; + CompEditor *editor; + CompEditorFlags flags = 0; + ECal *client; + ECalComponent *comp; + ESource *source; + ESourceList *source_list; + ECalSourceType source_type; + EUri *euri; + icalcomponent *icalcomp; + icalproperty *icalprop; + const gchar *cp; + gchar *source_uid = NULL; + gchar *comp_uid = NULL; + gchar *comp_rid = NULL; + time_t startdate = -1; + time_t enddate = -1; + gboolean handled = FALSE; + GError *error = NULL; + + source_type = E_CAL_SOURCE_TYPE_EVENT; + shell = e_shell_backend_get_shell (shell_backend); + + if (strncmp (uri, "calendar:", 9) != 0) + return FALSE; + + euri = e_uri_new (uri); + cp = euri->query; + if (cp == NULL) + goto exit; + + while (*cp != '\0') { + gchar *header; + gchar *content; + gsize header_len; + gsize content_len; + + header_len = strcspn (cp, "=&"); + + /* It it's malformed, give up. */ + if (cp[header_len] != '=') + break; + + header = (gchar *) cp; + header[header_len] = '\0'; + cp += header_len + 1; + + content_len = strcspn (cp, "&"); + + content = g_strndup (cp, content_len); + if (g_ascii_strcasecmp (header, "startdate") == 0) + startdate = time_from_isodate (content); + else if (g_ascii_strcasecmp (header, "enddate") == 0) + enddate = time_from_isodate (content); + else if (g_ascii_strcasecmp (header, "source-uid") == 0) + source_uid = g_strdup (content); + else if (g_ascii_strcasecmp (header, "comp-uid") == 0) + comp_uid = g_strdup (content); + else if (g_ascii_strcasecmp (header, "comp-rid") == 0) + comp_rid = g_strdup (content); + g_free (content); + + cp += content_len; + if (*cp == '&') { + cp++; + if (strcmp (cp, "amp;") == 0) + cp += 4; + } + } + + if (source_uid == NULL || comp_uid == NULL) + goto exit; + + /* URI is valid, so consider it handled. Whether + * we successfully open it is another matter... */ + handled = TRUE; + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_printerr ("Could not get calendar sources from GConf!\n"); + goto exit; + } + + source = e_source_list_peek_source_by_uid (source_list, source_uid); + if (source == NULL) { + g_printerr ("No source for UID `%s'\n", source_uid); + g_object_unref (source_list); + goto exit; + } + + client = auth_new_cal_from_source (source, source_type); + if (client == NULL || !e_cal_open (client, TRUE, &error)) { + g_printerr ("%s\n", error->message); + g_object_unref (source_list); + g_error_free (error); + goto exit; + } + + /* XXX Copied from e_cal_shell_view_open_event(). + * Clearly a new utility function is needed. */ + + editor = comp_editor_find_instance (comp_uid); + + if (editor != NULL) + goto present; + + if (!e_cal_get_object (client, comp_uid, comp_rid, &icalcomp, &error)) { + g_printerr ("%s\n", error->message); + g_object_unref (source_list); + g_error_free (error); + goto exit; + } + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomp); + + icalprop = icalcomponent_get_first_property ( + icalcomp, ICAL_ATTENDEE_PROPERTY); + if (icalprop != NULL) + flags |= COMP_EDITOR_MEETING; + + if (itip_organizer_is_user (comp, client)) + flags |= COMP_EDITOR_USER_ORG; + + if (itip_sentby_is_user (comp, client)) + flags |= COMP_EDITOR_USER_ORG; + + if (!e_cal_component_has_attendees (comp)) + flags |= COMP_EDITOR_USER_ORG; + + editor = event_editor_new (client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_unref (comp); + +present: + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (source_list); + g_object_unref (client); + +exit: + g_free (source_uid); + g_free (comp_uid); + g_free (comp_rid); + + e_uri_free (euri); + + return handled; +} + +static void +cal_shell_backend_window_created_cb (EShellBackend *shell_backend, + GtkWindow *window) +{ + const gchar *backend_name; + + if (!E_IS_SHELL_WINDOW (window)) + return; + + backend_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name; + + e_shell_window_register_new_item_actions ( + E_SHELL_WINDOW (window), backend_name, + item_entries, G_N_ELEMENTS (item_entries)); + + e_shell_window_register_new_source_actions ( + E_SHELL_WINDOW (window), backend_name, + source_entries, G_N_ELEMENTS (source_entries)); +} + +static void +cal_shell_backend_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE_LIST: + g_value_set_object ( + value, + e_cal_shell_backend_get_source_list ( + E_CAL_SHELL_BACKEND (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +cal_shell_backend_dispose (GObject *object) +{ + ECalShellBackendPrivate *priv; + + priv = E_CAL_SHELL_BACKEND_GET_PRIVATE (object); + + if (priv->source_list != NULL) { + g_object_unref (priv->source_list); + priv->source_list = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +cal_shell_backend_constructed (GObject *object) +{ + EShell *shell; + EShellBackend *shell_backend; + + shell_backend = E_SHELL_BACKEND (object); + shell = e_shell_backend_get_shell (shell_backend); + + cal_shell_backend_ensure_sources (shell_backend); + + g_signal_connect_swapped ( + shell, "handle-uri", + G_CALLBACK (cal_shell_backend_handle_uri_cb), + shell_backend); + + g_signal_connect_swapped ( + shell, "window-created", + G_CALLBACK (cal_shell_backend_window_created_cb), + shell_backend); + + cal_shell_backend_init_importers (); + + /* Initialize settings before initializing preferences, + * since the preferences bind to the shell settings. */ + e_cal_shell_backend_init_settings (shell); + cal_shell_backend_init_preferences (shell); +} + +static void +cal_shell_backend_class_init (ECalShellBackendClass *class) +{ + GObjectClass *object_class; + EShellBackendClass *shell_backend_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ECalShellBackendPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = cal_shell_backend_get_property; + object_class->dispose = cal_shell_backend_dispose; + object_class->constructed = cal_shell_backend_constructed; + + shell_backend_class = E_SHELL_BACKEND_CLASS (class); + shell_backend_class->shell_view_type = E_TYPE_CAL_SHELL_VIEW; + shell_backend_class->name = "calendar"; + shell_backend_class->aliases = ""; + shell_backend_class->schemes = "calendar"; + shell_backend_class->sort_order = 400; + shell_backend_class->start = NULL; + shell_backend_class->migrate = e_cal_shell_backend_migrate; + + g_object_class_install_property ( + object_class, + PROP_SOURCE_LIST, + g_param_spec_object ( + "source-list", + _("Source List"), + _("The registry of calendars"), + E_TYPE_SOURCE_LIST, + G_PARAM_READABLE)); +} + +static void +cal_shell_backend_init (ECalShellBackend *cal_shell_backend) +{ + cal_shell_backend->priv = + E_CAL_SHELL_BACKEND_GET_PRIVATE (cal_shell_backend); +} + +GType +e_cal_shell_backend_get_type (void) +{ + return cal_shell_backend_type; +} + +void +e_cal_shell_backend_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (ECalShellBackendClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_shell_backend_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ECalShellBackend), + 0, /* n_preallocs */ + (GInstanceInitFunc) cal_shell_backend_init, + NULL /* value_table */ + }; + + cal_shell_backend_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_BACKEND, + "ECalShellBackend", &type_info, 0); +} + +ESourceList * +e_cal_shell_backend_get_source_list (ECalShellBackend *cal_shell_backend) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_BACKEND (cal_shell_backend), NULL); + + return cal_shell_backend->priv->source_list; +} diff --git a/modules/calendar/e-cal-shell-backend.h b/modules/calendar/e-cal-shell-backend.h new file mode 100644 index 0000000000..497e200490 --- /dev/null +++ b/modules/calendar/e-cal-shell-backend.h @@ -0,0 +1,70 @@ +/* + * e-cal-shell-backend.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_BACKEND_H +#define E_CAL_SHELL_BACKEND_H + +#include <shell/e-shell-backend.h> +#include <libedataserver/e-source-list.h> + +/* Standard GObject macros */ +#define E_TYPE_CAL_SHELL_BACKEND \ + (e_cal_shell_backend_get_type ()) +#define E_CAL_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackend)) +#define E_CAL_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendClass)) +#define E_IS_CAL_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CAL_SHELL_BACKEND)) +#define E_IS_CAL_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CAL_SHELL_BACKEND)) +#define E_CAL_SHELL_BACKEND_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CAL_SHELL_BACKEND, ECalShellBackendClass)) + +G_BEGIN_DECLS + +typedef struct _ECalShellBackend ECalShellBackend; +typedef struct _ECalShellBackendClass ECalShellBackendClass; +typedef struct _ECalShellBackendPrivate ECalShellBackendPrivate; + +struct _ECalShellBackend { + EShellBackend parent; + ECalShellBackendPrivate *priv; +}; + +struct _ECalShellBackendClass { + EShellBackendClass parent_class; +}; + +GType e_cal_shell_backend_get_type (void); +void e_cal_shell_backend_register_type + (GTypeModule *type_module); +ESourceList * e_cal_shell_backend_get_source_list + (ECalShellBackend *cal_shell_backend); + +G_END_DECLS + +#endif /* E_CAL_SHELL_BACKEND_H */ diff --git a/modules/calendar/e-cal-shell-content.c b/modules/calendar/e-cal-shell-content.c new file mode 100644 index 0000000000..e199f0524d --- /dev/null +++ b/modules/calendar/e-cal-shell-content.c @@ -0,0 +1,768 @@ +/* + * e-cal-shell-content.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-content.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include "e-util/e-binding.h" +#include "e-util/gconf-bridge.h" +#include "widgets/menus/gal-view-etable.h" +#include "widgets/misc/e-paned.h" + +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/calendar-view.h" +#include "calendar/gui/e-cal-list-view.h" +#include "calendar/gui/e-cal-model-calendar.h" +#include "calendar/gui/e-calendar-table.h" +#include "calendar/gui/e-calendar-view.h" +#include "calendar/gui/e-day-view.h" +#include "calendar/gui/e-week-view.h" + +#define E_CAL_SHELL_CONTENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentPrivate)) + +struct _ECalShellContentPrivate { + GtkWidget *hpaned; + GtkWidget *notebook; + GtkWidget *vpaned; + + GtkWidget *calendar; + GtkWidget *task_table; + GtkWidget *memo_table; + + GalViewInstance *view_instance; + + guint paned_binding_id; +}; + +enum { + PROP_0 +}; + +/* Used to indicate who has the focus within the calendar view. */ +typedef enum { + FOCUS_CALENDAR, + FOCUS_MEMO_TABLE, + FOCUS_TASK_TABLE, + FOCUS_OTHER +} FocusLocation; + +static gpointer parent_class; +static GType cal_shell_content_type; + +static void +cal_shell_content_display_view_cb (ECalShellContent *cal_shell_content, + GalView *gal_view) +{ + GnomeCalendar *calendar; + GnomeCalendarViewType view_type; + + /* XXX This is confusing: we have CalendarView and ECalendarView. + * ECalendarView is an abstract base class for calendar view + * widgets (day view, week view, etc). CalendarView is a + * simple GalView subclass that represents a calendar view. */ + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + if (GAL_IS_VIEW_ETABLE (gal_view)) { + ECalendarView *calendar_view; + ETable *table; + + view_type = GNOME_CAL_LIST_VIEW; + calendar_view = gnome_calendar_get_calendar_view ( + calendar, view_type); + table = e_table_scrolled_get_table ( + E_CAL_LIST_VIEW (calendar_view)->table_scrolled); + gal_view_etable_attach_table ( + GAL_VIEW_ETABLE (gal_view), table); + } else { + view_type = calendar_view_get_view_type ( + CALENDAR_VIEW (gal_view)); + } + + gnome_calendar_display_view (calendar, view_type); +} + +static void +cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content) +{ + EShellContent *shell_content; + EShellView *shell_view; + GConfBridge *bridge; + GtkWidget *paned; + guint binding_id; + const gchar *key; + const gchar *view_id; + + bridge = gconf_bridge_get (); + paned = cal_shell_content->priv->hpaned; + binding_id = cal_shell_content->priv->paned_binding_id; + + shell_content = E_SHELL_CONTENT (cal_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + view_id = e_shell_view_get_view_id (shell_view); + + if (binding_id > 0) + gconf_bridge_unbind (bridge, binding_id); + + if (view_id != NULL && strcmp (view_id, "Month_View") == 0) + key = "/apps/evolution/calendar/display/month_hpane_position"; + else + key = "/apps/evolution/calendar/display/hpane_position"; + + binding_id = gconf_bridge_bind_property_delayed ( + bridge, key, G_OBJECT (paned), "hposition"); + + cal_shell_content->priv->paned_binding_id = binding_id; +} + +static FocusLocation +cal_shell_content_get_focus_location (ECalShellContent *cal_shell_content) +{ + GtkWidget *widget; + GnomeCalendar *calendar; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + ECalendarTable *task_table; + EMemoTable *memo_table; + ETable *table; + + calendar = GNOME_CALENDAR (cal_shell_content->priv->calendar); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + memo_table = E_MEMO_TABLE (cal_shell_content->priv->memo_table); + task_table = E_CALENDAR_TABLE (cal_shell_content->priv->task_table); + + table = e_memo_table_get_table (memo_table); + if (GTK_WIDGET_HAS_FOCUS (table->table_canvas)) + return FOCUS_MEMO_TABLE; + + table = e_calendar_table_get_table (task_table); + if (GTK_WIDGET_HAS_FOCUS (table->table_canvas)) + return FOCUS_TASK_TABLE; + + if (E_IS_DAY_VIEW (calendar_view)) { + EDayView *day_view = E_DAY_VIEW (calendar_view); + + if (GTK_WIDGET_HAS_FOCUS (day_view->top_canvas)) + return FOCUS_CALENDAR; + + if (GNOME_CANVAS (day_view->top_canvas)->focused_item != NULL) + return FOCUS_CALENDAR; + + if (GTK_WIDGET_HAS_FOCUS (day_view->main_canvas)) + return FOCUS_CALENDAR; + + if (GNOME_CANVAS (day_view->main_canvas)->focused_item != NULL) + return FOCUS_CALENDAR; + + if (GTK_WIDGET_HAS_FOCUS (day_view)) + return FOCUS_CALENDAR; + + } else if (E_IS_WEEK_VIEW (calendar_view)) { + EWeekView *week_view = E_WEEK_VIEW (calendar_view); + + if (GTK_WIDGET_HAS_FOCUS (week_view->main_canvas)) + return FOCUS_CALENDAR; + + if (GNOME_CANVAS (week_view->main_canvas)->focused_item != NULL) + return FOCUS_CALENDAR; + + if (GTK_WIDGET_HAS_FOCUS (week_view)) + return FOCUS_CALENDAR; + + } else if (E_IS_CAL_LIST_VIEW (calendar_view)) { + ECalListView *list_view = E_CAL_LIST_VIEW (widget); + + table = e_table_scrolled_get_table (list_view->table_scrolled); + if (GTK_WIDGET_HAS_FOCUS (table)) + return FOCUS_CALENDAR; + + if (GTK_WIDGET_HAS_FOCUS (list_view)) + return FOCUS_CALENDAR; + } + + return FOCUS_OTHER; +} + +static void +cal_shell_content_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +cal_shell_content_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +cal_shell_content_dispose (GObject *object) +{ + ECalShellContentPrivate *priv; + + priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object); + + if (priv->hpaned != NULL) { + g_object_unref (priv->hpaned); + priv->hpaned = NULL; + } + + if (priv->notebook != NULL) { + g_object_unref (priv->notebook); + priv->notebook = NULL; + } + + if (priv->vpaned != NULL) { + g_object_unref (priv->vpaned); + priv->vpaned = NULL; + } + + if (priv->calendar != NULL) { + g_object_unref (priv->calendar); + priv->calendar = NULL; + } + + if (priv->task_table != NULL) { + g_object_unref (priv->task_table); + priv->task_table = NULL; + } + + if (priv->memo_table != NULL) { + g_object_unref (priv->memo_table); + priv->memo_table = NULL; + } + + if (priv->view_instance != NULL) { + g_object_unref (priv->view_instance); + priv->view_instance = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +cal_shell_content_finalize (GObject *object) +{ + ECalShellContentPrivate *priv; + + priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +cal_shell_content_constructed (GObject *object) +{ + ECalShellContentPrivate *priv; + ECalendarView *calendar_view; + ECalModel *memo_model; + ECalModel *task_model; + EShell *shell; + EShellContent *shell_content; + EShellBackend *shell_backend; + EShellSettings *shell_settings; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *foreign_content; + EShellView *foreign_view; + GnomeCalendar *calendar; + GalViewInstance *view_instance; + GConfBridge *bridge; + GtkWidget *container; + GtkWidget *widget; + const gchar *config_dir; + const gchar *key; + gchar *filename; + gchar *markup; + gint ii; + + priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + shell_content = E_SHELL_CONTENT (object); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + shell_backend = e_shell_view_get_shell_backend (shell_view); + config_dir = e_shell_backend_get_config_dir (shell_backend); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + /* We borrow the memopad and taskpad models from the memo + * and task views, loading the views if necessary. */ + + foreign_view = e_shell_window_get_shell_view (shell_window, "memos"); + foreign_content = e_shell_view_get_shell_content (foreign_view); + g_object_get (foreign_content, "model", &memo_model, NULL); + + foreign_view = e_shell_window_get_shell_view (shell_window, "tasks"); + foreign_content = e_shell_view_get_shell_content (foreign_view); + g_object_get (foreign_content, "model", &task_model, NULL); + + /* Build content widgets. */ + + container = GTK_WIDGET (object); + + widget = e_paned_new (GTK_ORIENTATION_HORIZONTAL); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->hpaned = g_object_ref (widget); + gtk_widget_show (widget); + + container = priv->hpaned; + + widget = gtk_notebook_new (); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE); + gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE); + priv->notebook = g_object_ref (widget); + gtk_widget_show (widget); + + /* FIXME Need to deal with saving and restoring the position. + * Month view has its own position. */ + widget = gtk_vpaned_new (); + gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, TRUE); + priv->vpaned = g_object_ref (widget); + gtk_widget_show (widget); + + container = priv->notebook; + + /* Add views in the order defined by GnomeCalendarViewType, such + * that the notebook page number corresponds to the view type. */ + + /* XXX GnomeCalendar is a widget, but we don't pack it. + * Maybe it should just be a GObject instead? */ + priv->calendar = gnome_calendar_new (shell_settings); + g_object_ref_sink (priv->calendar); + calendar = GNOME_CALENDAR (priv->calendar); + + for (ii = 0; ii < GNOME_CAL_LAST_VIEW; ii++) { + calendar_view = gnome_calendar_get_calendar_view (calendar, ii); + + gtk_notebook_append_page ( + GTK_NOTEBOOK (container), + GTK_WIDGET (calendar_view), NULL); + gtk_widget_show (GTK_WIDGET (calendar_view)); + } + + e_binding_new ( + G_OBJECT (priv->calendar), "view", + G_OBJECT (priv->notebook), "page"); + + container = priv->vpaned; + + widget = gtk_vbox_new (FALSE, 0); + gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0); + gtk_widget_show (widget); + + widget = gtk_label_new (NULL); + markup = g_strdup_printf ("<b>%s</b>", _("Tasks")); + gtk_label_set_markup (GTK_LABEL (widget), markup); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0); + gtk_widget_show (widget); + g_free (markup); + + widget = e_calendar_table_new (shell_view, task_model); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->task_table = g_object_ref (widget); + gtk_widget_show (widget); + + filename = g_build_filename (config_dir, "TaskPad", NULL); + e_calendar_table_load_state (E_CALENDAR_TABLE (widget), filename); + g_free (filename); + + container = priv->vpaned; + + widget = gtk_vbox_new (FALSE, 0); + gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, TRUE); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_label_new (NULL); + markup = g_strdup_printf ("<b>%s</b>", _("Memos")); + gtk_label_set_markup (GTK_LABEL (widget), markup); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0); + gtk_widget_show (widget); + g_free (markup); + + widget = e_memo_table_new (shell_view, memo_model); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + priv->memo_table = g_object_ref (widget); + gtk_widget_show (widget); + + filename = g_build_filename (config_dir, "MemoPad", NULL); + e_memo_table_load_state (E_MEMO_TABLE (widget), filename); + g_free (filename); + + /* Load the view instance. */ + + view_instance = e_shell_view_new_view_instance (shell_view, NULL); + g_signal_connect_swapped ( + view_instance, "display-view", + G_CALLBACK (cal_shell_content_display_view_cb), + object); + /* XXX Actually, don't load the view instance just yet. + * The GtkWidget::map() callback below explains why. */ + priv->view_instance = view_instance; + + g_signal_connect_swapped ( + shell_view, "notify::view-id", + G_CALLBACK (cal_shell_content_notify_view_id_cb), + object); + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (priv->vpaned); + key = "/apps/evolution/calendar/display/vpane_position"; + gconf_bridge_bind_property_delayed (bridge, key, object, "position"); + + g_object_unref (memo_model); + g_object_unref (task_model); +} + +static void +cal_shell_content_map (GtkWidget *widget) +{ + ECalShellContentPrivate *priv; + + /* XXX Delay loading the GalViewInstance until after ECalShellView + * has a chance to install the sidebar's date navigator into + * GnomeCalendar, since loading the GalViewInstance triggers a + * callback in GnomeCalendar that requires the date navigator. + * Ordinarily we would do this at the end of constructed(), but + * that's too soon in this case. (This feels kind of kludgy.) */ + priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (widget); + gal_view_instance_load (priv->view_instance); + + /* Chain up to parent's map() method. */ + GTK_WIDGET_CLASS (parent_class)->map (widget); +} + +static void +cal_shell_content_class_init (ECalShellContentClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ECalShellContentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = cal_shell_content_set_property; + object_class->get_property = cal_shell_content_get_property; + object_class->dispose = cal_shell_content_dispose; + object_class->finalize = cal_shell_content_finalize; + object_class->constructed = cal_shell_content_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->map = cal_shell_content_map; +} + +static void +cal_shell_content_init (ECalShellContent *cal_shell_content) +{ + cal_shell_content->priv = + E_CAL_SHELL_CONTENT_GET_PRIVATE (cal_shell_content); + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_cal_shell_content_get_type (void) +{ + return cal_shell_content_type; +} + +void +e_cal_shell_content_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (ECalShellContentClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_shell_content_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ECalShellContent), + 0, /* n_preallocs */ + (GInstanceInitFunc) cal_shell_content_init, + NULL /* value_table */ + }; + + cal_shell_content_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_CONTENT, + "ECalShellContent", &type_info, 0); +} + +GtkWidget * +e_cal_shell_content_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_CAL_SHELL_CONTENT, + "shell-view", shell_view, NULL); +} + +ECalModel * +e_cal_shell_content_get_model (ECalShellContent *cal_shell_content) +{ + GnomeCalendar *calendar; + + g_return_val_if_fail ( + E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + return gnome_calendar_get_model (calendar); +} + +GnomeCalendar * +e_cal_shell_content_get_calendar (ECalShellContent *cal_shell_content) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); + + return GNOME_CALENDAR (cal_shell_content->priv->calendar); +} + +EMemoTable * +e_cal_shell_content_get_memo_table (ECalShellContent *cal_shell_content) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); + + return E_MEMO_TABLE (cal_shell_content->priv->memo_table); +} + +ECalendarTable * +e_cal_shell_content_get_task_table (ECalShellContent *cal_shell_content) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); + + return E_CALENDAR_TABLE (cal_shell_content->priv->task_table); +} + +GalViewInstance * +e_cal_shell_content_get_view_instance (ECalShellContent *cal_shell_content) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL); + + return cal_shell_content->priv->view_instance; +} + +void +e_cal_shell_content_copy_clipboard (ECalShellContent *cal_shell_content) +{ + GnomeCalendar *calendar; + EMemoTable *memo_table; + ECalendarTable *task_table; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + + g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + switch (cal_shell_content_get_focus_location (cal_shell_content)) { + case FOCUS_CALENDAR: + e_calendar_view_copy_clipboard (calendar_view); + break; + + case FOCUS_MEMO_TABLE: + e_memo_table_copy_clipboard (memo_table); + break; + + case FOCUS_TASK_TABLE: + e_calendar_table_copy_clipboard (task_table); + break; + + default: + g_return_if_reached (); + } +} + +void +e_cal_shell_content_cut_clipboard (ECalShellContent *cal_shell_content) +{ + GnomeCalendar *calendar; + EMemoTable *memo_table; + ECalendarTable *task_table; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + + g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + switch (cal_shell_content_get_focus_location (cal_shell_content)) { + case FOCUS_CALENDAR: + e_calendar_view_cut_clipboard (calendar_view); + break; + + case FOCUS_MEMO_TABLE: + e_memo_table_copy_clipboard (memo_table); + break; + + case FOCUS_TASK_TABLE: + e_calendar_table_copy_clipboard (task_table); + break; + + default: + g_return_if_reached (); + } +} + +void +e_cal_shell_content_paste_clipboard (ECalShellContent *cal_shell_content) +{ + GnomeCalendar *calendar; + EMemoTable *memo_table; + ECalendarTable *task_table; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + + g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + switch (cal_shell_content_get_focus_location (cal_shell_content)) { + case FOCUS_CALENDAR: + e_calendar_view_paste_clipboard (calendar_view); + break; + + case FOCUS_MEMO_TABLE: + e_memo_table_copy_clipboard (memo_table); + break; + + case FOCUS_TASK_TABLE: + e_calendar_table_copy_clipboard (task_table); + break; + + default: + g_return_if_reached (); + } +} + +void +e_cal_shell_content_delete_selection (ECalShellContent *cal_shell_content) +{ + GnomeCalendar *calendar; + EMemoTable *memo_table; + ECalendarTable *task_table; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + + g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + switch (cal_shell_content_get_focus_location (cal_shell_content)) { + case FOCUS_CALENDAR: + e_calendar_view_delete_selected_events (calendar_view); + break; + + case FOCUS_MEMO_TABLE: + e_memo_table_delete_selected (memo_table); + break; + + case FOCUS_TASK_TABLE: + e_calendar_table_delete_selected (task_table); + break; + + default: + g_return_if_reached (); + } +} + +void +e_cal_shell_content_delete_selected_occurrence (ECalShellContent *cal_shell_content) +{ + GnomeCalendar *calendar; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + FocusLocation focus; + + g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content)); + + focus = cal_shell_content_get_focus_location (cal_shell_content); + if (focus != FOCUS_CALENDAR) + return; + + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + e_calendar_view_delete_selected_occurrence (calendar_view); +} diff --git a/modules/calendar/e-cal-shell-content.h b/modules/calendar/e-cal-shell-content.h new file mode 100644 index 0000000000..3db7d33260 --- /dev/null +++ b/modules/calendar/e-cal-shell-content.h @@ -0,0 +1,108 @@ +/* + * e-cal-shell-content.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_CONTENT_H +#define E_CAL_SHELL_CONTENT_H + +#include <shell/e-shell-content.h> +#include <shell/e-shell-view.h> + +#include <calendar/gui/e-memo-table.h> +#include <calendar/gui/gnome-cal.h> +#include <menus/gal-view-instance.h> + +/* Standard GObject macros */ +#define E_TYPE_CAL_SHELL_CONTENT \ + (e_cal_shell_content_get_type ()) +#define E_CAL_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContent)) +#define E_CAL_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentClass)) +#define E_IS_CAL_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CAL_SHELL_CONTENT)) +#define E_IS_CAL_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CAL_SHELL_CONTENT)) +#define E_CAL_SHELL_CONTENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentClass)) + +G_BEGIN_DECLS + +typedef struct _ECalShellContent ECalShellContent; +typedef struct _ECalShellContentClass ECalShellContentClass; +typedef struct _ECalShellContentPrivate ECalShellContentPrivate; + +enum { + E_CAL_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0, + E_CAL_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1, + E_CAL_SHELL_CONTENT_SELECTION_IS_ASSIGNABLE = 1 << 2, + E_CAL_SHELL_CONTENT_SELECTION_IS_COMPLETE = 1 << 3, + E_CAL_SHELL_CONTENT_SELECTION_IS_EDITABLE = 1 << 4, + E_CAL_SHELL_CONTENT_SELECTION_IS_MEETING = 1 << 5, + E_CAL_SHELL_CONTENT_SELECTION_IS_ORGANIZER = 1 << 6, + E_CAL_SHELL_CONTENT_SELECTION_IS_RECURRING = 1 << 7, + E_CAL_SHELL_CONTENT_SELECTION_CAN_ACCEPT = 1 << 8, + E_CAL_SHELL_CONTENT_SELECTION_CAN_DELEGATE = 1 << 9, + E_CAL_SHELL_CONTENT_SELECTION_CAN_SAVE = 1 << 10 +}; + +struct _ECalShellContent { + EShellContent parent; + ECalShellContentPrivate *priv; +}; + +struct _ECalShellContentClass { + EShellContentClass parent_class; +}; + +GType e_cal_shell_content_get_type (void); +void e_cal_shell_content_register_type + (GTypeModule *type_module); +GtkWidget * e_cal_shell_content_new (EShellView *shell_view); +ECalModel * e_cal_shell_content_get_model + (ECalShellContent *cal_shell_content); +GnomeCalendar * e_cal_shell_content_get_calendar + (ECalShellContent *cal_shell_content); +EMemoTable * e_cal_shell_content_get_memo_table + (ECalShellContent *cal_shell_content); +ECalendarTable *e_cal_shell_content_get_task_table + (ECalShellContent *cal_shell_content); +GalViewInstance * + e_cal_shell_content_get_view_instance + (ECalShellContent *cal_shell_content); +void e_cal_shell_content_copy_clipboard + (ECalShellContent *cal_shell_content); +void e_cal_shell_content_cut_clipboard + (ECalShellContent *cal_shell_content); +void e_cal_shell_content_paste_clipboard + (ECalShellContent *cal_shell_content); +void e_cal_shell_content_delete_selection + (ECalShellContent *cal_shell_content); +void e_cal_shell_content_delete_selected_occurrence + (ECalShellContent *cal_shell_content); + +G_END_DECLS + +#endif /* E_CAL_SHELL_CONTENT_H */ diff --git a/modules/calendar/e-cal-shell-migrate.c b/modules/calendar/e-cal-shell-migrate.c new file mode 100644 index 0000000000..5ec9c99bf3 --- /dev/null +++ b/modules/calendar/e-cal-shell-migrate.c @@ -0,0 +1,794 @@ +/* + * e-cal-shell-backend-migrate.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-migrate.h" + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <libebackend/e-dbhash.h> +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-group.h> +#include <libedataserver/e-source-list.h> +#include <libedataserver/e-xml-hash-utils.h> + +#include "e-util/e-bconf-map.h" +#include "e-util/e-folder-map.h" +#include "e-util/e-util-private.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/calendar-config-keys.h" +#include "calendar/gui/e-cal-event.h" +#include "shell/e-shell.h" + +#define WEBCAL_BASE_URI "webcal://" +#define CONTACTS_BASE_URI "contacts://" +#define BAD_CONTACTS_BASE_URI "contact://" +#define PERSONAL_RELATIVE_URI "system" + +static e_gconf_map_t calendar_display_map[] = { + /* /Calendar/Display */ + { "Timezone", "calendar/display/timezone", E_GCONF_MAP_STRING }, + { "Use24HourFormat", "calendar/display/use_24hour_format", E_GCONF_MAP_BOOL }, + { "WeekStartDay", "calendar/display/week_start_day", E_GCONF_MAP_INT }, + { "DayStartHour", "calendar/display/day_start_hour", E_GCONF_MAP_INT }, + { "DayStartMinute", "calendar/display/day_start_minute", E_GCONF_MAP_INT }, + { "DayEndHour", "calendar/display/day_end_hour", E_GCONF_MAP_INT }, + { "DayEndMinute", "calendar/display/day_end_minute", E_GCONF_MAP_INT }, + { "TimeDivisions", "calendar/display/time_divisions", E_GCONF_MAP_INT }, + { "View", "calendar/display/default_view", E_GCONF_MAP_INT }, + { "HPanePosition", "calendar/display/hpane_position", E_GCONF_MAP_FLOAT }, + { "VPanePosition", "calendar/display/vpane_position", E_GCONF_MAP_FLOAT }, + { "MonthHPanePosition", "calendar/display/month_hpane_position", E_GCONF_MAP_FLOAT }, + { "MonthVPanePosition", "calendar/display/month_vpane_position", E_GCONF_MAP_FLOAT }, + { "CompressWeekend", "calendar/display/compress_weekend", E_GCONF_MAP_BOOL }, + { "ShowEventEndTime", "calendar/display/show_event_end", E_GCONF_MAP_BOOL }, + { "WorkingDays", "calendar/display/working_days", E_GCONF_MAP_INT }, + { NULL }, +}; + +static e_gconf_map_t calendar_other_map[] = { + /* /Calendar/Other */ + { "ConfirmDelete", "calendar/prompts/confirm_delete", E_GCONF_MAP_BOOL }, + { "ConfirmExpunge", "calendar/prompts/confirm_purge", E_GCONF_MAP_BOOL }, + { "UseDefaultReminder", "calendar/other/use_default_reminder", E_GCONF_MAP_BOOL }, + { "DefaultReminderInterval", "calendar/other/default_reminder_interval", E_GCONF_MAP_INT }, + { "DefaultReminderUnits", "calendar/other/default_reminder_units", E_GCONF_MAP_STRING }, + { NULL }, +}; + +static e_gconf_map_t calendar_datenavigator_map[] = { + /* /Calendar/DateNavigator */ + { "ShowWeekNumbers", "calendar/date_navigator/show_week_numbers", E_GCONF_MAP_BOOL }, + { NULL }, +}; + +static e_gconf_map_t calendar_alarmnotify_map[] = { + /* /Calendar/AlarmNotify */ + { "LastNotificationTime", "calendar/notify/last_notification_time", E_GCONF_MAP_INT }, + { "CalendarToLoad%i", "calendar/notify/calendars", E_GCONF_MAP_STRING|E_GCONF_MAP_LIST }, + { "BlessedProgram%i", "calendar/notify/programs", E_GCONF_MAP_STRING|E_GCONF_MAP_LIST }, + { NULL }, +}; + +static e_gconf_map_list_t calendar_remap_list[] = { + + { "/Calendar/Display", calendar_display_map }, + { "/Calendar/Other/Map", calendar_other_map }, + { "/Calendar/DateNavigator", calendar_datenavigator_map }, + { "/Calendar/AlarmNotify", calendar_alarmnotify_map }, + + { NULL }, +}; + +static GtkWidget *window; +static GtkLabel *label; +static GtkProgressBar *progress; + +#ifndef G_OS_WIN32 + +/* No previous versions have been available on Win32, so don't + * bother with upgrade support from 1.x on Win32. + */ + +static void +setup_progress_dialog (void) +{ + GtkWidget *vbox, *hbox, *w; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title ((GtkWindow *) window, _("Migrating...")); + gtk_window_set_modal ((GtkWindow *) window, TRUE); + gtk_container_set_border_width ((GtkContainer *) window, 6); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_container_add ((GtkContainer *) window, vbox); + + w = gtk_label_new (_("The location and hierarchy of the Evolution calendar " + "folders has changed since Evolution 1.x.\n\nPlease be " + "patient while Evolution migrates your folders...")); + + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_widget_show (w); + gtk_box_pack_start ((GtkBox *) vbox, w, TRUE, TRUE, 0); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox); + gtk_box_pack_start ((GtkBox *) vbox, hbox, TRUE, TRUE, 0); + + label = (GtkLabel *) gtk_label_new (""); + gtk_widget_show ((GtkWidget *) label); + gtk_box_pack_start ((GtkBox *) hbox, (GtkWidget *) label, TRUE, TRUE, 0); + + progress = (GtkProgressBar *) gtk_progress_bar_new (); + gtk_widget_show ((GtkWidget *) progress); + gtk_box_pack_start ((GtkBox *) hbox, (GtkWidget *) progress, TRUE, TRUE, 0); + + gtk_widget_show (window); +} + +static void +dialog_close (void) +{ + gtk_widget_destroy ((GtkWidget *) window); +} + +static void +dialog_set_folder_name (const gchar *folder_name) +{ + gchar *text; + + text = g_strdup_printf (_("Migrating '%s':"), folder_name); + gtk_label_set_text (label, text); + g_free (text); + + gtk_progress_bar_set_fraction (progress, 0.0); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +static void +dialog_set_progress (double percent) +{ + gchar text[5]; + + snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f)); + + gtk_progress_bar_set_fraction (progress, percent); + gtk_progress_bar_set_text (progress, text); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +static gboolean +check_for_conflict (ESourceGroup *group, gchar *name) +{ + GSList *sources; + GSList *s; + + sources = e_source_group_peek_sources (group); + + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + + if (!strcmp (e_source_peek_name (source), name)) + return TRUE; + } + + return FALSE; +} + +static gchar * +get_source_name (ESourceGroup *group, const gchar *path) +{ + gchar **p = g_strsplit (path, "/", 0); + gint i, j, starting_index; + gint num_elements; + gboolean conflict; + GString *s = g_string_new (NULL); + + for (i = 0; p[i]; i ++); + + num_elements = i; + i--; + + /* p[i] is now the last path element */ + + /* check if it conflicts */ + starting_index = i; + do { + for (j = starting_index; j < num_elements; j += 2) { + if (j != starting_index) + g_string_append_c (s, '_'); + g_string_append (s, p[j]); + } + + conflict = check_for_conflict (group, s->str); + + /* if there was a conflict back up 2 levels (skipping the /subfolder/ element) */ + if (conflict) + starting_index -= 2; + + /* we always break out if we can't go any further, + regardless of whether or not we conflict. */ + if (starting_index < 0) + break; + + } while (conflict); + g_strfreev (p); + + return g_string_free (s, FALSE); +} + +static gboolean +migrate_ical (ECal *old_ecal, ECal *new_ecal) +{ + GList *l, *objects; + gint num_added = 0; + gint num_objects; + gboolean retval = TRUE; + + /* both ecals are loaded, start the actual migration */ + if (!e_cal_get_object_list (old_ecal, "#t", &objects, NULL)) + return FALSE; + + num_objects = g_list_length (objects); + for (l = objects; l; l = l->next) { + icalcomponent *ical_comp = l->data; + GError *error = NULL; + + if (!e_cal_create_object (new_ecal, ical_comp, NULL, &error)) { + g_warning ("Migration of object failed: %s", error->message); + retval = FALSE; + } + + g_clear_error (&error); + + num_added ++; + dialog_set_progress ((double)num_added / num_objects); + } + + g_list_foreach (objects, (GFunc) icalcomponent_free, NULL); + g_list_free (objects); + + return retval; +} + +static gboolean +migrate_ical_folder_to_source (gchar *old_path, ESource *new_source, ECalSourceType type) +{ + ECal *old_ecal = NULL, *new_ecal = NULL; + ESource *old_source; + ESourceGroup *group; + gchar *old_uri = g_strdup_printf ("file://%s", old_path); + GError *error = NULL; + gboolean retval = FALSE; + + group = e_source_group_new ("", old_uri); + old_source = e_source_new ("", ""); + e_source_group_add_source (group, old_source, -1); + + dialog_set_folder_name (e_source_peek_name (new_source)); + + if (!(old_ecal = e_cal_new (old_source, type))) { + g_warning ("could not find a backend for '%s'", e_source_get_uri (old_source)); + goto finish; + } + if (!e_cal_open (old_ecal, FALSE, &error)) { + g_warning ("failed to load source ecal for migration: '%s' (%s)", error->message, + e_source_get_uri (old_source)); + goto finish; + } + + if (!(new_ecal = e_cal_new (new_source, type))) { + g_warning ("could not find a backend for '%s'", e_source_get_uri (new_source)); + goto finish; + } + if (!e_cal_open (new_ecal, FALSE, &error)) { + g_warning ("failed to load destination ecal for migration: '%s' (%s)", error->message, + e_source_get_uri (new_source)); + goto finish; + } + + retval = migrate_ical (old_ecal, new_ecal); + +finish: + g_clear_error (&error); + if (old_ecal) + g_object_unref (old_ecal); + g_object_unref (group); + if (new_ecal) + g_object_unref (new_ecal); + g_free (old_uri); + + return retval; +} + +static gboolean +migrate_ical_folder (gchar *old_path, ESourceGroup *dest_group, gchar *source_name, ECalSourceType type) +{ + ESource *new_source; + gboolean retval; + + new_source = e_source_new (source_name, source_name); + e_source_set_relative_uri (new_source, e_source_peek_uid (new_source)); + e_source_group_add_source (dest_group, new_source, -1); + + retval = migrate_ical_folder_to_source (old_path, new_source, type); + + g_object_unref (new_source); + + return retval; +} + +#endif /* !G_OS_WIN32 */ + +#ifndef G_OS_WIN32 + +static void +migrate_pilot_db_key (const gchar *key, gpointer user_data) +{ + EXmlHash *xmlhash = user_data; + + e_xmlhash_add (xmlhash, key, ""); +} + +static void +migrate_pilot_data (const gchar *component, const gchar *conduit, const gchar *old_path, const gchar *new_path) +{ + gchar *changelog, *map; + const gchar *dent; + const gchar *ext; + gchar *filename; + GDir *dir; + + if (!(dir = g_dir_open (old_path, 0, NULL))) + return; + + map = g_alloca (12 + strlen (conduit)); + sprintf (map, "pilot-map-%s-", conduit); + + changelog = g_alloca (24 + strlen (conduit)); + sprintf (changelog, "pilot-sync-evolution-%s-", conduit); + + while ((dent = g_dir_read_name (dir))) { + if (!strncmp (dent, map, strlen (map)) && + ((ext = strrchr (dent, '.')) && !strcmp (ext, ".xml"))) { + /* pilot map file - src and dest file formats are identical */ + guchar inbuf[4096]; + gsize nread, nwritten; + gint fd0, fd1; + gssize n; + + filename = g_build_filename (old_path, dent, NULL); + if ((fd0 = g_open (filename, O_RDONLY|O_BINARY, 0)) == -1) { + g_free (filename); + continue; + } + + g_free (filename); + filename = g_build_filename (new_path, dent, NULL); + if ((fd1 = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) == -1) { + g_free (filename); + close (fd0); + continue; + } + + do { + do { + n = read (fd0, inbuf, sizeof (inbuf)); + } while (n == -1 && errno == EINTR); + + if (n < 1) + break; + + nread = n; + nwritten = 0; + do { + do { + n = write (fd1, inbuf + nwritten, nread - nwritten); + } while (n == -1 && errno == EINTR); + + if (n > 0) + nwritten += n; + } while (nwritten < nread && n != -1); + + if (n == -1) + break; + } while (1); + + if (n != -1) + n = fsync (fd1); + + if (n == -1) { + g_warning ("Failed to migrate %s: %s", dent, strerror (errno)); + g_unlink (filename); + } + + close (fd0); + close (fd1); + g_free (filename); + } else if (!strncmp (dent, changelog, strlen (changelog)) && + ((ext = strrchr (dent, '.')) && !strcmp (ext, ".db"))) { + /* src and dest formats differ, src format is db3 while dest format is xml */ + EXmlHash *xmlhash; + EDbHash *dbhash; + struct stat st; + + filename = g_build_filename (old_path, dent, NULL); + if (g_stat (filename, &st) == -1) { + g_free (filename); + continue; + } + + dbhash = e_dbhash_new (filename); + g_free (filename); + + filename = g_strdup_printf ("%s/%s.ics-%s", new_path, component, dent); + if (g_stat (filename, &st) != -1) + g_unlink (filename); + xmlhash = e_xmlhash_new (filename); + g_free (filename); + + e_dbhash_foreach_key (dbhash, migrate_pilot_db_key, xmlhash); + + e_dbhash_destroy (dbhash); + + e_xmlhash_write (xmlhash); + e_xmlhash_destroy (xmlhash); + } + } + + g_dir_close (dir); +} + +#endif + +static ESourceGroup * +create_calendar_contact_source (ESourceList *source_list) +{ + ESourceGroup *group; + ESource *source; + + /* Create the contacts group */ + group = e_source_group_new (_("Contacts"), CONTACTS_BASE_URI); + e_source_list_add_group (source_list, group, -1); + + source = e_source_new (_("Birthdays & Anniversaries"), "/"); + e_source_group_add_source (group, source, -1); + g_object_unref (source); + + e_source_set_color_spec (source, "#FED4D3"); + e_source_group_set_readonly (group, TRUE); + + return group; +} + +static void +create_calendar_sources (EShellBackend *shell_backend, + ESourceList *source_list, + ESourceGroup **on_this_computer, + ESource **personal_source, + ESourceGroup **on_the_web, + ESourceGroup **contacts) +{ + EShell *shell; + EShellSettings *shell_settings; + GSList *groups; + ESourceGroup *group; + gchar *base_uri, *base_uri_proto; + const gchar *base_dir; + + *on_this_computer = NULL; + *on_the_web = NULL; + *contacts = NULL; + *personal_source = NULL; + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + base_dir = e_shell_backend_get_config_dir (shell_backend); + base_uri = g_build_filename (base_dir, "local", NULL); + + base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL); + + groups = e_source_list_peek_groups (source_list); + if (groups) { + /* groups are already there, we need to search for things... */ + GSList *g; + + for (g = groups; g; g = g->next) { + + group = E_SOURCE_GROUP (g->data); + + if (!strcmp (BAD_CONTACTS_BASE_URI, e_source_group_peek_base_uri (group))) + e_source_group_set_base_uri (group, CONTACTS_BASE_URI); + + if (!strcmp (base_uri, e_source_group_peek_base_uri (group))) + e_source_group_set_base_uri (group, base_uri_proto); + + if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group))) + *on_this_computer = g_object_ref (group); + else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group))) + *on_the_web = g_object_ref (group); + else if (!*contacts && !strcmp (CONTACTS_BASE_URI, e_source_group_peek_base_uri (group))) + *contacts = g_object_ref (group); + } + } + + if (*on_this_computer) { + /* make sure "Personal" shows up as a source under + this group */ + GSList *sources = e_source_group_peek_sources (*on_this_computer); + GSList *s; + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + const gchar *relative_uri; + + relative_uri = e_source_peek_relative_uri (source); + if (relative_uri == NULL) + continue; + if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) { + *personal_source = g_object_ref (source); + break; + } + } + } else { + /* create the local source group */ + group = e_source_group_new (_("On This Computer"), base_uri_proto); + e_source_list_add_group (source_list, group, -1); + + *on_this_computer = group; + } + + if (!*personal_source) { + gchar *primary_calendar; + + /* Create the default Person calendar */ + ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI); + e_source_group_add_source (*on_this_computer, source, -1); + + primary_calendar = e_shell_settings_get_string ( + shell_settings, "cal-primary-calendar"); + + if (!primary_calendar && !calendar_config_get_calendars_selected ()) { + GSList selected; + + e_shell_settings_set_string ( + shell_settings, "cal-primary-calendar", + e_source_peek_uid (source)); + + selected.data = (gpointer)e_source_peek_uid (source); + selected.next = NULL; + calendar_config_set_calendars_selected (&selected); + } + + g_free (primary_calendar); + e_source_set_color_spec (source, "#BECEDD"); + *personal_source = source; + } + + if (!*on_the_web) { + /* Create the Webcal source group */ + group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI); + e_source_list_add_group (source_list, group, -1); + + *on_the_web = group; + } + + if (!*contacts) { + group = create_calendar_contact_source (source_list); + + *contacts = group; + } + + g_free (base_uri_proto); + g_free (base_uri); +} + +gboolean +e_cal_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error) +{ + ESourceGroup *on_this_computer = NULL, *on_the_web = NULL, *contacts = NULL; + ESource *personal_source = NULL; + ESourceList *source_list; + ECalEvent *ece; + ECalEventTargetBackend *target; + gboolean retval = FALSE; + + g_object_get (shell_backend, "source-list", &source_list, NULL); + + /* we call this unconditionally now - create_groups either + creates the groups/sources or it finds the necessary + groups/sources. */ + create_calendar_sources ( + shell_backend, source_list, &on_this_computer, + &personal_source, &on_the_web, &contacts); + +#ifndef G_OS_WIN32 + if (major == 1) { + xmlDocPtr config_doc = NULL; + gchar *conf_file; + struct stat st; + + conf_file = g_build_filename (g_get_home_dir (), "evolution", "config.xmldb", NULL); + if (lstat (conf_file, &st) == 0 && S_ISREG (st.st_mode)) + config_doc = xmlParseFile (conf_file); + g_free (conf_file); + + if (config_doc && minor <= 2) { + GConfClient *gconf; + gint res = 0; + + /* move bonobo config to gconf */ + gconf = gconf_client_get_default (); + + res = e_bconf_import (gconf, config_doc, calendar_remap_list); + + g_object_unref (gconf); + + xmlFreeDoc(config_doc); + + if (res != 0) { + /* FIXME: set proper domain/code */ + g_set_error(error, 0, 0, _("Unable to migrate old settings from evolution/config.xmldb")); + goto fail; + } + } + + if (minor <= 4) { + GSList *migration_dirs, *l; + gchar *path, *local_cal_folder; + + setup_progress_dialog (); + + path = g_build_filename (g_get_home_dir (), "evolution", "local", NULL); + migration_dirs = e_folder_map_local_folders (path, "calendar"); + local_cal_folder = g_build_filename (path, "Calendar", NULL); + g_free (path); + + if (personal_source) + migrate_ical_folder_to_source (local_cal_folder, personal_source, E_CAL_SOURCE_TYPE_EVENT); + + for (l = migration_dirs; l; l = l->next) { + gchar *source_name; + + if (personal_source && !strcmp ((gchar *)l->data, local_cal_folder)) + continue; + + source_name = get_source_name (on_this_computer, (gchar *)l->data); + + if (!migrate_ical_folder (l->data, on_this_computer, source_name, E_CAL_SOURCE_TYPE_EVENT)) { + /* FIXME: domain/code */ + g_set_error(error, 0, 0, _("Unable to migrate calendar `%s'"), source_name); + g_free(source_name); + goto fail; + } + + g_free (source_name); + } + + g_free (local_cal_folder); + + dialog_close (); + } + + if (minor <= 4 || (minor == 5 && micro < 5)) { + GConfClient *gconf; + GConfValue *gconf_val; + gint i; + const gchar *keys[] = { + CALENDAR_CONFIG_HPANE_POS, + CALENDAR_CONFIG_VPANE_POS, + CALENDAR_CONFIG_MONTH_HPANE_POS, + CALENDAR_CONFIG_MONTH_VPANE_POS, + NULL + }; + + gconf = gconf_client_get_default (); + + for (i = 0; keys[i]; i++) { + gconf_val = gconf_client_get (gconf, keys[i], NULL); + if (gconf_val) { + if (gconf_val->type != GCONF_VALUE_INT) + gconf_client_unset (gconf, keys[i], NULL); + gconf_value_free (gconf_val); + } + } + + g_object_unref (gconf); + } + + if (minor < 5 || (minor == 5 && micro <= 10)) { + gchar *old_path, *new_path; + + old_path = g_build_filename (g_get_home_dir (), "evolution", "local", "Calendar", NULL); + new_path = g_build_filename (e_shell_backend_get_config_dir (shell_backend), + "local", "system", NULL); + migrate_pilot_data ("calendar", "calendar", old_path, new_path); + g_free (new_path); + g_free (old_path); + } + + /* we only need to do this next step if people ran + older versions of 1.5. We need to clear out the + absolute URI's that were assigned to ESources + during one phase of development, as they take + precedent over relative uris (but aren't updated + when editing an ESource). */ + if (minor == 5 && micro <= 11) { + GSList *g; + for (g = e_source_list_peek_groups (source_list); g; g = g->next) { + ESourceGroup *group = g->data; + GSList *s; + + for (s = e_source_group_peek_sources (group); s; s = s->next) { + ESource *source = s->data; + e_source_set_absolute_uri (source, NULL); + } + } + } + + } +#endif /* !G_OS_WIN32 */ + + e_source_list_sync (source_list, NULL); + + /** @Event: component.migration + * @Title: Migration step in component initialization + * @Target: ECalEventTargetComponent + * + * component.migration is emitted during the calendar component + * initialization process. This allows new calendar backend types + * to be distributed as an e-d-s backend and a plugin without + * reaching their grubby little fingers into migration.c + */ + /* Fire off migration event */ + ece = e_cal_event_peek (); + target = e_cal_event_target_new_module (ece, shell_backend, source_list, 0); + e_event_emit ((EEvent *) ece, "module.migration", (EEventTarget *) target); + + retval = TRUE; +fail: + if (on_this_computer) + g_object_unref (on_this_computer); + if (on_the_web) + g_object_unref (on_the_web); + if (contacts) + g_object_unref (contacts); + if (personal_source) + g_object_unref (personal_source); + + return retval; +} + diff --git a/modules/calendar/e-cal-shell-migrate.h b/modules/calendar/e-cal-shell-migrate.h new file mode 100644 index 0000000000..359ca5ea22 --- /dev/null +++ b/modules/calendar/e-cal-shell-migrate.h @@ -0,0 +1,38 @@ +/* + * e-cal-shell-backend-migrate.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_BACKEND_MIGRATE_H +#define E_CAL_SHELL_BACKEND_MIGRATE_H + +#include <glib.h> +#include <shell/e-shell-backend.h> + +G_BEGIN_DECLS + +gboolean e_cal_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error); + +G_END_DECLS + +#endif /* E_CAL_SHELL_BACKEND_MIGRATE_H */ diff --git a/modules/calendar/e-cal-shell-settings.c b/modules/calendar/e-cal-shell-settings.c new file mode 100644 index 0000000000..4e6939c892 --- /dev/null +++ b/modules/calendar/e-cal-shell-settings.c @@ -0,0 +1,742 @@ +/* + * e-cal-shell-settings.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-settings.h" + +#include <gconf/gconf-client.h> +#include <libecal/e-cal-util.h> + +#include "e-util/e-binding.h" + +static gboolean +transform_string_to_icaltimezone (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gboolean use_system_timezone; + const gchar *location = NULL; + icaltimezone *timezone = NULL; + + shell_settings = E_SHELL_SETTINGS (user_data); + + use_system_timezone = e_shell_settings_get_boolean ( + shell_settings, "cal-use-system-timezone"); + + if (use_system_timezone) + timezone = e_cal_util_get_system_timezone (); + else + location = g_value_get_string (src_value); + + if (location != NULL && *location != '\0') + timezone = icaltimezone_get_builtin_timezone (location); + + if (timezone == NULL) + timezone = icaltimezone_get_utc_timezone (); + + g_value_set_pointer (dst_value, timezone); + + return TRUE; +} + +static gboolean +transform_icaltimezone_to_string (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + const gchar *location = NULL; + icaltimezone *timezone; + + timezone = g_value_get_pointer (src_value); + + if (timezone != NULL) + location = icaltimezone_get_location (timezone); + + if (location == NULL) + location = "UTC"; + + g_value_set_string (dst_value, location); + + return TRUE; +} + +static gboolean +transform_weekdays_gconf_to_evolution (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + GDateWeekday weekday; + + /* XXX At some point, Evolution changed its weekday numbering + * from 0 = Sunday to 0 = Monday, but did not migrate the + * "week_start_day" key. Both enumerations are of course + * different from GDateWeekday. We should have saved the + * weekday as a string instead. */ + + /* This is purposefully verbose for better readability. */ + + /* GConf numbering */ + switch (g_value_get_int (src_value)) { + case 0: + weekday = G_DATE_SUNDAY; + break; + case 1: + weekday = G_DATE_MONDAY; + break; + case 2: + weekday = G_DATE_TUESDAY; + break; + case 3: + weekday = G_DATE_WEDNESDAY; + break; + case 4: + weekday = G_DATE_THURSDAY; + break; + case 5: + weekday = G_DATE_FRIDAY; + break; + case 6: + weekday = G_DATE_SATURDAY; + break; + default: + return FALSE; + } + + /* Evolution numbering */ + switch (weekday) { + case G_DATE_MONDAY: + g_value_set_int (dst_value, 0); + break; + case G_DATE_TUESDAY: + g_value_set_int (dst_value, 1); + break; + case G_DATE_WEDNESDAY: + g_value_set_int (dst_value, 2); + break; + case G_DATE_THURSDAY: + g_value_set_int (dst_value, 3); + break; + case G_DATE_FRIDAY: + g_value_set_int (dst_value, 4); + break; + case G_DATE_SATURDAY: + g_value_set_int (dst_value, 5); + break; + case G_DATE_SUNDAY: + g_value_set_int (dst_value, 6); + break; + default: + return FALSE; + } + + return TRUE; +} + +static gboolean +transform_weekdays_evolution_to_gconf (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + GDateWeekday weekday; + + /* XXX At some point, Evolution changed its weekday numbering + * from 0 = Sunday to 0 = Monday, but did not migrate the + * "week_start_day" key. Both enumerations are of course + * different from GDateWeekday. We should have saved the + * weekday as a string instead. */ + + /* This is purposefully verbose for better readability. */ + + /* GConf numbering */ + switch (g_value_get_int (src_value)) { + case 0: + weekday = G_DATE_MONDAY; + break; + case 1: + weekday = G_DATE_TUESDAY; + break; + case 2: + weekday = G_DATE_WEDNESDAY; + break; + case 3: + weekday = G_DATE_THURSDAY; + break; + case 4: + weekday = G_DATE_FRIDAY; + break; + case 5: + weekday = G_DATE_SATURDAY; + break; + case 6: + weekday = G_DATE_SUNDAY; + break; + default: + return FALSE; + } + + /* Evolution numbering */ + switch (weekday) { + case G_DATE_MONDAY: + g_value_set_int (dst_value, 1); + break; + case G_DATE_TUESDAY: + g_value_set_int (dst_value, 2); + break; + case G_DATE_WEDNESDAY: + g_value_set_int (dst_value, 3); + break; + case G_DATE_THURSDAY: + g_value_set_int (dst_value, 4); + break; + case G_DATE_FRIDAY: + g_value_set_int (dst_value, 5); + break; + case G_DATE_SATURDAY: + g_value_set_int (dst_value, 6); + break; + case G_DATE_SUNDAY: + g_value_set_int (dst_value, 0); + break; + default: + return FALSE; + } + + return TRUE; +} + +/* Working day flags */ +enum { + WORKING_DAY_SUNDAY = 1 << 0, + WORKING_DAY_MONDAY = 1 << 1, + WORKING_DAY_TUESDAY = 1 << 2, + WORKING_DAY_WEDNESDAY = 1 << 3, + WORKING_DAY_THURSDAY = 1 << 4, + WORKING_DAY_FRIDAY = 1 << 5, + WORKING_DAY_SATURDAY = 1 << 6 +}; + +static gboolean +transform_working_days_bitset_to_sunday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_SUNDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_sunday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_SUNDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_SUNDAY) | bit); + + return TRUE; +} + +static gboolean +transform_working_days_bitset_to_monday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_MONDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_monday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_MONDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_MONDAY) | bit); + + return TRUE; +} + +static gboolean +transform_working_days_bitset_to_tuesday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_TUESDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_tuesday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_TUESDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_TUESDAY) | bit); + + return TRUE; +} + +static gboolean +transform_working_days_bitset_to_wednesday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_WEDNESDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_wednesday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_WEDNESDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_WEDNESDAY) | bit); + + return TRUE; +} + +static gboolean +transform_working_days_bitset_to_thursday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_THURSDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_thursday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_THURSDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_THURSDAY) | bit); + + return TRUE; +} + +static gboolean +transform_working_days_bitset_to_friday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_FRIDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_friday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_FRIDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_FRIDAY) | bit); + + return TRUE; +} + +static gboolean +transform_working_days_bitset_to_saturday (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gint bitset; + gboolean working_day; + + bitset = g_value_get_int (src_value); + working_day = ((bitset & WORKING_DAY_SATURDAY) != 0); + g_value_set_boolean (dst_value, working_day); + + return TRUE; +} + +static gboolean +transform_working_days_saturday_to_bitset (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + EShellSettings *shell_settings; + gint bitset, bit; + + shell_settings = E_SHELL_SETTINGS (user_data); + + bitset = e_shell_settings_get_int ( + shell_settings, "cal-working-days-bitset"); + + bit = g_value_get_boolean (src_value) ? WORKING_DAY_SATURDAY : 0; + g_value_set_int (dst_value, (bitset & ~WORKING_DAY_SATURDAY) | bit); + + return TRUE; +} + +void +e_cal_shell_backend_init_settings (EShell *shell) +{ + EShellSettings *shell_settings; + + shell_settings = e_shell_get_shell_settings (shell); + + e_shell_settings_install_property_for_key ( + "cal-compress-weekend", + "/apps/evolution/calendar/display/compress_weekend"); + + e_shell_settings_install_property_for_key ( + "cal-confirm-delete", + "/apps/evolution/calendar/prompts/confirm_delete"); + + e_shell_settings_install_property_for_key ( + "cal-confirm-purge", + "/apps/evolution/calendar/prompts/confirm_purge"); + + e_shell_settings_install_property_for_key ( + "cal-day-view-show-week-numbers", + "/apps/evolution/calendar/display/day_view_show_week_number"); + + e_shell_settings_install_property_for_key ( + "cal-free-busy-template", + "/apps/evolution/calendar/publish/template"); + + e_shell_settings_install_property_for_key ( + "cal-hide-completed-tasks", + "/apps/evolution/calendar/tasks/hide_completed"); + + e_shell_settings_install_property_for_key ( + "cal-hide-completed-tasks-units", + "/apps/evolution/calendar/tasks/hide_completed_units"); + + e_shell_settings_install_property_for_key ( + "cal-hide-completed-tasks-value", + "/apps/evolution/calendar/tasks/hide_completed_value"); + + e_shell_settings_install_property_for_key ( + "cal-marcus-bains-day-view-color", + "/apps/evolution/calendar/display/marcus_bains_color_dayview"); + + e_shell_settings_install_property_for_key ( + "cal-marcus-bains-time-bar-color", + "/apps/evolution/calendar/display/marcus_bains_color_timebar"); + + e_shell_settings_install_property_for_key ( + "cal-marcus-bains-show-line", + "/apps/evolution/calendar/display/marcus_bains_line"); + + e_shell_settings_install_property_for_key ( + "cal-primary-calendar", + "/apps/evolution/calendar/display/primary_calendar"); + + e_shell_settings_install_property_for_key ( + "cal-primary-memo-list", + "/apps/evolution/calendar/memos/primary_memos"); + + e_shell_settings_install_property_for_key ( + "cal-primary-task-list", + "/apps/evolution/calendar/tasks/primary_tasks"); + + e_shell_settings_install_property_for_key ( + "cal-show-event-end-times", + "/apps/evolution/calendar/display/show_event_end"); + + e_shell_settings_install_property_for_key ( + "cal-show-week-numbers", + "/apps/evolution/calendar/date_navigator/show_week_numbers"); + + e_shell_settings_install_property_for_key ( + "cal-tasks-color-due-today", + "/apps/evolution/calendar/tasks/colors/due_today"); + + e_shell_settings_install_property_for_key ( + "cal-tasks-color-overdue", + "/apps/evolution/calendar/tasks/colors/overdue"); + + e_shell_settings_install_property_for_key ( + "cal-time-divisions", + "/apps/evolution/calendar/display/time_divisions"); + + /* Do not bind to this. Use "cal-timezone" instead. */ + e_shell_settings_install_property_for_key ( + "cal-timezone-string", + "/apps/evolution/calendar/display/timezone"); + + e_shell_settings_install_property_for_key ( + "cal-use-24-hour-format", + "/apps/evolution/calendar/display/use_24hour_format"); + + e_shell_settings_install_property_for_key ( + "cal-use-system-timezone", + "/apps/evolution/calendar/display/use_system_timezone"); + + /* Do not bind to this. Use "cal-week-start-day" instead. */ + e_shell_settings_install_property_for_key ( + "cal-week-start-day-gconf", + "/apps/evolution/calendar/display/week_start_day"); + + e_shell_settings_install_property_for_key ( + "cal-work-day-end-hour", + "/apps/evolution/calendar/display/day_end_hour"); + + e_shell_settings_install_property_for_key ( + "cal-work-day-end-minute", + "/apps/evolution/calendar/display/day_end_minute"); + + e_shell_settings_install_property_for_key ( + "cal-work-day-start-hour", + "/apps/evolution/calendar/display/day_start_hour"); + + e_shell_settings_install_property_for_key ( + "cal-work-day-start-minute", + "/apps/evolution/calendar/display/day_start_minute"); + + e_shell_settings_install_property_for_key ( + "cal-working-days-bitset", + "/apps/evolution/calendar/display/working_days"); + + /* These properties use transform functions to convert + * GConf values to forms more useful to Evolution. We + * have to use separate properties because GConfBridge + * does not support transform functions. Much of this + * is backward-compatibility cruft for poorly designed + * GConf schemas. */ + + e_shell_settings_install_property ( + g_param_spec_pointer ( + "cal-timezone", + NULL, + NULL, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-timezone-string", + G_OBJECT (shell_settings), "cal-timezone", + transform_string_to_icaltimezone, + transform_icaltimezone_to_string, + (GDestroyNotify) g_object_unref, + g_object_ref (shell_settings)); + + e_shell_settings_install_property ( + g_param_spec_int ( + "cal-week-start-day", + NULL, + NULL, + 0, /* Monday */ + 6, /* Sunday */ + 0, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-week-start-day-gconf", + G_OBJECT (shell_settings), "cal-week-start-day", + transform_weekdays_gconf_to_evolution, + transform_weekdays_evolution_to_gconf, + (GDestroyNotify) NULL, NULL); + + /* XXX These are my favorite. Storing a bit array in GConf + * instead of separate boolean keys. Brilliant move. */ + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-sunday", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-sunday", + transform_working_days_bitset_to_sunday, + transform_working_days_sunday_to_bitset, + (GDestroyNotify) NULL, shell_settings); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-monday", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-monday", + transform_working_days_bitset_to_monday, + transform_working_days_monday_to_bitset, + (GDestroyNotify) NULL, shell_settings); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-tuesday", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-tuesday", + transform_working_days_bitset_to_tuesday, + transform_working_days_tuesday_to_bitset, + (GDestroyNotify) NULL, shell_settings); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-wednesday", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-wednesday", + transform_working_days_bitset_to_wednesday, + transform_working_days_wednesday_to_bitset, + (GDestroyNotify) NULL, shell_settings); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-thursday", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-thursday", + transform_working_days_bitset_to_thursday, + transform_working_days_thursday_to_bitset, + (GDestroyNotify) NULL, shell_settings); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-friday", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-friday", + transform_working_days_bitset_to_friday, + transform_working_days_friday_to_bitset, + (GDestroyNotify) NULL, shell_settings); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "cal-working-days-saturday", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "cal-working-days-bitset", + G_OBJECT (shell_settings), "cal-working-days-saturday", + transform_working_days_bitset_to_saturday, + transform_working_days_saturday_to_bitset, + (GDestroyNotify) g_object_unref, + g_object_ref (shell_settings)); +} diff --git a/modules/calendar/e-cal-shell-settings.h b/modules/calendar/e-cal-shell-settings.h new file mode 100644 index 0000000000..5f7293bbed --- /dev/null +++ b/modules/calendar/e-cal-shell-settings.h @@ -0,0 +1,33 @@ +/* + * e-cal-shell-settings.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_SETTINGS_H +#define E_CAL_SHELL_SETTINGS_H + +#include <shell/e-shell.h> + +G_BEGIN_DECLS + +void e_cal_shell_backend_init_settings (EShell *shell); + +G_END_DECLS + +#endif /* E_CAL_SHELL_SETTINGS_H */ diff --git a/modules/calendar/e-cal-shell-sidebar.c b/modules/calendar/e-cal-shell-sidebar.c new file mode 100644 index 0000000000..05b3a47d72 --- /dev/null +++ b/modules/calendar/e-cal-shell-sidebar.c @@ -0,0 +1,757 @@ +/* + * e-cal-shell-sidebar.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-sidebar.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include "e-util/e-error.h" +#include "e-util/e-binding.h" +#include "e-util/gconf-bridge.h" +#include "widgets/misc/e-paned.h" + +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/e-calendar-selector.h" +#include "calendar/gui/misc.h" + +#include "e-cal-shell-backend.h" +#include "e-cal-shell-view.h" + +#define E_CAL_SHELL_SIDEBAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarPrivate)) + +struct _ECalShellSidebarPrivate { + GtkWidget *paned; + GtkWidget *selector; + GtkWidget *date_navigator; + + /* UID -> Client */ + GHashTable *client_table; +}; + +enum { + PROP_0, + PROP_DATE_NAVIGATOR, + PROP_SELECTOR +}; + +enum { + CLIENT_ADDED, + CLIENT_REMOVED, + STATUS_MESSAGE, + LAST_SIGNAL +}; + +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; +static GType cal_shell_sidebar_type; + +static void +cal_shell_sidebar_emit_client_added (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_ADDED]; + + g_signal_emit (cal_shell_sidebar, signal_id, 0, client); +} + +static void +cal_shell_sidebar_emit_client_removed (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_REMOVED]; + + g_signal_emit (cal_shell_sidebar, signal_id, 0, client); +} + +static void +cal_shell_sidebar_emit_status_message (ECalShellSidebar *cal_shell_sidebar, + const gchar *status_message) +{ + guint signal_id = signals[STATUS_MESSAGE]; + + g_signal_emit (cal_shell_sidebar, signal_id, 0, status_message); +} + +static void +cal_shell_sidebar_backend_died_cb (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = cal_shell_sidebar->priv->client_table; + + shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + source = e_cal_get_source (client); + uid = e_source_peek_uid (source); + + g_object_ref (source); + + g_hash_table_remove (client_table, uid); + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL); + + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:calendar-crashed", NULL); + + g_object_unref (source); +} + +static void +cal_shell_sidebar_backend_error_cb (ECalShellSidebar *cal_shell_sidebar, + const gchar *message, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GtkWidget *dialog; + const gchar *uri; + gchar *uri_no_passwd; + + shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + uri = e_cal_get_uri (client); + uri_no_passwd = get_uri_without_password (uri); + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Error on %s\n%s"), + uri_no_passwd, message); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_free (uri_no_passwd); +} + +static void +cal_shell_sidebar_client_opened_cb (ECalShellSidebar *cal_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + ESource *source; + + source = e_cal_get_source (client); + + shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED || + status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) + auth_cal_forget_password (client); + + switch (status) { + case E_CALENDAR_STATUS_OK: + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + cal_shell_sidebar_client_opened_cb, NULL); + + cal_shell_sidebar_emit_status_message ( + cal_shell_sidebar, _("Loading calendars")); + cal_shell_sidebar_emit_client_added ( + cal_shell_sidebar, client); + cal_shell_sidebar_emit_status_message ( + cal_shell_sidebar, NULL); + break; + + case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: + e_cal_open_async (client, FALSE); + break; + + case E_CALENDAR_STATUS_BUSY: + break; + + case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-no-contents-offline-calendar", + NULL); + break; + + default: + cal_shell_sidebar_emit_client_removed ( + cal_shell_sidebar, client); + break; + } +} + +static void +cal_shell_sidebar_row_changed_cb (ECalShellSidebar *cal_shell_sidebar, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + GtkTreeModel *tree_model) +{ + ESourceSelector *selector; + ESource *source; + + /* XXX ESourceSelector's underlying tree store has only one + * column: ESource objects. While we're not supposed to + * know this, listening for "row-changed" signals from + * the model is easier to deal with than the selector's + * "selection-changed" signal, which doesn't tell you + * _which_ row changed. */ + + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1); + + /* XXX This signal gets emitted a lot while the model is being + * rebuilt, during which time we won't get a valid ESource. + * ESourceSelector should probably block this signal while + * rebuilding the model, but we'll be forgiving and not + * emit a warning. */ + if (!E_IS_SOURCE (source)) + return; + + if (e_source_selector_source_is_selected (selector, source)) + e_cal_shell_sidebar_add_source (cal_shell_sidebar, source); + else + e_cal_shell_sidebar_remove_source (cal_shell_sidebar, source); +} + +static void +cal_shell_sidebar_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar, + ESourceSelector *selector) +{ + GSList *list, *iter; + + /* This signal is emitted less frequently than "row-changed", + * especially when the model is being rebuilt. So we'll take + * it easy on poor GConf. */ + + list = e_source_selector_get_selection (selector); + + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + iter->data = (gpointer) e_source_peek_uid (source); + g_object_unref (source); + } + + calendar_config_set_calendars_selected (list); + + g_slist_free (list); +} + +static void +cal_shell_sidebar_primary_selection_changed_cb (ECalShellSidebar *cal_shell_sidebar, + ESourceSelector *selector) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESource *source; + + /* XXX ESourceSelector needs a "primary-selection-uid" property + * so we can just bind the property with GConfBridge. */ + + source = e_source_selector_peek_primary_selection (selector); + if (source == NULL) + return; + + shell_sidebar = E_SHELL_SIDEBAR (cal_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + e_shell_settings_set_string ( + shell_settings, "cal-primary-calendar", + e_source_peek_uid (source)); +} + +static void +cal_shell_sidebar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DATE_NAVIGATOR: + g_value_set_object ( + value, e_cal_shell_sidebar_get_date_navigator ( + E_CAL_SHELL_SIDEBAR (object))); + return; + + case PROP_SELECTOR: + g_value_set_object ( + value, e_cal_shell_sidebar_get_selector ( + E_CAL_SHELL_SIDEBAR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +cal_shell_sidebar_dispose (GObject *object) +{ + ECalShellSidebarPrivate *priv; + + priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object); + + if (priv->paned != NULL) { + g_object_unref (priv->paned); + priv->paned = NULL; + } + + if (priv->selector != NULL) { + g_object_unref (priv->selector); + priv->selector = NULL; + } + + if (priv->date_navigator != NULL) { + g_object_unref (priv->date_navigator); + priv->date_navigator = NULL; + } + + g_hash_table_remove_all (priv->client_table); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +cal_shell_sidebar_finalize (GObject *object) +{ + ECalShellSidebarPrivate *priv; + + priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object); + + g_hash_table_destroy (priv->client_table); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +cal_shell_sidebar_constructed (GObject *object) +{ + ECalShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + ECalendarItem *calitem; + GConfBridge *bridge; + GtkTreeModel *model; + GtkWidget *container; + GtkWidget *widget; + AtkObject *a11y; + GSList *list, *iter; + const gchar *key; + gchar *uid; + + priv = E_CAL_SHELL_SIDEBAR_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + shell_sidebar = E_SHELL_SIDEBAR (object); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + source_list = e_cal_shell_backend_get_source_list ( + E_CAL_SHELL_BACKEND (shell_backend)); + + container = GTK_WIDGET (shell_sidebar); + + widget = e_paned_new (GTK_ORIENTATION_VERTICAL); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->paned = g_object_ref (widget); + gtk_widget_show (widget); + + container = widget; + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE); + gtk_widget_show (widget); + + container = widget; + + widget = e_calendar_selector_new (source_list); + e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE); + gtk_container_add (GTK_CONTAINER (container), widget); + a11y = gtk_widget_get_accessible (widget); + atk_object_set_name (a11y, _("Calendar Selector")); + priv->selector = g_object_ref (widget); + gtk_widget_show (widget); + + container = priv->paned; + + widget = e_calendar_new (); + calitem = E_CALENDAR (widget)->calitem; + e_calendar_item_set_days_start_week_sel (calitem, 9); + e_calendar_item_set_max_days_sel (calitem, 42); + gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, TRUE); + priv->date_navigator = g_object_ref (widget); + gtk_widget_show (widget); + + e_binding_new ( + G_OBJECT (shell_settings), "cal-show-week-numbers", + G_OBJECT (calitem), "show-week-numbers"); + + e_binding_new ( + G_OBJECT (shell_settings), "cal-week-start-day", + G_OBJECT (calitem), "week-start-day"); + + /* Restore the selector state from the last session. */ + + selector = E_SOURCE_SELECTOR (priv->selector); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + + g_signal_connect_swapped ( + model, "row-changed", + G_CALLBACK (cal_shell_sidebar_row_changed_cb), + object); + + source = NULL; + uid = e_shell_settings_get_string ( + shell_settings, "cal-primary-calendar"); + if (uid != NULL) + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source == NULL) + source = e_source_list_peek_source_any (source_list); + if (source != NULL) + e_source_selector_set_primary_selection (selector, source); + g_free (uid); + + list = calendar_config_get_calendars_selected (); + for (iter = list; iter != NULL; iter = iter->next) { + uid = iter->data; + source = e_source_list_peek_source_by_uid (source_list, uid); + g_free (uid); + + if (source == NULL) + continue; + + e_source_selector_select_source (selector, source); + } + g_slist_free (list); + + /* Listen for subsequent changes to the selector. */ + + g_signal_connect_swapped ( + selector, "selection-changed", + G_CALLBACK (cal_shell_sidebar_selection_changed_cb), + object); + + g_signal_connect_swapped ( + selector, "primary-selection-changed", + G_CALLBACK (cal_shell_sidebar_primary_selection_changed_cb), + object); + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (priv->paned); + key = "/apps/evolution/calendar/display/date_navigator_vpane_position"; + gconf_bridge_bind_property_delayed (bridge, key, object, "vposition"); +} + +static void +cal_shell_sidebar_client_removed (ECalShellSidebar *cal_shell_sidebar, + ECal *client) +{ + ESourceSelector *selector; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = cal_shell_sidebar->priv->client_table; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, cal_shell_sidebar); + + source = e_cal_get_source (client); + e_source_selector_unselect_source (selector, source); + + uid = e_source_peek_uid (source); + g_hash_table_remove (client_table, uid); + + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, NULL); +} + +static void +cal_shell_sidebar_class_init (ECalShellSidebarClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ECalShellSidebarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = cal_shell_sidebar_get_property; + object_class->dispose = cal_shell_sidebar_dispose; + object_class->finalize = cal_shell_sidebar_finalize; + object_class->constructed = cal_shell_sidebar_constructed; + + class->client_removed = cal_shell_sidebar_client_removed; + + g_object_class_install_property ( + object_class, + PROP_DATE_NAVIGATOR, + g_param_spec_object ( + "date-navigator", + _("Date Navigator Widget"), + _("This widget displays a miniature calendar"), + E_TYPE_CALENDAR, + G_PARAM_READABLE)); + + g_object_class_install_property ( + object_class, + PROP_SELECTOR, + g_param_spec_object ( + "selector", + _("Source Selector Widget"), + _("This widget displays groups of calendars"), + E_TYPE_SOURCE_SELECTOR, + G_PARAM_READABLE)); + + signals[CLIENT_ADDED] = g_signal_new ( + "client-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECalShellSidebarClass, client_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[CLIENT_REMOVED] = g_signal_new ( + "client-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECalShellSidebarClass, client_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[STATUS_MESSAGE] = g_signal_new ( + "status-message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ECalShellSidebarClass, status_message), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} + +static void +cal_shell_sidebar_init (ECalShellSidebar *cal_shell_sidebar) +{ + GHashTable *client_table; + + client_table = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + cal_shell_sidebar->priv = + E_CAL_SHELL_SIDEBAR_GET_PRIVATE (cal_shell_sidebar); + + cal_shell_sidebar->priv->client_table = client_table; + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_cal_shell_sidebar_get_type (void) +{ + return cal_shell_sidebar_type; +} + +void +e_cal_shell_sidebar_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (ECalShellSidebarClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_shell_sidebar_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ECalShellSidebar), + 0, /* n_preallocs */ + (GInstanceInitFunc) cal_shell_sidebar_init, + NULL /* value_table */ + }; + + cal_shell_sidebar_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_SIDEBAR, + "ECalShellSidebar", &type_info, 0); +} + +GtkWidget * +e_cal_shell_sidebar_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_CAL_SHELL_SIDEBAR, + "shell-view", shell_view, NULL); +} + +GList * +e_cal_shell_sidebar_get_clients (ECalShellSidebar *cal_shell_sidebar) +{ + GHashTable *client_table; + + g_return_val_if_fail ( + E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL); + + client_table = cal_shell_sidebar->priv->client_table; + + return g_hash_table_get_values (client_table); +} + +ECalendar * +e_cal_shell_sidebar_get_date_navigator (ECalShellSidebar *cal_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL); + + return E_CALENDAR (cal_shell_sidebar->priv->date_navigator); +} + +ESourceSelector * +e_cal_shell_sidebar_get_selector (ECalShellSidebar *cal_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar), NULL); + + return E_SOURCE_SELECTOR (cal_shell_sidebar->priv->selector); +} + +void +e_cal_shell_sidebar_add_source (ECalShellSidebar *cal_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + const gchar *uri; + gchar *message; + + g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = cal_shell_sidebar->priv->client_table; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client != NULL) + return; + + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_EVENT); + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "backend-died", + G_CALLBACK (cal_shell_sidebar_backend_died_cb), + cal_shell_sidebar); + + g_signal_connect_swapped ( + client, "backend-error", + G_CALLBACK (cal_shell_sidebar_backend_error_cb), + cal_shell_sidebar); + + g_hash_table_insert (client_table, g_strdup (uid), client); + e_source_selector_select_source (selector, source); + + uri = e_cal_get_uri (client); + message = g_strdup_printf (_("Opening calendar at %s"), uri); + cal_shell_sidebar_emit_status_message (cal_shell_sidebar, message); + g_free (message); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (cal_shell_sidebar_client_opened_cb), + cal_shell_sidebar); + + e_cal_open_async (client, FALSE); +} + +void +e_cal_shell_sidebar_remove_source (ECalShellSidebar *cal_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + g_return_if_fail (E_IS_CAL_SHELL_SIDEBAR (cal_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = cal_shell_sidebar->priv->client_table; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client == NULL) + return; + + cal_shell_sidebar_emit_client_removed (cal_shell_sidebar, client); +} diff --git a/modules/calendar/e-cal-shell-sidebar.h b/modules/calendar/e-cal-shell-sidebar.h new file mode 100644 index 0000000000..3b7c0fd3b3 --- /dev/null +++ b/modules/calendar/e-cal-shell-sidebar.h @@ -0,0 +1,101 @@ +/* + * e-cal-shell-sidebar.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_SIDEBAR_H +#define E_CAL_SHELL_SIDEBAR_H + +#include <libecal/e-cal.h> +#include <libedataserverui/e-source-selector.h> + +#include <shell/e-shell-sidebar.h> +#include <shell/e-shell-view.h> +#include <misc/e-calendar.h> + +/* Standard GObject macros */ +#define E_TYPE_CAL_SHELL_SIDEBAR \ + (e_cal_shell_sidebar_get_type ()) +#define E_CAL_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebar)) +#define E_CAL_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarClass)) +#define E_IS_CAL_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CAL_SHELL_SIDEBAR)) +#define E_IS_CAL_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CAL_SHELL_SIDEBAR)) +#define E_CAL_SHELL_SIDEBAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CAL_SHELL_SIDEBAR, ECalShellSidebarClass)) + +G_BEGIN_DECLS + +typedef struct _ECalShellSidebar ECalShellSidebar; +typedef struct _ECalShellSidebarClass ECalShellSidebarClass; +typedef struct _ECalShellSidebarPrivate ECalShellSidebarPrivate; + +enum { + E_CAL_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0, + E_CAL_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_EMPTY = 1 << 1, + E_CAL_SHELL_SIDEBAR_SOURCE_CAN_GO_OFFLINE = 1 << 2, + E_CAL_SHELL_SIDEBAR_SOURCE_CAN_DELETE = 1 << 3 +}; + +struct _ECalShellSidebar { + EShellSidebar parent; + ECalShellSidebarPrivate *priv; +}; + +struct _ECalShellSidebarClass { + EShellSidebarClass parent_class; + + /* Signals */ + void (*client_added) (ECalShellSidebar *cal_shell_sidebar, + ECal *client); + void (*client_removed) (ECalShellSidebar *cal_shell_sidebar, + ECal *client); + void (*status_message) (ECalShellSidebar *cal_shell_sidebar, + const gchar *status_message); +}; + +GType e_cal_shell_sidebar_get_type (void); +void e_cal_shell_sidebar_register_type + (GTypeModule *type_module); +GtkWidget * e_cal_shell_sidebar_new (EShellView *shell_view); +GList * e_cal_shell_sidebar_get_clients + (ECalShellSidebar *cal_shell_sidebar); +ECalendar * e_cal_shell_sidebar_get_date_navigator + (ECalShellSidebar *cal_shell_sidebar); +ESourceSelector * + e_cal_shell_sidebar_get_selector + (ECalShellSidebar *cal_shell_sidebar); +void e_cal_shell_sidebar_add_source + (ECalShellSidebar *cal_shell_sidebar, + ESource *source); +void e_cal_shell_sidebar_remove_source + (ECalShellSidebar *cal_shell_sidebar, + ESource *source); + +G_END_DECLS + +#endif /* E_CAL_SHELL_SIDEBAR_H */ diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c new file mode 100644 index 0000000000..b36c94b8d1 --- /dev/null +++ b/modules/calendar/e-cal-shell-view-actions.c @@ -0,0 +1,1792 @@ +/* + * e-cal-shell-view-actions.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-view-private.h" + +static void +action_calendar_copy_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellSidebar *cal_shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + ESourceSelector *selector; + ESource *source; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + copy_source_dialog ( + GTK_WINDOW (shell_window), + source, E_CAL_SOURCE_TYPE_EVENT); +} + +static void +action_calendar_delete_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalShellSidebar *cal_shell_sidebar; + EShellBackend *shell_backend; + EShellWindow *shell_window; + EShellView *shell_view; + ECalendarView *calendar_view; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalModel *model; + ECal *client; + ESourceSelector *selector; + ESourceGroup *source_group; + ESourceList *source_list; + ESource *source; + gint response; + gchar *uri; + GError *error = NULL; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + model = e_calendar_view_get_model (calendar_view); + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + /* Ask for confirmation. */ + response = e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-delete-calendar", + e_source_peek_name (source)); + if (response != GTK_RESPONSE_YES) + return; + + uri = e_source_get_uri (source); + client = e_cal_model_get_client_for_uri (model, uri); + if (client == NULL) + client = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_EVENT); + g_free (uri); + + g_return_if_fail (client != NULL); + + if (!e_cal_remove (client, &error)) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + if (e_source_selector_source_is_selected (selector, source)) { + e_cal_shell_sidebar_remove_source ( + cal_shell_sidebar, source); + e_source_selector_unselect_source (selector, source); + } + + source_group = e_source_peek_group (source); + e_source_group_remove_source (source_group, source); + + source_list = e_cal_shell_backend_get_source_list ( + E_CAL_SHELL_BACKEND (shell_backend)); + if (!e_source_list_sync (source_list, &error)) { + g_warning ("%s", error->message); + g_error_free (error); + } +} + +static void +action_calendar_go_back_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + gnome_calendar_previous (calendar); +} + +static void +action_calendar_go_forward_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + gnome_calendar_next (calendar); +} + +static void +action_calendar_go_today_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + gnome_calendar_goto_today (calendar); +} + +static void +action_calendar_jump_to_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + goto_dialog (calendar); +} + +static void +action_calendar_new_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + calendar_setup_new_calendar (GTK_WINDOW (shell_window)); +} + +static void +action_calendar_print_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *view; + GtkPrintOperationAction print_action; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + view = gnome_calendar_get_calendar_view (calendar, view_type); + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + + if (E_IS_CAL_LIST_VIEW (view)) { + ECalListView *list_view; + ETable *table; + + list_view = E_CAL_LIST_VIEW (view); + table = e_table_scrolled_get_table (list_view->table_scrolled); + print_table (table, _("Print"), _("Calendar"), print_action); + } else { + time_t start; + + gnome_calendar_get_current_time_range (calendar, &start, NULL); + print_calendar (calendar, print_action, start); + } +} + +static void +action_calendar_print_preview_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *view; + GtkPrintOperationAction print_action; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + view = gnome_calendar_get_calendar_view (calendar, view_type); + print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW; + + if (E_IS_CAL_LIST_VIEW (view)) { + ECalListView *list_view; + ETable *table; + + list_view = E_CAL_LIST_VIEW (view); + table = e_table_scrolled_get_table (list_view->table_scrolled); + print_table (table, _("Print"), _("Calendar"), print_action); + } else { + time_t start; + + gnome_calendar_get_current_time_range (calendar, &start, NULL); + print_calendar (calendar, print_action, start); + } +} + +static void +action_calendar_properties_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellSidebar *cal_shell_sidebar; + EShellView *shell_view; + EShellWindow *shell_window; + ESource *source; + ESourceSelector *selector; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + /* XXX Does this -really- need a source group parameter? */ + calendar_setup_edit_calendar ( + GTK_WINDOW (shell_window), source, + e_source_peek_group (source)); +} + +static void +action_calendar_purge_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + GtkSpinButton *spin_button; + GtkWidget *container; + GtkWidget *dialog; + GtkWidget *widget; + gint days; + time_t tt; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK_CANCEL, + _("This operation will permanently erase all events older " + "than the selected amount of time. If you continue, you " + "will not be able to recover these events.")); + + gtk_dialog_set_default_response ( + GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); + + container = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + widget = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, FALSE, 6); + gtk_widget_show (widget); + + container = widget; + + /* Translators: This is the first part of the sentence: + * "Purge events older than <<spin-button>> days" */ + widget = gtk_label_new (_("Purge events older than")); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, FALSE, 6); + gtk_widget_show (widget); + + widget = gtk_spin_button_new_with_range (0.0, 1000.0, 1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), 60.0); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 6); + gtk_widget_show (widget); + + spin_button = GTK_SPIN_BUTTON (widget); + + /* Translators: This is the last part of the sentence: + * "Purge events older than <<spin-button>> days" */ + widget = gtk_label_new (_("days")); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, FALSE, 6); + gtk_widget_show (widget); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) + goto exit; + + days = gtk_spin_button_get_value_as_int (spin_button); + + tt = time (NULL); + tt -= (days * (24 * 3600)); + + gnome_calendar_purge (calendar, tt); + +exit: + gtk_widget_destroy (dialog); +} + +static void +action_calendar_rename_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellSidebar *cal_shell_sidebar; + ESourceSelector *selector; + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + e_source_selector_edit_primary_selection (selector); +} + +static void +action_calendar_search_cb (GtkRadioAction *action, + GtkRadioAction *current, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellContent *shell_content; + const gchar *search_hint; + + /* XXX Figure out a way to handle this in EShellContent + * instead of every shell view having to handle it. + * The problem is EShellContent does not know what + * the search option actions are for this view. It + * would have to dig up the popup menu and retrieve + * the action for each menu item. Seems messy. */ + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + search_hint = gtk_action_get_label (GTK_ACTION (current)); + e_shell_content_set_search_hint (shell_content, search_hint); +} + +static void +action_calendar_select_one_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellSidebar *cal_shell_sidebar; + ESourceSelector *selector; + ESource *primary; + GSList *list, *iter; + + /* XXX ESourceSelector should provide a function for this. */ + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + primary = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (primary != NULL); + + list = e_source_selector_get_selection (selector); + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + if (source == primary) + continue; + + e_source_selector_unselect_source (selector, source); + } + e_source_selector_free_selection (list); + + e_source_selector_select_source (selector, primary); +} + +static void +action_calendar_view_cb (GtkRadioAction *action, + GtkRadioAction *current, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + GnomeCalendarViewType view_type; + const gchar *view_id; + + shell_view = E_SHELL_VIEW (cal_shell_view); + view_type = gtk_radio_action_get_current_value (action); + + switch (view_type) { + case GNOME_CAL_DAY_VIEW: + view_id = "Day_View"; + break; + + case GNOME_CAL_WORK_WEEK_VIEW: + view_id = "Work_Week_View"; + break; + + case GNOME_CAL_WEEK_VIEW: + view_id = "Week_View"; + break; + + case GNOME_CAL_MONTH_VIEW: + view_id = "Month_View"; + break; + + case GNOME_CAL_LIST_VIEW: + view_id = "List_View"; + break; + + default: + g_return_if_reached (); + } + + e_shell_view_set_view_id (shell_view, view_id); +} + +static void +action_event_all_day_new_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + GnomeCalendar *calendar; + + /* These are just for readability. */ + gboolean all_day = TRUE; + gboolean meeting = FALSE; + gboolean no_past_date = FALSE; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + e_calendar_view_new_appointment_full ( + calendar_view, all_day, meeting, no_past_date); +} + +static void +action_event_clipboard_copy_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + e_cal_shell_content_copy_clipboard (cal_shell_content); +} + +static void +action_event_clipboard_cut_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + e_cal_shell_content_cut_clipboard (cal_shell_content); +} + +static void +action_event_clipboard_paste_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + e_cal_shell_content_paste_clipboard (cal_shell_content); +} + +static void +action_event_copy_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *calendar_view; + ESource *destination_source = NULL; + ECal *destination_client = NULL; + GList *selected, *iter; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (selected != NULL); + + /* Get a destination source from the user. */ + destination_source = select_source_dialog ( + GTK_WINDOW (shell_window), E_CAL_SOURCE_TYPE_EVENT); + if (destination_source == NULL) + return; + + /* Open the destination calendar. */ + destination_client = auth_new_cal_from_source ( + destination_source, E_CAL_SOURCE_TYPE_EVENT); + if (destination_client == NULL) + goto exit; + if (!e_cal_open (destination_client, FALSE, NULL)) + goto exit; + + e_cal_shell_view_set_status_message ( + cal_shell_view, _("Copying Items"), -1.0); + + for (iter = selected; iter != NULL; iter = iter->next) { + ECalendarViewEvent *event = iter->data; + gboolean remove = FALSE; + + e_cal_shell_view_transfer_item_to ( + cal_shell_view, event, destination_client, remove); + } + + e_cal_shell_view_set_status_message (cal_shell_view, NULL, -1.0); + +exit: + if (destination_client != NULL) + g_object_unref (destination_client); + if (destination_source != NULL) + g_object_unref (destination_source); + g_list_free (selected); +} + +static void +action_event_delegate_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *calendar_view; + ECalendarViewEvent *event; + ECalComponent *component; + ECal *client; + GList *selected; + icalcomponent *clone; + icalproperty *property; + gboolean found = FALSE; + gchar *attendee; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + clone = icalcomponent_new_clone (event->comp_data->icalcomp); + + /* Set the attendee status for the delegate. */ + + component = e_cal_component_new (); + e_cal_component_set_icalcomponent ( + component, icalcomponent_new_clone (clone)); + + attendee = itip_get_comp_attendee (component, client); + property = icalcomponent_get_first_property ( + clone, ICAL_ATTENDEE_PROPERTY); + + while (property != NULL) { + const gchar *candidate; + + candidate = icalproperty_get_attendee (property); + candidate = itip_strip_mailto (candidate); + + if (g_ascii_strcasecmp (candidate, attendee) == 0) { + icalparameter *parameter; + + parameter = icalparameter_new_role ( + ICAL_ROLE_NONPARTICIPANT); + icalproperty_set_parameter (property, parameter); + + parameter = icalparameter_new_partstat ( + ICAL_PARTSTAT_DELEGATED); + icalproperty_set_parameter (property, parameter); + + found = TRUE; + break; + } + + property = icalcomponent_get_next_property ( + clone, ICAL_ATTENDEE_PROPERTY); + } + + /* If the attendee is not already in the component, add it. */ + if (!found) { + icalparameter *parameter; + gchar *address; + + address = g_strdup_printf ("MAILTO:%s", attendee); + + property = icalproperty_new_attendee (address); + icalcomponent_add_property (clone, property); + + parameter = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); + icalproperty_add_parameter (property, parameter); + + parameter = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); + icalproperty_add_parameter (property, parameter); + + parameter = icalparameter_new_rsvp (ICAL_RSVP_TRUE); + icalproperty_add_parameter (property, parameter); + + g_free (address); + } + + g_free (attendee); + g_object_unref (component); + + e_calendar_view_open_event_with_flags ( + calendar_view, event->comp_data->client, clone, + COMP_EDITOR_MEETING | COMP_EDITOR_DELEGATE); + + icalcomponent_free (clone); + g_list_free (selected); +} + +static void +action_event_delete_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + e_cal_shell_content_delete_selection (cal_shell_content); +} + +static void +action_event_delete_occurrence_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + e_cal_shell_content_delete_selected_occurrence (cal_shell_content); +} + +static void +action_event_delete_occurrence_all_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + + /* XXX Same as "event-delete". */ + cal_shell_content = cal_shell_view->priv->cal_shell_content; + e_cal_shell_content_delete_selection (cal_shell_content); +} + +static void +action_event_forward_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + GnomeCalendar *calendar; + ECalendarViewEvent *event; + ECalComponent *component; + ECal *client; + icalcomponent *icalcomp; + GList *selected; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + component = e_cal_component_new (); + + e_cal_component_set_icalcomponent ( + component, icalcomponent_new_clone (icalcomp)); + itip_send_comp ( + E_CAL_COMPONENT_METHOD_PUBLISH, + component, client, NULL, NULL, NULL, TRUE, FALSE); + + g_object_unref (component); + + g_list_free (selected); +} + +static void +action_event_meeting_new_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + GnomeCalendar *calendar; + + /* These are just for readability. */ + gboolean all_day = FALSE; + gboolean meeting = TRUE; + gboolean no_past_date = FALSE; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + e_calendar_view_new_appointment_full ( + calendar_view, all_day, meeting, no_past_date); +} + +static void +action_event_move_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *calendar_view; + ESource *destination_source = NULL; + ECal *destination_client = NULL; + GList *selected, *iter; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (selected != NULL); + + /* Get a destination source from the user. */ + destination_source = select_source_dialog ( + GTK_WINDOW (shell_window), E_CAL_SOURCE_TYPE_EVENT); + if (destination_source == NULL) + return; + + /* Open the destination calendar. */ + destination_client = auth_new_cal_from_source ( + destination_source, E_CAL_SOURCE_TYPE_EVENT); + if (destination_client == NULL) + goto exit; + if (!e_cal_open (destination_client, FALSE, NULL)) + goto exit; + + e_cal_shell_view_set_status_message ( + cal_shell_view, _("Moving Items"), -1.0); + + for (iter = selected; iter != NULL; iter = iter->next) { + ECalendarViewEvent *event = iter->data; + gboolean remove = TRUE; + + e_cal_shell_view_transfer_item_to ( + cal_shell_view, event, destination_client, remove); + } + + e_cal_shell_view_set_status_message (cal_shell_view, NULL, -1.0); + +exit: + if (destination_client != NULL) + g_object_unref (destination_client); + if (destination_source != NULL) + g_object_unref (destination_source); + g_list_free (selected); +} + +static void +action_event_new_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + GnomeCalendar *calendar; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + e_calendar_view_new_appointment (calendar_view); +} + +static void +action_event_occurrence_movable_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalModel *model; + ECalendarView *calendar_view; + ECalendarViewEvent *event; + ECalComponent *exception_component; + ECalComponent *recurring_component; + ECalComponentDateTime date; + ECalComponentId *id; + ECal *client; + icalcomponent *icalcomp; + icaltimetype itt; + icaltimezone *timezone; + GList *selected; + gchar *uid; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + model = e_calendar_view_get_model (calendar_view); + timezone = e_cal_model_get_timezone (model); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + /* For the recurring object, we add an exception + * to get rid of the instance. */ + + recurring_component = e_cal_component_new (); + e_cal_component_set_icalcomponent ( + recurring_component, icalcomponent_new_clone (icalcomp)); + id = e_cal_component_get_id (recurring_component); + + /* For the unrecurred instance, we duplicate the original object, + * create a new UID for it, get rid of the recurrence rules, and + * set the start and end times to the instance times. */ + + exception_component = e_cal_component_new (); + e_cal_component_set_icalcomponent ( + exception_component, icalcomponent_new_clone (icalcomp)); + + uid = e_cal_component_gen_uid (); + e_cal_component_set_uid (exception_component, uid); + g_free (uid); + + e_cal_component_set_recurid (exception_component, NULL); + e_cal_component_set_rdate_list (exception_component, NULL); + e_cal_component_set_rrule_list (exception_component, NULL); + e_cal_component_set_exdate_list (exception_component, NULL); + e_cal_component_set_exrule_list (exception_component, NULL); + + date.value = &itt; + date.tzid = icaltimezone_get_tzid (timezone); + *date.value = icaltime_from_timet_with_zone ( + event->comp_data->instance_start, FALSE, timezone); + cal_comp_set_dtstart_with_oldzone (client, exception_component, &date); + *date.value = icaltime_from_timet_with_zone ( + event->comp_data->instance_end, FALSE, timezone); + cal_comp_set_dtstart_with_oldzone (client, exception_component, &date); + e_cal_component_commit_sequence (exception_component); + + /* Now update both ECalComponents. Note that we do this last + * since at present the updates happend synchronously so our + * event may disappear. */ + + e_cal_remove_object_with_mod ( + client, id->uid, id->rid, CALOBJ_MOD_THIS, NULL); + + e_cal_component_free_id (id); + g_object_unref (recurring_component); + + icalcomp = e_cal_component_get_icalcomponent (exception_component); + if (e_cal_create_object (client, icalcomp, &uid, NULL)) + g_free (uid); + + g_object_unref (exception_component); + + g_list_free (selected); +} + +static void +action_event_open_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *view; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + view = gnome_calendar_get_calendar_view (calendar, view_type); + + e_calendar_view_open_event (view); +} + +static void +action_event_print_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *calendar_view; + ECalendarViewEvent *event; + ECalComponent *component; + ECal *client; + icalcomponent *icalcomp; + GList *selected; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + component = e_cal_component_new (); + + e_cal_component_set_icalcomponent ( + component, icalcomponent_new_clone (icalcomp)); + print_comp ( + component, client, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG); + + g_object_unref (component); + + g_list_free (selected); +} + +static void +action_event_reply_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + GnomeCalendar *calendar; + ECalendarViewEvent *event; + ECalComponent *component; + ECal *client; + icalcomponent *icalcomp; + GList *selected; + gboolean reply_all = FALSE; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + component = e_cal_component_new (); + + e_cal_component_set_icalcomponent ( + component, icalcomponent_new_clone (icalcomp)); + reply_to_calendar_comp ( + E_CAL_COMPONENT_METHOD_REPLY, + component, client, reply_all, NULL, NULL); + + g_object_unref (component); + + g_list_free (selected); +} + +static void +action_event_reply_all_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + GnomeCalendar *calendar; + ECalendarViewEvent *event; + ECalComponent *component; + ECal *client; + icalcomponent *icalcomp; + GList *selected; + gboolean reply_all = TRUE; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + component = e_cal_component_new (); + + e_cal_component_set_icalcomponent ( + component, icalcomponent_new_clone (icalcomp)); + reply_to_calendar_comp ( + E_CAL_COMPONENT_METHOD_REPLY, + component, client, reply_all, NULL, NULL); + + g_object_unref (component); + + g_list_free (selected); +} + +static void +action_event_save_as_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *calendar_view; + ECalendarViewEvent *event; + ECal *client; + icalcomponent *icalcomp; + GList *selected; + gchar *filename = NULL; + gchar *string = NULL; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + filename = e_file_dialog_save (_("Save As..."), NULL); + if (filename == NULL) + goto exit; + + string = e_cal_get_component_as_string (client, icalcomp); + if (string == NULL) { + g_warning ("Could not convert item to a string"); + goto exit; + } + + e_write_file_uri (filename, string); + +exit: + g_free (filename); + g_free (string); + + g_list_free (selected); +} + +static void +action_event_schedule_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *calendar_view; + ECalendarViewEvent *event; + ECal *client; + icalcomponent *icalcomp; + GList *selected; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + selected = e_calendar_view_get_selected_events (calendar_view); + g_return_if_fail (g_list_length (selected) == 1); + + event = selected->data; + client = event->comp_data->client; + icalcomp = event->comp_data->icalcomp; + + e_calendar_view_edit_appointment ( + calendar_view, client, icalcomp, TRUE); + + g_list_free (selected); +} + +static void +action_gal_save_custom_view_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EShellView *shell_view; + GalViewInstance *view_instance; + + /* All shell views respond to the activation of this action, + * which is defined by EShellWindow. But only the currently + * active shell view proceeds with saving the custom view. */ + shell_view = E_SHELL_VIEW (cal_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + view_instance = e_cal_shell_content_get_view_instance (cal_shell_content); + gal_view_instance_save_as (view_instance); +} + +static void +action_search_execute_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + + /* All shell views respond to the activation of this action, + * which is defined by EShellWindow. But only the currently + * active shell view proceeds with executing the search. */ + shell_view = E_SHELL_VIEW (cal_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + e_cal_shell_view_execute_search (cal_shell_view); +} + +static void +action_search_filter_cb (GtkRadioAction *action, + GtkRadioAction *current, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); +} + +static GtkActionEntry calendar_entries[] = { + + { "calendar-copy", + GTK_STOCK_COPY, + N_("_Copy..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_copy_cb) }, + + { "calendar-delete", + GTK_STOCK_DELETE, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_delete_cb) }, + + { "calendar-go-back", + GTK_STOCK_GO_BACK, + N_("Previous"), + NULL, + N_("Go Back"), + G_CALLBACK (action_calendar_go_back_cb) }, + + { "calendar-go-forward", + GTK_STOCK_GO_FORWARD, + N_("Next"), + NULL, + N_("Go Forward"), + G_CALLBACK (action_calendar_go_forward_cb) }, + + { "calendar-go-today", + "go-today", + N_("Select _Today"), + "<Control>t", + N_("Select today"), + G_CALLBACK (action_calendar_go_today_cb) }, + + { "calendar-jump-to", + GTK_STOCK_JUMP_TO, + N_("Select _Date"), + "<Control>g", + N_("Select a specific date"), + G_CALLBACK (action_calendar_jump_to_cb) }, + + { "calendar-new", + "x-office-calendar", + N_("_New Calendar"), + NULL, + N_("Create a new calendar"), + G_CALLBACK (action_calendar_new_cb) }, + + { "calendar-properties", + GTK_STOCK_PROPERTIES, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_properties_cb) }, + + { "calendar-purge", + NULL, + N_("Purg_e"), + "<Control>e", + N_("Purge old appointments and meetings"), + G_CALLBACK (action_calendar_purge_cb) }, + + { "calendar-rename", + NULL, + N_("_Rename..."), + "F2", + N_("Rename the selected calendar"), + G_CALLBACK (action_calendar_rename_cb) }, + + { "calendar-select-one", + "stock_check-filled", + N_("Show _Only This Calendar"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_select_one_cb) }, + + { "event-clipboard-copy", + GTK_STOCK_COPY, + NULL, + NULL, + N_("Copy the selection"), + G_CALLBACK (action_event_clipboard_copy_cb) }, + + { "event-clipboard-cut", + GTK_STOCK_CUT, + NULL, + NULL, + N_("Cut the selection"), + G_CALLBACK (action_event_clipboard_cut_cb) }, + + { "event-clipboard-paste", + GTK_STOCK_PASTE, + NULL, + NULL, + N_("Paste the clipboard"), + G_CALLBACK (action_event_clipboard_paste_cb) }, + + { "event-copy", + NULL, + N_("Cop_y to Calendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_copy_cb) }, + + { "event-delegate", + NULL, + N_("_Delegate Meeting..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_delegate_cb) }, + + { "event-delete", + GTK_STOCK_DELETE, + NULL, + NULL, + N_("Delete the appointment"), + G_CALLBACK (action_event_delete_cb) }, + + { "event-delete-occurrence", + GTK_STOCK_DELETE, + N_("Delete This _Occurrence"), + NULL, + N_("Delete this occurrence"), + G_CALLBACK (action_event_delete_occurrence_cb) }, + + { "event-delete-occurrence-all", + GTK_STOCK_DELETE, + N_("Delete _All Occurrences"), + NULL, + N_("Delete all occurrences"), + G_CALLBACK (action_event_delete_occurrence_all_cb) }, + + { "event-all-day-new", + NULL, + N_("New All Day _Event..."), + NULL, + N_("Create a new all day event"), + G_CALLBACK (action_event_all_day_new_cb) }, + + { "event-forward", + "mail-forward", + N_("_Forward as iCalendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_forward_cb) }, + + { "event-meeting-new", + NULL, + N_("New _Meeting..."), + NULL, + N_("Create a new meeting"), + G_CALLBACK (action_event_meeting_new_cb) }, + + { "event-move", + NULL, + N_("Mo_ve to Calendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_move_cb) }, + + { "event-new", + NULL, + N_("New _Appointment..."), + NULL, + N_("Create a new appointment"), + G_CALLBACK (action_event_new_cb) }, + + { "event-occurrence-movable", + NULL, + N_("Make this Occurrence _Movable"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_occurrence_movable_cb) }, + + { "event-open", + NULL, + N_("_Open Appointment"), + "<Control>o", + N_("View the current appointment"), + G_CALLBACK (action_event_open_cb) }, + + { "event-reply", + "mail-reply-sender", + N_("_Reply"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_reply_cb) }, + + { "event-reply-all", + "mail-reply-all", + N_("Reply to _All"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_reply_all_cb) }, + + { "event-save-as", + GTK_STOCK_SAVE_AS, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_save_as_cb) }, + + { "event-schedule", + NULL, + N_("_Schedule Meeting..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_schedule_cb) }, + + /*** Menus ***/ + + { "calendar-actions-menu", + NULL, + N_("_Actions"), + NULL, + NULL, + NULL } +}; + +static EPopupActionEntry calendar_popup_entries[] = { + + /* FIXME No equivalent main menu items for the any of the calendar + * popup menu items and for many of the event popup menu items. + * This is an accessibility issue. */ + + { "calendar-popup-copy", + NULL, + "calendar-copy" }, + + { "calendar-popup-delete", + NULL, + "calendar-delete" }, + + { "calendar-popup-go-today", + NULL, + "calendar-go-today" }, + + { "calendar-popup-jump-to", + NULL, + "calendar-jump-to" }, + + { "calendar-popup-properties", + NULL, + "calendar-properties" }, + + { "calendar-popup-rename", + NULL, + "calendar-rename" }, + + { "calendar-popup-select-one", + NULL, + "calendar-select-one" }, + + { "event-popup-clipboard-copy", + NULL, + "event-clipboard-copy" }, + + { "event-popup-clipboard-cut", + NULL, + "event-clipboard-cut" }, + + { "event-popup-clipboard-paste", + NULL, + "event-clipboard-paste" }, + + { "event-popup-copy", + NULL, + "event-copy" }, + + { "event-popup-delegate", + NULL, + "event-delegate" }, + + { "event-popup-delete", + NULL, + "event-delete" }, + + { "event-popup-delete-occurrence", + NULL, + "event-delete-occurrence" }, + + { "event-popup-delete-occurrence-all", + NULL, + "event-delete-occurrence-all" }, + + { "event-popup-forward", + NULL, + "event-forward" }, + + { "event-popup-move", + NULL, + "event-move" }, + + { "event-popup-occurrence-movable", + NULL, + "event-occurrence-movable" }, + + { "event-popup-open", + NULL, + "event-open" }, + + { "event-popup-reply", + NULL, + "event-reply" }, + + { "event-popup-reply-all", + NULL, + "event-reply-all" }, + + { "event-popup-save-as", + NULL, + "event-save-as" }, + + { "event-popup-schedule", + NULL, + "event-schedule" } +}; + +static GtkRadioActionEntry calendar_view_entries[] = { + + { "calendar-view-day", + "view-calendar-day", + N_("Day"), + NULL, + N_("Show one day"), + GNOME_CAL_DAY_VIEW }, + + { "calendar-view-list", + "view-calendar-list", + N_("List"), + NULL, + N_("Show as list"), + GNOME_CAL_LIST_VIEW }, + + { "calendar-view-month", + "view-calendar-month", + N_("Month"), + NULL, + N_("Show one month"), + GNOME_CAL_MONTH_VIEW }, + + { "calendar-view-week", + "view-calendar-week", + N_("Week"), + NULL, + N_("Show one week"), + GNOME_CAL_WEEK_VIEW }, + + { "calendar-view-workweek", + "view-calendar-workweek", + N_("Work Week"), + NULL, + N_("Show one work week"), + GNOME_CAL_WORK_WEEK_VIEW } +}; + +static GtkRadioActionEntry calendar_filter_entries[] = { + + { "calendar-filter-active-appointments", + NULL, + N_("Active Appointements"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_FILTER_ACTIVE_APPOINTMENTS }, + + { "calendar-filter-any-category", + NULL, + N_("Any Category"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_FILTER_ANY_CATEGORY }, + + { "calendar-filter-next-7-days-appointments", + NULL, + N_("Next 7 Days' Appointments"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS }, + + { "calendar-filter-unmatched", + NULL, + N_("Unmatched"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_FILTER_UNMATCHED } +}; + +static GtkRadioActionEntry calendar_search_entries[] = { + + { "calendar-search-any-field-contains", + NULL, + N_("Any field contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_SEARCH_ANY_FIELD_CONTAINS }, + + { "calendar-search-description-contains", + NULL, + N_("Description contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_SEARCH_DESCRIPTION_CONTAINS }, + + { "calendar-search-summary-contains", + NULL, + N_("Summary contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + CALENDAR_SEARCH_SUMMARY_CONTAINS } +}; + +static GtkActionEntry lockdown_printing_entries[] = { + + { "calendar-print", + GTK_STOCK_PRINT, + NULL, + "<Control>p", + N_("Print this calendar"), + G_CALLBACK (action_calendar_print_cb) }, + + { "calendar-print-preview", + GTK_STOCK_PRINT_PREVIEW, + NULL, + NULL, + N_("Preview the calendar to be printed"), + G_CALLBACK (action_calendar_print_preview_cb) }, + + { "event-print", + GTK_STOCK_PRINT, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_event_print_cb) } +}; + +static EPopupActionEntry lockdown_printing_popup_entries[] = { + + { "event-popup-print", + NULL, + "event-print" } +}; + +void +e_cal_shell_view_actions_init (ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkActionGroup *action_group; + GtkAction *action; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + /* Calendar Actions */ + action_group = ACTION_GROUP (CALENDAR); + gtk_action_group_add_actions ( + action_group, calendar_entries, + G_N_ELEMENTS (calendar_entries), cal_shell_view); + e_action_group_add_popup_actions ( + action_group, calendar_popup_entries, + G_N_ELEMENTS (calendar_popup_entries)); + gtk_action_group_add_radio_actions ( + action_group, calendar_view_entries, + G_N_ELEMENTS (calendar_view_entries), GNOME_CAL_DAY_VIEW, + G_CALLBACK (action_calendar_view_cb), cal_shell_view); + gtk_action_group_add_radio_actions ( + action_group, calendar_search_entries, + G_N_ELEMENTS (calendar_search_entries), + CALENDAR_SEARCH_SUMMARY_CONTAINS, + G_CALLBACK (action_calendar_search_cb), cal_shell_view); + + /* Lockdown Printing Actions */ + action_group = ACTION_GROUP (LOCKDOWN_PRINTING); + gtk_action_group_add_actions ( + action_group, lockdown_printing_entries, + G_N_ELEMENTS (lockdown_printing_entries), cal_shell_view); + e_action_group_add_popup_actions ( + action_group, lockdown_printing_popup_entries, + G_N_ELEMENTS (lockdown_printing_popup_entries)); + + /* Fine tuning. */ + + action = ACTION (CALENDAR_GO_TODAY); + g_object_set (action, "short-label", _("Today"), NULL); + + action = ACTION (CALENDAR_JUMP_TO); + g_object_set (action, "short-label", _("Go To"), NULL); + + action = ACTION (CALENDAR_VIEW_DAY); + g_object_set (action, "is-important", TRUE, NULL); + + action = ACTION (CALENDAR_VIEW_LIST); + g_object_set (action, "is-important", TRUE, NULL); + + action = ACTION (CALENDAR_VIEW_MONTH); + g_object_set (action, "is-important", TRUE, NULL); + + action = ACTION (CALENDAR_VIEW_WEEK); + g_object_set (action, "is-important", TRUE, NULL); + + action = ACTION (CALENDAR_VIEW_WORKWEEK); + g_object_set (action, "is-important", TRUE, NULL); + + action = ACTION (EVENT_DELETE); + g_object_set (action, "short-label", _("Delete"), NULL); + + g_signal_connect ( + ACTION (GAL_SAVE_CUSTOM_VIEW), "activate", + G_CALLBACK (action_gal_save_custom_view_cb), cal_shell_view); + + g_signal_connect ( + ACTION (SEARCH_EXECUTE), "activate", + G_CALLBACK (action_search_execute_cb), cal_shell_view); + + /* Initialize the memo and task pad actions. */ + e_cal_shell_view_memopad_actions_init (cal_shell_view); + e_cal_shell_view_taskpad_actions_init (cal_shell_view); +} + +void +e_cal_shell_view_update_search_filter (ECalShellView *cal_shell_view) +{ + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + GtkActionGroup *action_group; + GtkRadioAction *radio_action; + GList *list, *iter; + GSList *group; + gint ii; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + action_group = ACTION_GROUP (CALENDAR_FILTER); + e_action_group_remove_all_actions (action_group); + + /* Add the standard filter actions. */ + gtk_action_group_add_radio_actions ( + action_group, calendar_filter_entries, + G_N_ELEMENTS (calendar_filter_entries), + CALENDAR_FILTER_ANY_CATEGORY, + G_CALLBACK (action_search_filter_cb), + cal_shell_view); + + /* Retrieve the radio group from an action we just added. */ + list = gtk_action_group_list_actions (action_group); + radio_action = GTK_RADIO_ACTION (list->data); + group = gtk_radio_action_get_group (radio_action); + g_list_free (list); + + /* Build the category actions. */ + + list = e_categories_get_list (); + for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) { + const gchar *category_name = iter->data; + const gchar *filename; + GtkAction *action; + gchar *action_name; + + action_name = g_strdup_printf ( + "calendar-filter-category-%d", ii); + radio_action = gtk_radio_action_new ( + action_name, category_name, NULL, NULL, ii); + g_free (action_name); + + /* Convert the category icon file to a themed icon name. */ + filename = e_categories_get_icon_file_for (category_name); + if (filename != NULL && *filename != '\0') { + gchar *basename; + gchar *cp; + + basename = g_path_get_basename (filename); + + /* Lose the file extension. */ + if ((cp = strrchr (basename, '.')) != NULL) + *cp = '\0'; + + g_object_set ( + radio_action, "icon-name", basename, NULL); + + g_free (basename); + } + + gtk_radio_action_set_group (radio_action, group); + group = gtk_radio_action_get_group (radio_action); + + /* The action group takes ownership of the action. */ + action = GTK_ACTION (radio_action); + gtk_action_group_add_action (action_group, action); + g_object_unref (radio_action); + } + g_list_free (list); + + /* Use any action in the group; doesn't matter which. */ + e_shell_content_set_filter_action (shell_content, radio_action); + + ii = CALENDAR_FILTER_UNMATCHED; + e_shell_content_add_filter_separator_after (shell_content, ii); + + ii = CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS; + e_shell_content_add_filter_separator_after (shell_content, ii); +} diff --git a/modules/calendar/e-cal-shell-view-actions.h b/modules/calendar/e-cal-shell-view-actions.h new file mode 100644 index 0000000000..b02906f179 --- /dev/null +++ b/modules/calendar/e-cal-shell-view-actions.h @@ -0,0 +1,153 @@ +/* + * e-cal-shell-view-actions.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_VIEW_ACTIONS_H +#define E_CAL_SHELL_VIEW_ACTIONS_H + +#include <shell/e-shell-window-actions.h> + +/* Calendar Actions */ +#define E_SHELL_WINDOW_ACTION_CALENDAR_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-copy") +#define E_SHELL_WINDOW_ACTION_CALENDAR_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-delete") +#define E_SHELL_WINDOW_ACTION_CALENDAR_GO_BACK(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-go-back") +#define E_SHELL_WINDOW_ACTION_CALENDAR_GO_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-go-forward") +#define E_SHELL_WINDOW_ACTION_CALENDAR_GO_TODAY(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-go-today") +#define E_SHELL_WINDOW_ACTION_CALENDAR_JUMP_TO(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-jump-to") +#define E_SHELL_WINDOW_ACTION_CALENDAR_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-new") +#define E_SHELL_WINDOW_ACTION_CALENDAR_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-print") +#define E_SHELL_WINDOW_ACTION_CALENDAR_PRINT_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-print-preview") +#define E_SHELL_WINDOW_ACTION_CALENDAR_PROPERTIES(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-properties") +#define E_SHELL_WINDOW_ACTION_CALENDAR_PURGE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-purge") +#define E_SHELL_WINDOW_ACTION_CALENDAR_RENAME(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-rename") +#define E_SHELL_WINDOW_ACTION_CALENDAR_SELECT_ONE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-select-one") +#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_DAY(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-view-day") +#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_LIST(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-view-list") +#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_MONTH(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-view-month") +#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_WEEK(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-view-week") +#define E_SHELL_WINDOW_ACTION_CALENDAR_VIEW_WORKWEEK(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-view-workweek") + +/* Event Actions */ +#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-clipboard-copy") +#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_CUT(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-clipboard-cut") +#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_PASTE(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-clipboard-paste") +#define E_SHELL_WINDOW_ACTION_EVENT_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-delete") +#define E_SHELL_WINDOW_ACTION_EVENT_DELETE_OCCURRENCE(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-delete-occurrence") +#define E_SHELL_WINDOW_ACTION_EVENT_DELETE_OCCURRENCE_ALL(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-delete-occurrence-all") +#define E_SHELL_WINDOW_ACTION_EVENT_OPEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "event-open") + +/* Memo Pad Actions */ +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-copy") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_CUT(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-cut") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_PASTE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-paste") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-delete") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-forward") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-new") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_OPEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-open") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_OPEN_URL(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-open-url") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-print") +#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_SAVE_AS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-save-as") + +/* Task Pad Actions */ +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_ASSIGN(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-assign") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-copy") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_CUT(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-cut") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_PASTE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-paste") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-delete") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-forward") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_MARK_COMPLETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-mark-complete") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_MARK_INCOMPLETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-mark-incomplete") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-new") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_OPEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-open") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_OPEN_URL(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-open-url") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-print") +#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_SAVE_AS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-save-as") + +/* Calendar Query Actions */ +#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_ACTIVE_APPOINTMENTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-filter-active-appointments") +#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_ANY_CATEGORY(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-filter-any-category") +#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-filter-next-7-days-appointments") +#define E_SHELL_WINDOW_ACTION_CALENDAR_FILTER_UNMATCHED(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-filter-unmatched") +#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_ANY_FIELD_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-search-any-field-contains") +#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_DESCRIPTION_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-search-description-contains") +#define E_SHELL_WINDOW_ACTION_CALENDAR_SEARCH_SUMMARY_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "calendar-search-summary-contains") + +/* Action Groups */ +#define E_SHELL_WINDOW_ACTION_GROUP_CALENDAR(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "calendar") +#define E_SHELL_WINDOW_ACTION_GROUP_CALENDAR_FILTER(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "calendar-filter") + +#endif /* E_CAL_SHELL_VIEW_ACTIONS_H */ diff --git a/modules/calendar/e-cal-shell-view-memopad.c b/modules/calendar/e-cal-shell-view-memopad.c new file mode 100644 index 0000000000..1c4e863345 --- /dev/null +++ b/modules/calendar/e-cal-shell-view-memopad.c @@ -0,0 +1,526 @@ +/* + * e-cal-shell-view-memopad.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-view-private.h" + +/* Much of this file is based on e-memo-shell-view-actions.c. */ + +static void +action_calendar_memopad_clipboard_copy_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + e_memo_table_copy_clipboard (memo_table); +} + +static void +action_calendar_memopad_clipboard_cut_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + e_memo_table_cut_clipboard (memo_table); +} + +static void +action_calendar_memopad_clipboard_paste_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + e_memo_table_paste_clipboard (memo_table); +} + +static void +action_calendar_memopad_delete_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + e_cal_shell_view_memopad_set_status_message ( + cal_shell_view, _("Deleting selected memos..."), -1.0); + e_memo_table_delete_selected (memo_table); + e_cal_shell_view_memopad_set_status_message ( + cal_shell_view, NULL, -1.0); +} + +static void +action_calendar_memopad_forward_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only forward the first selected memo. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + itip_send_comp ( + E_CAL_COMPONENT_METHOD_PUBLISH, comp, + comp_data->client, NULL, NULL, NULL, TRUE, FALSE); + g_object_unref (comp); +} + +static void +action_calendar_memopad_new_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + ECal *client; + ECalComponent *comp; + CompEditor *editor; + GSList *list; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + client = comp_data->client; + editor = memo_editor_new (client, shell, COMP_EDITOR_NEW_ITEM); + comp = cal_comp_memo_new_with_defaults (client); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (client); +} + +static void +action_calendar_memopad_open_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the first selected memo. */ + e_cal_shell_view_memopad_open_memo (cal_shell_view, comp_data); +} + +static void +action_calendar_memopad_open_url_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + icalproperty *prop; + const gchar *uri; + GSList *list; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the URI of the first selected memo. */ + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + g_return_if_fail (prop == NULL); + + uri = icalproperty_get_url (prop); + e_show_uri (GTK_WINDOW (shell_window), uri); +} + +static void +action_calendar_memopad_print_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GtkPrintOperationAction print_action; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only print the first selected memo. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + e_cal_component_set_icalcomponent (comp, clone); + print_comp (comp, comp_data->client, print_action); + g_object_unref (comp); +} + +static void +action_calendar_memopad_save_as_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + GSList *list; + gchar *filename; + gchar *string; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + filename = e_file_dialog_save (_("Save as..."), NULL); + if (filename == NULL) + return; + + /* XXX We only save the first selected memo. */ + string = e_cal_get_component_as_string ( + comp_data->client, comp_data->icalcomp); + if (string == NULL) { + g_warning ("Could not convert memo to a string."); + return; + } + + e_write_file_uri (filename, string); + + g_free (filename); + g_free (string); +} + +static GtkActionEntry calendar_memopad_entries[] = { + + { "calendar-memopad-clipboard-copy", + GTK_STOCK_COPY, + NULL, + NULL, + N_("Copy selected memo"), + G_CALLBACK (action_calendar_memopad_clipboard_copy_cb) }, + + { "calendar-memopad-clipboard-cut", + GTK_STOCK_CUT, + NULL, + NULL, + N_("Cut selected memo"), + G_CALLBACK (action_calendar_memopad_clipboard_cut_cb) }, + + { "calendar-memopad-clipboard-paste", + GTK_STOCK_PASTE, + NULL, + NULL, + N_("Paste memo from the clipboard"), + G_CALLBACK (action_calendar_memopad_clipboard_paste_cb) }, + + { "calendar-memopad-delete", + GTK_STOCK_DELETE, + N_("_Delete Memo"), + NULL, + N_("Delete selected memos"), + G_CALLBACK (action_calendar_memopad_delete_cb) }, + + { "calendar-memopad-forward", + "mail-forward", + N_("_Forward as iCalendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_memopad_forward_cb) }, + + { "calendar-memopad-new", + "stock_insert-note", + N_("New _Memo"), + NULL, + N_("Create a new memo"), + G_CALLBACK (action_calendar_memopad_new_cb) }, + + { "calendar-memopad-open", + GTK_STOCK_OPEN, + N_("_Open Memo"), + NULL, + N_("View the selected memo"), + G_CALLBACK (action_calendar_memopad_open_cb) }, + + { "calendar-memopad-open-url", + "applications-internet", + N_("Open _Web Page"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_memopad_open_url_cb) }, + + { "calendar-memopad-save-as", + GTK_STOCK_SAVE_AS, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_memopad_save_as_cb) } +}; + +static GtkActionEntry lockdown_printing_entries[] = { + + { "calendar-memopad-print", + GTK_STOCK_PRINT, + NULL, + NULL, + N_("Print the selected memo"), + G_CALLBACK (action_calendar_memopad_print_cb) } +}; + +void +e_cal_shell_view_memopad_actions_init (ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkActionGroup *action_group; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + /* Calendar Actions */ + action_group = ACTION_GROUP (CALENDAR); + gtk_action_group_add_actions ( + action_group, calendar_memopad_entries, + G_N_ELEMENTS (calendar_memopad_entries), cal_shell_view); + + /* Lockdown Printing Actions */ + action_group = ACTION_GROUP (LOCKDOWN_PRINTING); + gtk_action_group_add_actions ( + action_group, lockdown_printing_entries, + G_N_ELEMENTS (lockdown_printing_entries), cal_shell_view); +} + +void +e_cal_shell_view_memopad_actions_update (ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + EMemoTable *memo_table; + ETable *table; + GtkAction *action; + GSList *list, *iter; + const gchar *label; + gboolean editable = TRUE; + gboolean has_url = FALSE; + gboolean sensitive; + gint n_selected; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + + table = e_memo_table_get_table (memo_table); + n_selected = e_table_selected_count (table); + + list = e_memo_table_get_selected (memo_table); + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + icalproperty *prop; + gboolean read_only; + + e_cal_is_read_only (comp_data->client, &read_only, NULL); + editable &= !read_only; + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + has_url |= (prop != NULL); + } + g_slist_free (list); + + action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_COPY); + sensitive = (n_selected > 0); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_CUT); + sensitive = (n_selected > 0) && editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_PASTE); + sensitive = editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_DELETE); + sensitive = (n_selected > 0) && editable; + gtk_action_set_sensitive (action, sensitive); + label = ngettext ("Delete Memo", "Delete Memos", n_selected); + g_object_set (action, "label", label, NULL); + + action = ACTION (CALENDAR_MEMOPAD_FORWARD); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_OPEN); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_OPEN_URL); + sensitive = (n_selected == 1) && has_url; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_PRINT); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_MEMOPAD_SAVE_AS); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); +} + +void +e_cal_shell_view_memopad_open_memo (ECalShellView *cal_shell_view, + ECalModelComponent *comp_data) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + CompEditor *editor; + CompEditorFlags flags = 0; + ECalComponent *comp; + icalcomponent *clone; + const gchar *uid; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + uid = icalcomponent_get_uid (comp_data->icalcomp); + editor = comp_editor_find_instance (uid); + + if (editor != NULL) + goto exit; + + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + + if (e_cal_component_has_organizer (comp)) + flags |= COMP_EDITOR_IS_SHARED; + + if (itip_organizer_is_user (comp, comp_data->client)) + flags |= COMP_EDITOR_USER_ORG; + + editor = memo_editor_new (comp_data->client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_unref (comp); + +exit: + gtk_window_present (GTK_WINDOW (editor)); +} + +void +e_cal_shell_view_memopad_set_status_message (ECalShellView *cal_shell_view, + const gchar *status_message, + gdouble percent) +{ + EActivity *activity; + EShellView *shell_view; + EShellBackend *shell_backend; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + activity = cal_shell_view->priv->memopad_activity; + + if (status_message == NULL || *status_message == '\0') { + if (activity != NULL) { + e_activity_complete (activity); + g_object_unref (activity); + activity = NULL; + } + + } else if (activity == NULL) { + activity = e_activity_new (status_message); + e_activity_set_percent (activity, percent); + e_shell_backend_add_activity (shell_backend, activity); + + } else { + e_activity_set_percent (activity, percent); + e_activity_set_primary_text (activity, status_message); + } + + cal_shell_view->priv->memopad_activity = activity; +} diff --git a/modules/calendar/e-cal-shell-view-private.c b/modules/calendar/e-cal-shell-view-private.c new file mode 100644 index 0000000000..1b9c8d367a --- /dev/null +++ b/modules/calendar/e-cal-shell-view-private.c @@ -0,0 +1,1098 @@ +/* + * e-cal-shell-view-private.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-view-private.h" + +#include "calendar/gui/calendar-view-factory.h" +#include "widgets/menus/gal-view-factory-etable.h" + +static void +cal_shell_view_process_completed_tasks (ECalShellView *cal_shell_view, + gboolean config_changed) +{ +#if 0 + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + e_calendar_table_process_completed_tasks ( + task_table, clients, config_changed); +#endif +} + +static struct tm +cal_shell_view_get_current_time (ECalendarItem *calitem, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + struct icaltimetype tt; + icaltimezone *timezone; + ECalModel *model; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + model = e_cal_shell_content_get_model (cal_shell_content); + timezone = e_cal_model_get_timezone (model); + + tt = icaltime_from_timet_with_zone (time (NULL), FALSE, timezone); + + return icaltimetype_to_tm (&tt); +} + +static void +cal_shell_view_date_navigator_date_range_changed_cb (ECalShellView *cal_shell_view, + ECalendarItem *calitem) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + gnome_calendar_update_query (calendar); +} + +static void +cal_shell_view_date_navigator_selection_changed_cb (ECalShellView *cal_shell_view, + ECalendarItem *calitem) +{ + ECalShellContent *cal_shell_content; + GnomeCalendarViewType switch_to; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalModel *model; + GDate start_date, end_date; + GDate new_start_date, new_end_date; + icaltimetype tt; + icaltimezone *timezone; + time_t start, end, new_time; + gboolean starts_on_week_start_day; + gint new_days_shown; + gint week_start_day; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + model = gnome_calendar_get_model (calendar); + view_type = gnome_calendar_get_view (calendar); + switch_to = view_type; + + timezone = e_cal_model_get_timezone (model); + week_start_day = e_cal_model_get_week_start_day (model); + e_cal_model_get_time_range (model, &start, &end); + + time_to_gdate_with_zone (&start_date, start, timezone); + time_to_gdate_with_zone (&end_date, end, timezone); + + if (view_type == GNOME_CAL_MONTH_VIEW) { + EWeekView *week_view; + ECalendarView *calendar_view; + gboolean multi_week_view; + gboolean compress_weekend; + + calendar_view = gnome_calendar_get_calendar_view ( + calendar, GNOME_CAL_MONTH_VIEW); + + week_view = E_WEEK_VIEW (calendar_view); + multi_week_view = e_week_view_get_multi_week_view (week_view); + compress_weekend = e_week_view_get_compress_weekend (week_view); + + if (week_start_day == 0 && (!multi_week_view || compress_weekend)) + g_date_add_days (&start_date, 1); + } + + g_date_subtract_days (&end_date, 1); + + e_calendar_item_get_selection ( + calitem, &new_start_date, &new_end_date); + + /* If the selection hasn't changed, just return. */ + if (g_date_compare (&start_date, &new_start_date) == 0 && + g_date_compare (&end_date, &new_end_date) == 0) + return; + + new_days_shown = + g_date_get_julian (&new_end_date) - + g_date_get_julian (&new_start_date) + 1; + + /* If a complete week is selected we show the week view. + * Note that if weekends are compressed and the week start + * day is set to Sunday, we don't actually show complete + * weeks in the week view, so this may need tweaking. */ + starts_on_week_start_day = + (g_date_get_weekday (&new_start_date) % 7 == week_start_day); + + /* Update selection to be in the new time range. */ + tt = icaltime_null_time (); + tt.year = g_date_get_year (&new_start_date); + tt.month = g_date_get_month (&new_start_date); + tt.day = g_date_get_day (&new_start_date); + new_time = icaltime_as_timet_with_zone (tt, timezone); + + /* Switch views as appropriate, and change the number of + * days or weeks shown. */ + if (new_days_shown > 9) { + if (view_type != GNOME_CAL_LIST_VIEW) { + ECalendarView *calendar_view; + + calendar_view = gnome_calendar_get_calendar_view ( + calendar, GNOME_CAL_MONTH_VIEW); + e_week_view_set_weeks_shown ( + E_WEEK_VIEW (calendar_view), + (new_days_shown + 6) / 7); + switch_to = GNOME_CAL_MONTH_VIEW; + } + } else if (new_days_shown == 7 && starts_on_week_start_day) + switch_to = GNOME_CAL_WEEK_VIEW; + else { + ECalendarView *calendar_view; + + calendar_view = gnome_calendar_get_calendar_view ( + calendar, GNOME_CAL_DAY_VIEW); + e_day_view_set_days_shown ( + E_DAY_VIEW (calendar_view), new_days_shown); + + if (new_days_shown != 5 || !starts_on_week_start_day) + switch_to = GNOME_CAL_DAY_VIEW; + + else if (view_type != GNOME_CAL_WORK_WEEK_VIEW) + switch_to = GNOME_CAL_DAY_VIEW; + } + + /* Make the views display things properly. */ + gnome_calendar_update_view_times (calendar, new_time); + gnome_calendar_set_view (calendar, switch_to); + gnome_calendar_set_range_selected (calendar, TRUE); + + gnome_calendar_notify_dates_shown_changed (calendar); +} + +static void +cal_shell_view_date_navigator_scroll_event_cb (ECalShellView *cal_shell_view, + GdkEventScroll *event, + ECalendar *date_navigator) +{ + ECalendarItem *calitem; + GDate start_date, end_date; + + calitem = date_navigator->calitem; + if (!e_calendar_item_get_selection (calitem, &start_date, &end_date)) + return; + + switch (event->direction) { + case GDK_SCROLL_UP: + g_date_subtract_months (&start_date, 1); + g_date_subtract_months (&end_date, 1); + break; + + case GDK_SCROLL_DOWN: + g_date_add_months (&start_date, 1); + g_date_add_months (&end_date, 1); + break; + + default: + g_return_if_reached (); + } + + /* XXX Does ECalendarItem emit a signal for this? If so, maybe + * we could move this handler into ECalShellSidebar. */ + e_calendar_item_set_selection (calitem, &start_date, &end_date); + + cal_shell_view_date_navigator_date_range_changed_cb ( + cal_shell_view, calitem); +} + +static void +cal_shell_view_popup_event_cb (EShellView *shell_view, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/calendar-event-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); +} + +static gboolean +cal_shell_view_selector_popup_event_cb (EShellView *shell_view, + ESource *primary_source, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/calendar-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); + + return TRUE; +} + +static void +cal_shell_view_selector_client_added_cb (ECalShellView *cal_shell_view, + ECal *client) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + ECalModel *model; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + model = gnome_calendar_get_model (calendar); + + e_cal_model_add_client (model, client); +} + +static void +cal_shell_view_selector_client_removed_cb (ECalShellView *cal_shell_view, + ECal *client) +{ + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + ECalModel *model; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + model = gnome_calendar_get_model (calendar); + + e_cal_model_remove_client (model, client); +} + +static void +cal_shell_view_memopad_popup_event_cb (EShellView *shell_view, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/calendar-memopad-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); +} + +static void +cal_shell_view_taskpad_popup_event_cb (EShellView *shell_view, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/calendar-taskpad-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); +} + +static void +cal_shell_view_user_created_cb (ECalShellView *cal_shell_view, + ECalendarView *calendar_view) +{ + ECalShellSidebar *cal_shell_sidebar; + ECalModel *model; + ECal *client; + ESource *source; + + model = e_calendar_view_get_model (calendar_view); + client = e_cal_model_get_default_client (model); + source = e_cal_get_source (client); + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + e_cal_shell_sidebar_add_source (cal_shell_sidebar, source); + + e_cal_model_add_client (model, client); +} + +static void +cal_shell_view_load_view_collection (EShellViewClass *shell_view_class) +{ + GalViewCollection *collection; + GalViewFactory *factory; + ETableSpecification *spec; + const gchar *base_dir; + gchar *filename; + + collection = shell_view_class->view_collection; + + base_dir = EVOLUTION_ETSPECDIR; + spec = e_table_specification_new (); + filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL); + if (!e_table_specification_load_from_file (spec, filename)) + g_critical ("Unable to load ETable specification file " + "for calendars"); + g_free (filename); + + factory = calendar_view_factory_new (GNOME_CAL_DAY_VIEW); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + + factory = calendar_view_factory_new (GNOME_CAL_WORK_WEEK_VIEW); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + + factory = calendar_view_factory_new (GNOME_CAL_WEEK_VIEW); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + + factory = calendar_view_factory_new (GNOME_CAL_MONTH_VIEW); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + + factory = gal_view_factory_etable_new (spec); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + g_object_unref (spec); + + gal_view_collection_load (collection); +} + +static void +cal_shell_view_notify_view_id_cb (ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + GalViewInstance *view_instance; + const gchar *view_id; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + view_instance = + e_cal_shell_content_get_view_instance (cal_shell_content); + view_id = e_shell_view_get_view_id (E_SHELL_VIEW (cal_shell_view)); + + /* A NULL view ID implies we're in a custom view. But you can + * only get to a custom view via the "Define Views" dialog, which + * would have already modified the view instance appropriately. + * Furthermore, there's no way to refer to a custom view by ID + * anyway, since custom views have no IDs. */ + if (view_id == NULL) + return; + + gal_view_instance_set_current_view_id (view_instance, view_id); +} + +void +e_cal_shell_view_private_init (ECalShellView *cal_shell_view, + EShellViewClass *shell_view_class) +{ + if (!gal_view_collection_loaded (shell_view_class->view_collection)) + cal_shell_view_load_view_collection (shell_view_class); + + g_signal_connect ( + cal_shell_view, "notify::view-id", + G_CALLBACK (cal_shell_view_notify_view_id_cb), NULL); +} + +void +e_cal_shell_view_private_constructed (ECalShellView *cal_shell_view) +{ + ECalShellViewPrivate *priv = cal_shell_view->priv; + ECalShellContent *cal_shell_content; + ECalShellSidebar *cal_shell_sidebar; + EShellBackend *shell_backend; + EShellContent *shell_content; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + GnomeCalendar *calendar; + ECalendar *date_navigator; + EMemoTable *memo_table; + ECalendarTable *task_table; + ESourceSelector *selector; + ECalModel *model; + gint ii; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + e_shell_window_add_action_group (shell_window, "calendar"); + e_shell_window_add_action_group (shell_window, "calendar-filter"); + + /* Cache these to avoid lots of awkward casting. */ + priv->cal_shell_backend = g_object_ref (shell_backend); + priv->cal_shell_content = g_object_ref (shell_content); + priv->cal_shell_sidebar = g_object_ref (shell_sidebar); + + cal_shell_content = E_CAL_SHELL_CONTENT (shell_content); + model = e_cal_shell_content_get_model (cal_shell_content); + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + memo_table = e_cal_shell_content_get_memo_table (cal_shell_content); + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + cal_shell_sidebar = E_CAL_SHELL_SIDEBAR (shell_sidebar); + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + date_navigator = e_cal_shell_sidebar_get_date_navigator (cal_shell_sidebar); + + /* Give GnomeCalendar a handle to the date navigator. */ + gnome_calendar_set_date_navigator (calendar, date_navigator); + + e_calendar_item_set_get_time_callback ( + date_navigator->calitem, (ECalendarItemGetTimeCallback) + cal_shell_view_get_current_time, cal_shell_view, NULL); + + for (ii = 0; ii < GNOME_CAL_LAST_VIEW; ii++) { + ECalendarView *calendar_view; + + calendar_view = + gnome_calendar_get_calendar_view (calendar, ii); + + g_signal_connect_swapped ( + calendar_view, "popup-event", + G_CALLBACK (cal_shell_view_popup_event_cb), + cal_shell_view); + + g_signal_connect_swapped ( + calendar_view, "user-created", + G_CALLBACK (cal_shell_view_user_created_cb), + cal_shell_view); + } + + g_signal_connect_swapped ( + calendar, "dates-shown-changed", + G_CALLBACK (e_cal_shell_view_update_sidebar), + cal_shell_view); + + g_signal_connect_swapped ( + model, "notify::timezone", + G_CALLBACK (e_cal_shell_view_update_timezone), + cal_shell_view); + + g_signal_connect_swapped ( + date_navigator, "scroll-event", + G_CALLBACK (cal_shell_view_date_navigator_scroll_event_cb), + cal_shell_view); + + g_signal_connect_swapped ( + date_navigator->calitem, "date-range-changed", + G_CALLBACK (cal_shell_view_date_navigator_date_range_changed_cb), + cal_shell_view); + + g_signal_connect_swapped ( + date_navigator->calitem, "selection-changed", + G_CALLBACK (cal_shell_view_date_navigator_selection_changed_cb), + cal_shell_view); + + g_signal_connect_swapped ( + selector, "popup-event", + G_CALLBACK (cal_shell_view_selector_popup_event_cb), + cal_shell_view); + + g_signal_connect_swapped ( + cal_shell_sidebar, "client-added", + G_CALLBACK (cal_shell_view_selector_client_added_cb), + cal_shell_view); + + g_signal_connect_swapped ( + cal_shell_sidebar, "client-removed", + G_CALLBACK (cal_shell_view_selector_client_removed_cb), + cal_shell_view); + + g_signal_connect_swapped ( + memo_table, "popup-event", + G_CALLBACK (cal_shell_view_memopad_popup_event_cb), + cal_shell_view); + + g_signal_connect_swapped ( + memo_table, "status-message", + G_CALLBACK (e_cal_shell_view_memopad_set_status_message), + cal_shell_view); + + g_signal_connect_swapped ( + task_table, "popup-event", + G_CALLBACK (cal_shell_view_taskpad_popup_event_cb), + cal_shell_view); + + g_signal_connect_swapped ( + task_table, "status-message", + G_CALLBACK (e_cal_shell_view_taskpad_set_status_message), + cal_shell_view); + + g_signal_connect_swapped ( + e_memo_table_get_table (memo_table), "selection-change", + G_CALLBACK (e_cal_shell_view_memopad_actions_update), + cal_shell_view); + + g_signal_connect_swapped ( + e_calendar_table_get_table (task_table), "selection-change", + G_CALLBACK (e_cal_shell_view_taskpad_actions_update), + cal_shell_view); + + e_categories_register_change_listener ( + G_CALLBACK (e_cal_shell_view_update_search_filter), + cal_shell_view); + + e_cal_shell_view_actions_init (cal_shell_view); + e_cal_shell_view_update_sidebar (cal_shell_view); + e_cal_shell_view_update_search_filter (cal_shell_view); + e_cal_shell_view_update_timezone (cal_shell_view); + + /* Keep the toolbar view buttons in sync with the calendar. */ + e_mutual_binding_new ( + G_OBJECT (calendar), "view", + G_OBJECT (ACTION (CALENDAR_VIEW_DAY)), "current-value"); +} + +void +e_cal_shell_view_private_dispose (ECalShellView *cal_shell_view) +{ + ECalShellViewPrivate *priv = cal_shell_view->priv; + + DISPOSE (priv->cal_shell_backend); + DISPOSE (priv->cal_shell_content); + DISPOSE (priv->cal_shell_sidebar); + + if (priv->calendar_activity != NULL) { + /* XXX Activity is not cancellable. */ + e_activity_complete (priv->calendar_activity); + g_object_unref (priv->calendar_activity); + priv->calendar_activity = NULL; + } + + if (priv->memopad_activity != NULL) { + /* XXX Activity is not cancellable. */ + e_activity_complete (priv->memopad_activity); + g_object_unref (priv->memopad_activity); + priv->memopad_activity = NULL; + } + + if (priv->taskpad_activity != NULL) { + /* XXX Activity is not cancellable. */ + e_activity_complete (priv->taskpad_activity); + g_object_unref (priv->taskpad_activity); + priv->taskpad_activity = NULL; + } +} + +void +e_cal_shell_view_private_finalize (ECalShellView *cal_shell_view) +{ + /* XXX Nothing to do? */ +} + +void +e_cal_shell_view_execute_search (ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalShellSidebar *cal_shell_sidebar; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + GnomeCalendar *calendar; + ECalendar *date_navigator; + GtkRadioAction *action; + GString *string; + FilterRule *rule; + const gchar *format; + const gchar *text; + time_t start_range; + time_t end_range; + gboolean range_search; + gchar *start, *end; + gchar *query; + gchar *temp; + gint value; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + text = e_shell_content_get_search_text (shell_content); + + shell_window = e_shell_view_get_shell_window (shell_view); + action = GTK_RADIO_ACTION (ACTION (CALENDAR_SEARCH_ANY_FIELD_CONTAINS)); + value = gtk_radio_action_get_current_value (action); + + if (text == NULL || *text == '\0') { + text = ""; + value = CALENDAR_SEARCH_SUMMARY_CONTAINS; + } + + switch (value) { + default: + text = ""; + /* fall through */ + + case CALENDAR_SEARCH_SUMMARY_CONTAINS: + format = "(contains? \"summary\" %s)"; + break; + + case CALENDAR_SEARCH_DESCRIPTION_CONTAINS: + format = "(contains? \"description\" %s)"; + break; + + case CALENDAR_SEARCH_ANY_FIELD_CONTAINS: + format = "(contains? \"any\" %s)"; + break; + } + + /* Build the query. */ + string = g_string_new (""); + e_sexp_encode_string (string, text); + query = g_strdup_printf (format, string->str); + g_string_free (string, TRUE); + + range_search = FALSE; + start_range = end_range = 0; + + /* Apply selected filter. */ + value = e_shell_content_get_filter_value (shell_content); + switch (value) { + case CALENDAR_FILTER_ANY_CATEGORY: + break; + + case CALENDAR_FILTER_UNMATCHED: + temp = g_strdup_printf ( + "(and (has-categories? #f) %s)", query); + g_free (query); + query = temp; + break; + + case CALENDAR_FILTER_ACTIVE_APPOINTMENTS: + /* Show a year's worth of appointments. */ + start_range = time (NULL); + end_range = time_add_day (start_range, 365); + start = isodate_from_time_t (start_range); + end = isodate_from_time_t (end_range); + + temp = g_strdup_printf ( + "(and %s (occur-in-time-range? " + "(make-time \"%s\") (make-time \"%s\")))", + query, start, end); + g_free (query); + query = temp; + + range_search = TRUE; + break; + + case CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS: + start_range = time (NULL); + end_range = time_add_day (start_range, 7); + start = isodate_from_time_t (start_range); + end = isodate_from_time_t (end_range); + + temp = g_strdup_printf ( + "(and %s (occur-in-time-range? " + "(make-time \"%s\") (make-time \"%s\")))", + query, start, end); + g_free (query); + query = temp; + + range_search = TRUE; + break; + + default: + { + GList *categories; + const gchar *category_name; + + categories = e_categories_get_list (); + category_name = g_list_nth_data (categories, value); + g_list_free (categories); + + temp = g_strdup_printf ( + "(and (has-categories? \"%s\") %s)", + category_name, query); + g_free (query); + query = temp; + break; + } + } + + /* XXX This is wrong. We need to programmatically construct a + * FilterRule, tell it to build code, and pass the resulting + * expressing string to ECalModel. */ + rule = filter_rule_new (); + e_shell_content_set_search_rule (shell_content, rule); + g_object_unref (rule); + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + date_navigator = e_cal_shell_sidebar_get_date_navigator (cal_shell_sidebar); + + if (range_search) { + /* Switch to list view and hide the date navigator. */ + action = GTK_RADIO_ACTION (ACTION (CALENDAR_VIEW_LIST)); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + gtk_widget_hide (GTK_WIDGET (date_navigator)); + } else { + /* Ensure the date navigator is visible. */ + gtk_widget_show (GTK_WIDGET (date_navigator)); + } + + /* Submit the query. */ + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + gnome_calendar_set_search_query ( + calendar, query, range_search, start_range, end_range); + g_free (query); +} + +void +e_cal_shell_view_open_event (ECalShellView *cal_shell_view, + ECalModelComponent *comp_data) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + CompEditor *editor; + CompEditorFlags flags = 0; + ECalComponent *comp; + icalcomponent *clone; + icalproperty *prop; + const gchar *uid; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + uid = icalcomponent_get_uid (comp_data->icalcomp); + editor = comp_editor_find_instance (uid); + + if (editor != NULL) + goto exit; + + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY); + if (prop != NULL) + flags |= COMP_EDITOR_MEETING; + + if (itip_organizer_is_user (comp, comp_data->client)) + flags |= COMP_EDITOR_USER_ORG; + + if (itip_sentby_is_user (comp, comp_data->client)) + flags |= COMP_EDITOR_USER_ORG; + + if (!e_cal_component_has_attendees (comp)) + flags |= COMP_EDITOR_USER_ORG; + + editor = event_editor_new (comp_data->client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_unref (comp); + +exit: + gtk_window_present (GTK_WINDOW (editor)); +} + +void +e_cal_shell_view_set_status_message (ECalShellView *cal_shell_view, + const gchar *status_message, + gdouble percent) +{ + EActivity *activity; + EShellView *shell_view; + EShellBackend *shell_backend; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + activity = cal_shell_view->priv->calendar_activity; + + if (status_message == NULL || *status_message == '\0') { + if (activity != NULL) { + e_activity_complete (activity); + g_object_unref (activity); + activity = NULL; + } + + } else if (activity == NULL) { + activity = e_activity_new (status_message); + e_activity_set_percent (activity, percent); + e_shell_backend_add_activity (shell_backend, activity); + + } else { + e_activity_set_percent (activity, percent); + e_activity_set_primary_text (activity, status_message); + } + + cal_shell_view->priv->calendar_activity = activity; +} + +void +e_cal_shell_view_transfer_item_to (ECalShellView *cal_shell_view, + ECalendarViewEvent *event, + ECal *destination_client, + gboolean remove) +{ + icalcomponent *icalcomp; + icalcomponent *icalcomp_clone; + icalcomponent *icalcomp_event; + gboolean success; + const gchar *uid; + + /* XXX This function should be split up into + * smaller, more understandable pieces. */ + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + g_return_if_fail (event != NULL); + g_return_if_fail (E_IS_CAL (destination_client)); + + icalcomp_event = event->comp_data->icalcomp; + uid = icalcomponent_get_uid (icalcomp_event); + + /* Put the new object into the destination calendar. */ + + success = e_cal_get_object ( + destination_client, uid, NULL, &icalcomp, NULL); + + if (success) { + icalcomponent_free (icalcomp); + success = e_cal_modify_object ( + destination_client, icalcomp_event, + CALOBJ_MOD_ALL, NULL); + if (!success) + return; + } else { + icalproperty *icalprop; + gchar *new_uid; + + if (e_cal_util_component_is_instance (icalcomp_event)) { + success = e_cal_get_object ( + event->comp_data->client, + uid, NULL, &icalcomp, NULL); + if (success) { + /* Use master object when working + * with a recurring event ... */ + icalcomp_clone = + icalcomponent_new_clone (icalcomp); + icalcomponent_free (icalcomp); + } else { + /* ... or remove the recurrence ID ... */ + icalcomp_clone = + icalcomponent_new_clone (icalcomp_event); + if (e_cal_util_component_has_recurrences (icalcomp_clone)) { + /* ... for non-detached instances, + * to make it a master object. */ + icalprop = icalcomponent_get_first_property ( + icalcomp_clone, ICAL_RECURRENCEID_PROPERTY); + if (icalprop != NULL) + icalcomponent_remove_property ( + icalcomp_clone, icalprop); + } + } + } else + icalcomp_clone = + icalcomponent_new_clone (icalcomp_event); + + icalprop = icalproperty_new_x ("1"); + icalproperty_set_x_name (icalprop, "X-EVOLUTION-MOVE-CALENDAR"); + icalcomponent_add_property (icalcomp_clone, icalprop); + + if (!remove) { + /* Change the UID to avoid problems with + * duplicated UIDs. */ + new_uid = e_cal_component_gen_uid (); + icalcomponent_set_uid (icalcomp_clone, new_uid); + g_free (new_uid); + } + + new_uid = NULL; + success = e_cal_create_object ( + destination_client, icalcomp_clone, &new_uid, NULL); + if (!success) { + icalcomponent_free (icalcomp_clone); + return; + } + + icalcomponent_free (icalcomp_clone); + g_free (new_uid); + } + + if (remove) { + ECal *source_client = event->comp_data->client; + + /* Remove the item from the source calendar. */ + if (e_cal_util_component_is_instance (icalcomp_event) || + e_cal_util_component_has_recurrences (icalcomp_event)) { + icaltimetype icaltime; + gchar *rid; + + icaltime = + icalcomponent_get_recurrenceid (icalcomp_event); + if (!icaltime_is_null_time (icaltime)) + rid = icaltime_as_ical_string_r (icaltime); + else + rid = NULL; + e_cal_remove_object_with_mod ( + source_client, uid, rid, CALOBJ_MOD_ALL, NULL); + g_free (rid); + } else + e_cal_remove_object (source_client, uid, NULL); + } +} + +void +e_cal_shell_view_update_sidebar (ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellSidebar *shell_sidebar; + ECalShellContent *cal_shell_content; + GnomeCalendar *calendar; + GnomeCalendarViewType view_type; + ECalendarView *calendar_view; + ECalModel *model; + time_t start_time, end_time; + struct tm start_tm, end_tm; + struct icaltimetype start_tt, end_tt; + icaltimezone *timezone; + gchar buffer[512]; + gchar end_buffer[512]; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + + model = gnome_calendar_get_model (calendar); + timezone = e_cal_model_get_timezone (model); + + view_type = gnome_calendar_get_view (calendar); + calendar_view = gnome_calendar_get_calendar_view (calendar, view_type); + + e_calendar_view_get_visible_time_range ( + calendar_view, &start_time, &end_time); + + start_tt = icaltime_from_timet_with_zone (start_time, FALSE, timezone); + start_tm.tm_year = start_tt.year - 1900; + start_tm.tm_mon = start_tt.month - 1; + start_tm.tm_mday = start_tt.day; + start_tm.tm_hour = start_tt.hour; + start_tm.tm_min = start_tt.minute; + start_tm.tm_sec = start_tt.second; + start_tm.tm_isdst = -1; + start_tm.tm_wday = time_day_of_week ( + start_tt.day, start_tt.month - 1, start_tt.year); + + /* Subtract one from end_time so we don't get an extra day. */ + end_tt = icaltime_from_timet_with_zone (end_time - 1, FALSE, timezone); + end_tm.tm_year = end_tt.year - 1900; + end_tm.tm_mon = end_tt.month - 1; + end_tm.tm_mday = end_tt.day; + end_tm.tm_hour = end_tt.hour; + end_tm.tm_min = end_tt.minute; + end_tm.tm_sec = end_tt.second; + end_tm.tm_isdst = -1; + end_tm.tm_wday = time_day_of_week ( + end_tt.day, end_tt.month - 1, end_tt.year); + + switch (view_type) { + case GNOME_CAL_DAY_VIEW: + case GNOME_CAL_WORK_WEEK_VIEW: + case GNOME_CAL_WEEK_VIEW: + if (start_tm.tm_year == end_tm.tm_year && + start_tm.tm_mon == end_tm.tm_mon && + start_tm.tm_mday == end_tm.tm_mday) { + e_utf8_strftime ( + buffer, sizeof (buffer), + _("%A %d %b %Y"), &start_tm); + } else if (start_tm.tm_year == end_tm.tm_year) { + e_utf8_strftime ( + buffer, sizeof (buffer), + _("%a %d %b"), &start_tm); + e_utf8_strftime ( + end_buffer, sizeof (end_buffer), + _("%a %d %b %Y"), &end_tm); + strcat (buffer, " - "); + strcat (buffer, end_buffer); + } else { + e_utf8_strftime ( + buffer, sizeof (buffer), + _("%a %d %b %Y"), &start_tm); + e_utf8_strftime ( + end_buffer, sizeof (end_buffer), + _("%a %d %b %Y"), &end_tm); + strcat (buffer, " - "); + strcat (buffer, end_buffer); + } + break; + + case GNOME_CAL_MONTH_VIEW: + case GNOME_CAL_LIST_VIEW: + if (start_tm.tm_year == end_tm.tm_year) { + if (start_tm.tm_mon == end_tm.tm_mon) { + e_utf8_strftime ( + buffer, + sizeof (buffer), + "%d", &start_tm); + e_utf8_strftime ( + end_buffer, + sizeof (end_buffer), + _("%d %b %Y"), &end_tm); + strcat (buffer, " - "); + strcat (buffer, end_buffer); + } else { + e_utf8_strftime ( + buffer, + sizeof (buffer), + _("%d %b"), &start_tm); + e_utf8_strftime ( + end_buffer, + sizeof (end_buffer), + _("%d %b %Y"), &end_tm); + strcat (buffer, " - "); + strcat (buffer, end_buffer); + } + } else { + e_utf8_strftime ( + buffer, sizeof (buffer), + _("%d %b %Y"), &start_tm); + e_utf8_strftime ( + end_buffer, sizeof (end_buffer), + _("%d %b %Y"), &end_tm); + strcat (buffer, " - "); + strcat (buffer, end_buffer); + } + break; + + default: + g_return_if_reached (); + } + + e_shell_sidebar_set_secondary_text (shell_sidebar, buffer); +} + +void +e_cal_shell_view_update_timezone (ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalShellSidebar *cal_shell_sidebar; + icaltimezone *timezone; + ECalModel *model; + GList *clients, *iter; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + model = e_cal_shell_content_get_model (cal_shell_content); + timezone = e_cal_model_get_timezone (model); + + cal_shell_sidebar = cal_shell_view->priv->cal_shell_sidebar; + clients = e_cal_shell_sidebar_get_clients (cal_shell_sidebar); + + for (iter = clients; iter != NULL; iter = iter->next) { + ECal *client = iter->data; + + if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED) + e_cal_set_default_timezone (client, timezone, NULL); + } + + g_list_free (clients); +} diff --git a/modules/calendar/e-cal-shell-view-private.h b/modules/calendar/e-cal-shell-view-private.h new file mode 100644 index 0000000000..690031198e --- /dev/null +++ b/modules/calendar/e-cal-shell-view-private.h @@ -0,0 +1,180 @@ +/* + * e-cal-shell-view-private.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_VIEW_PRIVATE_H +#define E_CAL_SHELL_VIEW_PRIVATE_H + +#include "e-cal-shell-view.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal-time-util.h> +#include <libedataserver/e-categories.h> +#include <libedataserver/e-data-server-util.h> +#include <libedataserver/e-sexp.h> + +#include "e-util/e-binding.h" +#include "e-util/e-dialog-utils.h" +#include "e-util/e-error.h" +#include "e-util/e-util.h" +#include "widgets/misc/e-popup-action.h" + +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/comp-util.h" +#include "calendar/gui/e-cal-list-view.h" +#include "calendar/gui/e-cal-model-tasks.h" +#include "calendar/gui/e-calendar-view.h" +#include "calendar/gui/e-day-view.h" +#include "calendar/gui/e-week-view.h" +#include "calendar/gui/gnome-cal.h" +#include "calendar/gui/goto.h" +#include "calendar/gui/print.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/copy-source-dialog.h" +#include "calendar/gui/dialogs/event-editor.h" +#include "calendar/gui/dialogs/memo-editor.h" +#include "calendar/gui/dialogs/select-source-dialog.h" +#include "calendar/gui/dialogs/task-editor.h" + +#include "e-cal-shell-backend.h" +#include "e-cal-shell-content.h" +#include "e-cal-shell-sidebar.h" +#include "e-cal-shell-view-actions.h" + +#define E_CAL_SHELL_VIEW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CAL_SHELL_VIEW, ECalShellViewPrivate)) + +/* Shorthand, requires a variable named "shell_window". */ +#define ACTION(name) \ + (E_SHELL_WINDOW_ACTION_##name (shell_window)) +#define ACTION_GROUP(name) \ + (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window)) + +/* For use in dispose() methods. */ +#define DISPOSE(obj) \ + G_STMT_START { \ + if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \ + } G_STMT_END + +/* ETable Specifications */ +#define ETSPEC_FILENAME "e-calendar-table.etspec" + +G_BEGIN_DECLS + +/* Filter items are displayed in ascending order. + * Non-negative values are reserved for categories. */ +enum { + CALENDAR_FILTER_ANY_CATEGORY = -4, + CALENDAR_FILTER_UNMATCHED = -3, + CALENDAR_FILTER_ACTIVE_APPOINTMENTS = -2, + CALENDAR_FILTER_NEXT_7_DAYS_APPOINTMENTS = -1 +}; + +/* Search items are displayed in ascending order. */ +enum { + CALENDAR_SEARCH_SUMMARY_CONTAINS, + CALENDAR_SEARCH_DESCRIPTION_CONTAINS, + CALENDAR_SEARCH_ANY_FIELD_CONTAINS +}; + +struct _ECalShellViewPrivate { + + /* These are just for convenience. */ + ECalShellBackend *cal_shell_backend; + ECalShellContent *cal_shell_content; + ECalShellSidebar *cal_shell_sidebar; + + /* The last time explicitly selected by the user. */ + time_t base_view_time; + + EActivity *calendar_activity; + EActivity *memopad_activity; + EActivity *taskpad_activity; +}; + +void e_cal_shell_view_private_init + (ECalShellView *cal_shell_view, + EShellViewClass *shell_view_class); +void e_cal_shell_view_private_constructed + (ECalShellView *cal_shell_view); +void e_cal_shell_view_private_dispose + (ECalShellView *cal_shell_view); +void e_cal_shell_view_private_finalize + (ECalShellView *cal_shell_view); + +/* Private Utilities */ + +void e_cal_shell_view_actions_init + (ECalShellView *cal_shell_view); +void e_cal_shell_view_execute_search + (ECalShellView *cal_shell_view); +void e_cal_shell_view_open_event + (ECalShellView *cal_shell_view, + ECalModelComponent *comp_data); +void e_cal_shell_view_set_status_message + (ECalShellView *cal_shell_view, + const gchar *status_message, + gdouble percent); +void e_cal_shell_view_transfer_item_to + (ECalShellView *cal_shell_view, + ECalendarViewEvent *event, + ECal *destination_client, + gboolean remove); +void e_cal_shell_view_update_sidebar + (ECalShellView *cal_shell_view); +void e_cal_shell_view_update_search_filter + (ECalShellView *cal_shell_view); +void e_cal_shell_view_update_timezone + (ECalShellView *cal_shell_view); + +/* Memo Pad Utilities */ + +void e_cal_shell_view_memopad_actions_init + (ECalShellView *cal_shell_view); +void e_cal_shell_view_memopad_actions_update + (ECalShellView *cal_shell_view); +void e_cal_shell_view_memopad_open_memo + (ECalShellView *cal_shell_view, + ECalModelComponent *comp_data); +void e_cal_shell_view_memopad_set_status_message + (ECalShellView *cal_shell_view, + const gchar *status_message, + gdouble percent); + +/* Task Pad Utilities */ + +void e_cal_shell_view_taskpad_actions_init + (ECalShellView *cal_shell_view); +void e_cal_shell_view_taskpad_actions_update + (ECalShellView *cal_shell_view); +void e_cal_shell_view_taskpad_open_task + (ECalShellView *cal_shell_view, + ECalModelComponent *comp_data); +void e_cal_shell_view_taskpad_set_status_message + (ECalShellView *cal_shell_view, + const gchar *status_message, + gdouble percent); + +G_END_DECLS + +#endif /* E_CAL_SHELL_VIEW_PRIVATE_H */ diff --git a/modules/calendar/e-cal-shell-view-taskpad.c b/modules/calendar/e-cal-shell-view-taskpad.c new file mode 100644 index 0000000000..965ec47c49 --- /dev/null +++ b/modules/calendar/e-cal-shell-view-taskpad.c @@ -0,0 +1,654 @@ +/* + * e-cal-shell-view-taskpad.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-view-private.h" + +/* Much of this file is based on e-task-shell-view-actions.c. */ + +static void +action_calendar_taskpad_assign_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the first selected task. */ + e_cal_shell_view_taskpad_open_task (cal_shell_view, comp_data); + + /* FIXME Need to actually assign the task. */ +} + +static void +action_calendar_taskpad_clipboard_copy_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + e_calendar_table_copy_clipboard (task_table); +} + +static void +action_calendar_taskpad_clipboard_cut_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + e_calendar_table_cut_clipboard (task_table); +} + +static void +action_calendar_taskpad_clipboard_paste_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + e_calendar_table_paste_clipboard (task_table); +} + +static void +action_calendar_taskpad_delete_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + e_cal_shell_view_taskpad_set_status_message ( + cal_shell_view, _("Deleting selected tasks..."), -1.0); + e_calendar_table_delete_selected (task_table); + e_cal_shell_view_taskpad_set_status_message ( + cal_shell_view, NULL, -1.0); +} + +static void +action_calendar_taskpad_forward_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only forward the first selected task. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + itip_send_comp ( + E_CAL_COMPONENT_METHOD_PUBLISH, comp, + comp_data->client, NULL, NULL, NULL, TRUE, FALSE); + g_object_unref (comp); +} + +static void +action_calendar_taskpad_mark_complete_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModel *model; + GSList *list, *iter; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + list = e_calendar_table_get_selected (task_table); + model = e_calendar_table_get_model (task_table); + + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + e_cal_model_tasks_mark_comp_complete ( + E_CAL_MODEL_TASKS (model), comp_data); + } + + g_slist_free (list); +} + +static void +action_calendar_taskpad_mark_incomplete_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModel *model; + GSList *list, *iter; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + list = e_calendar_table_get_selected (task_table); + model = e_calendar_table_get_model (task_table); + + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + e_cal_model_tasks_mark_comp_incomplete ( + E_CAL_MODEL_TASKS (model), comp_data); + } + + g_slist_free (list); +} + +static void +action_calendar_taskpad_new_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + ECal *client; + ECalComponent *comp; + CompEditor *editor; + GSList *list; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + client = comp_data->client; + editor = task_editor_new (client, shell, COMP_EDITOR_NEW_ITEM); + comp = cal_comp_task_new_with_defaults (client); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (client); +} + +static void +action_calendar_taskpad_open_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the first selected task. */ + e_cal_shell_view_taskpad_open_task (cal_shell_view, comp_data); +} + +static void +action_calendar_taskpad_open_url_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + icalproperty *prop; + const gchar *uri; + GSList *list; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + + /* XXX We only open the URI of the first selected task. */ + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + g_return_if_fail (prop == NULL); + + uri = icalproperty_get_url (prop); + e_show_uri (GTK_WINDOW (shell_window), uri); +} + +static void +action_calendar_taskpad_print_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GtkPrintOperationAction print_action; + GSList *list; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only print the first selected task. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + e_cal_component_set_icalcomponent (comp, clone); + print_comp (comp, comp_data->client, print_action); + g_object_unref (comp); +} + +static void +action_calendar_taskpad_save_as_cb (GtkAction *action, + ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + GSList *list; + gchar *filename; + gchar *string; + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + filename = e_file_dialog_save (_("Save as..."), NULL); + if (filename == NULL) + return; + + string = e_cal_get_component_as_string ( + comp_data->client, comp_data->icalcomp); + if (string == NULL) { + g_warning ("Could not convert task to a string"); + return; + } + + e_write_file_uri (filename, string); + + g_free (filename); + g_free (string); +} + +static GtkActionEntry calendar_taskpad_entries[] = { + + { "calendar-taskpad-assign", + NULL, + N_("_Assign Task"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_taskpad_assign_cb) }, + + { "calendar-taskpad-clipboard-copy", + GTK_STOCK_COPY, + NULL, + NULL, + N_("Copy selected tasks"), + G_CALLBACK (action_calendar_taskpad_clipboard_copy_cb) }, + + { "calendar-taskpad-clipboard-cut", + GTK_STOCK_CUT, + NULL, + NULL, + N_("Cut selected tasks"), + G_CALLBACK (action_calendar_taskpad_clipboard_cut_cb) }, + + { "calendar-taskpad-clipboard-paste", + GTK_STOCK_PASTE, + NULL, + NULL, + N_("Paste tasks from the clipboard"), + G_CALLBACK (action_calendar_taskpad_clipboard_paste_cb) }, + + { "calendar-taskpad-delete", + GTK_STOCK_DELETE, + N_("_Delete Task"), + NULL, + N_("Delete selected tasks"), + G_CALLBACK (action_calendar_taskpad_delete_cb) }, + + { "calendar-taskpad-forward", + "mail-forward", + N_("_Forward as iCalendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_taskpad_forward_cb) }, + + { "calendar-taskpad-mark-complete", + NULL, + N_("_Mark as Complete"), + NULL, + N_("Mark selected tasks as complete"), + G_CALLBACK (action_calendar_taskpad_mark_complete_cb) }, + + { "calendar-taskpad-mark-incomplete", + NULL, + N_("_Mar_k as Incomplete"), + NULL, + N_("Mark selected tasks as incomplete"), + G_CALLBACK (action_calendar_taskpad_mark_incomplete_cb) }, + + { "calendar-taskpad-new", + "stock_task", + N_("New _Task"), + NULL, + N_("Create a new task"), + G_CALLBACK (action_calendar_taskpad_new_cb) }, + + { "calendar-taskpad-open", + GTK_STOCK_OPEN, + N_("_Open Task"), + NULL, + N_("View the selected task"), + G_CALLBACK (action_calendar_taskpad_open_cb) }, + + { "calendar-taskpad-open-url", + "applications-internet", + N_("Open _Web Page"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_taskpad_open_url_cb) }, + + { "calendar-taskpad-save-as", + GTK_STOCK_SAVE_AS, + N_("_Save as iCalendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_calendar_taskpad_save_as_cb) } +}; + +static GtkActionEntry lockdown_printing_entries[] = { + + { "calendar-taskpad-print", + GTK_STOCK_PRINT, + NULL, + NULL, + N_("Print the selected task"), + G_CALLBACK (action_calendar_taskpad_print_cb) } +}; + +void +e_cal_shell_view_taskpad_actions_init (ECalShellView *cal_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkActionGroup *action_group; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + /* Calendar Actions */ + action_group = ACTION_GROUP (CALENDAR); + gtk_action_group_add_actions ( + action_group, calendar_taskpad_entries, + G_N_ELEMENTS (calendar_taskpad_entries), cal_shell_view); + + /* Lockdown Printing Actions */ + action_group = ACTION_GROUP (LOCKDOWN_PRINTING); + gtk_action_group_add_actions ( + action_group, lockdown_printing_entries, + G_N_ELEMENTS (lockdown_printing_entries), cal_shell_view); +} + +void +e_cal_shell_view_taskpad_actions_update (ECalShellView *cal_shell_view) +{ + ECalShellContent *cal_shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + ECalendarTable *task_table; + ETable *table; + GtkAction *action; + GSList *list, *iter; + const gchar *label; + gboolean assignable = TRUE; + gboolean editable = TRUE; + gboolean has_url = FALSE; + gboolean sensitive; + gint n_selected; + gint n_complete = 0; + gint n_incomplete = 0; + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = cal_shell_view->priv->cal_shell_content; + task_table = e_cal_shell_content_get_task_table (cal_shell_content); + + table = e_calendar_table_get_table (task_table); + n_selected = e_table_selected_count (table); + + list = e_calendar_table_get_selected (task_table); + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + icalproperty *prop; + const gchar *cap; + gboolean read_only; + + e_cal_is_read_only (comp_data->client, &read_only, NULL); + editable &= !read_only; + + cap = CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT; + if (e_cal_get_static_capability (comp_data->client, cap)) + assignable = FALSE; + + cap = CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK; + if (e_cal_get_static_capability (comp_data->client, cap)) + assignable = FALSE; + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + has_url |= (prop != NULL); + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_COMPLETED_PROPERTY); + if (prop != NULL) + n_complete++; + else + n_incomplete++; + } + g_slist_free (list); + + action = ACTION (CALENDAR_TASKPAD_ASSIGN); + sensitive = (n_selected == 1) && editable && assignable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_COPY); + sensitive = (n_selected > 0); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_CUT); + sensitive = (n_selected > 0) && editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_PASTE); + sensitive = editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_DELETE); + sensitive = (n_selected > 0) && editable; + gtk_action_set_sensitive (action, sensitive); + label = ngettext ("Delete Task", "Delete Tasks", n_selected); + g_object_set (action, "label", label, NULL); + + action = ACTION (CALENDAR_TASKPAD_FORWARD); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_MARK_COMPLETE); + sensitive = (n_selected > 0) && editable && (n_incomplete > 0); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_MARK_INCOMPLETE); + sensitive = (n_selected > 0) && editable && (n_complete > 0); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_OPEN); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_OPEN_URL); + sensitive = (n_selected == 1) && has_url; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_PRINT); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_TASKPAD_SAVE_AS); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); +} + +void +e_cal_shell_view_taskpad_open_task (ECalShellView *cal_shell_view, + ECalModelComponent *comp_data) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + CompEditor *editor; + CompEditorFlags flags = 0; + ECalComponent *comp; + icalcomponent *clone; + icalproperty *prop; + const gchar *uid; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + uid = icalcomponent_get_uid (comp_data->icalcomp); + editor = comp_editor_find_instance (uid); + + if (editor != NULL) + goto exit; + + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY); + if (prop != NULL) + flags |= COMP_EDITOR_IS_ASSIGNED; + + if (itip_organizer_is_user (comp, comp_data->client)) + flags |= COMP_EDITOR_USER_ORG; + + if (!e_cal_component_has_attendees (comp)) + flags |= COMP_EDITOR_USER_ORG; + + editor = task_editor_new (comp_data->client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_ref (comp); + + if (flags & COMP_EDITOR_IS_ASSIGNED) + task_editor_show_assignment (TASK_EDITOR (editor)); + +exit: + gtk_window_present (GTK_WINDOW (editor)); +} + +void +e_cal_shell_view_taskpad_set_status_message (ECalShellView *cal_shell_view, + const gchar *status_message, + gdouble percent) +{ + EActivity *activity; + EShellView *shell_view; + EShellBackend *shell_backend; + + g_return_if_fail (E_IS_CAL_SHELL_VIEW (cal_shell_view)); + + shell_view = E_SHELL_VIEW (cal_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + activity = cal_shell_view->priv->taskpad_activity; + + if (status_message == NULL || *status_message == '\0') { + if (activity != NULL) { + e_activity_complete (activity); + g_object_unref (activity); + activity = NULL; + } + + } else if (activity == NULL) { + activity = e_activity_new (status_message); + e_activity_set_percent (activity, percent); + e_shell_backend_add_activity (shell_backend, activity); + + } else { + e_activity_set_percent (activity, percent); + e_activity_set_primary_text (activity, status_message); + } + + cal_shell_view->priv->taskpad_activity = activity; +} diff --git a/modules/calendar/e-cal-shell-view.c b/modules/calendar/e-cal-shell-view.c new file mode 100644 index 0000000000..d9d21799b9 --- /dev/null +++ b/modules/calendar/e-cal-shell-view.c @@ -0,0 +1,219 @@ +/* + * e-cal-shell-view.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-shell-view-private.h" + +static gpointer parent_class; +static GType cal_shell_view_type; + +static void +cal_shell_view_dispose (GObject *object) +{ + e_cal_shell_view_private_dispose (E_CAL_SHELL_VIEW (object)); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +cal_shell_view_finalize (GObject *object) +{ + e_cal_shell_view_private_finalize (E_CAL_SHELL_VIEW (object)); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +cal_shell_view_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + e_cal_shell_view_private_constructed (E_CAL_SHELL_VIEW (object)); +} + +static void +cal_shell_view_update_actions (EShellView *shell_view) +{ +#if 0 + ECalShellViewPrivate *priv; + ECalShellContent *cal_shell_content; + ECalShellSidebar *cal_shell_sidebar; + EShellWindow *shell_window; + GnomeCalendarViewType view_type; + GnomeCalendar *calendar; + ECalendarView *view; + ECalModel *model; + ESourceSelector *selector; + ESource *source; + GtkAction *action; + GList *list, *iter; + const gchar *uri = NULL; + gboolean user_created_source; + gboolean editable = TRUE; + gboolean recurring = FALSE; + gboolean sensitive; + gint n_selected; + + priv = E_CAL_SHELL_VIEW_GET_PRIVATE (shell_view); + + shell_window = e_shell_view_get_shell_window (shell_view); + + cal_shell_content = priv->cal_shell_content; + calendar = e_cal_shell_content_get_calendar (cal_shell_content); + view_type = gnome_calendar_get_view (calendar); + view = gnome_calendar_get_calendar_view (calendar, view_type); + model = e_calendar_view_get_model (view); + + cal_shell_sidebar = priv->cal_shell_sidebar; + selector = e_cal_shell_sidebar_get_selector (cal_shell_sidebar); + + list = e_calendar_view_get_selected_events (view); + n_selected = g_list_length (list); + + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + gboolean read_only; + + e_cal_is_read_only (comp_data->client, &read_only, NULL); + editable &= !read_only; + + if (e_cal_util_component_has_recurrences (comp_data->icalcomp)) + recurring |= TRUE; + else if (e_cal_util_component_is_instance (comp_data->icalcomp)) + recurring |= TRUE; + } + + source = e_source_selector_peek_primary_selection (selector); + if (source != NULL) + uri = e_source_peek_relative_uri (source); + user_created_source = (uri != NULL && strcmp (uri, "system") != 0); + + action = ACTION (CALENDAR_COPY); + sensitive = (source != NULL); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_DELETE); + sensitive = user_created_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_PROPERTIES); + sensitive = (source != NULL); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (CALENDAR_RENAME); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_CLIPBOARD_COPY); + sensitive = (n_selected > 0); + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_CLIPBOARD_CUT); + sensitive = (n_selected > 0) && editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_CLIPBOARD_PASTE); + sensitive = editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_DELETE); + sensitive = (n_selected > 0) && editable && !recurring; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_DELETE_OCCURRENCE); + sensitive = (n_selected > 0) && editable && recurring; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_DELETE_OCCURRENCE_ALL); + sensitive = (n_selected > 0) && editable && recurring; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (EVENT_OPEN); + sensitive = (n_selected == 1); + gtk_action_set_sensitive (action, sensitive); +#endif +} + +static void +cal_shell_view_class_init (ECalShellViewClass *class, + GTypeModule *type_module) +{ + GObjectClass *object_class; + EShellViewClass *shell_view_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ECalShellViewPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = cal_shell_view_dispose; + object_class->finalize = cal_shell_view_finalize; + object_class->constructed = cal_shell_view_constructed; + + shell_view_class = E_SHELL_VIEW_CLASS (class); + shell_view_class->label = _("Calendar"); + shell_view_class->icon_name = "x-office-calendar"; + shell_view_class->ui_definition = "evolution-calendars.ui"; + shell_view_class->ui_manager_id = "org.gnome.evolution.calendars"; + shell_view_class->search_options = "/calendar-search-options"; + shell_view_class->search_rules = "caltypes.xml"; + shell_view_class->new_shell_content = e_cal_shell_content_new; + shell_view_class->new_shell_sidebar = e_cal_shell_sidebar_new; + shell_view_class->update_actions = cal_shell_view_update_actions; +} + +static void +cal_shell_view_init (ECalShellView *cal_shell_view, + EShellViewClass *shell_view_class) +{ + cal_shell_view->priv = + E_CAL_SHELL_VIEW_GET_PRIVATE (cal_shell_view); + + e_cal_shell_view_private_init (cal_shell_view, shell_view_class); +} + +GType +e_cal_shell_view_get_type (void) +{ + return cal_shell_view_type; +} + +void +e_cal_shell_view_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (ECalShellViewClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) cal_shell_view_class_init, + (GClassFinalizeFunc) NULL, + type_module, + sizeof (ECalShellView), + 0, /* n_preallocs */ + (GInstanceInitFunc) cal_shell_view_init, + NULL /* value_table */ + }; + + cal_shell_view_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_VIEW, + "ECalShellView", &type_info, 0); +} diff --git a/modules/calendar/e-cal-shell-view.h b/modules/calendar/e-cal-shell-view.h new file mode 100644 index 0000000000..0cd7382012 --- /dev/null +++ b/modules/calendar/e-cal-shell-view.h @@ -0,0 +1,66 @@ +/* + * e-cal-shell-view.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CAL_SHELL_VIEW_H +#define E_CAL_SHELL_VIEW_H + +#include <shell/e-shell-view.h> + +/* Standard GObject macros */ +#define E_TYPE_CAL_SHELL_VIEW \ + (e_cal_shell_view_get_type ()) +#define E_CAL_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CAL_SHELL_VIEW, ECalShellView)) +#define E_CAL_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CAL_SHELL_VIEW, ECalShellViewClass)) +#define E_IS_CAL_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CAL_SHELL_VIEW)) +#define E_IS_CAL_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CAL_SHELL_VIEW)) +#define E_CAL_SHELL_VIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CAL_SHELL_VIEW, ECalShellViewClass)) + +G_BEGIN_DECLS + +typedef struct _ECalShellView ECalShellView; +typedef struct _ECalShellViewClass ECalShellViewClass; +typedef struct _ECalShellViewPrivate ECalShellViewPrivate; + +struct _ECalShellView { + EShellView parent; + ECalShellViewPrivate *priv; +}; + +struct _ECalShellViewClass { + EShellViewClass parent_class; +}; + +GType e_cal_shell_view_get_type (void); +void e_cal_shell_view_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_CAL_SHELL_VIEW_H */ diff --git a/modules/calendar/e-memo-shell-backend.c b/modules/calendar/e-memo-shell-backend.c new file mode 100644 index 0000000000..6c6a36491c --- /dev/null +++ b/modules/calendar/e-memo-shell-backend.c @@ -0,0 +1,653 @@ +/* + * e-memo-shell-backend.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-backend.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal.h> +#include <libedataserver/e-url.h> +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-list.h> +#include <libedataserver/e-source-group.h> + +#include "shell/e-shell.h" +#include "shell/e-shell-backend.h" +#include "shell/e-shell-window.h" + +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/comp-util.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/memo-editor.h" + +#include "e-memo-shell-migrate.h" +#include "e-memo-shell-view.h" + +#define E_MEMO_SHELL_BACKEND_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackendPrivate)) + +#define WEB_BASE_URI "webcal://" +#define PERSONAL_RELATIVE_URI "system" + +struct _EMemoShellBackendPrivate { + ESourceList *source_list; +}; + +enum { + PROP_0, + PROP_SOURCE_LIST +}; + +static gpointer parent_class; +static GType memo_shell_backend_type; + +static void +memo_shell_backend_ensure_sources (EShellBackend *shell_backend) +{ + /* XXX This is basically the same algorithm across all modules. + * Maybe we could somehow integrate this into EShellBackend? */ + + EMemoShellBackendPrivate *priv; + ESourceGroup *on_this_computer; + ESourceGroup *on_the_web; + ESource *personal; + EShell *shell; + EShellSettings *shell_settings; + GSList *groups, *iter; + const gchar *data_dir; + const gchar *name; + gchar *base_uri; + gchar *filename; + + on_this_computer = NULL; + on_the_web = NULL; + personal = NULL; + + priv = E_MEMO_SHELL_BACKEND_GET_PRIVATE (shell_backend); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + if (!e_cal_get_sources (&priv->source_list, E_CAL_SOURCE_TYPE_JOURNAL, NULL)) { + g_warning ("Could not get memo sources from GConf!"); + return; + } + + data_dir = e_shell_backend_get_data_dir (shell_backend); + filename = g_build_filename (data_dir, "local", NULL); + base_uri = g_filename_to_uri (filename, NULL, NULL); + g_free (filename); + + groups = e_source_list_peek_groups (priv->source_list); + for (iter = groups; iter != NULL; iter = iter->next) { + ESourceGroup *source_group = iter->data; + const gchar *group_base_uri; + + group_base_uri = e_source_group_peek_base_uri (source_group); + + /* Compare only "file://" part. If the user's home + * changes, we do not want to create another group. */ + if (on_this_computer == NULL && + strncmp (base_uri, group_base_uri, 7) == 0) + on_this_computer = source_group; + + else if (on_the_web == NULL && + strcmp (WEB_BASE_URI, group_base_uri) == 0) + on_the_web = source_group; + } + + name = _("On This Computer"); + + if (on_this_computer != NULL) { + GSList *sources; + const gchar *group_base_uri; + + /* Force the group name to the current locale. */ + e_source_group_set_name (on_this_computer, name); + + sources = e_source_group_peek_sources (on_this_computer); + group_base_uri = e_source_group_peek_base_uri (on_this_computer); + + /* Make sure this group includes a "Personal" source. */ + for (iter = sources; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + const gchar *relative_uri; + + relative_uri = e_source_peek_relative_uri (source); + if (relative_uri == NULL) + continue; + + if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0) + continue; + + personal = source; + break; + } + + /* Make sure we have the correct base URI. This can + * change when the user's home directory changes. */ + if (strcmp (base_uri, group_base_uri) != 0) { + e_source_group_set_base_uri ( + on_this_computer, base_uri); + + /* XXX We shouldn't need this sync call here as + * set_base_uri() results in synching to GConf, + * but that happens in an idle loop and too late + * to prevent the user from seeing a "Cannot + * Open ... because of invalid URI" error. */ + e_source_list_sync (priv->source_list, NULL); + } + + } else { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, base_uri); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } + + name = _("Personal"); + + if (personal == NULL) { + ESource *source; + GSList *selected; + gchar *primary; + + source = e_source_new (name, PERSONAL_RELATIVE_URI); + e_source_group_add_source (on_this_computer, source, -1); + g_object_unref (source); + + primary = e_shell_settings_get_string ( + shell_settings, "cal-primary-memo-list"); + + selected = calendar_config_get_memos_selected (); + + if (primary == NULL && selected == NULL) { + const gchar *uid; + + uid = e_source_peek_uid (source); + selected = g_slist_prepend (NULL, g_strdup (uid)); + + e_shell_settings_set_string ( + shell_settings, "cal-primary-memo-list", uid); + calendar_config_set_memos_selected (selected); + } + + g_slist_foreach (selected, (GFunc) g_free, NULL); + g_slist_free (selected); + g_free (primary); + } else { + /* Force the source name to the current locale. */ + e_source_set_name (personal, name); + } + + name = _("On The Web"); + + if (on_the_web == NULL) { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, WEB_BASE_URI); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } else { + /* Force the group name to the current locale. */ + e_source_group_set_name (on_the_web, name); + } + + g_free (base_uri); +} + +static void +memo_shell_backend_memo_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + + editor = memo_editor_new (cal, shell, flags); + comp = cal_comp_memo_new_with_defaults (cal); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +memo_shell_backend_memo_shared_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + flags |= COMP_EDITOR_IS_SHARED; + flags |= COMP_EDITOR_USER_ORG; + + editor = memo_editor_new (cal, shell, flags); + comp = cal_comp_memo_new_with_defaults (cal); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +action_memo_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + ECal *cal = NULL; + ECalSourceType source_type; + ESourceList *source_list; + EShellSettings *shell_settings; + EShell *shell; + const gchar *action_name; + gchar *uid; + + /* This callback is used for both memos and shared memos. */ + + source_type = E_CAL_SOURCE_TYPE_JOURNAL; + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_warning ("Could not get memo sources from GConf!"); + return; + } + + uid = e_shell_settings_get_string ( + shell_settings, "cal-primary-memo-list"); + + if (uid != NULL) { + ESource *source; + + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source != NULL) + cal = auth_new_cal_from_source (source, source_type); + g_free (uid); + } + + if (cal == NULL) + cal = auth_new_cal_from_default (source_type); + + g_return_if_fail (cal != NULL); + + /* Connect the appropriate signal handler. */ + action_name = gtk_action_get_name (action); + if (strcmp (action_name, "memo-shared-new") == 0) + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (memo_shell_backend_memo_shared_new_cb), + shell); + else + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (memo_shell_backend_memo_new_cb), + shell); + + e_cal_open_async (cal, FALSE); +} + +static void +action_memo_list_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + calendar_setup_new_memo_list (GTK_WINDOW (shell_window)); +} + +static GtkActionEntry item_entries[] = { + + { "memo-new", + "stock_insert-note", + NC_("New", "Mem_o"), + "<Shift><Control>o", + N_("Create a new memo"), + G_CALLBACK (action_memo_new_cb) }, + + { "memo-shared-new", + "stock_insert-note", + N_("_Shared Memo"), + "<Shift><Control>h", + N_("Create a new shared memo"), + G_CALLBACK (action_memo_new_cb) } +}; + +static GtkActionEntry source_entries[] = { + + { "memo-list-new", + "stock_notes", + NC_("New", "Memo Li_st"), + NULL, + N_("Create a new memo list"), + G_CALLBACK (action_memo_list_new_cb) } +}; + +static gboolean +memo_shell_backend_handle_uri_cb (EShellBackend *shell_backend, + const gchar *uri) +{ + EShell *shell; + CompEditor *editor; + CompEditorFlags flags = 0; + ECal *client; + ECalComponent *comp; + ESource *source; + ESourceList *source_list; + ECalSourceType source_type; + EUri *euri; + icalcomponent *icalcomp; + const gchar *cp; + gchar *source_uid = NULL; + gchar *comp_uid = NULL; + gchar *comp_rid = NULL; + gboolean handled = FALSE; + GError *error = NULL; + + source_type = E_CAL_SOURCE_TYPE_JOURNAL; + shell = e_shell_backend_get_shell (shell_backend); + + if (strncmp (uri, "memo:", 5) != 0) + return FALSE; + + euri = e_uri_new (uri); + cp = euri->query; + if (cp == NULL) + goto exit; + + while (*cp != '\0') { + gchar *header; + gchar *content; + gsize header_len; + gsize content_len; + + header_len = strcspn (cp, "=&"); + + /* If it's malformed, give up. */ + if (cp[header_len] != '=') + break; + + header = (gchar *) cp; + header[header_len] = '\0'; + cp += header_len + 1; + + content_len = strcspn (cp, "&"); + + content = g_strndup (cp, content_len); + if (g_ascii_strcasecmp (header, "source-uid") == 0) + source_uid = g_strdup (content); + else if (g_ascii_strcasecmp (header, "comp-uid") == 0) + comp_uid = g_strdup (content); + else if (g_ascii_strcasecmp (header, "comp-rid") == 0) + comp_rid = g_strdup (content); + g_free (content); + + cp += content_len; + if (*cp == '&') { + cp++; + if (strcmp (cp, "amp;") == 0) + cp += 4; + } + } + + if (source_uid == NULL || comp_uid == NULL) + goto exit; + + /* URI is valid, so consider it handled. Whether + * we successfully open it is another matter... */ + handled = TRUE; + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_printerr ("Could not get memo sources from GConf!\n"); + goto exit; + } + + source = e_source_list_peek_source_by_uid (source_list, source_uid); + if (source == NULL) { + g_printerr ("No source for UID `%s'\n", source_uid); + g_object_unref (source_list); + goto exit; + } + + client = auth_new_cal_from_source (source, source_type); + if (client == NULL || !e_cal_open (client, TRUE, &error)) { + g_printerr ("%s\n", error->message); + g_object_unref (source_list); + g_error_free (error); + goto exit; + } + + /* XXX Copied from e_memo_shell_view_open_memo(). + * Clearly a new utility function is needed. */ + + editor = comp_editor_find_instance (comp_uid); + + if (editor != NULL) + goto present; + + if (!e_cal_get_object (client, comp_uid, comp_rid, &icalcomp, &error)) { + g_printerr ("%s\n", error->message); + g_object_unref (source_list); + g_error_free (error); + goto exit; + } + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomp); + + if (e_cal_component_has_organizer (comp)) + flags |= COMP_EDITOR_IS_SHARED; + + if (itip_organizer_is_user (comp, client)) + flags |= COMP_EDITOR_USER_ORG; + + editor = memo_editor_new (client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_unref (comp); + +present: + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (source_list); + g_object_unref (client); + +exit: + g_free (source_uid); + g_free (comp_uid); + g_free (comp_rid); + + e_uri_free (euri); + + return handled; +} + +static void +memo_shell_backend_window_created_cb (EShellBackend *shell_backend, + GtkWindow *window) +{ + const gchar *module_name; + + if (!E_IS_SHELL_WINDOW (window)) + return; + + module_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name; + + e_shell_window_register_new_item_actions ( + E_SHELL_WINDOW (window), module_name, + item_entries, G_N_ELEMENTS (item_entries)); + + e_shell_window_register_new_source_actions ( + E_SHELL_WINDOW (window), module_name, + source_entries, G_N_ELEMENTS (source_entries)); +} + +static void +memo_shell_backend_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE_LIST: + g_value_set_object ( + value, + e_memo_shell_backend_get_source_list ( + E_MEMO_SHELL_BACKEND (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +memo_shell_backend_dispose (GObject *object) +{ + EMemoShellBackendPrivate *priv; + + priv = E_MEMO_SHELL_BACKEND_GET_PRIVATE (object); + + if (priv->source_list != NULL) { + g_object_unref (priv->source_list); + priv->source_list = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +memo_shell_backend_constructed (GObject *object) +{ + EShell *shell; + EShellBackend *shell_backend; + + shell_backend = E_SHELL_BACKEND (object); + shell = e_shell_backend_get_shell (shell_backend); + + memo_shell_backend_ensure_sources (shell_backend); + + g_signal_connect_swapped ( + shell, "handle-uri", + G_CALLBACK (memo_shell_backend_handle_uri_cb), + shell_backend); + + g_signal_connect_swapped ( + shell, "window-created", + G_CALLBACK (memo_shell_backend_window_created_cb), + shell_backend); +} + +static void +memo_shell_backend_class_init (EMemoShellBackendClass *class) +{ + GObjectClass *object_class; + EShellBackendClass *shell_backend_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMemoShellBackendPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = memo_shell_backend_get_property; + object_class->dispose = memo_shell_backend_dispose; + object_class->constructed = memo_shell_backend_constructed; + + shell_backend_class = E_SHELL_BACKEND_CLASS (class); + shell_backend_class->shell_view_type = E_TYPE_MEMO_SHELL_VIEW; + shell_backend_class->name = "memos"; + shell_backend_class->aliases = ""; + shell_backend_class->schemes = "memo"; + shell_backend_class->sort_order = 500; + shell_backend_class->start = NULL; + shell_backend_class->migrate = e_memo_shell_backend_migrate; + + g_object_class_install_property ( + object_class, + PROP_SOURCE_LIST, + g_param_spec_object ( + "source-list", + _("Source List"), + _("The registry of memo lists"), + E_TYPE_SOURCE_LIST, + G_PARAM_READABLE)); +} + +static void +memo_shell_backend_init (EMemoShellBackend *memo_shell_backend) +{ + memo_shell_backend->priv = + E_MEMO_SHELL_BACKEND_GET_PRIVATE (memo_shell_backend); +} + +GType +e_memo_shell_backend_get_type (void) +{ + return memo_shell_backend_type; +} + +void +e_memo_shell_backend_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EMemoShellBackendClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) memo_shell_backend_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMemoShellBackend), + 0, /* n_preallocs */ + (GInstanceInitFunc) memo_shell_backend_init, + NULL /* value_table */ + }; + + memo_shell_backend_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_BACKEND, + "EMemoShellBackend", &type_info, 0); +} + +ESourceList * +e_memo_shell_backend_get_source_list (EMemoShellBackend *memo_shell_backend) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_BACKEND (memo_shell_backend), NULL); + + return memo_shell_backend->priv->source_list; +} diff --git a/modules/calendar/e-memo-shell-backend.h b/modules/calendar/e-memo-shell-backend.h new file mode 100644 index 0000000000..37fe41a784 --- /dev/null +++ b/modules/calendar/e-memo-shell-backend.h @@ -0,0 +1,70 @@ +/* + * e-memo-shell-backend.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_BACKEND_H +#define E_MEMO_SHELL_BACKEND_H + +#include <shell/e-shell-backend.h> +#include <libedataserver/e-source-list.h> + +/* Standard GObject macros */ +#define E_TYPE_MEMO_SHELL_BACKEND \ + (e_memo_shell_backend_get_type ()) +#define E_MEMO_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackend)) +#define E_MEMO_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackendClass)) +#define E_IS_MEMO_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MEMO_SHELL_BACKEND)) +#define E_IS_MEMO_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MEMO_SHELL_BACKEND)) +#define E_MEMO_SHELL_BACKEND_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MEMO_SHELL_BACKEND, EMemoShellBackendClass)) + +G_BEGIN_DECLS + +typedef struct _EMemoShellBackend EMemoShellBackend; +typedef struct _EMemoShellBackendClass EMemoShellBackendClass; +typedef struct _EMemoShellBackendPrivate EMemoShellBackendPrivate; + +struct _EMemoShellBackend { + EShellBackend parent; + EMemoShellBackendPrivate *priv; +}; + +struct _EMemoShellBackendClass { + EShellBackendClass parent_class; +}; + +GType e_memo_shell_backend_get_type (void); +void e_memo_shell_backend_register_type + (GTypeModule *type_module); +ESourceList * e_memo_shell_backend_get_source_list + (EMemoShellBackend *memo_shell_backend); + +G_END_DECLS + +#endif /* E_MEMO_SHELL_BACKEND_H */ diff --git a/modules/calendar/e-memo-shell-content.c b/modules/calendar/e-memo-shell-content.c new file mode 100644 index 0000000000..2f4e436056 --- /dev/null +++ b/modules/calendar/e-memo-shell-content.c @@ -0,0 +1,721 @@ +/* + * e-memo-shell-content.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-content.h" + +#include <glib/gi18n.h> + +#include "e-util/e-binding.h" +#include "e-util/gconf-bridge.h" +#include "widgets/menus/gal-view-etable.h" +#include "widgets/misc/e-paned.h" + +#include "calendar/gui/comp-util.h" +#include "calendar/gui/e-cal-model-memos.h" +#include "calendar/gui/e-memo-table.h" + +#define E_MEMO_SHELL_CONTENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContentPrivate)) + +#define E_MEMO_TABLE_DEFAULT_STATE \ + "<?xml version=\"1.0\"?>" \ + "<ETableState>" \ + " <column source=\"1\"/>" \ + " <column source=\"0\"/>" \ + " <column source=\"2\"/>" \ + " <grouping/>" \ + "</ETableState>" + +struct _EMemoShellContentPrivate { + GtkWidget *paned; + GtkWidget *memo_table; + GtkWidget *memo_preview; + + ECalModel *memo_model; + GalViewInstance *view_instance; + GtkOrientation orientation; + + gchar *current_uid; + + guint preview_visible : 1; +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_ORIENTATION, + PROP_PREVIEW_VISIBLE +}; + +enum { + TARGET_VCALENDAR +}; + +static GtkTargetEntry drag_types[] = { + { (gchar *) "text/calendar", 0, TARGET_VCALENDAR }, + { (gchar *) "text/x-calendar", 0, TARGET_VCALENDAR } +}; + +static gpointer parent_class; +static GType memo_shell_content_type; + +static void +memo_shell_content_display_view_cb (EMemoShellContent *memo_shell_content, + GalView *gal_view) +{ + EMemoTable *memo_table; + ETable *table; + + if (!GAL_IS_VIEW_ETABLE (gal_view)) + return; + + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + table = e_memo_table_get_table (memo_table); + + gal_view_etable_attach_table (GAL_VIEW_ETABLE (gal_view), table); +} + +static void +memo_shell_content_table_foreach_cb (gint model_row, + gpointer user_data) +{ + ECalModelComponent *comp_data; + icalcomponent *clone; + icalcomponent *vcal; + gchar *string; + + struct { + ECalModel *model; + GSList *list; + } *foreach_data = user_data; + + comp_data = e_cal_model_get_component_at ( + foreach_data->model, model_row); + + vcal = e_cal_util_new_top_level (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); + icalcomponent_add_component (vcal, clone); + + /* String is owned by libical; do not free. */ + string = icalcomponent_as_ical_string (vcal); + if (string != NULL) { + ESource *source; + const gchar *source_uid; + + source = e_cal_get_source (comp_data->client); + source_uid = e_source_peek_uid (source); + + foreach_data->list = g_slist_prepend ( + foreach_data->list, + g_strdup_printf ("%s\n%s", source_uid, string)); + } + + icalcomponent_free (vcal); +} + +static void +memo_shell_content_table_drag_data_get_cb (EMemoShellContent *memo_shell_content, + gint row, + gint col, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + EMemoTable *memo_table; + ETable *table; + + struct { + ECalModel *model; + GSList *list; + } foreach_data; + + if (info != TARGET_VCALENDAR) + return; + + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + table = e_memo_table_get_table (memo_table); + + foreach_data.model = e_memo_table_get_model (memo_table); + foreach_data.list = NULL; + + e_table_selected_row_foreach ( + table, memo_shell_content_table_foreach_cb, + &foreach_data); + + if (foreach_data.list != NULL) { + cal_comp_selection_set_string_list ( + selection_data, foreach_data.list); + g_slist_foreach (foreach_data.list, (GFunc) g_free, NULL); + g_slist_free (foreach_data.list); + } +} + +static void +memo_shell_content_table_drag_data_delete_cb (EMemoShellContent *memo_shell_content, + gint row, + gint col, + GdkDragContext *context) +{ + /* Moved components are deleted from source immediately when moved, + * because some of them can be part of destination source, and we + * don't want to delete not-moved memos. There is no such information + * which event has been moved and which not, so skip this method. */ +} + +static void +memo_shell_content_cursor_change_cb (EMemoShellContent *memo_shell_content, + gint row, + ETable *table) +{ + ECalComponentPreview *memo_preview; + EMemoTable *memo_table; + ECalModel *memo_model; + ECalModelComponent *comp_data; + ECalComponent *comp; + const gchar *uid; + + memo_model = e_memo_shell_content_get_memo_model (memo_shell_content); + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content); + + if (e_table_selected_count (table) != 1) { + e_cal_component_preview_clear (memo_preview); + return; + } + + row = e_table_get_cursor_row (table); + comp_data = e_cal_model_get_component_at (memo_model, row); + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent ( + comp, icalcomponent_new_clone (comp_data->icalcomp)); + e_cal_component_preview_display ( + memo_preview, comp_data->client, comp); + + e_cal_component_get_uid (comp, &uid); + g_free (memo_shell_content->priv->current_uid); + memo_shell_content->priv->current_uid = g_strdup (uid); + + g_object_unref (comp); +} + +static void +memo_shell_content_selection_change_cb (EMemoShellContent *memo_shell_content, + ETable *table) +{ + ECalComponentPreview *memo_preview; + + memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content); + + /* XXX Old code emits a "selection-changed" signal here. */ + + if (e_table_selected_count (table) != 1) + e_cal_component_preview_clear (memo_preview); +} + +static void +memo_shell_content_model_row_changed_cb (EMemoShellContent *memo_shell_content, + gint row, + ETableModel *model) +{ + ECalModelComponent *comp_data; + EMemoTable *memo_table; + ETable *table; + const gchar *current_uid; + const gchar *uid; + + current_uid = memo_shell_content->priv->current_uid; + if (current_uid == NULL) + return; + + comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row); + if (comp_data == NULL) + return; + + uid = icalcomponent_get_uid (comp_data->icalcomp); + if (g_strcmp0 (uid, current_uid) != 0) + return; + + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + table = e_memo_table_get_table (memo_table); + + memo_shell_content_cursor_change_cb (memo_shell_content, 0, table); +} + +static GtkOrientation +memo_shell_content_get_orientation (EMemoShellContent *memo_shell_content) +{ + return memo_shell_content->priv->orientation; +} + +static void +memo_shell_content_set_orientation (EMemoShellContent *memo_shell_content, + GtkOrientation orientation) +{ + memo_shell_content->priv->orientation = orientation; + + g_object_notify (G_OBJECT (memo_shell_content), "orientation"); +} + +static void +memo_shell_content_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ORIENTATION: + memo_shell_content_set_orientation ( + E_MEMO_SHELL_CONTENT (object), + g_value_get_enum (value)); + return; + + case PROP_PREVIEW_VISIBLE: + e_memo_shell_content_set_preview_visible ( + E_MEMO_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +memo_shell_content_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_MODEL: + g_value_set_object ( + value, + e_memo_shell_content_get_memo_model ( + E_MEMO_SHELL_CONTENT (object))); + return; + + case PROP_ORIENTATION: + g_value_set_enum ( + value, + memo_shell_content_get_orientation ( + E_MEMO_SHELL_CONTENT (object))); + return; + + case PROP_PREVIEW_VISIBLE: + g_value_set_boolean ( + value, + e_memo_shell_content_get_preview_visible ( + E_MEMO_SHELL_CONTENT (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +memo_shell_content_dispose (GObject *object) +{ + EMemoShellContentPrivate *priv; + + priv = E_MEMO_SHELL_CONTENT_GET_PRIVATE (object); + + if (priv->paned != NULL) { + g_object_unref (priv->paned); + priv->paned = NULL; + } + + if (priv->memo_table != NULL) { + g_object_unref (priv->memo_table); + priv->memo_table = NULL; + } + + if (priv->memo_preview != NULL) { + g_object_unref (priv->memo_preview); + priv->memo_preview = NULL; + } + + if (priv->memo_model != NULL) { + g_object_unref (priv->memo_model); + priv->memo_model = NULL; + } + + if (priv->view_instance != NULL) { + g_object_unref (priv->view_instance); + priv->view_instance = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +memo_shell_content_finalize (GObject *object) +{ + EMemoShellContentPrivate *priv; + + priv = E_MEMO_SHELL_CONTENT_GET_PRIVATE (object); + + g_free (priv->current_uid); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +memo_shell_content_constructed (GObject *object) +{ + EMemoShellContentPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellSettings *shell_settings; + EShellBackend *shell_backend; + EShellContent *shell_content; + GalViewInstance *view_instance; + icaltimezone *timezone; + ETable *table; + GConfBridge *bridge; + GtkWidget *container; + GtkWidget *widget; + const gchar *key; + + priv = E_MEMO_SHELL_CONTENT_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + shell_content = E_SHELL_CONTENT (object); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + priv->memo_model = e_cal_model_memos_new (shell_settings); + + timezone = e_shell_settings_get_pointer ( + shell_settings, "cal-timezone"); + + /* Build content widgets. */ + + container = GTK_WIDGET (object); + + widget = e_paned_new (GTK_ORIENTATION_VERTICAL); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->paned = g_object_ref (widget); + gtk_widget_show (widget); + + e_binding_new ( + G_OBJECT (object), "orientation", + G_OBJECT (widget), "orientation"); + + container = widget; + + widget = e_memo_table_new (shell_view, priv->memo_model); + gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE); + priv->memo_table = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE); + gtk_widget_show (widget); + + e_binding_new ( + G_OBJECT (object), "preview-visible", + G_OBJECT (widget), "visible"); + + container = widget; + + widget = e_cal_component_preview_new (); + e_cal_component_preview_set_default_timezone ( + E_CAL_COMPONENT_PREVIEW (widget), timezone); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->memo_preview = g_object_ref (widget); + gtk_widget_show (widget); + + /* Configure the memo table. */ + + widget = E_MEMO_TABLE (priv->memo_table)->etable; + table = e_table_scrolled_get_table (E_TABLE_SCROLLED (widget)); + + e_table_set_state (table, E_MEMO_TABLE_DEFAULT_STATE); + + e_table_drag_source_set ( + table, GDK_BUTTON1_MASK, + drag_types, G_N_ELEMENTS (drag_types), + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_ASK); + + g_signal_connect_swapped ( + table, "table-drag-data-get", + G_CALLBACK (memo_shell_content_table_drag_data_get_cb), + object); + + g_signal_connect_swapped ( + table, "table-drag-data-delete", + G_CALLBACK (memo_shell_content_table_drag_data_delete_cb), + object); + + g_signal_connect_swapped ( + table, "cursor-change", + G_CALLBACK (memo_shell_content_cursor_change_cb), + object); + + g_signal_connect_swapped ( + table, "selection-change", + G_CALLBACK (memo_shell_content_selection_change_cb), + object); + + g_signal_connect_swapped ( + priv->memo_model, "model-row-changed", + G_CALLBACK (memo_shell_content_model_row_changed_cb), + object); + + /* Load the view instance. */ + + view_instance = e_shell_view_new_view_instance (shell_view, NULL); + g_signal_connect_swapped ( + view_instance, "display-view", + G_CALLBACK (memo_shell_content_display_view_cb), + object); + gal_view_instance_load (view_instance); + priv->view_instance = view_instance; + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (priv->paned); + key = "/apps/evolution/calendar/display/memo_hpane_position"; + gconf_bridge_bind_property_delayed (bridge, key, object, "hposition"); + + object = G_OBJECT (priv->paned); + key = "/apps/evolution/calendar/display/memo_vpane_position"; + gconf_bridge_bind_property_delayed (bridge, key, object, "vposition"); +} + +static guint32 +memo_shell_content_check_state (EShellContent *shell_content) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ETable *table; + GSList *list, *iter; + gboolean editable = TRUE; + gboolean has_url = FALSE; + gint n_selected; + guint32 state = 0; + + memo_shell_content = E_MEMO_SHELL_CONTENT (shell_content); + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + table = e_memo_table_get_table (memo_table); + n_selected = e_table_selected_count (table); + + list = e_memo_table_get_selected (memo_table); + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + icalproperty *prop; + gboolean read_only; + + e_cal_is_read_only (comp_data->client, &read_only, NULL); + editable &= !read_only; + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + has_url |= (prop != NULL); + } + g_slist_free (list); + + if (n_selected == 1) + state |= E_MEMO_SHELL_CONTENT_SELECTION_SINGLE; + if (n_selected > 1) + state |= E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE; + if (editable) + state |= E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT; + if (has_url) + state |= E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL; + + return state; +} + +static void +memo_shell_content_class_init (EMemoShellContentClass *class) +{ + GObjectClass *object_class; + EShellContentClass *shell_content_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMemoShellContentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = memo_shell_content_set_property; + object_class->get_property = memo_shell_content_get_property; + object_class->dispose = memo_shell_content_dispose; + object_class->finalize = memo_shell_content_finalize; + object_class->constructed = memo_shell_content_constructed; + + shell_content_class = E_SHELL_CONTENT_CLASS (class); + shell_content_class->check_state = memo_shell_content_check_state; + + g_object_class_install_property ( + object_class, + PROP_MODEL, + g_param_spec_object ( + "model", + _("Model"), + _("The memo table model"), + E_TYPE_CAL_MODEL, + G_PARAM_READABLE)); + + g_object_class_install_property ( + object_class, + PROP_PREVIEW_VISIBLE, + g_param_spec_boolean ( + "preview-visible", + _("Preview is Visible"), + _("Whether the preview pane is visible"), + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_override_property ( + object_class, PROP_ORIENTATION, "orientation"); +} + +static void +memo_shell_content_init (EMemoShellContent *memo_shell_content) +{ + memo_shell_content->priv = + E_MEMO_SHELL_CONTENT_GET_PRIVATE (memo_shell_content); + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_memo_shell_content_get_type (void) +{ + return memo_shell_content_type; +} + +void +e_memo_shell_content_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (EMemoShellContentClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) memo_shell_content_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMemoShellContent), + 0, /* n_preallocs */ + (GInstanceInitFunc) memo_shell_content_init, + NULL /* value_table */ + }; + + static const GInterfaceInfo orientable_info = { + (GInterfaceInitFunc) NULL, + (GInterfaceFinalizeFunc) NULL, + NULL /* interface_data */ + }; + + memo_shell_content_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_CONTENT, + "EMemoShellContent", &type_info, 0); + + g_type_module_add_interface ( + type_module, memo_shell_content_type, + GTK_TYPE_ORIENTABLE, &orientable_info); +} + +GtkWidget * +e_memo_shell_content_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_MEMO_SHELL_CONTENT, + "shell-view", shell_view, NULL); +} + +ECalModel * +e_memo_shell_content_get_memo_model (EMemoShellContent *memo_shell_content) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL); + + return memo_shell_content->priv->memo_model; +} + +ECalComponentPreview * +e_memo_shell_content_get_memo_preview (EMemoShellContent *memo_shell_content) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL); + + return E_CAL_COMPONENT_PREVIEW ( + memo_shell_content->priv->memo_preview); +} + +EMemoTable * +e_memo_shell_content_get_memo_table (EMemoShellContent *memo_shell_content) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL); + + return E_MEMO_TABLE (memo_shell_content->priv->memo_table); +} + +gboolean +e_memo_shell_content_get_preview_visible (EMemoShellContent *memo_shell_content) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_CONTENT (memo_shell_content), FALSE); + + return memo_shell_content->priv->preview_visible; +} + +void +e_memo_shell_content_set_preview_visible (EMemoShellContent *memo_shell_content, + gboolean preview_visible) +{ + g_return_if_fail (E_IS_MEMO_SHELL_CONTENT (memo_shell_content)); + + memo_shell_content->priv->preview_visible = preview_visible; + + g_object_notify (G_OBJECT (memo_shell_content), "preview-visible"); +} + +GalViewInstance * +e_memo_shell_content_get_view_instance (EMemoShellContent *memo_shell_content) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_CONTENT (memo_shell_content), NULL); + + return memo_shell_content->priv->view_instance; +} diff --git a/modules/calendar/e-memo-shell-content.h b/modules/calendar/e-memo-shell-content.h new file mode 100644 index 0000000000..ae2710e148 --- /dev/null +++ b/modules/calendar/e-memo-shell-content.h @@ -0,0 +1,96 @@ +/* + * e-memo-shell-content.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_CONTENT_H +#define E_MEMO_SHELL_CONTENT_H + +#include <shell/e-shell-content.h> +#include <shell/e-shell-view.h> + +#include <calendar/gui/e-memo-table.h> +#include <calendar/gui/e-cal-component-preview.h> + +#include <menus/gal-view-instance.h> + +/* Standard GObject macros */ +#define E_TYPE_MEMO_SHELL_CONTENT \ + (e_memo_shell_content_get_type ()) +#define E_MEMO_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContent)) +#define E_MEMO_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContentClass)) +#define E_IS_MEMO_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MEMO_SHELL_CONTENT)) +#define E_IS_MEMO_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MEMO_SHELL_CONTENT)) +#define E_MEMO_SHELL_CONTENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MEMO_SHELL_CONTENT, EMemoShellContentClass)) + +G_BEGIN_DECLS + +typedef struct _EMemoShellContent EMemoShellContent; +typedef struct _EMemoShellContentClass EMemoShellContentClass; +typedef struct _EMemoShellContentPrivate EMemoShellContentPrivate; + +enum { + E_MEMO_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0, + E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1, + E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT = 1 << 2, + E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL = 1 << 3 +}; + +struct _EMemoShellContent { + EShellContent parent; + EMemoShellContentPrivate *priv; +}; + +struct _EMemoShellContentClass { + EShellContentClass parent_class; +}; + +GType e_memo_shell_content_get_type (void); +void e_memo_shell_content_register_type + (GTypeModule *type_module); +GtkWidget * e_memo_shell_content_new(EShellView *shell_view); +ECalModel * e_memo_shell_content_get_memo_model + (EMemoShellContent *memo_shell_conent); +ECalComponentPreview * + e_memo_shell_content_get_memo_preview + (EMemoShellContent *memo_shell_content); +EMemoTable * e_memo_shell_content_get_memo_table + (EMemoShellContent *memo_shell_content); +gboolean e_memo_shell_content_get_preview_visible + (EMemoShellContent *memo_shell_content); +void e_memo_shell_content_set_preview_visible + (EMemoShellContent *memo_shell_content, + gboolean preview_visible); +GalViewInstance * + e_memo_shell_content_get_view_instance + (EMemoShellContent *memo_shell_content); + +G_END_DECLS + +#endif /* E_MEMO_SHELL_CONTENT_H */ diff --git a/modules/calendar/e-memo-shell-migrate.c b/modules/calendar/e-memo-shell-migrate.c new file mode 100644 index 0000000000..3bcf3156a5 --- /dev/null +++ b/modules/calendar/e-memo-shell-migrate.c @@ -0,0 +1,268 @@ +/* + * e-memo-shell-migrate.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-migrate.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <camel/camel-url.h> +#include <libedataserver/e-account.h> +#include <libedataserver/e-account-list.h> +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-group.h> +#include <libedataserver/e-source-list.h> + +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/calendar-config-keys.h" +#include "shell/e-shell.h" + +#define WEBCAL_BASE_URI "webcal://" +#define PERSONAL_RELATIVE_URI "system" +#define GROUPWISE_BASE_URI "groupwise://" + +static void +create_memo_sources (EShellBackend *shell_backend, + ESourceList *source_list, + ESourceGroup **on_this_computer, + ESourceGroup **on_the_web, + ESource **personal_source) +{ + EShell *shell; + EShellSettings *shell_settings; + GSList *groups; + ESourceGroup *group; + gchar *base_uri, *base_uri_proto; + const gchar *base_dir; + + *on_this_computer = NULL; + *on_the_web = NULL; + *personal_source = NULL; + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + base_dir = e_shell_backend_get_config_dir (shell_backend); + base_uri = g_build_filename (base_dir, "local", NULL); + + base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL); + + groups = e_source_list_peek_groups (source_list); + if (groups) { + /* groups are already there, we need to search for things... */ + GSList *g; + + for (g = groups; g; g = g->next) { + + group = E_SOURCE_GROUP (g->data); + + if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group))) + *on_this_computer = g_object_ref (group); + else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group))) + *on_the_web = g_object_ref (group); + } + } + + if (*on_this_computer) { + /* make sure "Personal" shows up as a source under + this group */ + GSList *sources = e_source_group_peek_sources (*on_this_computer); + GSList *s; + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + const gchar *relative_uri; + + relative_uri = e_source_peek_relative_uri (source); + if (relative_uri == NULL) + continue; + if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) { + *personal_source = g_object_ref (source); + break; + } + } + } else { + /* create the local source group */ + group = e_source_group_new (_("On This Computer"), base_uri_proto); + e_source_list_add_group (source_list, group, -1); + + *on_this_computer = group; + } + + if (!*personal_source) { + gchar *primary_memo_list; + + /* Create the default Person memo list */ + ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI); + e_source_group_add_source (*on_this_computer, source, -1); + + primary_memo_list = e_shell_settings_get_string ( + shell_settings, "cal-primary-memo-list"); + + if (!primary_memo_list && !calendar_config_get_memos_selected ()) { + GSList selected; + + e_shell_settings_set_string ( + shell_settings, "cal-primary-memo-list", + e_source_peek_uid (source)); + + selected.data = (gpointer)e_source_peek_uid (source); + selected.next = NULL; + calendar_config_set_memos_selected (&selected); + } + + e_source_set_color_spec (source, "#BECEDD"); + *personal_source = source; + } + + if (!*on_the_web) { + /* Create the Webcal source group */ + group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI); + e_source_list_add_group (source_list, group, -1); + + *on_the_web = group; + } + + g_free (base_uri_proto); + g_free (base_uri); +} + +static gboolean +is_groupwise_account (EAccount *account) +{ + if (account->source->url != NULL) { + return g_str_has_prefix (account->source->url, GROUPWISE_BASE_URI); + } else { + return FALSE; + } +} + +static void +add_gw_esource (ESourceList *source_list, const gchar *group_name, const gchar *source_name, CamelURL *url, GConfClient *client) +{ + ESourceGroup *group; + ESource *source; + GSList *ids, *temp; + GError *error = NULL; + gchar *relative_uri; + const gchar *soap_port; + const gchar * use_ssl; + const gchar *poa_address; + const gchar *offline_sync; + + poa_address = url->host; + if (!poa_address || strlen (poa_address) ==0) + return; + soap_port = camel_url_get_param (url, "soap_port"); + + if (!soap_port || strlen (soap_port) == 0) + soap_port = "7191"; + + use_ssl = camel_url_get_param (url, "use_ssl"); + offline_sync = camel_url_get_param (url, "offline_sync"); + + group = e_source_group_new (group_name, GROUPWISE_BASE_URI); + if (!e_source_list_add_group (source_list, group, -1)) + return; + relative_uri = g_strdup_printf ("%s@%s/", url->user, poa_address); + + source = e_source_new (source_name, relative_uri); + e_source_set_property (source, "auth", "1"); + e_source_set_property (source, "username", url->user); + e_source_set_property (source, "port", camel_url_get_param (url, "soap_port")); + e_source_set_property (source, "auth-domain", "Groupwise"); + e_source_set_property (source, "use_ssl", use_ssl); + e_source_set_property (source, "offline_sync", offline_sync ? "1" : "0" ); + + e_source_set_color_spec (source, "#EEBC60"); + e_source_group_add_source (group, source, -1); + + ids = gconf_client_get_list (client, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, GCONF_VALUE_STRING, &error); + if ( error != NULL ) { + g_warning("%s (%s) %s\n", G_STRLOC, G_STRFUNC, error->message); + g_error_free(error); + } + ids = g_slist_append (ids, g_strdup (e_source_peek_uid (source))); + gconf_client_set_list (client, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, GCONF_VALUE_STRING, ids, NULL); + temp = ids; + for (; temp != NULL; temp = g_slist_next (temp)) + g_free (temp->data); + + g_slist_free (ids); + g_object_unref (source); + g_object_unref (group); + g_free (relative_uri); +} + +gboolean +e_memo_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint revision, + GError **error) +{ + ESourceGroup *on_this_computer = NULL; + ESourceGroup *on_the_web = NULL; + ESource *personal_source = NULL; + ESourceList *source_list = NULL; + gboolean retval = FALSE; + + g_object_get (shell_backend, "source-list", &source_list, NULL); + + /* we call this unconditionally now - create_groups either + creates the groups/sources or it finds the necessary + groups/sources. */ + create_memo_sources ( + shell_backend, source_list, &on_this_computer, + &on_the_web, &personal_source); + + /* Migration for Gw accounts between versions < 2.8 */ + if (major == 2 && minor < 8) { + EAccountList *al; + EAccount *a; + CamelURL *url; + EIterator *it; + GConfClient *gconf_client = gconf_client_get_default (); + al = e_account_list_new (gconf_client); + for (it = e_list_get_iterator((EList *)al); + e_iterator_is_valid(it); + e_iterator_next(it)) { + a = (EAccount *) e_iterator_get(it); + if (!a->enabled || !is_groupwise_account (a)) + continue; + url = camel_url_new (a->source->url, NULL); + add_gw_esource (source_list, a->name, _("Notes"), url, gconf_client); + camel_url_free (url); + } + g_object_unref (al); + g_object_unref (gconf_client); + } + + e_source_list_sync (source_list, NULL); + retval = TRUE; + + if (on_this_computer) + g_object_unref (on_this_computer); + if (on_the_web) + g_object_unref (on_the_web); + if (personal_source) + g_object_unref (personal_source); + + return retval; +} diff --git a/modules/calendar/e-memo-shell-migrate.h b/modules/calendar/e-memo-shell-migrate.h new file mode 100644 index 0000000000..ba163c6950 --- /dev/null +++ b/modules/calendar/e-memo-shell-migrate.h @@ -0,0 +1,38 @@ +/* + * e-memo-shell-backend-migrate.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_BACKEND_MIGRATE_H +#define E_MEMO_SHELL_BACKEND_MIGRATE_H + +#include <glib.h> +#include <shell/e-shell-backend.h> + +G_BEGIN_DECLS + +gboolean e_memo_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error); + +G_END_DECLS + +#endif /* E_MEMO_SHELL_BACKEND_MIGRATE_H */ diff --git a/modules/calendar/e-memo-shell-sidebar.c b/modules/calendar/e-memo-shell-sidebar.c new file mode 100644 index 0000000000..33a2226c00 --- /dev/null +++ b/modules/calendar/e-memo-shell-sidebar.c @@ -0,0 +1,716 @@ +/* + * e-memo-shell-sidebar.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-sidebar.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal.h> + +#include "e-util/e-binding.h" +#include "e-util/e-error.h" +#include "e-util/e-util.h" +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/e-memo-list-selector.h" +#include "calendar/gui/misc.h" + +#include "e-memo-shell-view.h" +#include "e-memo-shell-backend.h" + +#define E_MEMO_SHELL_SIDEBAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebarPrivate)) + +struct _EMemoShellSidebarPrivate { + GtkWidget *selector; + icaltimezone *timezone; + + /* UID -> Client */ + GHashTable *client_table; +}; + +enum { + PROP_0, + PROP_SELECTOR +}; + +enum { + CLIENT_ADDED, + CLIENT_REMOVED, + STATUS_MESSAGE, + LAST_SIGNAL +}; + +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; +static GType memo_shell_sidebar_type; + +static void +memo_shell_sidebar_emit_client_added (EMemoShellSidebar *memo_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_ADDED]; + + g_signal_emit (memo_shell_sidebar, signal_id, 0, client); +} + +static void +memo_shell_sidebar_emit_client_removed (EMemoShellSidebar *memo_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_REMOVED]; + + g_signal_emit (memo_shell_sidebar, signal_id, 0, client); +} + +static void +memo_shell_sidebar_emit_status_message (EMemoShellSidebar *memo_shell_sidebar, + const gchar *status_message) +{ + guint signal_id = signals[STATUS_MESSAGE]; + + g_signal_emit (memo_shell_sidebar, signal_id, 0, status_message, -1.0); +} + +static void +memo_shell_sidebar_backend_died_cb (EMemoShellSidebar *memo_shell_sidebar, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = memo_shell_sidebar->priv->client_table; + + shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + source = e_cal_get_source (client); + uid = e_source_peek_uid (source); + + g_object_ref (source); + + g_hash_table_remove (client_table, uid); + memo_shell_sidebar_emit_status_message (memo_shell_sidebar, NULL); + + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:memos-crashed", NULL); + + g_object_unref (source); +} + +static void +memo_shell_sidebar_backend_error_cb (EMemoShellSidebar *memo_shell_sidebar, + const gchar *message, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GtkWidget *dialog; + const gchar *uri; + gchar *uri_no_passwd; + + shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + uri = e_cal_get_uri (client); + uri_no_passwd = get_uri_without_password (uri); + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Error on %s\n%s"), + uri_no_passwd, message); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_free (uri_no_passwd); +} + +static void +memo_shell_sidebar_client_opened_cb (EMemoShellSidebar *memo_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + ESource *source; + + source = e_cal_get_source (client); + + shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED || + status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) + auth_cal_forget_password (client); + + switch (status) { + case E_CALENDAR_STATUS_OK: + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + memo_shell_sidebar_client_opened_cb, NULL); + + memo_shell_sidebar_emit_status_message ( + memo_shell_sidebar, _("Loading memos")); + memo_shell_sidebar_emit_client_added ( + memo_shell_sidebar, client); + memo_shell_sidebar_emit_status_message ( + memo_shell_sidebar, NULL); + break; + + case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: + e_cal_open_async (client, FALSE); + break; + + case E_CALENDAR_STATUS_BUSY: + break; + + case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-no-contents-offline-memos", + NULL); + break; + + default: + memo_shell_sidebar_emit_client_removed ( + memo_shell_sidebar, client); + break; + } +} + +static void +memo_shell_sidebar_row_changed_cb (EMemoShellSidebar *memo_shell_sidebar, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + GtkTreeModel *tree_model) +{ + ESourceSelector *selector; + ESource *source; + + /* XXX ESourceSelector's underlying tree store has only one + * column: ESource objects. While we're not supposed to + * know this, listening for "row-changed" signals from + * the model is easier to deal with than the selector's + * "selection-changed" signal, which doesn't tell you + * _which_ row changed. */ + + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1); + + /* XXX This signal gets emitted a lot while the model is being + * rebuilt, during which time we won't get a valid ESource. + * ESourceSelector should probably block this signal while + * rebuilding the model, but we'll be forgiving and not + * emit a warning. */ + if (!E_IS_SOURCE (source)) + return; + + if (e_source_selector_source_is_selected (selector, source)) + e_memo_shell_sidebar_add_source (memo_shell_sidebar, source); + else + e_memo_shell_sidebar_remove_source (memo_shell_sidebar, source); +} + +static void +memo_shell_sidebar_selection_changed_cb (EMemoShellSidebar *memo_shell_sidebar, + ESourceSelector *selector) +{ + GSList *list, *iter; + + /* This signal is emitted less frequently than "row-changed", + * especially when the model is being rebuilt. So we'll take + * it easy on poor GConf. */ + + list = e_source_selector_get_selection (selector); + + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + iter->data = (gpointer) e_source_peek_uid (source); + g_object_unref (source); + } + + calendar_config_set_memos_selected (list); + + g_slist_free (list); +} + +static void +memo_shell_sidebar_primary_selection_changed_cb (EMemoShellSidebar *memo_shell_sidebar, + ESourceSelector *selector) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESource *source; + + /* XXX ESourceSelector needs a "primary-selection-uid" property + * so we can just bind the property with GConfBridge. */ + + source = e_source_selector_peek_primary_selection (selector); + if (source == NULL) + return; + + shell_sidebar = E_SHELL_SIDEBAR (memo_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + e_shell_settings_set_string ( + shell_settings, "cal-primary-memo-list", + e_source_peek_uid (source)); +} + +static void +memo_shell_sidebar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SELECTOR: + g_value_set_object ( + value, e_memo_shell_sidebar_get_selector ( + E_MEMO_SHELL_SIDEBAR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +memo_shell_sidebar_dispose (GObject *object) +{ + EMemoShellSidebarPrivate *priv; + + priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object); + + if (priv->selector != NULL) { + g_object_unref (priv->selector); + priv->selector = NULL; + } + + g_hash_table_remove_all (priv->client_table); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +memo_shell_sidebar_finalize (GObject *object) +{ + EMemoShellSidebarPrivate *priv; + + priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object); + + g_hash_table_destroy (priv->client_table); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +memo_shell_sidebar_constructed (GObject *object) +{ + EMemoShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + GtkContainer *container; + GtkTreeModel *model; + GtkWidget *widget; + AtkObject *a11y; + GSList *list, *iter; + gchar *uid; + + priv = E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + shell_sidebar = E_SHELL_SIDEBAR (object); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + source_list = e_memo_shell_backend_get_source_list ( + E_MEMO_SHELL_BACKEND (shell_backend)); + + container = GTK_CONTAINER (shell_sidebar); + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_container_add (container, widget); + gtk_widget_show (widget); + + container = GTK_CONTAINER (widget); + + widget = e_memo_list_selector_new (source_list); + e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE); + gtk_container_add (container, widget); + a11y = gtk_widget_get_accessible (widget); + atk_object_set_name (a11y, _("Memo List Selector")); + priv->selector = g_object_ref (widget); + gtk_widget_show (widget); + + /* Restore the selector state from the last session. */ + + selector = E_SOURCE_SELECTOR (priv->selector); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + g_signal_connect_swapped ( + model, "row-changed", + G_CALLBACK (memo_shell_sidebar_row_changed_cb), + object); + + source = NULL; + uid = e_shell_settings_get_string ( + shell_settings, "cal-primary-memo-list"); + if (uid != NULL) + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source == NULL) + source = e_source_list_peek_source_any (source_list); + if (source != NULL) + e_source_selector_set_primary_selection (selector, source); + g_free (uid); + + list = calendar_config_get_memos_selected (); + for (iter = list; iter != NULL; iter = iter->next) { + uid = iter->data; + source = e_source_list_peek_source_by_uid (source_list, uid); + g_free (uid); + + if (source == NULL) + continue; + + e_source_selector_select_source (selector, source); + } + g_slist_free (list); + + /* Listen for subsequent changes to the selector. */ + + g_signal_connect_swapped ( + widget, "selection-changed", + G_CALLBACK (memo_shell_sidebar_selection_changed_cb), + object); + + g_signal_connect_swapped ( + widget, "primary-selection-changed", + G_CALLBACK (memo_shell_sidebar_primary_selection_changed_cb), + object); +} + +static guint32 +memo_shell_sidebar_check_state (EShellSidebar *shell_sidebar) +{ + EMemoShellSidebar *memo_shell_sidebar; + ESourceSelector *selector; + ESource *source; + gboolean is_system = FALSE; + guint32 state = 0; + + memo_shell_sidebar = E_MEMO_SHELL_SIDEBAR (shell_sidebar); + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + + if (source != NULL) { + const gchar *uri; + + uri = e_source_peek_relative_uri (source); + is_system = (uri == NULL || strcmp (uri, "system") == 0); + } + + if (source != NULL) + state |= E_MEMO_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE; + if (is_system) + state |= E_MEMO_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM; + + return state; +} + +static void +memo_shell_sidebar_client_removed (EMemoShellSidebar *memo_shell_sidebar, + ECal *client) +{ + ESourceSelector *selector; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = memo_shell_sidebar->priv->client_table; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, memo_shell_sidebar); + + source = e_cal_get_source (client); + e_source_selector_unselect_source (selector, source); + + uid = e_source_peek_uid (source); + g_hash_table_remove (client_table, uid); + + memo_shell_sidebar_emit_status_message (memo_shell_sidebar, NULL); +} + +static void +memo_shell_sidebar_class_init (EMemoShellSidebarClass *class) +{ + GObjectClass *object_class; + EShellSidebarClass *shell_sidebar_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMemoShellSidebarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = memo_shell_sidebar_get_property; + object_class->dispose = memo_shell_sidebar_dispose; + object_class->finalize = memo_shell_sidebar_finalize; + object_class->constructed = memo_shell_sidebar_constructed; + + shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class); + shell_sidebar_class->check_state = memo_shell_sidebar_check_state; + + class->client_removed = memo_shell_sidebar_client_removed; + + g_object_class_install_property ( + object_class, + PROP_SELECTOR, + g_param_spec_object ( + "selector", + _("Source Selector Widget"), + _("This widget displays groups of memo lists"), + E_TYPE_SOURCE_SELECTOR, + G_PARAM_READABLE)); + + signals[CLIENT_ADDED] = g_signal_new ( + "client-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EMemoShellSidebarClass, client_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[CLIENT_REMOVED] = g_signal_new ( + "client-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EMemoShellSidebarClass, client_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[STATUS_MESSAGE] = g_signal_new ( + "status-message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EMemoShellSidebarClass, status_message), + NULL, NULL, + e_marshal_VOID__STRING_DOUBLE, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_DOUBLE); +} + +static void +memo_shell_sidebar_init (EMemoShellSidebar *memo_shell_sidebar) +{ + GHashTable *client_table; + + client_table = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + memo_shell_sidebar->priv = + E_MEMO_SHELL_SIDEBAR_GET_PRIVATE (memo_shell_sidebar); + + memo_shell_sidebar->priv->client_table = client_table; + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_memo_shell_sidebar_get_type (void) +{ + return memo_shell_sidebar_type; +} + +void +e_memo_shell_sidebar_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (EMemoShellSidebarClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) memo_shell_sidebar_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMemoShellSidebar), + 0, /* n_preallocs */ + (GInstanceInitFunc) memo_shell_sidebar_init, + NULL /* value_table */ + }; + + memo_shell_sidebar_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_SIDEBAR, + "EMemoShellSidebar", &type_info, 0); +} + +GtkWidget * +e_memo_shell_sidebar_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_MEMO_SHELL_SIDEBAR, + "shell-view", shell_view, NULL); +} + +GList * +e_memo_shell_sidebar_get_clients (EMemoShellSidebar *memo_shell_sidebar) +{ + GHashTable *client_table; + + g_return_val_if_fail ( + E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar), NULL); + + client_table = memo_shell_sidebar->priv->client_table; + + return g_hash_table_get_values (client_table); +} + +ESourceSelector * +e_memo_shell_sidebar_get_selector (EMemoShellSidebar *memo_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar), NULL); + + return E_SOURCE_SELECTOR (memo_shell_sidebar->priv->selector); +} + +void +e_memo_shell_sidebar_add_source (EMemoShellSidebar *memo_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + const gchar *uri; + gchar *message; + + g_return_if_fail (E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = memo_shell_sidebar->priv->client_table; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client != NULL) + return; + + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "backend-died", + G_CALLBACK (memo_shell_sidebar_backend_died_cb), + memo_shell_sidebar); + + g_signal_connect_swapped ( + client, "backend-error", + G_CALLBACK (memo_shell_sidebar_backend_error_cb), + memo_shell_sidebar); + + g_hash_table_insert (client_table, g_strdup (uid), client); + e_source_selector_select_source (selector, source); + + uri = e_cal_get_uri (client); + message = g_strdup_printf (_("Opening memos at %s"), uri); + memo_shell_sidebar_emit_status_message (memo_shell_sidebar, message); + g_free (message); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (memo_shell_sidebar_client_opened_cb), + memo_shell_sidebar); + + e_cal_open_async (client, FALSE); +} + +void +e_memo_shell_sidebar_remove_source (EMemoShellSidebar *memo_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + g_return_if_fail (E_IS_MEMO_SHELL_SIDEBAR (memo_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = memo_shell_sidebar->priv->client_table; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client == NULL) + return; + + memo_shell_sidebar_emit_client_removed (memo_shell_sidebar, client); +} diff --git a/modules/calendar/e-memo-shell-sidebar.h b/modules/calendar/e-memo-shell-sidebar.h new file mode 100644 index 0000000000..068d7436bc --- /dev/null +++ b/modules/calendar/e-memo-shell-sidebar.h @@ -0,0 +1,97 @@ +/* + * e-memo-shell-sidebar.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_SIDEBAR_H +#define E_MEMO_SHELL_SIDEBAR_H + +#include <libecal/e-cal.h> +#include <libedataserverui/e-source-selector.h> + +#include <shell/e-shell-sidebar.h> +#include <shell/e-shell-view.h> + +/* Standard GObject macros */ +#define E_TYPE_MEMO_SHELL_SIDEBAR \ + (e_memo_shell_sidebar_get_type ()) +#define E_MEMO_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebar)) +#define E_MEMO_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebarClass)) +#define E_IS_MEMO_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MEMO_SHELL_SIDEBAR)) +#define E_IS_MEMO_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MEMO_SHELL_SIDEBAR)) +#define E_MEMO_SHELL_SIDEBAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MEMO_SHELL_SIDEBAR, EMemoShellSidebarClass)) + +G_BEGIN_DECLS + +typedef struct _EMemoShellSidebar EMemoShellSidebar; +typedef struct _EMemoShellSidebarClass EMemoShellSidebarClass; +typedef struct _EMemoShellSidebarPrivate EMemoShellSidebarPrivate; + +enum { + E_MEMO_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0, + E_MEMO_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM = 1 << 1 +}; + +struct _EMemoShellSidebar { + EShellSidebar parent; + EMemoShellSidebarPrivate *priv; +}; + +struct _EMemoShellSidebarClass { + EShellSidebarClass parent_class; + + /* Signals */ + void (*client_added) (EMemoShellSidebar *memo_shell_sidebar, + ECal *client); + void (*client_removed) (EMemoShellSidebar *memo_shell_sidebar, + ECal *client); + void (*status_message) (EMemoShellSidebar *memo_shell_sidebar, + const gchar *status_message, + gdouble percent); +}; + +GType e_memo_shell_sidebar_get_type (void); +void e_memo_shell_sidebar_register_type + (GTypeModule *type_module); +GtkWidget * e_memo_shell_sidebar_new(EShellView *shell_view); +GList * e_memo_shell_sidebar_get_clients + (EMemoShellSidebar *memo_shell_sidebar); +ESourceSelector * + e_memo_shell_sidebar_get_selector + (EMemoShellSidebar *memo_shell_sidebar); +void e_memo_shell_sidebar_add_source + (EMemoShellSidebar *memo_shell_sidebar, + ESource *source); +void e_memo_shell_sidebar_remove_source + (EMemoShellSidebar *memo_shell_sidebar, + ESource *source); + +G_END_DECLS + +#endif /* E_MEMO_SHELL_SIDEBAR_H */ diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c new file mode 100644 index 0000000000..0161eeb198 --- /dev/null +++ b/modules/calendar/e-memo-shell-view-actions.c @@ -0,0 +1,1019 @@ +/* + * e-memo-shell-view-actions.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-view-private.h" + +static void +action_gal_save_custom_view_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EShellView *shell_view; + GalViewInstance *view_instance; + + /* All shell views respond to the activation of this action, + * which is defined by EShellWindow. But only the currently + * active shell view proceeds with saving the custom view. */ + shell_view = E_SHELL_VIEW (memo_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + view_instance = e_memo_shell_content_get_view_instance (memo_shell_content); + gal_view_instance_save_as (view_instance); +} + +static void +action_memo_clipboard_copy_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + e_memo_table_copy_clipboard (memo_table); +} + +static void +action_memo_clipboard_cut_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + e_memo_table_cut_clipboard (memo_table); +} + +static void +action_memo_clipboard_paste_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + e_memo_table_paste_clipboard (memo_table); +} + +static void +action_memo_delete_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + ECalComponentPreview *memo_preview; + EMemoTable *memo_table; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content); + + e_memo_shell_view_set_status_message ( + memo_shell_view, _("Deleting selected memos..."), -1.0); + e_memo_table_delete_selected (memo_table); + e_memo_shell_view_set_status_message (memo_shell_view, NULL, -1.0); + + e_cal_component_preview_clear (memo_preview); +} + +static void +action_memo_forward_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GSList *list; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only forward the first selected memo. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + itip_send_comp ( + E_CAL_COMPONENT_METHOD_PUBLISH, comp, + comp_data->client, NULL, NULL, NULL, TRUE, FALSE); + g_object_unref (comp); +} + +static void +action_memo_list_copy_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellSidebar *memo_shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + ESourceSelector *selector; + ESource *source; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + copy_source_dialog ( + GTK_WINDOW (shell_window), + source, E_CAL_SOURCE_TYPE_JOURNAL); +} + +static void +action_memo_list_delete_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellBackend *memo_shell_backend; + EMemoShellContent *memo_shell_content; + EMemoShellSidebar *memo_shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + EMemoTable *memo_table; + ECal *client; + ECalModel *model; + ESourceSelector *selector; + ESourceGroup *source_group; + ESourceList *source_list; + ESource *source; + gint response; + gchar *uri; + GError *error = NULL; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + memo_shell_backend = memo_shell_view->priv->memo_shell_backend; + source_list = e_memo_shell_backend_get_source_list (memo_shell_backend); + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + model = e_memo_table_get_model (memo_table); + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + /* Ask for confirmation. */ + response = e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-delete-memo-list", + e_source_peek_name (source)); + if (response != GTK_RESPONSE_YES) + return; + + uri = e_source_get_uri (source); + client = e_cal_model_get_client_for_uri (model, uri); + if (client == NULL) + client = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_JOURNAL); + g_free (uri); + + g_return_if_fail (client != NULL); + + if (!e_cal_remove (client, &error)) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + if (e_source_selector_source_is_selected (selector, source)) { + e_memo_shell_sidebar_remove_source ( + memo_shell_sidebar, source); + e_source_selector_unselect_source (selector, source); + } + + source_group = e_source_peek_group (source); + e_source_group_remove_source (source_group, source); + + if (!e_source_list_sync (source_list, &error)) { + g_warning ("%s", error->message); + g_error_free (error); + } +} + +static void +action_memo_list_new_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + calendar_setup_new_memo_list (GTK_WINDOW (shell_window)); +} + +static void +action_memo_list_print_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ETable *table; + GtkPrintOperationAction print_action; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + table = e_memo_table_get_table (memo_table); + + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + print_table (table, _("Print Memos"), _("Memos"), print_action); +} + +static void +action_memo_list_print_preview_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ETable *table; + GtkPrintOperationAction print_action; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + table = e_memo_table_get_table (memo_table); + + print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW; + print_table (table, _("Print Memos"), _("Memos"), print_action); +} + +static void +action_memo_list_properties_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellSidebar *memo_shell_sidebar; + EShellView *shell_view; + EShellWindow *shell_window; + ESource *source; + ESourceSelector *selector; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + calendar_setup_edit_memo_list (GTK_WINDOW (shell_window), source); +} + +static void +action_memo_list_rename_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellSidebar *memo_shell_sidebar; + ESourceSelector *selector; + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + + e_source_selector_edit_primary_selection (selector); +} + +static void +action_memo_list_select_one_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellSidebar *memo_shell_sidebar; + ESourceSelector *selector; + ESource *primary; + GSList *list, *iter; + + /* XXX ESourceSelector should provide a function for this. */ + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + primary = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (primary != NULL); + + list = e_source_selector_get_selection (selector); + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + if (source == primary) + continue; + + e_source_selector_unselect_source (selector, source); + } + e_source_selector_free_selection (list); + + e_source_selector_select_source (selector, primary); +} + +static void +action_memo_new_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + ECal *client; + ECalComponent *comp; + CompEditor *editor; + GSList *list; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + client = comp_data->client; + editor = memo_editor_new (client, shell, COMP_EDITOR_NEW_ITEM); + comp = cal_comp_memo_new_with_defaults (client); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (client); +} + +static void +action_memo_open_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + GSList *list; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the first selected memo. */ + e_memo_shell_view_open_memo (memo_shell_view, comp_data); +} + +static void +action_memo_open_url_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + icalproperty *prop; + const gchar *uri; + GSList *list; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the URI of the first selected memo. */ + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + g_return_if_fail (prop == NULL); + + uri = icalproperty_get_url (prop); + e_show_uri (GTK_WINDOW (shell_window), uri); +} + +static void +action_memo_preview_cb (GtkToggleAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + gboolean visible; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + visible = gtk_toggle_action_get_active (action); + e_memo_shell_content_set_preview_visible (memo_shell_content, visible); +} + +static void +action_memo_print_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GtkPrintOperationAction print_action; + GSList *list; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only print the first selected memo. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + e_cal_component_set_icalcomponent (comp, clone); + print_comp (comp, comp_data->client, print_action); + g_object_unref (comp); +} + +static void +action_memo_save_as_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModelComponent *comp_data; + GSList *list; + gchar *filename; + gchar *string; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + list = e_memo_table_get_selected (memo_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + filename = e_file_dialog_save (_("Save as..."), NULL); + if (filename == NULL) + return; + + /* XXX We only save the first selected memo. */ + string = e_cal_get_component_as_string ( + comp_data->client, comp_data->icalcomp); + if (string == NULL) { + g_warning ("Could not convert memo to a string"); + return; + } + + e_write_file_uri (filename, string); + + g_free (filename); + g_free (string); +} + +static void +action_memo_search_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMemoShellView *memo_shell_view) +{ + EShellView *shell_view; + EShellContent *shell_content; + const gchar *search_hint; + + /* XXX Figure out a way to handle this in EShellContent + * instead of every shell view having to handle it. + * The problem is EShellContent does not know what + * the search option actions are for this view. It + * would have to dig up the popup menu and retrieve + * the action for each menu item. Seems messy. */ + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + search_hint = gtk_action_get_label (GTK_ACTION (current)); + e_shell_content_set_search_hint (shell_content, search_hint); +} + +static void +action_memo_view_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + GtkOrientable *orientable; + GtkOrientation orientation; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + orientable = GTK_ORIENTABLE (memo_shell_content); + + switch (gtk_radio_action_get_current_value (action)) { + case 0: + orientation = GTK_ORIENTATION_VERTICAL; + break; + case 1: + orientation = GTK_ORIENTATION_HORIZONTAL; + break; + default: + g_return_if_reached (); + } + + gtk_orientable_set_orientation (orientable, orientation); +} + +static void +action_search_execute_cb (GtkAction *action, + EMemoShellView *memo_shell_view) +{ + EShellView *shell_view; + + /* All shell views respond to the activation of this action, + * which is defined by EShellWindow. But only the currently + * active shell view proceeds with executing the search. */ + shell_view = E_SHELL_VIEW (memo_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + e_memo_shell_view_execute_search (memo_shell_view); +} + +static void +action_search_filter_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMemoShellView *memo_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); +} + +static GtkActionEntry memo_entries[] = { + + { "memo-clipboard-copy", + GTK_STOCK_COPY, + NULL, + NULL, + N_("Copy selected memo"), + G_CALLBACK (action_memo_clipboard_copy_cb) }, + + { "memo-clipboard-cut", + GTK_STOCK_CUT, + NULL, + NULL, + N_("Cut selected memo"), + G_CALLBACK (action_memo_clipboard_cut_cb) }, + + { "memo-clipboard-paste", + GTK_STOCK_PASTE, + NULL, + NULL, + N_("Paste memo from the clipboard"), + G_CALLBACK (action_memo_clipboard_paste_cb) }, + + { "memo-delete", + GTK_STOCK_DELETE, + N_("_Delete Memo"), + NULL, + N_("Delete selected memos"), + G_CALLBACK (action_memo_delete_cb) }, + + { "memo-forward", + "mail-forward", + N_("_Forward as iCalendar..."), + "<Control>f", + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_forward_cb) }, + + { "memo-list-copy", + GTK_STOCK_COPY, + N_("_Copy..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_list_copy_cb) }, + + { "memo-list-delete", + GTK_STOCK_DELETE, + N_("_Delete"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_list_delete_cb) }, + + { "memo-list-new", + "stock_notes", + N_("_New Memo List"), + NULL, + N_("Create a new memo list"), + G_CALLBACK (action_memo_list_new_cb) }, + + { "memo-list-properties", + GTK_STOCK_PROPERTIES, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_list_properties_cb) }, + + { "memo-list-rename", + NULL, + N_("_Rename..."), + "F2", + N_("Rename the selected memo list"), + G_CALLBACK (action_memo_list_rename_cb) }, + + { "memo-list-select-one", + "stock_check-filled", + N_("Show _Only This Memo List"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_list_select_one_cb) }, + + { "memo-new", + "stock_insert-note", + N_("New _Memo"), + NULL, + N_("Create a new memo"), + G_CALLBACK (action_memo_new_cb) }, + + { "memo-open", + GTK_STOCK_OPEN, + N_("_Open Memo"), + "<Control>o", + N_("View the selected memo"), + G_CALLBACK (action_memo_open_cb) }, + + { "memo-open-url", + "applications-internet", + N_("Open _Web Page"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_open_url_cb) }, + + { "memo-save-as", + GTK_STOCK_SAVE_AS, + N_("_Save as iCalendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_memo_save_as_cb) }, + + /*** Menus ***/ + + { "memo-preview-menu", + NULL, + N_("_Preview"), + NULL, + NULL, + NULL } +}; + +static EPopupActionEntry memo_popup_entries[] = { + + { "memo-list-popup-copy", + NULL, + "memo-list-copy" }, + + { "memo-list-popup-delete", + NULL, + "memo-list-delete" }, + + { "memo-list-popup-properties", + NULL, + "memo-list-properties" }, + + { "memo-list-popup-rename", + NULL, + "memo-list-rename" }, + + { "memo-list-popup-select-one", + NULL, + "memo-list-select-one" }, + + { "memo-popup-clipboard-copy", + NULL, + "memo-clipboard-copy" }, + + { "memo-popup-clipboard-cut", + NULL, + "memo-clipboard-cut" }, + + { "memo-popup-clipboard-paste", + NULL, + "memo-clipboard-paste" }, + + { "memo-popup-delete", + NULL, + "memo-delete" }, + + { "memo-popup-forward", + NULL, + "memo-forward" }, + + { "memo-popup-open", + NULL, + "memo-open" }, + + { "memo-popup-open-url", + NULL, + "memo-open-url" }, + + { "memo-popup-save-as", + NULL, + "memo-save-as" } +}; + +static GtkToggleActionEntry memo_toggle_entries[] = { + + { "memo-preview", + NULL, + N_("Memo _Preview"), + "<Control>m", + N_("Show memo preview pane"), + G_CALLBACK (action_memo_preview_cb), + TRUE } +}; + +static GtkRadioActionEntry memo_view_entries[] = { + + /* This action represents the initial active memo view. + * It should not be visible in the UI, nor should it be + * possible to switch to it from another shell view. */ + { "memo-view-initial", + NULL, + NULL, + NULL, + NULL, + -1 }, + + { "memo-view-classic", + NULL, + N_("_Classic View"), + NULL, + N_("Show memo preview below the memo list"), + 0 }, + + { "memo-view-vertical", + NULL, + N_("_Vertical View"), + NULL, + N_("Show memo preview alongside the memo list"), + 1 } +}; + +static GtkRadioActionEntry memo_filter_entries[] = { + + { "memo-filter-any-category", + NULL, + N_("Any Category"), + NULL, + NULL, + MEMO_FILTER_ANY_CATEGORY }, + + { "memo-filter-unmatched", + NULL, + N_("Unmatched"), + NULL, + NULL, + MEMO_FILTER_UNMATCHED } +}; + +static GtkRadioActionEntry memo_search_entries[] = { + + { "memo-search-any-field-contains", + NULL, + N_("Any field contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MEMO_SEARCH_ANY_FIELD_CONTAINS }, + + { "memo-search-description-contains", + NULL, + N_("Description contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MEMO_SEARCH_DESCRIPTION_CONTAINS }, + + { "memo-search-summary-contains", + NULL, + N_("Summary contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MEMO_SEARCH_SUMMARY_CONTAINS } +}; + +static GtkActionEntry lockdown_printing_entries[] = { + + { "memo-list-print", + GTK_STOCK_PRINT, + NULL, + "<Control>p", + N_("Print the list of memos"), + G_CALLBACK (action_memo_list_print_cb) }, + + { "memo-list-print-preview", + GTK_STOCK_PRINT_PREVIEW, + NULL, + NULL, + N_("Preview the list of memos to be printed"), + G_CALLBACK (action_memo_list_print_preview_cb) }, + + { "memo-print", + GTK_STOCK_PRINT, + NULL, + NULL, + N_("Print the selected memo"), + G_CALLBACK (action_memo_print_cb) } +}; + +static EPopupActionEntry lockdown_printing_popup_entries[] = { + + { "memo-popup-print", + NULL, + "memo-print" } +}; + +void +e_memo_shell_view_actions_init (EMemoShellView *memo_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkActionGroup *action_group; + GConfBridge *bridge; + GtkAction *action; + GObject *object; + const gchar *key; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + /* Memo Actions */ + action_group = ACTION_GROUP (MEMOS); + gtk_action_group_add_actions ( + action_group, memo_entries, + G_N_ELEMENTS (memo_entries), memo_shell_view); + e_action_group_add_popup_actions ( + action_group, memo_popup_entries, + G_N_ELEMENTS (memo_popup_entries)); + gtk_action_group_add_toggle_actions ( + action_group, memo_toggle_entries, + G_N_ELEMENTS (memo_toggle_entries), memo_shell_view); + gtk_action_group_add_radio_actions ( + action_group, memo_view_entries, + G_N_ELEMENTS (memo_view_entries), -1, + G_CALLBACK (action_memo_view_cb), memo_shell_view); + gtk_action_group_add_radio_actions ( + action_group, memo_search_entries, + G_N_ELEMENTS (memo_search_entries), + MEMO_SEARCH_SUMMARY_CONTAINS, + G_CALLBACK (action_memo_search_cb), memo_shell_view); + + /* Lockdown Printing Actions */ + action_group = ACTION_GROUP (LOCKDOWN_PRINTING); + gtk_action_group_add_actions ( + action_group, lockdown_printing_entries, + G_N_ELEMENTS (lockdown_printing_entries), memo_shell_view); + e_action_group_add_popup_actions ( + action_group, lockdown_printing_popup_entries, + G_N_ELEMENTS (lockdown_printing_popup_entries)); + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (ACTION (MEMO_PREVIEW)); + key = "/apps/evolution/calendar/display/show_memo_preview"; + gconf_bridge_bind_property (bridge, key, object, "active"); + + object = G_OBJECT (ACTION (MEMO_VIEW_VERTICAL)); + key = "/apps/evolution/calendar/display/memo_layout"; + gconf_bridge_bind_property (bridge, key, object, "current-value"); + + /* Fine tuning. */ + + action = ACTION (MEMO_DELETE); + g_object_set (action, "short-label", _("Delete"), NULL); + + g_signal_connect ( + ACTION (GAL_SAVE_CUSTOM_VIEW), "activate", + G_CALLBACK (action_gal_save_custom_view_cb), memo_shell_view); + + g_signal_connect ( + ACTION (SEARCH_EXECUTE), "activate", + G_CALLBACK (action_search_execute_cb), memo_shell_view); +} + +void +e_memo_shell_view_update_search_filter (EMemoShellView *memo_shell_view) +{ + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + GtkActionGroup *action_group; + GtkRadioAction *radio_action; + GList *list, *iter; + GSList *group; + gint ii; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + action_group = ACTION_GROUP (MEMOS_FILTER); + e_action_group_remove_all_actions (action_group); + + /* Add the standard filter actions. */ + gtk_action_group_add_radio_actions ( + action_group, memo_filter_entries, + G_N_ELEMENTS (memo_filter_entries), + MEMO_FILTER_ANY_CATEGORY, + G_CALLBACK (action_search_filter_cb), + memo_shell_view); + + /* Retrieve the radio group from an action we just added. */ + list = gtk_action_group_list_actions (action_group); + radio_action = GTK_RADIO_ACTION (list->data); + group = gtk_radio_action_get_group (radio_action); + g_list_free (list); + + /* Build the category actions. */ + + list = e_categories_get_list (); + for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) { + const gchar *category_name = iter->data; + const gchar *filename; + GtkAction *action; + gchar *action_name; + + action_name = g_strdup_printf ( + "memo-filter-category-%d", ii); + radio_action = gtk_radio_action_new ( + action_name, category_name, NULL, NULL, ii); + g_free (action_name); + + /* Convert the category icon file to a themed icon name. */ + filename = e_categories_get_icon_file_for (category_name); + if (filename != NULL && *filename != '\0') { + gchar *basename; + gchar *cp; + + basename = g_path_get_basename (filename); + + /* Lose the file extension. */ + if ((cp = strrchr (basename, '.')) != NULL) + *cp = '\0'; + + g_object_set ( + radio_action, "icon-name", basename, NULL); + + g_free (basename); + } + + gtk_radio_action_set_group (radio_action, group); + group = gtk_radio_action_get_group (radio_action); + + /* The action group takes ownership of the action. */ + action = GTK_ACTION (radio_action); + gtk_action_group_add_action (action_group, action); + g_object_unref (radio_action); + } + g_list_free (list); + + /* Use any action in the group; doesn't matter which. */ + e_shell_content_set_filter_action (shell_content, radio_action); + + ii = MEMO_FILTER_UNMATCHED; + e_shell_content_add_filter_separator_after (shell_content, ii); +} diff --git a/modules/calendar/e-memo-shell-view-actions.h b/modules/calendar/e-memo-shell-view-actions.h new file mode 100644 index 0000000000..d43d0239d9 --- /dev/null +++ b/modules/calendar/e-memo-shell-view-actions.h @@ -0,0 +1,91 @@ +/* + * e-memo-shell-view-actions.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_VIEW_ACTIONS_H +#define E_MEMO_SHELL_VIEW_ACTIONS_H + +#include <shell/e-shell-window-actions.h> + +/* Memo Actions */ +#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-copy") +#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_CUT(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-cut") +#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_PASTE(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-paste") +#define E_SHELL_WINDOW_ACTION_MEMO_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-delete") +#define E_SHELL_WINDOW_ACTION_MEMO_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-forward") +#define E_SHELL_WINDOW_ACTION_MEMO_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-new") +#define E_SHELL_WINDOW_ACTION_MEMO_OPEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-open") +#define E_SHELL_WINDOW_ACTION_MEMO_OPEN_URL(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-open-url") +#define E_SHELL_WINDOW_ACTION_MEMO_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-preview") +#define E_SHELL_WINDOW_ACTION_MEMO_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-print") +#define E_SHELL_WINDOW_ACTION_MEMO_SAVE_AS(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-save-as") +#define E_SHELL_WINDOW_ACTION_MEMO_VIEW_CLASSIC(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-view-classic") +#define E_SHELL_WINDOW_ACTION_MEMO_VIEW_VERTICAL(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-view-vertical") + +/* Memo List Actions */ +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-copy") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-delete") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-new") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-print") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_PRINT_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-print-preview") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_PROPERTIES(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-properties") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_RENAME(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-rename") +#define E_SHELL_WINDOW_ACTION_MEMO_LIST_SELECT_ONE(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-list-select-one") + +/* Memo Query Actions */ +#define E_SHELL_WINDOW_ACTION_MEMO_FILTER_ANY_CATEGORY(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-filter-any-category") +#define E_SHELL_WINDOW_ACTION_MEMO_FILTER_UNMATCHED(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-filter-unmatched") +#define E_SHELL_WINDOW_ACTION_MEMO_SEARCH_ANY_FIELD_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-search-any-field-contains") +#define E_SHELL_WINDOW_ACTION_MEMO_SEARCH_DESCRIPTION_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-search-description-contains") +#define E_SHELL_WINDOW_ACTION_MEMO_SEARCH_SUMMARY_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "memo-search-summary-contains") + +/* Action Groups */ +#define E_SHELL_WINDOW_ACTION_GROUP_MEMOS(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "memos") +#define E_SHELL_WINDOW_ACTION_GROUP_MEMOS_FILTER(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "memos-filter") + +#endif /* E_MEMO_SHELL_VIEW_ACTIONS_H */ diff --git a/modules/calendar/e-memo-shell-view-private.c b/modules/calendar/e-memo-shell-view-private.c new file mode 100644 index 0000000000..da321d5c19 --- /dev/null +++ b/modules/calendar/e-memo-shell-view-private.c @@ -0,0 +1,561 @@ +/* + * e-memo-shell-view-private.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-view-private.h" + +#include "widgets/menus/gal-view-factory-etable.h" + +static void +memo_shell_view_table_popup_event_cb (EShellView *shell_view, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/memo-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); +} + +static void +memo_shell_view_table_user_created_cb (EMemoShellView *memo_shell_view, + EMemoTable *memo_table) +{ + EMemoShellSidebar *memo_shell_sidebar; + ECalModel *model; + ECal *client; + ESource *source; + + /* This is the "Click to Add" handler. */ + + model = e_memo_table_get_model (memo_table); + client = e_cal_model_get_default_client (model); + source = e_cal_get_source (client); + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + e_memo_shell_sidebar_add_source (memo_shell_sidebar, source); + + e_cal_model_add_client (model, client); +} + +static void +memo_shell_view_selector_client_added_cb (EMemoShellView *memo_shell_view, + ECal *client) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModel *model; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + model = e_memo_table_get_model (memo_table); + + e_cal_model_add_client (model, client); + e_memo_shell_view_update_timezone (memo_shell_view); +} + +static void +memo_shell_view_selector_client_removed_cb (EMemoShellView *memo_shell_view, + ECal *client) +{ + EMemoShellContent *memo_shell_content; + EMemoTable *memo_table; + ECalModel *model; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + model = e_memo_table_get_model (memo_table); + + e_cal_model_remove_client (model, client); +} + +static gboolean +memo_shell_view_selector_popup_event_cb (EShellView *shell_view, + ESource *primary_source, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/memo-list-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); + + return TRUE; +} + +static void +memo_shell_view_load_view_collection (EShellViewClass *shell_view_class) +{ + GalViewCollection *collection; + GalViewFactory *factory; + ETableSpecification *spec; + const gchar *base_dir; + gchar *filename; + + collection = shell_view_class->view_collection; + + base_dir = EVOLUTION_ETSPECDIR; + spec = e_table_specification_new (); + filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL); + if (!e_table_specification_load_from_file (spec, filename)) + g_critical ("Unable to load ETable specification file " + "for memos"); + g_free (filename); + + factory = gal_view_factory_etable_new (spec); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + g_object_unref (spec); + + gal_view_collection_load (collection); +} + +static void +memo_shell_view_notify_view_id_cb (EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + GalViewInstance *view_instance; + const gchar *view_id; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + view_instance = + e_memo_shell_content_get_view_instance (memo_shell_content); + view_id = e_shell_view_get_view_id (E_SHELL_VIEW (memo_shell_view)); + + /* A NULL view ID implies we're in a custom view. But you can + * only get to a custom view via the "Define Views" dialog, which + * would have already modified the view instance appropriately. + * Furthermore, there's no way to refer to a custom view by ID + * anyway, since custom views have no IDs. */ + if (view_id == NULL) + return; + + gal_view_instance_set_current_view_id (view_instance, view_id); +} + +void +e_memo_shell_view_private_init (EMemoShellView *memo_shell_view, + EShellViewClass *shell_view_class) +{ + if (!gal_view_collection_loaded (shell_view_class->view_collection)) + memo_shell_view_load_view_collection (shell_view_class); + + g_signal_connect ( + memo_shell_view, "notify::view-id", + G_CALLBACK (memo_shell_view_notify_view_id_cb), NULL); +} + +void +e_memo_shell_view_private_constructed (EMemoShellView *memo_shell_view) +{ + EMemoShellViewPrivate *priv = memo_shell_view->priv; + EMemoShellContent *memo_shell_content; + EMemoShellSidebar *memo_shell_sidebar; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellContent *shell_content; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + EMemoTable *memo_table; + ECalModel *model; + ETable *table; + ESourceSelector *selector; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + e_shell_window_add_action_group (shell_window, "memos"); + e_shell_window_add_action_group (shell_window, "memos-filter"); + + /* Cache these to avoid lots of awkward casting. */ + priv->memo_shell_backend = g_object_ref (shell_backend); + priv->memo_shell_content = g_object_ref (shell_content); + priv->memo_shell_sidebar = g_object_ref (shell_sidebar); + + memo_shell_content = E_MEMO_SHELL_CONTENT (shell_content); + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + model = e_memo_table_get_model (memo_table); + table = e_memo_table_get_table (memo_table); + + memo_shell_sidebar = E_MEMO_SHELL_SIDEBAR (shell_sidebar); + selector = e_memo_shell_sidebar_get_selector (memo_shell_sidebar); + + g_signal_connect_swapped ( + model, "notify::timezone", + G_CALLBACK (e_memo_shell_view_update_timezone), + memo_shell_view); + + g_signal_connect_swapped ( + memo_table, "open-component", + G_CALLBACK (e_memo_shell_view_open_memo), + memo_shell_view); + + g_signal_connect_swapped ( + memo_table, "popup-event", + G_CALLBACK (memo_shell_view_table_popup_event_cb), + memo_shell_view); + + g_signal_connect_swapped ( + memo_table, "status-message", + G_CALLBACK (e_memo_shell_view_set_status_message), + memo_shell_view); + + g_signal_connect_swapped ( + memo_table, "user-created", + G_CALLBACK (memo_shell_view_table_user_created_cb), + memo_shell_view); + + g_signal_connect_swapped ( + model, "model-changed", + G_CALLBACK (e_memo_shell_view_update_sidebar), + memo_shell_view); + + g_signal_connect_swapped ( + model, "model-rows-deleted", + G_CALLBACK (e_memo_shell_view_update_sidebar), + memo_shell_view); + + g_signal_connect_swapped ( + model, "model-rows-inserted", + G_CALLBACK (e_memo_shell_view_update_sidebar), + memo_shell_view); + + g_signal_connect_swapped ( + table, "selection-change", + G_CALLBACK (e_memo_shell_view_update_sidebar), + memo_shell_view); + + g_signal_connect_swapped ( + memo_shell_sidebar, "client-added", + G_CALLBACK (memo_shell_view_selector_client_added_cb), + memo_shell_view); + + g_signal_connect_swapped ( + memo_shell_sidebar, "client-removed", + G_CALLBACK (memo_shell_view_selector_client_removed_cb), + memo_shell_view); + + g_signal_connect_swapped ( + memo_shell_sidebar, "status-message", + G_CALLBACK (e_memo_shell_view_set_status_message), + memo_shell_view); + + g_signal_connect_swapped ( + selector, "popup-event", + G_CALLBACK (memo_shell_view_selector_popup_event_cb), + memo_shell_view); + + g_signal_connect_swapped ( + selector, "primary-selection-changed", + G_CALLBACK (e_shell_view_update_actions), + memo_shell_view); + + e_categories_register_change_listener ( + G_CALLBACK (e_memo_shell_view_update_search_filter), + memo_shell_view); + + e_memo_shell_view_actions_init (memo_shell_view); + e_memo_shell_view_update_sidebar (memo_shell_view); + e_memo_shell_view_update_search_filter (memo_shell_view); + e_memo_shell_view_update_timezone (memo_shell_view); + + e_memo_shell_view_execute_search (memo_shell_view); +} + +void +e_memo_shell_view_private_dispose (EMemoShellView *memo_shell_view) +{ + EMemoShellViewPrivate *priv = memo_shell_view->priv; + + DISPOSE (priv->memo_shell_backend); + DISPOSE (priv->memo_shell_content); + DISPOSE (priv->memo_shell_sidebar); + + if (memo_shell_view->priv->activity != NULL) { + /* XXX Activity is not cancellable. */ + e_activity_complete (memo_shell_view->priv->activity); + g_object_unref (memo_shell_view->priv->activity); + memo_shell_view->priv->activity = NULL; + } +} + +void +e_memo_shell_view_private_finalize (EMemoShellView *memo_shell_view) +{ + /* XXX Nothing to do? */ +} + +void +e_memo_shell_view_execute_search (EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + GtkRadioAction *action; + GString *string; + ECalComponentPreview *memo_preview; + EMemoTable *memo_table; + ECalModel *model; + FilterRule *rule; + const gchar *format; + const gchar *text; + gchar *query; + gchar *temp; + gint value; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + text = e_shell_content_get_search_text (shell_content); + + shell_window = e_shell_view_get_shell_window (shell_view); + action = GTK_RADIO_ACTION (ACTION (MEMO_SEARCH_ANY_FIELD_CONTAINS)); + value = gtk_radio_action_get_current_value (action); + + if (text == NULL || *text == '\0') { + text = ""; + value = MEMO_SEARCH_SUMMARY_CONTAINS; + } + + switch (value) { + default: + text = ""; + /* fall through */ + + case MEMO_SEARCH_SUMMARY_CONTAINS: + format = "(contains? \"summary\" %s)"; + break; + + case MEMO_SEARCH_DESCRIPTION_CONTAINS: + format = "(contains? \"description\" %s)"; + break; + + case MEMO_SEARCH_ANY_FIELD_CONTAINS: + format = "(contains? \"any\" %s)"; + break; + } + + /* Build the query. */ + string = g_string_new (""); + e_sexp_encode_string (string, text); + query = g_strdup_printf (format, string->str); + g_string_free (string, TRUE); + + /* Apply selected filter. */ + value = e_shell_content_get_filter_value (shell_content); + switch (value) { + case MEMO_FILTER_ANY_CATEGORY: + break; + + case MEMO_FILTER_UNMATCHED: + temp = g_strdup_printf ( + "(and (has-categories? #f) %s", query); + g_free (query); + query = temp; + break; + + default: + { + GList *categories; + const gchar *category_name; + + categories = e_categories_get_list (); + category_name = g_list_nth_data (categories, value); + g_list_free (categories); + + temp = g_strdup_printf ( + "(and (has-categories? \"%s\") %s)", + category_name, query); + g_free (query); + query = temp; + } + } + + /* XXX This is wrong. We need to programmatically construct a + * FilterRule, tell it to build code, and pass the resulting + * expression string to ECalModel. */ + rule = filter_rule_new (); + e_shell_content_set_search_rule (shell_content, rule); + g_object_unref (rule); + + /* Submit the query. */ + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + model = e_memo_table_get_model (memo_table); + e_cal_model_set_search_query (model, query); + g_free (query); + + memo_preview = + e_memo_shell_content_get_memo_preview (memo_shell_content); + e_cal_component_preview_clear (memo_preview); +} + +void +e_memo_shell_view_open_memo (EMemoShellView *memo_shell_view, + ECalModelComponent *comp_data) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + CompEditor *editor; + CompEditorFlags flags = 0; + ECalComponent *comp; + icalcomponent *clone; + const gchar *uid; + + g_return_if_fail (E_IS_MEMO_SHELL_VIEW (memo_shell_view)); + g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data)); + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + uid = icalcomponent_get_uid (comp_data->icalcomp); + editor = comp_editor_find_instance (uid); + + if (editor != NULL) + goto exit; + + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + + if (e_cal_component_has_organizer (comp)) + flags |= COMP_EDITOR_IS_SHARED; + + if (itip_organizer_is_user (comp, comp_data->client)) + flags |= COMP_EDITOR_USER_ORG; + + editor = memo_editor_new (comp_data->client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_unref (comp); + +exit: + gtk_window_present (GTK_WINDOW (editor)); +} + +void +e_memo_shell_view_set_status_message (EMemoShellView *memo_shell_view, + const gchar *status_message, + gdouble percent) +{ + EActivity *activity; + EShellView *shell_view; + EShellBackend *shell_backend; + + g_return_if_fail (E_IS_MEMO_SHELL_VIEW (memo_shell_view)); + + activity = memo_shell_view->priv->activity; + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + if (status_message == NULL || *status_message == '\0') { + if (activity != NULL) { + e_activity_complete (activity); + g_object_unref (activity); + activity = NULL; + } + + } else if (activity == NULL) { + activity = e_activity_new (status_message); + e_activity_set_percent (activity, percent); + e_shell_backend_add_activity (shell_backend, activity); + + } else { + e_activity_set_percent (activity, percent); + e_activity_set_primary_text (activity, status_message); + } + + memo_shell_view->priv->activity = activity; +} + +void +e_memo_shell_view_update_sidebar (EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EShellView *shell_view; + EShellSidebar *shell_sidebar; + EMemoTable *memo_table; + ECalModel *model; + ETable *table; + GString *string; + const gchar *format; + gint n_rows; + gint n_selected; + + shell_view = E_SHELL_VIEW (memo_shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_table = e_memo_shell_content_get_memo_table (memo_shell_content); + + model = e_memo_table_get_model (memo_table); + table = e_memo_table_get_table (memo_table); + + n_rows = e_table_model_row_count (E_TABLE_MODEL (model)); + n_selected = e_table_selected_count (table); + + string = g_string_sized_new (64); + + format = ngettext ("%d memo", "%d memos", n_rows); + g_string_append_printf (string, format, n_rows); + + if (n_selected > 0) { + format = _("%d selected"); + g_string_append_len (string, ", ", 2); + g_string_append_printf (string, format, n_selected); + } + + e_shell_sidebar_set_secondary_text (shell_sidebar, string->str); + + g_string_free (string, TRUE); +} + +void +e_memo_shell_view_update_timezone (EMemoShellView *memo_shell_view) +{ + EMemoShellContent *memo_shell_content; + EMemoShellSidebar *memo_shell_sidebar; + ECalComponentPreview *memo_preview; + icaltimezone *timezone; + ECalModel *model; + GList *clients, *iter; + + memo_shell_content = memo_shell_view->priv->memo_shell_content; + memo_preview = e_memo_shell_content_get_memo_preview (memo_shell_content); + model = e_memo_shell_content_get_memo_model (memo_shell_content); + timezone = e_cal_model_get_timezone (model); + + memo_shell_sidebar = memo_shell_view->priv->memo_shell_sidebar; + clients = e_memo_shell_sidebar_get_clients (memo_shell_sidebar); + + for (iter = clients; iter != NULL; iter = iter->next) { + ECal *client = iter->data; + + if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED) + e_cal_set_default_timezone (client, timezone, NULL); + } + + e_cal_component_preview_set_default_timezone (memo_preview, timezone); + + g_list_free (clients); +} diff --git a/modules/calendar/e-memo-shell-view-private.h b/modules/calendar/e-memo-shell-view-private.h new file mode 100644 index 0000000000..c7ece91f68 --- /dev/null +++ b/modules/calendar/e-memo-shell-view-private.h @@ -0,0 +1,128 @@ +/* + * e-memo-shell-view-private.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_VIEW_PRIVATE_H +#define E_MEMO_SHELL_VIEW_PRIVATE_H + +#include "e-memo-shell-view.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libedataserver/e-categories.h> +#include <libedataserver/e-sexp.h> + +#include "e-util/e-dialog-utils.h" +#include "e-util/e-error.h" +#include "e-util/e-util.h" +#include "e-util/gconf-bridge.h" +#include "widgets/misc/e-popup-action.h" + +#include "calendar/gui/comp-util.h" +#include "calendar/gui/e-cal-component-preview.h" +#include "calendar/gui/e-calendar-selector.h" +#include "calendar/gui/print.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/copy-source-dialog.h" +#include "calendar/gui/dialogs/memo-editor.h" + +#include "e-memo-shell-backend.h" +#include "e-memo-shell-content.h" +#include "e-memo-shell-sidebar.h" +#include "e-memo-shell-view-actions.h" + +#define E_MEMO_SHELL_VIEW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MEMO_SHELL_VIEW, EMemoShellViewPrivate)) + +/* Shorthand, requires a variable named "shell_window". */ +#define ACTION(name) \ + (E_SHELL_WINDOW_ACTION_##name (shell_window)) +#define ACTION_GROUP(name) \ + (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window)) + +/* For use in dispose() methods. */ +#define DISPOSE(obj) \ + G_STMT_START { \ + if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \ + } G_STMT_END + +/* ETable Specifications */ +#define ETSPEC_FILENAME "e-memo-table.etspec" + +G_BEGIN_DECLS + +/* Filter items are displayed in ascending order. + * Non-negative values are reserved for categories. */ +enum { + MEMO_FILTER_ANY_CATEGORY = -2, + MEMO_FILTER_UNMATCHED = -1 +}; + +/* Search items are displayed in ascending order. */ +enum { + MEMO_SEARCH_SUMMARY_CONTAINS, + MEMO_SEARCH_DESCRIPTION_CONTAINS, + MEMO_SEARCH_ANY_FIELD_CONTAINS +}; + +struct _EMemoShellViewPrivate { + + /* These are just for convenience. */ + EMemoShellBackend *memo_shell_backend; + EMemoShellContent *memo_shell_content; + EMemoShellSidebar *memo_shell_sidebar; + + EActivity *activity; +}; + +void e_memo_shell_view_private_init + (EMemoShellView *memo_shell_view, + EShellViewClass *shell_view_class); +void e_memo_shell_view_private_constructed + (EMemoShellView *memo_shell_view); +void e_memo_shell_view_private_dispose + (EMemoShellView *memo_shell_view); +void e_memo_shell_view_private_finalize + (EMemoShellView *memo_shell_view); + +/* Private Utilities */ + +void e_memo_shell_view_actions_init + (EMemoShellView *memo_shell_view); +void e_memo_shell_view_execute_search + (EMemoShellView *memo_shell_view); +void e_memo_shell_view_open_memo + (EMemoShellView *memo_shell_view, + ECalModelComponent *comp_data); +void e_memo_shell_view_set_status_message + (EMemoShellView *memo_shell_view, + const gchar *status_message, + gdouble percent); +void e_memo_shell_view_update_sidebar + (EMemoShellView *memo_shell_view); +void e_memo_shell_view_update_search_filter + (EMemoShellView *memo_shell_view); +void e_memo_shell_view_update_timezone + (EMemoShellView *memo_shell_view); + +G_END_DECLS + +#endif /* E_MEMO_SHELL_VIEW_PRIVATE_H */ diff --git a/modules/calendar/e-memo-shell-view.c b/modules/calendar/e-memo-shell-view.c new file mode 100644 index 0000000000..e2964b061c --- /dev/null +++ b/modules/calendar/e-memo-shell-view.c @@ -0,0 +1,222 @@ +/* + * e-memo-shell-view.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-memo-shell-view-private.h" + +static gpointer parent_class; +static GType memo_shell_view_type; + +static void +memo_shell_view_dispose (GObject *object) +{ + e_memo_shell_view_private_dispose (E_MEMO_SHELL_VIEW (object)); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +memo_shell_view_finalize (GObject *object) +{ + e_memo_shell_view_private_finalize (E_MEMO_SHELL_VIEW (object)); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +memo_shell_view_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + e_memo_shell_view_private_constructed (E_MEMO_SHELL_VIEW (object)); +} + +static void +memo_shell_view_update_actions (EShellView *shell_view) +{ + EMemoShellViewPrivate *priv; + EShellContent *shell_content; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + GtkAction *action; + const gchar *label; + gboolean sensitive; + guint32 state; + + /* Be descriptive. */ + gboolean any_memos_selected; + gboolean has_primary_source; + gboolean multiple_memos_selected; + gboolean primary_source_is_system; + gboolean selection_has_url; + gboolean single_memo_selected; + gboolean sources_are_editable; + + priv = E_MEMO_SHELL_VIEW_GET_PRIVATE (shell_view); + + shell_window = e_shell_view_get_shell_window (shell_view); + + shell_content = e_shell_view_get_shell_content (shell_view); + state = e_shell_content_check_state (shell_content); + + single_memo_selected = + (state & E_MEMO_SHELL_CONTENT_SELECTION_SINGLE); + multiple_memos_selected = + (state & E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE); + sources_are_editable = + (state & E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT); + selection_has_url = + (state & E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL); + + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + state = e_shell_sidebar_check_state (shell_sidebar); + + has_primary_source = + (state & E_MEMO_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE); + primary_source_is_system = + (state & E_MEMO_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM); + + any_memos_selected = + (single_memo_selected || multiple_memos_selected); + + action = ACTION (MEMO_CLIPBOARD_COPY); + sensitive = any_memos_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_CLIPBOARD_CUT); + sensitive = any_memos_selected && sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_CLIPBOARD_PASTE); + sensitive = sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_DELETE); + sensitive = any_memos_selected && sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + if (multiple_memos_selected) + label = _("Delete Memos"); + else + label = _("Delete Memo"); + g_object_set (action, "label", label, NULL); + + action = ACTION (MEMO_FORWARD); + sensitive = single_memo_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_LIST_COPY); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_LIST_DELETE); + sensitive = has_primary_source && !primary_source_is_system; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_LIST_PROPERTIES); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_LIST_RENAME); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_OPEN); + sensitive = single_memo_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_OPEN_URL); + sensitive = single_memo_selected && selection_has_url; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_PRINT); + sensitive = single_memo_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MEMO_SAVE_AS); + sensitive = single_memo_selected; + gtk_action_set_sensitive (action, sensitive); +} + +static void +memo_shell_view_class_init (EMemoShellViewClass *class, + GTypeModule *type_module) +{ + GObjectClass *object_class; + EShellViewClass *shell_view_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMemoShellViewPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = memo_shell_view_dispose; + object_class->finalize = memo_shell_view_finalize; + object_class->constructed = memo_shell_view_constructed; + + shell_view_class = E_SHELL_VIEW_CLASS (class); + shell_view_class->label = _("Memos"); + shell_view_class->icon_name = "evolution-memos"; + shell_view_class->ui_definition = "evolution-memos.ui"; + shell_view_class->ui_manager_id = "org.gnome.evolution.memos"; + shell_view_class->search_options = "/memo-search-options"; + shell_view_class->search_rules = "memotypes.xml"; + shell_view_class->new_shell_content = e_memo_shell_content_new; + shell_view_class->new_shell_sidebar = e_memo_shell_sidebar_new; + shell_view_class->update_actions = memo_shell_view_update_actions; +} + +static void +memo_shell_view_init (EMemoShellView *memo_shell_view, + EShellViewClass *shell_view_class) +{ + memo_shell_view->priv = + E_MEMO_SHELL_VIEW_GET_PRIVATE (memo_shell_view); + + e_memo_shell_view_private_init (memo_shell_view, shell_view_class); +} + +GType +e_memo_shell_view_get_type (void) +{ + return memo_shell_view_type; +} + +void +e_memo_shell_view_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EMemoShellViewClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) memo_shell_view_class_init, + (GClassFinalizeFunc) NULL, + type_module, + sizeof (EMemoShellView), + 0, /* n_preallocs */ + (GInstanceInitFunc) memo_shell_view_init, + NULL /* value_table */ + }; + + memo_shell_view_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_VIEW, + "EMemoShellView", &type_info, 0); +} diff --git a/modules/calendar/e-memo-shell-view.h b/modules/calendar/e-memo-shell-view.h new file mode 100644 index 0000000000..686ae734c9 --- /dev/null +++ b/modules/calendar/e-memo-shell-view.h @@ -0,0 +1,67 @@ +/* + * e-memo-shell-view.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_MEMO_SHELL_VIEW_H +#define E_MEMO_SHELL_VIEW_H + +#include <shell/e-shell-view.h> +#include <libedataserver/e-source-list.h> + +/* Standard GObject macros */ +#define E_TYPE_MEMO_SHELL_VIEW \ + (e_memo_shell_view_get_type ()) +#define E_MEMO_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MEMO_SHELL_VIEW, EMemoShellView)) +#define E_MEMO_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MEMO_SHELL_VIEW, EMemoShellViewClass)) +#define E_IS_MEMO_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MEMO_SHELL_VIEW)) +#define E_IS_MEMO_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MEMO_SHELL_VIEW)) +#define E_MEMO_SHELL_VIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MEMO_SHELL_VIEW, EMemoShellViewClass)) + +G_BEGIN_DECLS + +typedef struct _EMemoShellView EMemoShellView; +typedef struct _EMemoShellViewClass EMemoShellViewClass; +typedef struct _EMemoShellViewPrivate EMemoShellViewPrivate; + +struct _EMemoShellView { + EShellView parent; + EMemoShellViewPrivate *priv; +}; + +struct _EMemoShellViewClass { + EShellViewClass parent_class; +}; + +GType e_memo_shell_view_get_type (void); +void e_memo_shell_view_register_type (GTypeModule *type_module); + +G_END_DECLS + +#endif /* E_MEMO_SHELL_VIEW_H */ diff --git a/modules/calendar/e-task-shell-backend.c b/modules/calendar/e-task-shell-backend.c new file mode 100644 index 0000000000..59a87e653c --- /dev/null +++ b/modules/calendar/e-task-shell-backend.c @@ -0,0 +1,661 @@ +/* + * e-task-shell-backend.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-backend.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal.h> +#include <libedataserver/e-url.h> +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-list.h> +#include <libedataserver/e-source-group.h> + +#include "shell/e-shell.h" +#include "shell/e-shell-backend.h" +#include "shell/e-shell-window.h" + +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/comp-util.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/task-editor.h" + +#include "e-task-shell-content.h" +#include "e-task-shell-migrate.h" +#include "e-task-shell-sidebar.h" +#include "e-task-shell-view.h" + +#define E_TASK_SHELL_BACKEND_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendPrivate)) + +#define WEB_BASE_URI "webcal://" +#define PERSONAL_RELATIVE_URI "system" + +struct _ETaskShellBackendPrivate { + ESourceList *source_list; +}; + +enum { + PROP_0, + PROP_SOURCE_LIST +}; + +static gpointer parent_class; +static GType task_shell_backend_type; + +static void +task_shell_backend_ensure_sources (EShellBackend *shell_backend) +{ + /* XXX This is basically the same algorithm across all modules. + * Maybe we could somehow integrate this into EShellBackend? */ + + ETaskShellBackendPrivate *priv; + ESourceGroup *on_this_computer; + ESourceGroup *on_the_web; + ESource *personal; + EShell *shell; + EShellSettings *shell_settings; + GSList *groups, *iter; + const gchar *data_dir; + const gchar *name; + gchar *base_uri; + gchar *filename; + + on_this_computer = NULL; + on_the_web = NULL; + personal = NULL; + + priv = E_TASK_SHELL_BACKEND_GET_PRIVATE (shell_backend); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + if (!e_cal_get_sources (&priv->source_list, E_CAL_SOURCE_TYPE_TODO, NULL)) { + g_warning ("Could not get task sources from GConf!"); + return; + } + + data_dir = e_shell_backend_get_data_dir (shell_backend); + filename = g_build_filename (data_dir, "local", NULL); + base_uri = g_filename_to_uri (filename, NULL, NULL); + g_free (filename); + + groups = e_source_list_peek_groups (priv->source_list); + for (iter = groups; iter != NULL; iter = iter->next) { + ESourceGroup *source_group = iter->data; + const gchar *group_base_uri; + + group_base_uri = e_source_group_peek_base_uri (source_group); + + /* Compare only "file://" part. If the user's home + * changes, we do not want to create another group. */ + if (on_this_computer == NULL && + strncmp (base_uri, group_base_uri, 7) == 0) + on_this_computer = source_group; + + else if (on_the_web == NULL && + strcmp (WEB_BASE_URI, group_base_uri) == 0) + on_the_web = source_group; + } + + name = _("On This Computer"); + + if (on_this_computer != NULL) { + GSList *sources; + const gchar *group_base_uri; + + /* Force the group name to the current locale. */ + e_source_group_set_name (on_this_computer, name); + + sources = e_source_group_peek_sources (on_this_computer); + group_base_uri = e_source_group_peek_base_uri (on_this_computer); + + /* Make sure this group includes a "Personal" source. */ + for (iter = sources; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + const gchar *relative_uri; + + relative_uri = e_source_peek_relative_uri (source); + if (relative_uri == NULL) + continue; + + if (strcmp (PERSONAL_RELATIVE_URI, relative_uri) != 0) + continue; + + personal = source; + break; + } + + /* Make sure we have the correct base URI. This can + * change when the user's home directory changes. */ + if (strcmp (base_uri, group_base_uri) != 0) { + e_source_group_set_base_uri ( + on_this_computer, base_uri); + + /* XXX We shouldn't need this sync call here as + * set_base_uri() results in synching to GConf, + * but that happens in an idle loop and too late + * to prevent the user from seeing a "Cannot + * Open ... because of invalid URI" error. */ + e_source_list_sync (priv->source_list, NULL); + } + + } else { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, base_uri); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } + + name = _("Personal"); + + if (personal == NULL) { + ESource *source; + GSList *selected; + gchar *primary; + + source = e_source_new (name, PERSONAL_RELATIVE_URI); + e_source_group_add_source (on_this_computer, source, -1); + g_object_unref (source); + + primary = e_shell_settings_get_string ( + shell_settings, "cal-primary-task-list"); + + selected = calendar_config_get_tasks_selected (); + + if (primary == NULL && selected == NULL) { + const gchar *uid; + + uid = e_source_peek_uid (source); + selected = g_slist_prepend (NULL, g_strdup (uid)); + + e_shell_settings_set_string ( + shell_settings, "cal-primary-task-list", uid); + calendar_config_set_tasks_selected (selected); + } + + g_slist_foreach (selected, (GFunc) g_free, NULL); + g_slist_free (selected); + g_free (primary); + } else { + /* Force the source name to the current locale. */ + e_source_set_name (personal, name); + } + + name = _("On The Web"); + + if (on_the_web == NULL) { + ESourceGroup *source_group; + + source_group = e_source_group_new (name, WEB_BASE_URI); + e_source_list_add_group (priv->source_list, source_group, -1); + g_object_unref (source_group); + } else { + /* Force the group name to the current locale. */ + e_source_group_set_name (on_the_web, name); + } + + g_free (base_uri); +} + +static void +task_shell_backend_task_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + + editor = task_editor_new (cal, shell, flags); + comp = cal_comp_task_new_with_defaults (cal); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +task_shell_backend_task_assigned_new_cb (ECal *cal, + ECalendarStatus status, + EShell *shell) +{ + ECalComponent *comp; + CompEditor *editor; + CompEditorFlags flags = 0; + + /* XXX Handle errors better. */ + if (status != E_CALENDAR_STATUS_OK) + return; + + flags |= COMP_EDITOR_NEW_ITEM; + flags |= COMP_EDITOR_IS_ASSIGNED; + flags |= COMP_EDITOR_USER_ORG; + + editor = task_editor_new (cal, shell, flags); + comp = cal_comp_task_new_with_defaults (cal); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (cal); +} + +static void +action_task_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + ECal *cal = NULL; + ECalSourceType source_type; + ESourceList *source_list; + EShellSettings *shell_settings; + EShell *shell; + const gchar *action_name; + gchar *uid; + + /* This callback is used for both tasks and assigned tasks. */ + + source_type = E_CAL_SOURCE_TYPE_TODO; + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_warning ("Could not get task sources from GConf!"); + return; + } + + uid = e_shell_settings_get_string ( + shell_settings, "cal-primary-task-list"); + + if (uid != NULL) { + ESource *source; + + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source != NULL) + cal = auth_new_cal_from_source (source, source_type); + g_free (uid); + } + + if (cal == NULL) + cal = auth_new_cal_from_default (source_type); + + g_return_if_fail (cal != NULL); + + /* Connect the appropriate signal handler. */ + action_name = gtk_action_get_name (action); + if (strcmp (action_name, "task-assigned-new") == 0) + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (task_shell_backend_task_assigned_new_cb), + shell); + else + g_signal_connect ( + cal, "cal-opened", + G_CALLBACK (task_shell_backend_task_new_cb), + shell); + + e_cal_open_async (cal, FALSE); +} + +static void +action_task_list_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + calendar_setup_new_task_list (GTK_WINDOW (shell_window)); +} + +static GtkActionEntry item_entries[] = { + + { "task-new", + "stock_task", + NC_("New", "_Task"), + "<Shift><Control>t", + N_("Create a new task"), + G_CALLBACK (action_task_new_cb) }, + + { "task-assigned-new", + "stock_task", + N_("Assigne_d Task"), + NULL, + N_("Create a new assigned task"), + G_CALLBACK (action_task_new_cb) } +}; + +static GtkActionEntry source_entries[] = { + + { "task-list-new", + "stock_todo", + NC_("New", "Tas_k List"), + NULL, + N_("Create a new task list"), + G_CALLBACK (action_task_list_new_cb) } +}; + +static gboolean +task_shell_backend_handle_uri_cb (EShellBackend *shell_backend, + const gchar *uri) +{ + EShell *shell; + CompEditor *editor; + CompEditorFlags flags = 0; + ECal *client; + ECalComponent *comp; + ESource *source; + ESourceList *source_list; + ECalSourceType source_type; + EUri *euri; + icalcomponent *icalcomp; + icalproperty *icalprop; + const gchar *cp; + gchar *source_uid = NULL; + gchar *comp_uid = NULL; + gchar *comp_rid = NULL; + gboolean handled = FALSE; + GError *error = NULL; + + source_type = E_CAL_SOURCE_TYPE_TODO; + shell = e_shell_backend_get_shell (shell_backend); + + if (strncmp (uri, "task:", 5) != 0) + return FALSE; + + euri = e_uri_new (uri); + cp = euri->query; + if (cp == NULL) + goto exit; + + while (*cp != '\0') { + gchar *header; + gchar *content; + gsize header_len; + gsize content_len; + + header_len = strcspn (cp, "=&"); + + /* If it's malformed, give up. */ + if (cp[header_len] != '=') + break; + + header = (gchar *) cp; + header[header_len] = '\0'; + cp += header_len + 1; + + content_len = strcspn (cp, "&"); + + content = g_strndup (cp, content_len); + if (g_ascii_strcasecmp (header, "source-uid") == 0) + source_uid = g_strdup (content); + else if (g_ascii_strcasecmp (header, "comp-uid") == 0) + comp_uid = g_strdup (content); + else if (g_ascii_strcasecmp (header, "comp-rid") == 0) + comp_rid = g_strdup (content); + g_free (content); + + cp += content_len; + if (*cp == '&') { + cp++; + if (strcmp (cp, "amp;") == 0) + cp += 4; + } + } + + if (source_uid == NULL || comp_uid == NULL) + goto exit; + + /* URI is valid, so consider it handled. Whether + * we successfully open it is another matter... */ + handled = TRUE; + + if (!e_cal_get_sources (&source_list, source_type, NULL)) { + g_printerr ("Could not get task sources from GConf!\n"); + goto exit; + } + + source = e_source_list_peek_source_by_uid (source_list, source_uid); + if (source == NULL) { + g_printerr ("No source for UID `%s'\n", source_uid); + g_object_unref (source_list); + goto exit; + } + + client = auth_new_cal_from_source (source, source_type); + if (client == NULL || !e_cal_open (client, TRUE, &error)) { + g_printerr ("%s\n", error->message); + g_object_unref (source_list); + g_error_free (error); + goto exit; + } + + /* XXX Copied from e_task_shell_view_open_task(). + * Clearly a new utility function is needed. */ + + editor = comp_editor_find_instance (comp_uid); + + if (editor != NULL) + goto present; + + if (!e_cal_get_object (client, comp_uid, comp_rid, &icalcomp, &error)) { + g_printerr ("%s\n", error->message); + g_object_unref (source_list); + g_error_free (error); + goto exit; + } + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomp); + + icalprop = icalcomponent_get_first_property ( + icalcomp, ICAL_ATTENDEE_PROPERTY); + if (icalprop != NULL) + flags |= COMP_EDITOR_IS_ASSIGNED; + + if (itip_organizer_is_user (comp, client)) + flags |= COMP_EDITOR_USER_ORG; + + if (!e_cal_component_has_attendees (comp)) + flags |= COMP_EDITOR_USER_ORG; + + editor = task_editor_new (client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_unref (comp); + +present: + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (source_list); + g_object_unref (client); + +exit: + g_free (source_uid); + g_free (comp_uid); + g_free (comp_rid); + + e_uri_free (euri); + + return handled; +} + +static void +task_shell_backend_window_created_cb (EShellBackend *shell_backend, + GtkWindow *window) +{ + const gchar *module_name; + + if (!E_IS_SHELL_WINDOW (window)) + return; + + module_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name; + + e_shell_window_register_new_item_actions ( + E_SHELL_WINDOW (window), module_name, + item_entries, G_N_ELEMENTS (item_entries)); + + e_shell_window_register_new_source_actions ( + E_SHELL_WINDOW (window), module_name, + source_entries, G_N_ELEMENTS (source_entries)); +} + +static void +task_shell_backend_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE_LIST: + g_value_set_object ( + value, + e_task_shell_backend_get_source_list ( + E_TASK_SHELL_BACKEND (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +task_shell_backend_dispose (GObject *object) +{ + ETaskShellBackendPrivate *priv; + + priv = E_TASK_SHELL_BACKEND_GET_PRIVATE (object); + + if (priv->source_list != NULL) { + g_object_unref (priv->source_list); + priv->source_list = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +task_shell_backend_constructed (GObject *object) +{ + EShell *shell; + EShellBackend *shell_backend; + + shell_backend = E_SHELL_BACKEND (object); + shell = e_shell_backend_get_shell (shell_backend); + + task_shell_backend_ensure_sources (shell_backend); + + g_signal_connect_swapped ( + shell, "handle-uri", + G_CALLBACK (task_shell_backend_handle_uri_cb), + shell_backend); + + g_signal_connect_swapped ( + shell, "window-created", + G_CALLBACK (task_shell_backend_window_created_cb), + shell_backend); +} + +static void +task_shell_backend_class_init (ETaskShellBackendClass *class) +{ + GObjectClass *object_class; + EShellBackendClass *shell_backend_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ETaskShellBackendPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = task_shell_backend_get_property; + object_class->dispose = task_shell_backend_dispose; + object_class->constructed = task_shell_backend_constructed; + + shell_backend_class = E_SHELL_BACKEND_CLASS (class); + shell_backend_class->shell_view_type = E_TYPE_TASK_SHELL_VIEW; + shell_backend_class->name = "tasks"; + shell_backend_class->aliases = ""; + shell_backend_class->schemes = "task"; + shell_backend_class->sort_order = 600; + shell_backend_class->start = NULL; + shell_backend_class->migrate = e_task_shell_backend_migrate; + + g_object_class_install_property ( + object_class, + PROP_SOURCE_LIST, + g_param_spec_object ( + "source-list", + _("Source List"), + _("The registry of task lists"), + E_TYPE_SOURCE_LIST, + G_PARAM_READABLE)); +} + +static void +task_shell_backend_init (ETaskShellBackend *task_shell_backend) +{ + task_shell_backend->priv = + E_TASK_SHELL_BACKEND_GET_PRIVATE (task_shell_backend); +} + +GType +e_task_shell_backend_get_type (void) +{ + return task_shell_backend_type; +} + +void +e_task_shell_backend_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (ETaskShellBackendClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) task_shell_backend_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ETaskShellBackend), + 0, /* n_preallocs */ + (GInstanceInitFunc) task_shell_backend_init, + NULL /* value_table */ + }; + + task_shell_backend_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_BACKEND, + "ETaskShellBackend", &type_info, 0); +} + +ESourceList * +e_task_shell_backend_get_source_list (ETaskShellBackend *task_shell_backend) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_BACKEND (task_shell_backend), NULL); + + return task_shell_backend->priv->source_list; +} diff --git a/modules/calendar/e-task-shell-backend.h b/modules/calendar/e-task-shell-backend.h new file mode 100644 index 0000000000..63b157ad85 --- /dev/null +++ b/modules/calendar/e-task-shell-backend.h @@ -0,0 +1,70 @@ +/* + * e-task-shell-backend.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_BACKEND_H +#define E_TASK_SHELL_BACKEND_H + +#include <shell/e-shell-backend.h> +#include <libedataserver/e-source-list.h> + +/* Standard GObject macros */ +#define E_TYPE_TASK_SHELL_BACKEND \ + (e_task_shell_backend_get_type ()) +#define E_TASK_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackend)) +#define E_TASK_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendClass)) +#define E_IS_TASK_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_TASK_SHELL_BACKEND)) +#define E_IS_TASK_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_TASK_SHELL_BACKEND)) +#define E_TASK_SHELL_BACKEND_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendClass)) + +G_BEGIN_DECLS + +typedef struct _ETaskShellBackend ETaskShellBackend; +typedef struct _ETaskShellBackendClass ETaskShellBackendClass; +typedef struct _ETaskShellBackendPrivate ETaskShellBackendPrivate; + +struct _ETaskShellBackend { + EShellBackend parent; + ETaskShellBackendPrivate *priv; +}; + +struct _ETaskShellBackendClass { + EShellBackendClass parent_class; +}; + +GType e_task_shell_backend_get_type (void); +void e_task_shell_backend_register_type + (GTypeModule *type_module); +ESourceList * e_task_shell_backend_get_source_list + (ETaskShellBackend *task_shell_backend); + +G_END_DECLS + +#endif /* E_TASK_SHELL_BACKEND_H */ diff --git a/modules/calendar/e-task-shell-content.c b/modules/calendar/e-task-shell-content.c new file mode 100644 index 0000000000..a095003978 --- /dev/null +++ b/modules/calendar/e-task-shell-content.c @@ -0,0 +1,744 @@ +/* + * e-task-shell-content.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-content.h" + +#include <glib/gi18n.h> + +#include "e-util/e-binding.h" +#include "e-util/gconf-bridge.h" +#include "widgets/menus/gal-view-etable.h" +#include "widgets/misc/e-paned.h" + +#include "calendar/gui/comp-util.h" +#include "calendar/gui/e-cal-model-tasks.h" +#include "calendar/gui/e-calendar-table.h" + +#define E_TASK_SHELL_CONTENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContentPrivate)) + +#define E_CALENDAR_TABLE_DEFAULT_STATE \ + "<?xml version=\"1.0\"?>" \ + "<ETableState>" \ + " <column source=\"13\"/>" \ + " <column source=\"14\"/>" \ + " <column source=\"9\"/>" \ + " <column source=\"5\"/>" \ + " <grouping/>" \ + "</ETableState>" + +struct _ETaskShellContentPrivate { + GtkWidget *paned; + GtkWidget *task_table; + GtkWidget *task_preview; + + ECalModel *task_model; + GalViewInstance *view_instance; + GtkOrientation orientation; + + gchar *current_uid; + + guint preview_visible : 1; +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_ORIENTATION, + PROP_PREVIEW_VISIBLE +}; + +enum { + TARGET_VCALENDAR +}; + +static GtkTargetEntry drag_types[] = { + { (gchar *) "text/calendar", 0, TARGET_VCALENDAR }, + { (gchar *) "text/x-calendar", 0, TARGET_VCALENDAR } +}; + +static gpointer parent_class; +static GType task_shell_content_type; + +static void +task_shell_content_display_view_cb (ETaskShellContent *task_shell_content, + GalView *gal_view) +{ + ECalendarTable *task_table; + ETable *table; + + if (!GAL_IS_VIEW_ETABLE (gal_view)) + return; + + task_table = e_task_shell_content_get_task_table (task_shell_content); + table = e_calendar_table_get_table (task_table); + + gal_view_etable_attach_table (GAL_VIEW_ETABLE (gal_view), table); +} + +static void +task_shell_content_table_foreach_cb (gint model_row, + gpointer user_data) +{ + ECalModelComponent *comp_data; + icalcomponent *clone; + icalcomponent *vcal; + gchar *string; + + struct { + ECalModel *model; + GSList *list; + } *foreach_data = user_data; + + comp_data = e_cal_model_get_component_at ( + foreach_data->model, model_row); + + vcal = e_cal_util_new_top_level (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); + icalcomponent_add_component (vcal, clone); + + /* String is owned by libical; do not free. */ + string = icalcomponent_as_ical_string (vcal); + if (string != NULL) { + ESource *source; + const gchar *source_uid; + + source = e_cal_get_source (comp_data->client); + source_uid = e_source_peek_uid (source); + + foreach_data->list = g_slist_prepend ( + foreach_data->list, + g_strdup_printf ("%s\n%s", source_uid, string)); + } + + icalcomponent_free (vcal); +} + +static void +task_shell_content_table_drag_data_get_cb (ETaskShellContent *task_shell_content, + gint row, + gint col, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + ECalendarTable *task_table; + ETable *table; + + struct { + ECalModel *model; + GSList *list; + } foreach_data; + + if (info != TARGET_VCALENDAR) + return; + + task_table = e_task_shell_content_get_task_table (task_shell_content); + table = e_calendar_table_get_table (task_table); + + foreach_data.model = e_calendar_table_get_model (task_table); + foreach_data.list = NULL; + + e_table_selected_row_foreach ( + table, task_shell_content_table_foreach_cb, + &foreach_data); + + if (foreach_data.list != NULL) { + cal_comp_selection_set_string_list ( + selection_data, foreach_data.list); + g_slist_foreach (foreach_data.list, (GFunc) g_free, NULL); + g_slist_free (foreach_data.list); + } +} + +static void +task_shell_content_table_drag_data_delete_cb (ETaskShellContent *task_shell_content, + gint row, + gint col, + GdkDragContext *context) +{ + /* Moved components are deleted from source immediately when moved, + * because some of them can be part of destination source, and we + * don't want to delete not-moved tasks. There is no such information + * which event has been moved and which not, so skip this method. */ +} + +static void +task_shell_content_cursor_change_cb (ETaskShellContent *task_shell_content, + gint row, + ETable *table) +{ + ECalComponentPreview *task_preview; + ECalendarTable *task_table; + ECalModel *task_model; + ECalModelComponent *comp_data; + ECalComponent *comp; + const gchar *uid; + + task_model = e_task_shell_content_get_task_model (task_shell_content); + task_table = e_task_shell_content_get_task_table (task_shell_content); + task_preview = e_task_shell_content_get_task_preview (task_shell_content); + + if (e_table_selected_count (table) != 1) { + e_cal_component_preview_clear (task_preview); + return; + } + + row = e_table_get_cursor_row (table); + comp_data = e_cal_model_get_component_at (task_model, row); + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent ( + comp, icalcomponent_new_clone (comp_data->icalcomp)); + e_cal_component_preview_display ( + task_preview, comp_data->client, comp); + + e_cal_component_get_uid (comp, &uid); + g_free (task_shell_content->priv->current_uid); + task_shell_content->priv->current_uid = g_strdup (uid); + + g_object_unref (comp); +} + +static void +task_shell_content_selection_change_cb (ETaskShellContent *task_shell_content, + ETable *table) +{ + ECalComponentPreview *task_preview; + + task_preview = e_task_shell_content_get_task_preview (task_shell_content); + + if (e_table_selected_count (table) != 1) + e_cal_component_preview_clear (task_preview); +} + +static void +task_shell_content_model_row_changed_cb (ETaskShellContent *task_shell_content, + gint row, + ETableModel *model) +{ + ECalModelComponent *comp_data; + ECalendarTable *task_table; + ETable *table; + const gchar *current_uid; + const gchar *uid; + + current_uid = task_shell_content->priv->current_uid; + if (current_uid == NULL) + return; + + comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row); + if (comp_data == NULL) + return; + + uid = icalcomponent_get_uid (comp_data->icalcomp); + if (g_strcmp0 (uid, current_uid) != 0) + return; + + task_table = e_task_shell_content_get_task_table (task_shell_content); + table = e_calendar_table_get_table (task_table); + + task_shell_content_cursor_change_cb (task_shell_content, 0, table); +} + +static GtkOrientation +task_shell_content_get_orientation (ETaskShellContent *task_shell_content) +{ + return task_shell_content->priv->orientation; +} + +static void +task_shell_content_set_orientation (ETaskShellContent *task_shell_content, + GtkOrientation orientation) +{ + task_shell_content->priv->orientation = orientation; + + g_object_notify (G_OBJECT (task_shell_content), "orientation"); +} + +static void +task_shell_content_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ORIENTATION: + task_shell_content_set_orientation ( + E_TASK_SHELL_CONTENT (object), + g_value_get_enum (value)); + return; + + case PROP_PREVIEW_VISIBLE: + e_task_shell_content_set_preview_visible ( + E_TASK_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +task_shell_content_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_MODEL: + g_value_set_object ( + value, + e_task_shell_content_get_task_model ( + E_TASK_SHELL_CONTENT (object))); + return; + + case PROP_ORIENTATION: + g_value_set_enum ( + value, + task_shell_content_get_orientation ( + E_TASK_SHELL_CONTENT (object))); + return; + + case PROP_PREVIEW_VISIBLE: + g_value_set_boolean ( + value, + e_task_shell_content_get_preview_visible ( + E_TASK_SHELL_CONTENT (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +task_shell_content_dispose (GObject *object) +{ + ETaskShellContentPrivate *priv; + + priv = E_TASK_SHELL_CONTENT_GET_PRIVATE (object); + + if (priv->paned != NULL) { + g_object_unref (priv->paned); + priv->paned = NULL; + } + + if (priv->task_table != NULL) { + g_object_unref (priv->task_table); + priv->task_table = NULL; + } + + if (priv->task_preview != NULL) { + g_object_unref (priv->task_preview); + priv->task_preview = NULL; + } + + if (priv->task_model != NULL) { + g_object_unref (priv->task_model); + priv->task_model = NULL; + } + + if (priv->view_instance != NULL) { + g_object_unref (priv->view_instance); + priv->view_instance = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +task_shell_content_finalize (GObject *object) +{ + ETaskShellContentPrivate *priv; + + priv = E_TASK_SHELL_CONTENT_GET_PRIVATE (object); + + g_free (priv->current_uid); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +task_shell_content_constructed (GObject *object) +{ + ETaskShellContentPrivate *priv; + EShell *shell; + EShellSettings *shell_settings; + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + GalViewInstance *view_instance; + icaltimezone *timezone; + ETable *table; + GConfBridge *bridge; + GtkWidget *container; + GtkWidget *widget; + const gchar *key; + + priv = E_TASK_SHELL_CONTENT_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + shell_content = E_SHELL_CONTENT (object); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + priv->task_model = e_cal_model_tasks_new (shell_settings); + + timezone = e_shell_settings_get_pointer ( + shell_settings, "cal-timezone"); + + /* Build content widgets. */ + + container = GTK_WIDGET (object); + + widget = e_paned_new (GTK_ORIENTATION_VERTICAL); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->paned = g_object_ref (widget); + gtk_widget_show (widget); + + e_binding_new ( + G_OBJECT (object), "orientation", + G_OBJECT (widget), "orientation"); + + container = widget; + + widget = e_calendar_table_new (shell_view, priv->task_model); + gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE); + priv->task_table = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, FALSE); + gtk_widget_show (widget); + + e_binding_new ( + G_OBJECT (object), "preview-visible", + G_OBJECT (widget), "visible"); + + container = widget; + + widget = e_cal_component_preview_new (); + e_cal_component_preview_set_default_timezone ( + E_CAL_COMPONENT_PREVIEW (widget), timezone); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->task_preview = g_object_ref (widget); + gtk_widget_show (widget); + + /* Configure the task table. */ + + widget = E_CALENDAR_TABLE (priv->task_table)->etable; + table = e_table_scrolled_get_table (E_TABLE_SCROLLED (widget)); + + e_table_set_state (table, E_CALENDAR_TABLE_DEFAULT_STATE); + + e_table_drag_source_set ( + table, GDK_BUTTON1_MASK, + drag_types, G_N_ELEMENTS (drag_types), + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_ASK); + + g_signal_connect_swapped ( + table, "table-drag-data-get", + G_CALLBACK (task_shell_content_table_drag_data_get_cb), + object); + + g_signal_connect_swapped ( + table, "table-drag-data-delete", + G_CALLBACK (task_shell_content_table_drag_data_delete_cb), + object); + + g_signal_connect_swapped ( + table, "cursor-change", + G_CALLBACK (task_shell_content_cursor_change_cb), + object); + + g_signal_connect_swapped ( + table, "selection-change", + G_CALLBACK (task_shell_content_selection_change_cb), + object); + + g_signal_connect_swapped ( + priv->task_model, "model-row-changed", + G_CALLBACK (task_shell_content_model_row_changed_cb), + object); + + /* Load the view instance. */ + + view_instance = e_shell_view_new_view_instance (shell_view, NULL); + g_signal_connect_swapped ( + view_instance, "display-view", + G_CALLBACK (task_shell_content_display_view_cb), + object); + gal_view_instance_load (view_instance); + priv->view_instance = view_instance; + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (priv->paned); + key = "/apps/evolution/calendar/display/task_hpane_position"; + gconf_bridge_bind_property_delayed (bridge, key, object, "hposition"); + + object = G_OBJECT (priv->paned); + key = "/apps/evolution/calendar/display/task_vpane_position"; + gconf_bridge_bind_property_delayed (bridge, key, object, "vposition"); +} + +static guint32 +task_shell_content_check_state (EShellContent *shell_content) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ETable *table; + GSList *list, *iter; + gboolean assignable = TRUE; + gboolean editable = TRUE; + gboolean has_url = FALSE; + gint n_selected; + gint n_complete = 0; + gint n_incomplete = 0; + guint32 state = 0; + + task_shell_content = E_TASK_SHELL_CONTENT (shell_content); + task_table = e_task_shell_content_get_task_table (task_shell_content); + + table = e_calendar_table_get_table (task_table); + n_selected = e_table_selected_count (table); + + list = e_calendar_table_get_selected (task_table); + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + icalproperty *prop; + const gchar *cap; + gboolean read_only; + + e_cal_is_read_only (comp_data->client, &read_only, NULL); + editable &= !read_only; + + cap = CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT; + if (e_cal_get_static_capability (comp_data->client, cap)) + assignable = FALSE; + + cap = CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK; + if (e_cal_get_static_capability (comp_data->client, cap)) + assignable = FALSE; + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + has_url |= (prop != NULL); + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_COMPLETED_PROPERTY); + if (prop != NULL) + n_complete++; + else + n_incomplete++; + } + g_slist_free (list); + + if (n_selected == 1) + state |= E_TASK_SHELL_CONTENT_SELECTION_SINGLE; + if (n_selected > 1) + state |= E_TASK_SHELL_CONTENT_SELECTION_MULTIPLE; + if (assignable) + state |= E_TASK_SHELL_CONTENT_SELECTION_CAN_ASSIGN; + if (editable) + state |= E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT; + if (n_complete > 0) + state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE; + if (n_incomplete > 0) + state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE; + if (has_url) + state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_URL; + + return state; +} + +static void +task_shell_content_class_init (ETaskShellContentClass *class) +{ + GObjectClass *object_class; + EShellContentClass *shell_content_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ETaskShellContentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = task_shell_content_set_property; + object_class->get_property = task_shell_content_get_property; + object_class->dispose = task_shell_content_dispose; + object_class->finalize = task_shell_content_finalize; + object_class->constructed = task_shell_content_constructed; + + shell_content_class = E_SHELL_CONTENT_CLASS (class); + shell_content_class->check_state = task_shell_content_check_state; + + g_object_class_install_property ( + object_class, + PROP_MODEL, + g_param_spec_object ( + "model", + _("Model"), + _("The task table model"), + E_TYPE_CAL_MODEL, + G_PARAM_READABLE)); + + g_object_class_install_property ( + object_class, + PROP_PREVIEW_VISIBLE, + g_param_spec_boolean ( + "preview-visible", + _("Preview is Visible"), + _("Whether the preview pane is visible"), + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_override_property ( + object_class, PROP_ORIENTATION, "orientation"); +} + +static void +task_shell_content_init (ETaskShellContent *task_shell_content) +{ + task_shell_content->priv = + E_TASK_SHELL_CONTENT_GET_PRIVATE (task_shell_content); + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_task_shell_content_get_type (void) +{ + return task_shell_content_type; +} + +void +e_task_shell_content_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (ETaskShellContentClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) task_shell_content_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ETaskShellContent), + 0, /* n_preallocs */ + (GInstanceInitFunc) task_shell_content_init, + NULL /* value_table */ + }; + + static const GInterfaceInfo orientable_info = { + (GInterfaceInitFunc) NULL, + (GInterfaceFinalizeFunc) NULL, + NULL /* interface_data */ + }; + + task_shell_content_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_CONTENT, + "ETaskShellContent", &type_info, 0); + + g_type_module_add_interface ( + type_module, task_shell_content_type, + GTK_TYPE_ORIENTABLE, &orientable_info); +} + +GtkWidget * +e_task_shell_content_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_TASK_SHELL_CONTENT, + "shell-view", shell_view, NULL); +} + +ECalModel * +e_task_shell_content_get_task_model (ETaskShellContent *task_shell_content) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL); + + return task_shell_content->priv->task_model; +} + +ECalComponentPreview * +e_task_shell_content_get_task_preview (ETaskShellContent *task_shell_content) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL); + + return E_CAL_COMPONENT_PREVIEW ( + task_shell_content->priv->task_preview); +} + +ECalendarTable * +e_task_shell_content_get_task_table (ETaskShellContent *task_shell_content) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL); + + return E_CALENDAR_TABLE (task_shell_content->priv->task_table); +} + +gboolean +e_task_shell_content_get_preview_visible (ETaskShellContent *task_shell_content) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_CONTENT (task_shell_content), FALSE); + + return task_shell_content->priv->preview_visible; +} + +void +e_task_shell_content_set_preview_visible (ETaskShellContent *task_shell_content, + gboolean preview_visible) +{ + g_return_if_fail (E_IS_TASK_SHELL_CONTENT (task_shell_content)); + + task_shell_content->priv->preview_visible = preview_visible; + + g_object_notify (G_OBJECT (task_shell_content), "preview-visible"); +} + +GalViewInstance * +e_task_shell_content_get_view_instance (ETaskShellContent *task_shell_content) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_CONTENT (task_shell_content), NULL); + + return task_shell_content->priv->view_instance; +} diff --git a/modules/calendar/e-task-shell-content.h b/modules/calendar/e-task-shell-content.h new file mode 100644 index 0000000000..f5d4fc9665 --- /dev/null +++ b/modules/calendar/e-task-shell-content.h @@ -0,0 +1,100 @@ +/* + * e-task-shell-content.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_CONTENT_H +#define E_TASK_SHELL_CONTENT_H + +#include <shell/e-shell-content.h> +#include <shell/e-shell-view.h> + +#include <calendar/gui/e-cal-model.h> +#include <calendar/gui/e-calendar-table.h> +#include <calendar/gui/e-cal-component-preview.h> + +#include <menus/gal-view-instance.h> + +/* Standard GObject macros */ +#define E_TYPE_TASK_SHELL_CONTENT \ + (e_task_shell_content_get_type ()) +#define E_TASK_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContent)) +#define E_TASK_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContentClass)) +#define E_IS_TASK_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_TASK_SHELL_CONTENT)) +#define E_IS_TASK_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_TASK_SHELL_CONTENT)) +#define E_TASK_SHELL_CONTENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_TASK_SHELL_CONTENT, ETaskShellContentClass)) + +G_BEGIN_DECLS + +typedef struct _ETaskShellContent ETaskShellContent; +typedef struct _ETaskShellContentClass ETaskShellContentClass; +typedef struct _ETaskShellContentPrivate ETaskShellContentPrivate; + +enum { + E_TASK_SHELL_CONTENT_SELECTION_SINGLE = 1 << 0, + E_TASK_SHELL_CONTENT_SELECTION_MULTIPLE = 1 << 1, + E_TASK_SHELL_CONTENT_SELECTION_CAN_ASSIGN = 1 << 2, + E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT = 1 << 3, + E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE = 1 << 4, + E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE = 1 << 5, + E_TASK_SHELL_CONTENT_SELECTION_HAS_URL = 1 << 6 +}; + +struct _ETaskShellContent { + EShellContent parent; + ETaskShellContentPrivate *priv; +}; + +struct _ETaskShellContentClass { + EShellContentClass parent_class; +}; + +GType e_task_shell_content_get_type (void); +void e_task_shell_content_register_type + (GTypeModule *type_module); +GtkWidget * e_task_shell_content_new(EShellView *shell_view); +ECalModel * e_task_shell_content_get_task_model + (ETaskShellContent *task_shell_content); +ECalComponentPreview * + e_task_shell_content_get_task_preview + (ETaskShellContent *task_shell_content); +ECalendarTable *e_task_shell_content_get_task_table + (ETaskShellContent *task_shell_content); +gboolean e_task_shell_content_get_preview_visible + (ETaskShellContent *task_shell_content); +void e_task_shell_content_set_preview_visible + (ETaskShellContent *task_shell_content, + gboolean preview_visible); +GalViewInstance * + e_task_shell_content_get_view_instance + (ETaskShellContent *task_shell_content); + +G_END_DECLS + +#endif /* E_TASK_SHELL_CONTENT_H */ diff --git a/modules/calendar/e-task-shell-migrate.c b/modules/calendar/e-task-shell-migrate.c new file mode 100644 index 0000000000..799298f5a7 --- /dev/null +++ b/modules/calendar/e-task-shell-migrate.c @@ -0,0 +1,675 @@ +/* + * e-task-shell-backend-migrate.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-migrate.h" + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <libebackend/e-dbhash.h> +#include <libedataserver/e-source.h> +#include <libedataserver/e-source-group.h> +#include <libedataserver/e-source-list.h> +#include <libedataserver/e-xml-hash-utils.h> +#include <libedataserver/e-xml-utils.h> + +#include "e-util/e-bconf-map.h" +#include "e-util/e-folder-map.h" +#include "e-util/e-util-private.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/calendar-config-keys.h" +#include "shell/e-shell.h" + +#define WEBCAL_BASE_URI "webcal://" +#define PERSONAL_RELATIVE_URI "system" + +static e_gconf_map_t calendar_tasks_map[] = { + /* /Calendar/Tasks */ + { "HideCompletedTasks", "calendar/tasks/hide_completed", E_GCONF_MAP_BOOL }, + { "HideCompletedTasksUnits", "calendar/tasks/hide_completed_units", E_GCONF_MAP_STRING }, + { "HideCompletedTasksValue", "calendar/tasks/hide_completed_value", E_GCONF_MAP_INT }, + { NULL }, +}; + +static e_gconf_map_t calendar_tasks_colours_map[] = { + /* /Calendar/Tasks/Colors */ + { "TasksDueToday", "calendar/tasks/colors/due_today", E_GCONF_MAP_STRING }, + { "TasksOverDue", "calendar/tasks/colors/overdue", E_GCONF_MAP_STRING }, + { "TasksDueToday", "calendar/tasks/colors/due_today", E_GCONF_MAP_STRING }, + { NULL }, +}; + +static e_gconf_map_list_t task_remap_list[] = { + + { "/Calendar/Tasks", calendar_tasks_map }, + { "/Calendar/Tasks/Colors", calendar_tasks_colours_map }, + + { NULL }, +}; + +static GtkWidget *window; +static GtkLabel *label; +static GtkProgressBar *progress; + +#ifndef G_OS_WIN32 + +/* No previous versions have been available on Win32, so don't + * bother with upgrade support from 1.x on Win32. + */ + +static void +setup_progress_dialog (void) +{ + GtkWidget *vbox, *hbox, *w; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title ((GtkWindow *) window, _("Migrating...")); + gtk_window_set_modal ((GtkWindow *) window, TRUE); + gtk_container_set_border_width ((GtkContainer *) window, 6); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_widget_show (vbox); + gtk_container_add ((GtkContainer *) window, vbox); + + w = gtk_label_new (_("The location and hierarchy of the Evolution task " + "folders has changed since Evolution 1.x.\n\nPlease be " + "patient while Evolution migrates your folders...")); + + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_widget_show (w); + gtk_box_pack_start ((GtkBox *) vbox, w, TRUE, TRUE, 0); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox); + gtk_box_pack_start ((GtkBox *) vbox, hbox, TRUE, TRUE, 0); + + label = (GtkLabel *) gtk_label_new (""); + gtk_widget_show ((GtkWidget *) label); + gtk_box_pack_start ((GtkBox *) hbox, (GtkWidget *) label, TRUE, TRUE, 0); + + progress = (GtkProgressBar *) gtk_progress_bar_new (); + gtk_widget_show ((GtkWidget *) progress); + gtk_box_pack_start ((GtkBox *) hbox, (GtkWidget *) progress, TRUE, TRUE, 0); + + gtk_widget_show (window); +} + +static void +dialog_close (void) +{ + gtk_widget_destroy ((GtkWidget *) window); +} + +static void +dialog_set_folder_name (const gchar *folder_name) +{ + gchar *text; + + text = g_strdup_printf (_("Migrating '%s':"), folder_name); + gtk_label_set_text (label, text); + g_free (text); + + gtk_progress_bar_set_fraction (progress, 0.0); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +static void +dialog_set_progress (double percent) +{ + gchar text[5]; + + snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f)); + + gtk_progress_bar_set_fraction (progress, percent); + gtk_progress_bar_set_text (progress, text); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +static gboolean +check_for_conflict (ESourceGroup *group, gchar *name) +{ + GSList *sources; + GSList *s; + + sources = e_source_group_peek_sources (group); + + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + + if (!strcmp (e_source_peek_name (source), name)) + return TRUE; + } + + return FALSE; +} + +static gchar * +get_source_name (ESourceGroup *group, const gchar *path) +{ + gchar **p = g_strsplit (path, "/", 0); + gint i, j, starting_index; + gint num_elements; + gboolean conflict; + GString *s = g_string_new (NULL); + + for (i = 0; p[i]; i ++); + + num_elements = i; + i--; + + /* p[i] is now the last path element */ + + /* check if it conflicts */ + starting_index = i; + do { + for (j = starting_index; j < num_elements; j += 2) { + if (j != starting_index) + g_string_append_c (s, '_'); + g_string_append (s, p[j]); + } + + conflict = check_for_conflict (group, s->str); + + /* if there was a conflict back up 2 levels (skipping the /subfolder/ element) */ + if (conflict) + starting_index -= 2; + + /* we always break out if we can't go any further, + regardless of whether or not we conflict. */ + if (starting_index < 0) + break; + + } while (conflict); + g_strfreev (p); + + return g_string_free (s, FALSE); +} + +static gboolean +migrate_ical (ECal *old_ecal, ECal *new_ecal) +{ + GList *l, *objects; + gint num_added = 0; + gint num_objects; + gboolean retval = TRUE; + + /* both ecals are loaded, start the actual migration */ + if (!e_cal_get_object_list (old_ecal, "#t", &objects, NULL)) + return FALSE; + + num_objects = g_list_length (objects); + for (l = objects; l; l = l->next) { + icalcomponent *ical_comp = l->data; + GError *error = NULL; + + if (!e_cal_create_object (new_ecal, ical_comp, NULL, &error)) { + g_warning ("Migration of object failed: %s", error->message); + retval = FALSE; + } + + g_clear_error (&error); + + num_added ++; + dialog_set_progress ((double)num_added / num_objects); + } + + g_list_foreach (objects, (GFunc) icalcomponent_free, NULL); + g_list_free (objects); + + return retval; +} + +static gboolean +migrate_ical_folder_to_source (gchar *old_path, ESource *new_source, ECalSourceType type) +{ + ECal *old_ecal = NULL, *new_ecal = NULL; + ESource *old_source; + ESourceGroup *group; + gchar *old_uri = g_strdup_printf ("file://%s", old_path); + GError *error = NULL; + gboolean retval = FALSE; + + group = e_source_group_new ("", old_uri); + old_source = e_source_new ("", ""); + e_source_group_add_source (group, old_source, -1); + + dialog_set_folder_name (e_source_peek_name (new_source)); + + if (!(old_ecal = e_cal_new (old_source, type))) { + g_warning ("could not find a backend for '%s'", e_source_get_uri (old_source)); + goto finish; + } + if (!e_cal_open (old_ecal, FALSE, &error)) { + g_warning ("failed to load source ecal for migration: '%s' (%s)", error->message, + e_source_get_uri (old_source)); + goto finish; + } + + if (!(new_ecal = e_cal_new (new_source, type))) { + g_warning ("could not find a backend for '%s'", e_source_get_uri (new_source)); + goto finish; + } + if (!e_cal_open (new_ecal, FALSE, &error)) { + g_warning ("failed to load destination ecal for migration: '%s' (%s)", error->message, + e_source_get_uri (new_source)); + goto finish; + } + + retval = migrate_ical (old_ecal, new_ecal); + +finish: + g_clear_error (&error); + if (old_ecal) + g_object_unref (old_ecal); + g_object_unref (group); + if (new_ecal) + g_object_unref (new_ecal); + g_free (old_uri); + + return retval; +} + +static gboolean +migrate_ical_folder (gchar *old_path, ESourceGroup *dest_group, gchar *source_name, ECalSourceType type) +{ + ESource *new_source; + gboolean retval; + + new_source = e_source_new (source_name, source_name); + e_source_set_relative_uri (new_source, e_source_peek_uid (new_source)); + e_source_group_add_source (dest_group, new_source, -1); + + retval = migrate_ical_folder_to_source (old_path, new_source, type); + + g_object_unref (new_source); + + return retval; +} + +#endif /* !G_OS_WIN32 */ + +#ifndef G_OS_WIN32 + +static void +migrate_pilot_db_key (const gchar *key, gpointer user_data) +{ + EXmlHash *xmlhash = user_data; + + e_xmlhash_add (xmlhash, key, ""); +} + +static void +migrate_pilot_data (const gchar *component, const gchar *conduit, const gchar *old_path, const gchar *new_path) +{ + gchar *changelog, *map; + const gchar *dent; + const gchar *ext; + gchar *filename; + GDir *dir; + + if (!(dir = g_dir_open (old_path, 0, NULL))) + return; + + map = g_alloca (12 + strlen (conduit)); + sprintf (map, "pilot-map-%s-", conduit); + + changelog = g_alloca (24 + strlen (conduit)); + sprintf (changelog, "pilot-sync-evolution-%s-", conduit); + + while ((dent = g_dir_read_name (dir))) { + if (!strncmp (dent, map, strlen (map)) && + ((ext = strrchr (dent, '.')) && !strcmp (ext, ".xml"))) { + /* pilot map file - src and dest file formats are identical */ + guchar inbuf[4096]; + gsize nread, nwritten; + gint fd0, fd1; + gssize n; + + filename = g_build_filename (old_path, dent, NULL); + if ((fd0 = g_open (filename, O_RDONLY|O_BINARY, 0)) == -1) { + g_free (filename); + continue; + } + + g_free (filename); + filename = g_build_filename (new_path, dent, NULL); + if ((fd1 = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) == -1) { + g_free (filename); + close (fd0); + continue; + } + + do { + do { + n = read (fd0, inbuf, sizeof (inbuf)); + } while (n == -1 && errno == EINTR); + + if (n < 1) + break; + + nread = n; + nwritten = 0; + do { + do { + n = write (fd1, inbuf + nwritten, nread - nwritten); + } while (n == -1 && errno == EINTR); + + if (n > 0) + nwritten += n; + } while (nwritten < nread && n != -1); + + if (n == -1) + break; + } while (1); + + if (n != -1) + n = fsync (fd1); + + if (n == -1) { + g_warning ("Failed to migrate %s: %s", dent, strerror (errno)); + g_unlink (filename); + } + + close (fd0); + close (fd1); + g_free (filename); + } else if (!strncmp (dent, changelog, strlen (changelog)) && + ((ext = strrchr (dent, '.')) && !strcmp (ext, ".db"))) { + /* src and dest formats differ, src format is db3 while dest format is xml */ + EXmlHash *xmlhash; + EDbHash *dbhash; + struct stat st; + + filename = g_build_filename (old_path, dent, NULL); + if (g_stat (filename, &st) == -1) { + g_free (filename); + continue; + } + + dbhash = e_dbhash_new (filename); + g_free (filename); + + filename = g_strdup_printf ("%s/%s.ics-%s", new_path, component, dent); + if (g_stat (filename, &st) != -1) + g_unlink (filename); + xmlhash = e_xmlhash_new (filename); + g_free (filename); + + e_dbhash_foreach_key (dbhash, migrate_pilot_db_key, xmlhash); + + e_dbhash_destroy (dbhash); + + e_xmlhash_write (xmlhash); + e_xmlhash_destroy (xmlhash); + } + } + + g_dir_close (dir); +} + +#endif + +static void +create_task_sources (EShellBackend *shell_backend, + ESourceList *source_list, + ESourceGroup **on_this_computer, + ESourceGroup **on_the_web, + ESource **personal_source) +{ + EShell *shell; + EShellSettings *shell_settings; + GSList *groups; + ESourceGroup *group; + gchar *base_uri, *base_uri_proto; + const gchar *base_dir; + + *on_this_computer = NULL; + *on_the_web = NULL; + *personal_source = NULL; + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + base_dir = e_shell_backend_get_config_dir (shell_backend); + base_uri = g_build_filename (base_dir, "local", NULL); + + base_uri_proto = g_filename_to_uri (base_uri, NULL, NULL); + + groups = e_source_list_peek_groups (source_list); + if (groups) { + /* groups are already there, we need to search for things... */ + GSList *g; + + for (g = groups; g; g = g->next) { + + group = E_SOURCE_GROUP (g->data); + + if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group))) + *on_this_computer = g_object_ref (group); + else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group))) + *on_the_web = g_object_ref (group); + } + } + + if (*on_this_computer) { + /* make sure "Personal" shows up as a source under + this group */ + GSList *sources = e_source_group_peek_sources (*on_this_computer); + GSList *s; + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + const gchar *relative_uri; + + relative_uri = e_source_peek_relative_uri (source); + if (relative_uri == NULL) + continue; + if (!strcmp (PERSONAL_RELATIVE_URI, relative_uri)) { + *personal_source = g_object_ref (source); + break; + } + } + } else { + /* create the local source group */ + group = e_source_group_new (_("On This Computer"), base_uri_proto); + e_source_list_add_group (source_list, group, -1); + + *on_this_computer = group; + } + + if (!*personal_source) { + gchar *primary_task_list; + + /* Create the default Person task list */ + ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI); + e_source_group_add_source (*on_this_computer, source, -1); + + primary_task_list = e_shell_settings_get_string ( + shell_settings, "cal-primary-task-list"); + + if (!primary_task_list && !calendar_config_get_tasks_selected ()) { + GSList selected; + + e_shell_settings_set_string ( + shell_settings, "cal-primary-task-list", + e_source_peek_uid (source)); + + selected.data = (gpointer)e_source_peek_uid (source); + selected.next = NULL; + calendar_config_set_tasks_selected (&selected); + } + + e_source_set_color_spec (source, "#BECEDD"); + *personal_source = source; + } + + if (!*on_the_web) { + /* Create the Webcal source group */ + group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI); + e_source_list_add_group (source_list, group, -1); + + *on_the_web = group; + } + + g_free (base_uri_proto); + g_free (base_uri); +} + +gboolean +e_task_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error) +{ + ESourceGroup *on_this_computer = NULL; + ESourceGroup *on_the_web = NULL; + ESource *personal_source = NULL; + ESourceList *source_list; + gboolean retval = FALSE; + + g_object_get (shell_backend, "source-list", &source_list, NULL); + + /* we call this unconditionally now - create_groups either + creates the groups/sources or it finds the necessary + groups/sources. */ + create_task_sources ( + shell_backend, source_list, &on_this_computer, + &on_the_web, &personal_source); + +#ifndef G_OS_WIN32 + if (major == 1) { + xmlDocPtr config_doc = NULL; + gchar *conf_file; + + conf_file = g_build_filename (g_get_home_dir (), "evolution", "config.xmldb", NULL); + if (g_file_test (conf_file, G_FILE_TEST_IS_REGULAR)) + config_doc = e_xml_parse_file (conf_file); + g_free (conf_file); + + if (config_doc && minor <= 2) { + GConfClient *gconf; + gint res = 0; + + /* move bonobo config to gconf */ + gconf = gconf_client_get_default (); + + res = e_bconf_import (gconf, config_doc, task_remap_list); + + g_object_unref (gconf); + + xmlFreeDoc(config_doc); + + if (res != 0) { + g_set_error(error, 0, 0, _("Unable to migrate old settings from evolution/config.xmldb")); + goto fail; + } + } + + if (minor <= 4) { + GSList *migration_dirs, *l; + gchar *path, *local_task_folder; + + setup_progress_dialog (); + + path = g_build_filename (g_get_home_dir (), "evolution", "local", NULL); + migration_dirs = e_folder_map_local_folders (path, "tasks"); + local_task_folder = g_build_filename (path, "Tasks", NULL); + g_free (path); + + if (personal_source) + migrate_ical_folder_to_source (local_task_folder, personal_source, E_CAL_SOURCE_TYPE_TODO); + + for (l = migration_dirs; l; l = l->next) { + gchar *source_name; + + if (personal_source && !strcmp ((gchar *)l->data, local_task_folder)) + continue; + + source_name = get_source_name (on_this_computer, (gchar *)l->data); + + if (!migrate_ical_folder (l->data, on_this_computer, source_name, E_CAL_SOURCE_TYPE_TODO)) { + /* FIXME: domain/code */ + g_set_error(error, 0, 0, _("Unable to migrate tasks `%s'"), source_name); + g_free(source_name); + goto fail; + } + + g_free (source_name); + } + + g_free (local_task_folder); + + dialog_close (); + } + + if (minor < 5 || (minor == 5 && micro <= 10)) { + gchar *old_path, *new_path; + + old_path = g_build_filename (g_get_home_dir (), "evolution", "local", "Tasks", NULL); + new_path = g_build_filename (e_shell_backend_get_config_dir (shell_backend), + "local", "system", NULL); + migrate_pilot_data ("tasks", "todo", old_path, new_path); + g_free (new_path); + g_free (old_path); + } + + /* we only need to do this next step if people ran + older versions of 1.5. We need to clear out the + absolute URI's that were assigned to ESources + during one phase of development, as they take + precedent over relative uris (but aren't updated + when editing an ESource). */ + if (minor == 5 && micro <= 11) { + GSList *g; + for (g = e_source_list_peek_groups (source_list); g; g = g->next) { + ESourceGroup *group = g->data; + GSList *s; + + for (s = e_source_group_peek_sources (group); s; s = s->next) { + ESource *source = s->data; + e_source_set_absolute_uri (source, NULL); + } + } + } + } +#endif /* !G_OS_WIN32 */ + e_source_list_sync (source_list, NULL); + retval = TRUE; +fail: + if (on_this_computer) + g_object_unref (on_this_computer); + if (on_the_web) + g_object_unref (on_the_web); + if (personal_source) + g_object_unref (personal_source); + + return retval; +} diff --git a/modules/calendar/e-task-shell-migrate.h b/modules/calendar/e-task-shell-migrate.h new file mode 100644 index 0000000000..4cb91c9f4a --- /dev/null +++ b/modules/calendar/e-task-shell-migrate.h @@ -0,0 +1,38 @@ +/* + * e-task-shell-backend-migrate.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_BACKEND_MIGRATE_H +#define E_TASK_SHELL_BACKEND_MIGRATE_H + +#include <glib.h> +#include <shell/e-shell-backend.h> + +G_BEGIN_DECLS + +gboolean e_task_shell_backend_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error); + +G_END_DECLS + +#endif /* E_TASK_SHELL_BACKEND_MIGRATE_H */ diff --git a/modules/calendar/e-task-shell-sidebar.c b/modules/calendar/e-task-shell-sidebar.c new file mode 100644 index 0000000000..6bd7700fcf --- /dev/null +++ b/modules/calendar/e-task-shell-sidebar.c @@ -0,0 +1,714 @@ +/* + * e-task-shell-sidebar.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-sidebar.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal.h> + +#include "e-util/e-error.h" +#include "e-util/e-util.h" +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/e-task-list-selector.h" +#include "calendar/gui/misc.h" + +#include "e-task-shell-backend.h" +#include "e-task-shell-view.h" + +#define E_TASK_SHELL_SIDEBAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarPrivate)) + +struct _ETaskShellSidebarPrivate { + GtkWidget *selector; + + /* UID -> Client */ + GHashTable *client_table; +}; + +enum { + PROP_0, + PROP_SELECTOR +}; + +enum { + CLIENT_ADDED, + CLIENT_REMOVED, + STATUS_MESSAGE, + LAST_SIGNAL +}; + +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; +static GType task_shell_sidebar_type; + +static void +task_shell_sidebar_emit_client_added (ETaskShellSidebar *task_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_ADDED]; + + g_signal_emit (task_shell_sidebar, signal_id, 0, client); +} + +static void +task_shell_sidebar_emit_client_removed (ETaskShellSidebar *task_shell_sidebar, + ECal *client) +{ + guint signal_id = signals[CLIENT_REMOVED]; + + g_signal_emit (task_shell_sidebar, signal_id, 0, client); +} + +static void +task_shell_sidebar_emit_status_message (ETaskShellSidebar *task_shell_sidebar, + const gchar *status_message) +{ + guint signal_id = signals[STATUS_MESSAGE]; + + g_signal_emit (task_shell_sidebar, signal_id, 0, status_message, -1.0); +} + +static void +task_shell_sidebar_backend_died_cb (ETaskShellSidebar *task_shell_sidebar, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = task_shell_sidebar->priv->client_table; + + shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + source = e_cal_get_source (client); + uid = e_source_peek_uid (source); + + g_object_ref (source); + + g_hash_table_remove (client_table, uid); + task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL); + + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:tasks-crashed", NULL); + + g_object_unref (source); +} + +static void +task_shell_sidebar_backend_error_cb (ETaskShellSidebar *task_shell_sidebar, + const gchar *message, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + GtkWidget *dialog; + const gchar *uri; + gchar *uri_no_passwd; + + shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + uri = e_cal_get_uri (client); + uri_no_passwd = get_uri_without_password (uri); + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("Error on %s\n%s"), + uri_no_passwd, message); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + g_free (uri_no_passwd); +} + +static void +task_shell_sidebar_client_opened_cb (ETaskShellSidebar *task_shell_sidebar, + ECalendarStatus status, + ECal *client) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + ESource *source; + + source = e_cal_get_source (client); + + shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (status == E_CALENDAR_STATUS_AUTHENTICATION_FAILED || + status == E_CALENDAR_STATUS_AUTHENTICATION_REQUIRED) + auth_cal_forget_password (client); + + switch (status) { + case E_CALENDAR_STATUS_OK: + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + task_shell_sidebar_client_opened_cb, NULL); + + task_shell_sidebar_emit_status_message ( + task_shell_sidebar, _("Loading tasks")); + task_shell_sidebar_emit_client_added ( + task_shell_sidebar, client); + task_shell_sidebar_emit_status_message ( + task_shell_sidebar, NULL); + break; + + case E_CALENDAR_STATUS_AUTHENTICATION_FAILED: + e_cal_open_async (client, FALSE); + break; + + case E_CALENDAR_STATUS_BUSY: + break; + + case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: + e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-no-contents-offline-tasks", + NULL); + break; + + default: + task_shell_sidebar_emit_client_removed ( + task_shell_sidebar, client); + break; + } +} + +static void +task_shell_sidebar_row_changed_cb (ETaskShellSidebar *task_shell_sidebar, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + GtkTreeModel *tree_model) +{ + ESourceSelector *selector; + ESource *source; + + /* XXX ESourceSelector's underlying tree store has only one + * column: ESource objects. While we're not supposed to + * know this, listening for "row-changed" signals from + * the model is easier to deal with than the selector's + * "selection-changed" signal, which doesn't tell you + * _which_ row changed. */ + + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + gtk_tree_model_get (tree_model, tree_iter, 0, &source, -1); + + /* XXX This signal gets emitted a lot while the model is being + * rebuilt, during which time we won't get a valid ESource. + * ESourceSelector should probably block this signal while + * rebuilding the model, but we'll be forgiving and not + * emit a warning. */ + if (!E_IS_SOURCE (source)) + return; + + if (e_source_selector_source_is_selected (selector, source)) + e_task_shell_sidebar_add_source (task_shell_sidebar, source); + else + e_task_shell_sidebar_remove_source (task_shell_sidebar, source); +} + +static void +task_shell_sidebar_selection_changed_cb (ETaskShellSidebar *task_shell_sidebar, + ESourceSelector *selector) +{ + GSList *list, *iter; + + /* This signal is emitted less frequently than "row-changed", + * especially when the model is being rebuilt. So we'll take + * it easy on poor GConf. */ + + list = e_source_selector_get_selection (selector); + + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + iter->data = (gpointer) e_source_peek_uid (source); + g_object_unref (source); + } + + calendar_config_set_tasks_selected (list); + + g_slist_free (list); +} + +static void +task_shell_sidebar_primary_selection_changed_cb (ETaskShellSidebar *task_shell_sidebar, + ESourceSelector *selector) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESource *source; + + /* XXX ESourceSelector needs a "primary-selection-uid" property + * so we can just bind the property with GConfBridge. */ + + source = e_source_selector_peek_primary_selection (selector); + if (source == NULL) + return; + + shell_sidebar = E_SHELL_SIDEBAR (task_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_window = e_shell_view_get_shell_window (shell_view); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + e_shell_settings_set_string ( + shell_settings, "cal-primary-task-list", + e_source_peek_uid (source)); +} + +static void +task_shell_sidebar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SELECTOR: + g_value_set_object ( + value, e_task_shell_sidebar_get_selector ( + E_TASK_SHELL_SIDEBAR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +task_shell_sidebar_dispose (GObject *object) +{ + ETaskShellSidebarPrivate *priv; + + priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object); + + if (priv->selector != NULL) { + g_object_unref (priv->selector); + priv->selector = NULL; + } + + g_hash_table_remove_all (priv->client_table); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +task_shell_sidebar_finalize (GObject *object) +{ + ETaskShellSidebarPrivate *priv; + + priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object); + + g_hash_table_destroy (priv->client_table); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +task_shell_sidebar_constructed (GObject *object) +{ + ETaskShellSidebarPrivate *priv; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellSidebar *shell_sidebar; + EShellSettings *shell_settings; + ESourceSelector *selector; + ESourceList *source_list; + ESource *source; + GtkContainer *container; + GtkTreeModel *model; + GtkWidget *widget; + AtkObject *a11y; + GSList *list, *iter; + gchar *uid; + + priv = E_TASK_SHELL_SIDEBAR_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + shell_sidebar = E_SHELL_SIDEBAR (object); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + source_list = e_task_shell_backend_get_source_list ( + E_TASK_SHELL_BACKEND (shell_backend)); + + container = GTK_CONTAINER (shell_sidebar); + + widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_container_add (container, widget); + gtk_widget_show (widget); + + container = GTK_CONTAINER (widget); + + widget = e_task_list_selector_new (source_list); + e_source_selector_set_select_new (E_SOURCE_SELECTOR (widget), TRUE); + gtk_container_add (container, widget); + a11y = gtk_widget_get_accessible (widget); + atk_object_set_name (a11y, _("Task List Selector")); + priv->selector = g_object_ref (widget); + gtk_widget_show (widget); + + /* Restore the selector state from the last session. */ + + selector = E_SOURCE_SELECTOR (priv->selector); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + g_signal_connect_swapped ( + model, "row-changed", + G_CALLBACK (task_shell_sidebar_row_changed_cb), + object); + + source = NULL; + uid = e_shell_settings_get_string ( + shell_settings, "cal-primary-task-list"); + if (uid != NULL) + source = e_source_list_peek_source_by_uid (source_list, uid); + if (source == NULL) + source = e_source_list_peek_source_any (source_list); + if (source != NULL) + e_source_selector_set_primary_selection (selector, source); + g_free (uid); + + list = calendar_config_get_tasks_selected (); + for (iter = list; iter != NULL; iter = iter->next) { + uid = iter->data; + source = e_source_list_peek_source_by_uid (source_list, uid); + g_free (uid); + + if (source == NULL) + continue; + + e_source_selector_select_source (selector, source); + } + g_slist_free (list); + + /* Listen for subsequent changes to the selector. */ + + g_signal_connect_swapped ( + widget, "selection-changed", + G_CALLBACK (task_shell_sidebar_selection_changed_cb), + object); + + g_signal_connect_swapped ( + widget, "primary-selection-changed", + G_CALLBACK (task_shell_sidebar_primary_selection_changed_cb), + object); +} + +static guint32 +task_shell_sidebar_check_state (EShellSidebar *shell_sidebar) +{ + ETaskShellSidebar *task_shell_sidebar; + ESourceSelector *selector; + ESource *source; + gboolean is_system = FALSE; + guint32 state = 0; + + task_shell_sidebar = E_TASK_SHELL_SIDEBAR (shell_sidebar); + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + + if (source != NULL) { + const gchar *uri; + + uri = e_source_peek_relative_uri (source); + is_system = (uri == NULL || strcmp (uri, "system") == 0); + } + + if (source != NULL) + state |= E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE; + if (is_system) + state |= E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM; + + return state; +} + +static void +task_shell_sidebar_client_removed (ETaskShellSidebar *task_shell_sidebar, + ECal *client) +{ + ESourceSelector *selector; + GHashTable *client_table; + ESource *source; + const gchar *uid; + + client_table = task_shell_sidebar->priv->client_table; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + + g_signal_handlers_disconnect_matched ( + client, G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, task_shell_sidebar); + + source = e_cal_get_source (client); + e_source_selector_unselect_source (selector, source); + + uid = e_source_peek_uid (source); + g_hash_table_remove (client_table, uid); + + task_shell_sidebar_emit_status_message (task_shell_sidebar, NULL); +} + +static void +task_shell_sidebar_class_init (ETaskShellSidebarClass *class) +{ + GObjectClass *object_class; + EShellSidebarClass *shell_sidebar_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ETaskShellSidebarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = task_shell_sidebar_get_property; + object_class->dispose = task_shell_sidebar_dispose; + object_class->finalize = task_shell_sidebar_finalize; + object_class->constructed = task_shell_sidebar_constructed; + + shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class); + shell_sidebar_class->check_state = task_shell_sidebar_check_state; + + class->client_removed = task_shell_sidebar_client_removed; + + g_object_class_install_property ( + object_class, + PROP_SELECTOR, + g_param_spec_object ( + "selector", + _("Source Selector Widget"), + _("This widget displays groups of task lists"), + E_TYPE_SOURCE_SELECTOR, + G_PARAM_READABLE)); + + signals[CLIENT_ADDED] = g_signal_new ( + "client-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ETaskShellSidebarClass, client_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[CLIENT_REMOVED] = g_signal_new ( + "client-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ETaskShellSidebarClass, client_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_CAL); + + signals[STATUS_MESSAGE] = g_signal_new ( + "status-message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ETaskShellSidebarClass, status_message), + NULL, NULL, + e_marshal_VOID__STRING_DOUBLE, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_DOUBLE); +} + +static void +task_shell_sidebar_init (ETaskShellSidebar *task_shell_sidebar) +{ + GHashTable *client_table; + + client_table = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + task_shell_sidebar->priv = + E_TASK_SHELL_SIDEBAR_GET_PRIVATE (task_shell_sidebar); + + task_shell_sidebar->priv->client_table = client_table; + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_task_shell_sidebar_get_type (void) +{ + return task_shell_sidebar_type; +} + +void +e_task_shell_sidebar_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (ETaskShellSidebarClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) task_shell_sidebar_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (ETaskShellSidebar), + 0, /* n_preallocs */ + (GInstanceInitFunc) task_shell_sidebar_init, + NULL /* value_table */ + }; + + task_shell_sidebar_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_SIDEBAR, + "ETaskShellSidebar", &type_info, 0); +} + +GtkWidget * +e_task_shell_sidebar_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_TASK_SHELL_SIDEBAR, + "shell-view", shell_view, NULL); +} + +GList * +e_task_shell_sidebar_get_clients (ETaskShellSidebar *task_shell_sidebar) +{ + GHashTable *client_table; + + g_return_val_if_fail ( + E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL); + + client_table = task_shell_sidebar->priv->client_table; + + return g_hash_table_get_values (client_table); +} + +ESourceSelector * +e_task_shell_sidebar_get_selector (ETaskShellSidebar *task_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar), NULL); + + return E_SOURCE_SELECTOR (task_shell_sidebar->priv->selector); +} + +void +e_task_shell_sidebar_add_source (ETaskShellSidebar *task_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + const gchar *uri; + gchar *message; + + g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = task_shell_sidebar->priv->client_table; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client != NULL) + return; + + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_TODO); + g_return_if_fail (client != NULL); + + g_signal_connect_swapped ( + client, "backend-died", + G_CALLBACK (task_shell_sidebar_backend_died_cb), + task_shell_sidebar); + + g_signal_connect_swapped ( + client, "backend-error", + G_CALLBACK (task_shell_sidebar_backend_error_cb), + task_shell_sidebar); + + g_hash_table_insert (client_table, g_strdup (uid), client); + e_source_selector_select_source (selector, source); + + uri = e_cal_get_uri (client); + message = g_strdup_printf (_("Opening tasks at %s"), uri); + task_shell_sidebar_emit_status_message (task_shell_sidebar, message); + g_free (message); + + g_signal_connect_swapped ( + client, "cal-opened", + G_CALLBACK (task_shell_sidebar_client_opened_cb), + task_shell_sidebar); + + e_cal_open_async (client, FALSE); +} + +void +e_task_shell_sidebar_remove_source (ETaskShellSidebar *task_shell_sidebar, + ESource *source) +{ + ESourceSelector *selector; + GHashTable *client_table; + ECal *client; + const gchar *uid; + + g_return_if_fail (E_IS_TASK_SHELL_SIDEBAR (task_shell_sidebar)); + g_return_if_fail (E_IS_SOURCE (source)); + + client_table = task_shell_sidebar->priv->client_table; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (client_table, uid); + + if (client == NULL) + return; + + task_shell_sidebar_emit_client_removed (task_shell_sidebar, client); +} diff --git a/modules/calendar/e-task-shell-sidebar.h b/modules/calendar/e-task-shell-sidebar.h new file mode 100644 index 0000000000..5d4c74fe11 --- /dev/null +++ b/modules/calendar/e-task-shell-sidebar.h @@ -0,0 +1,97 @@ +/* + * e-task-shell-sidebar.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_SIDEBAR_H +#define E_TASK_SHELL_SIDEBAR_H + +#include <libecal/e-cal.h> +#include <libedataserverui/e-source-selector.h> + +#include <shell/e-shell-sidebar.h> +#include <shell/e-shell-view.h> + +/* Standard GObject macros */ +#define E_TYPE_TASK_SHELL_SIDEBAR \ + (e_task_shell_sidebar_get_type ()) +#define E_TASK_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebar)) +#define E_TASK_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarClass)) +#define E_IS_TASK_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_TASK_SHELL_SIDEBAR)) +#define E_IS_TASK_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_TASK_SHELL_SIDEBAR)) +#define E_TASK_SHELL_SIDEBAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_TASK_SHELL_SIDEBAR, ETaskShellSidebarClass)) + +G_BEGIN_DECLS + +typedef struct _ETaskShellSidebar ETaskShellSidebar; +typedef struct _ETaskShellSidebarClass ETaskShellSidebarClass; +typedef struct _ETaskShellSidebarPrivate ETaskShellSidebarPrivate; + +enum { + E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE = 1 << 0, + E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM = 1 << 1 +}; + +struct _ETaskShellSidebar { + EShellSidebar parent; + ETaskShellSidebarPrivate *priv; +}; + +struct _ETaskShellSidebarClass { + EShellSidebarClass parent_class; + + /* Signals */ + void (*client_added) (ETaskShellSidebar *task_shell_sidebar, + ECal *client); + void (*client_removed) (ETaskShellSidebar *task_shell_sidebar, + ECal *client); + void (*status_message) (ETaskShellSidebar *task_shell_sidebar, + const gchar *status_message, + gdouble percent); +}; + +GType e_task_shell_sidebar_get_type (void); +void e_task_shell_sidebar_register_type + (GTypeModule *type_module); +GtkWidget * e_task_shell_sidebar_new(EShellView *shell_view); +GList * e_task_shell_sidebar_get_clients + (ETaskShellSidebar *task_shell_sidebar); +ESourceSelector * + e_task_shell_sidebar_get_selector + (ETaskShellSidebar *task_shell_sidebar); +void e_task_shell_sidebar_add_source + (ETaskShellSidebar *task_shell_sidebar, + ESource *source); +void e_task_shell_sidebar_remove_source + (ETaskShellSidebar *task_shell_sidebar, + ESource *source); + +G_END_DECLS + +#endif /* E_TASK_SHELL_SIDEBAR_H */ diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c new file mode 100644 index 0000000000..d4fb94c2e5 --- /dev/null +++ b/modules/calendar/e-task-shell-view-actions.c @@ -0,0 +1,1222 @@ +/* + * e-task-shell-view-actions.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-view-private.h" + +static void +action_gal_save_custom_view_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + EShellView *shell_view; + GalViewInstance *view_instance; + + /* All shell views respond to the activation of this action, + * which is defined by EShellWindow. But only the currently + * active shell view proceeds with saving the custom view. */ + shell_view = E_SHELL_VIEW (task_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + task_shell_content = task_shell_view->priv->task_shell_content; + view_instance = e_task_shell_content_get_view_instance (task_shell_content); + gal_view_instance_save_as (view_instance); +} + +static void +action_search_execute_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + + /* All shell views respond to the activation of this action, + * which is defined by EShellWindow. But only the currently + * active shell view proceeds with executing the search. */ + shell_view = E_SHELL_VIEW (task_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + e_task_shell_view_execute_search (task_shell_view); +} + +static void +action_search_filter_cb (GtkRadioAction *action, + GtkRadioAction *current, + ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); +} + +static void +action_task_assign_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + GSList *list; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the first selected task. */ + e_task_shell_view_open_task (task_shell_view, comp_data); + + /* FIXME Need to actually assign the task. */ +} + +static void +action_task_clipboard_copy_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + e_calendar_table_copy_clipboard (task_table); +} + +static void +action_task_clipboard_cut_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + e_calendar_table_cut_clipboard (task_table); +} + +static void +action_task_clipboard_paste_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + e_calendar_table_paste_clipboard (task_table); +} + +static void +action_task_delete_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalComponentPreview *task_preview; + ECalendarTable *task_table; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + task_preview = e_task_shell_content_get_task_preview (task_shell_content); + + e_task_shell_view_set_status_message ( + task_shell_view, _("Deleting selected tasks..."), -1.0); + e_calendar_table_delete_selected (task_table); + e_task_shell_view_set_status_message (task_shell_view, NULL, -1.0); + + e_cal_component_preview_clear (task_preview); +} + +static void +action_task_forward_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GSList *list; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only forward the first selected task. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + itip_send_comp ( + E_CAL_COMPONENT_METHOD_PUBLISH, comp, + comp_data->client, NULL, NULL, NULL, TRUE, FALSE); + g_object_unref (comp); +} + +static void +action_task_list_copy_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellSidebar *task_shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + ESourceSelector *selector; + ESource *source; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + copy_source_dialog ( + GTK_WINDOW (shell_window), + source, E_CAL_SOURCE_TYPE_TODO); +} + +static void +action_task_list_delete_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellBackend *task_shell_backend; + ETaskShellContent *task_shell_content; + ETaskShellSidebar *task_shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + ECalendarTable *task_table; + ECal *client; + ECalModel *model; + ESourceSelector *selector; + ESourceGroup *source_group; + ESourceList *source_list; + ESource *source; + gint response; + gchar *uri; + GError *error = NULL; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + task_shell_backend = task_shell_view->priv->task_shell_backend; + source_list = e_task_shell_backend_get_source_list (task_shell_backend); + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + model = e_calendar_table_get_model (task_table); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + /* Ask for confirmation. */ + response = e_error_run ( + GTK_WINDOW (shell_window), + "calendar:prompt-delete-task-list", + e_source_peek_name (source)); + if (response != GTK_RESPONSE_YES) + return; + + uri = e_source_get_uri (source); + client = e_cal_model_get_client_for_uri (model, uri); + if (client == NULL) + client = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_JOURNAL); + g_free (uri); + + g_return_if_fail (client != NULL); + + if (!e_cal_remove (client, &error)) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + if (e_source_selector_source_is_selected (selector, source)) { + e_task_shell_sidebar_remove_source ( + task_shell_sidebar, source); + e_source_selector_unselect_source (selector, source); + } + + source_group = e_source_peek_group (source); + e_source_group_remove_source (source_group, source); + + if (!e_source_list_sync (source_list, &error)) { + g_warning ("%s", error->message); + g_error_free (error); + } +} + +static void +action_task_list_new_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + calendar_setup_new_task_list (GTK_WINDOW (shell_window)); +} + +static void +action_task_list_print_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ETable *table; + GtkPrintOperationAction print_action; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + table = e_calendar_table_get_table (task_table); + + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + print_table (table, _("Print Tasks"), _("Tasks"), print_action); +} + +static void +action_task_list_print_preview_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ETable *table; + GtkPrintOperationAction print_action; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + table = e_calendar_table_get_table (task_table); + + print_action = GTK_PRINT_OPERATION_ACTION_PREVIEW; + print_table (table, _("Print Tasks"), _("Tasks"), print_action); +} + +static void +action_task_list_properties_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellSidebar *task_shell_sidebar; + EShellView *shell_view; + EShellWindow *shell_window; + ESource *source; + ESourceSelector *selector; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + source = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (E_IS_SOURCE (source)); + + calendar_setup_edit_task_list (GTK_WINDOW (shell_window), source); +} + +static void +action_task_list_rename_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellSidebar *task_shell_sidebar; + ESourceSelector *selector; + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + + e_source_selector_edit_primary_selection (selector); +} + +static void +action_task_list_select_one_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellSidebar *task_shell_sidebar; + ESourceSelector *selector; + ESource *primary; + GSList *list, *iter; + + /* XXX ESourceSelector should provide a function for this. */ + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + primary = e_source_selector_peek_primary_selection (selector); + g_return_if_fail (primary != NULL); + + list = e_source_selector_get_selection (selector); + for (iter = list; iter != NULL; iter = iter->next) { + ESource *source = iter->data; + + if (source == primary) + continue; + + e_source_selector_unselect_source (selector, source); + } + e_source_selector_free_selection (list); + + e_source_selector_select_source (selector, primary); +} + +static void +action_task_mark_complete_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModel *model; + GSList *list, *iter; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + list = e_calendar_table_get_selected (task_table); + model = e_calendar_table_get_model (task_table); + + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + e_cal_model_tasks_mark_comp_complete ( + E_CAL_MODEL_TASKS (model), comp_data); + } + + g_slist_free (list); +} + +static void +action_task_mark_incomplete_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModel *model; + GSList *list, *iter; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + list = e_calendar_table_get_selected (task_table); + model = e_calendar_table_get_model (task_table); + + for (iter = list; iter != NULL; iter = iter->next) { + ECalModelComponent *comp_data = iter->data; + e_cal_model_tasks_mark_comp_incomplete ( + E_CAL_MODEL_TASKS (model), comp_data); + } + + g_slist_free (list); +} + +static void +action_task_new_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + ECal *client; + ECalComponent *comp; + CompEditor *editor; + GSList *list; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + client = comp_data->client; + editor = task_editor_new (client, shell, COMP_EDITOR_NEW_ITEM); + comp = cal_comp_task_new_with_defaults (client); + comp_editor_edit_comp (editor, comp); + + gtk_window_present (GTK_WINDOW (editor)); + + g_object_unref (comp); + g_object_unref (client); +} + +static void +action_task_open_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + GSList *list; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only open the first selected task. */ + e_task_shell_view_open_task (task_shell_view, comp_data); +} + +static void +action_task_open_url_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + icalproperty *prop; + const gchar *uri; + GSList *list; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + + /* XXX We only open the URI of the first selected task. */ + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_URL_PROPERTY); + g_return_if_fail (prop == NULL); + + uri = icalproperty_get_url (prop); + e_show_uri (GTK_WINDOW (shell_window), uri); +} + +static void +action_task_preview_cb (GtkToggleAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + gboolean visible; + + task_shell_content = task_shell_view->priv->task_shell_content; + visible = gtk_toggle_action_get_active (action); + e_task_shell_content_set_preview_visible (task_shell_content, visible); +} + +static void +action_task_print_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + ECalComponent *comp; + icalcomponent *clone; + GtkPrintOperationAction print_action; + GSList *list; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + /* XXX We only print the first selected task. */ + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + print_action = GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG; + e_cal_component_set_icalcomponent (comp, clone); + print_comp (comp, comp_data->client, print_action); + g_object_unref (comp); +} + +static void +action_task_purge_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkWidget *dialog; + GtkWidget *widget; + gboolean active; + gint response; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (!e_task_shell_view_get_confirm_purge (task_shell_view)) + goto purge; + + /* XXX This needs reworked. The dialog looks like ass. */ + + dialog = gtk_message_dialog_new ( + GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_YES_NO, + "%s", _("This operation will permanently erase all tasks " + "marked as completed. If you continue, you will not be able " + "to recover these tasks.\n\nReally erase these tasks?")); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_NO); + + widget = gtk_check_button_new_with_label (_("Do not ask me again")); + gtk_box_pack_start ( + GTK_BOX (GTK_DIALOG (dialog)->vbox), widget, TRUE, TRUE, 6); + gtk_widget_show (widget); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + gtk_widget_destroy (dialog); + + if (response != GTK_RESPONSE_YES) + return; + + if (active) + e_task_shell_view_set_confirm_purge (task_shell_view, FALSE); + +purge: + + /* FIXME KILL-BONOBO */ + ; +} + +static void +action_task_save_as_cb (GtkAction *action, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModelComponent *comp_data; + GSList *list; + gchar *filename; + gchar *string; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + list = e_calendar_table_get_selected (task_table); + g_return_if_fail (list != NULL); + comp_data = list->data; + g_slist_free (list); + + filename = e_file_dialog_save (_("Save as..."), NULL); + if (filename == NULL) + return; + + string = e_cal_get_component_as_string ( + comp_data->client, comp_data->icalcomp); + if (string == NULL) { + g_warning ("Could not convert task to a string"); + return; + } + + e_write_file_uri (filename, string); + + g_free (filename); + g_free (string); +} + +static void +action_task_search_cb (GtkRadioAction *action, + GtkRadioAction *current, + ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + EShellContent *shell_content; + const gchar *search_hint; + + /* XXX Figure out a way to handle this in EShellContent + * instead of every shell view having to handle it. + * The problem is EShellContent does not know what + * the search option actions are for this view. It + * would have to dig up the popup menu and retrieve + * the action for each menu item. Seems messy. */ + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + search_hint = gtk_action_get_label (GTK_ACTION (current)); + e_shell_content_set_search_hint (shell_content, search_hint); +} + +static void +action_task_view_cb (GtkRadioAction *action, + GtkRadioAction *current, + ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + GtkOrientable *orientable; + GtkOrientation orientation; + + task_shell_content = task_shell_view->priv->task_shell_content; + orientable = GTK_ORIENTABLE (task_shell_content); + + switch (gtk_radio_action_get_current_value (action)) { + case 0: + orientation = GTK_ORIENTATION_VERTICAL; + break; + case 1: + orientation = GTK_ORIENTATION_HORIZONTAL; + break; + default: + g_return_if_reached (); + } + + gtk_orientable_set_orientation (orientable, orientation); +} + +static GtkActionEntry task_entries[] = { + + { "task-assign", + NULL, + N_("_Assign Task"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_assign_cb) }, + + { "task-clipboard-copy", + GTK_STOCK_COPY, + NULL, + NULL, + N_("Copy selected tasks"), + G_CALLBACK (action_task_clipboard_copy_cb) }, + + { "task-clipboard-cut", + GTK_STOCK_CUT, + NULL, + NULL, + N_("Cut selected tasks"), + G_CALLBACK (action_task_clipboard_cut_cb) }, + + { "task-clipboard-paste", + GTK_STOCK_PASTE, + NULL, + NULL, + N_("Paste tasks from the clipboard"), + G_CALLBACK (action_task_clipboard_paste_cb) }, + + { "task-delete", + GTK_STOCK_DELETE, + N_("_Delete Task"), + NULL, + N_("Delete selected tasks"), + G_CALLBACK (action_task_delete_cb) }, + + { "task-forward", + "mail-forward", + N_("_Forward as iCalendar..."), + "<Control>f", + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_forward_cb) }, + + { "task-list-copy", + GTK_STOCK_COPY, + N_("Copy..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_list_copy_cb) }, + + { "task-list-delete", + GTK_STOCK_DELETE, + N_("_Delete"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_list_delete_cb) }, + + { "task-list-new", + "stock_todo", + N_("_New Task List"), + NULL, + N_("Create a new task list"), + G_CALLBACK (action_task_list_new_cb) }, + + { "task-list-properties", + GTK_STOCK_PROPERTIES, + NULL, + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_list_properties_cb) }, + + { "task-list-rename", + NULL, + N_("_Rename..."), + "F2", + N_("Rename the selected task list"), + G_CALLBACK (action_task_list_rename_cb) }, + + { "task-list-select-one", + "stock_check-filled", + N_("Show _Only This Task List"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_list_select_one_cb) }, + + { "task-mark-complete", + NULL, + N_("_Mark as Complete"), + "<Control>k", + N_("Mark selected tasks as complete"), + G_CALLBACK (action_task_mark_complete_cb) }, + + { "task-mark-incomplete", + NULL, + N_("Mar_k as Incomplete"), + NULL, + N_("Mark selected tasks as incomplete"), + G_CALLBACK (action_task_mark_incomplete_cb) }, + + { "task-new", + "stock_task", + N_("New _Task"), + NULL, + N_("Create a new task"), + G_CALLBACK (action_task_new_cb) }, + + { "task-open", + GTK_STOCK_OPEN, + N_("_Open Task"), + "<Control>o", + N_("View the selected task"), + G_CALLBACK (action_task_open_cb) }, + + { "task-open-url", + "applications-internet", + N_("Open _Web Page"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_open_url_cb) }, + + { "task-purge", + NULL, + N_("Purg_e"), + "<Control>e", + N_("Delete completed tasks"), + G_CALLBACK (action_task_purge_cb) }, + + { "task-save-as", + GTK_STOCK_SAVE_AS, + N_("_Save as iCalendar..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_task_save_as_cb) }, + + /*** Menus ***/ + + { "task-actions-menu", + NULL, + N_("_Actions"), + NULL, + NULL, + NULL }, + + { "task-preview-menu", + NULL, + N_("_Preview"), + NULL, + NULL, + NULL } +}; + +static EPopupActionEntry task_popup_entries[] = { + + { "task-list-popup-copy", + NULL, + "task-list-copy" }, + + { "task-list-popup-delete", + NULL, + "task-list-delete" }, + + { "task-list-popup-properties", + NULL, + "task-list-properties" }, + + { "task-list-popup-rename", + NULL, + "task-list-rename" }, + + { "task-list-popup-select-one", + NULL, + "task-list-select-one" }, + + { "task-popup-assign", + NULL, + "task-assign" }, + + { "task-popup-clipboard-copy", + NULL, + "task-clipboard-copy" }, + + { "task-popup-clipboard-cut", + NULL, + "task-clipboard-cut" }, + + { "task-popup-clipboard-paste", + NULL, + "task-clipboard-paste" }, + + { "task-popup-delete", + NULL, + "task-delete" }, + + { "task-popup-forward", + NULL, + "task-forward" }, + + { "task-popup-mark-complete", + NULL, + "task-mark-complete" }, + + { "task-popup-mark-incomplete", + NULL, + "task-mark-incomplete" }, + + { "task-popup-open", + NULL, + "task-open" }, + + { "task-popup-open-url", + NULL, + "task-open-url" }, + + { "task-popup-save-as", + NULL, + "task-save-as" }, +}; + +static GtkToggleActionEntry task_toggle_entries[] = { + + { "task-preview", + NULL, + N_("Task _Preview"), + "<Control>m", + N_("Show task preview pane"), + G_CALLBACK (action_task_preview_cb), + TRUE } +}; + +static GtkRadioActionEntry task_view_entries[] = { + + /* This action represents the inital active memo view. + * It should not be visible in the UI, nor should it be + * possible to switch to it from another shell view. */ + { "task-view-initial", + NULL, + NULL, + NULL, + NULL, + -1 }, + + { "task-view-classic", + NULL, + N_("_Classic View"), + NULL, + N_("Show task preview below the task list"), + 0 }, + + { "task-view-vertical", + NULL, + N_("_Vertical View"), + NULL, + N_("Show task preview alongside the task list"), + 1 } +}; + +static GtkRadioActionEntry task_filter_entries[] = { + + { "task-filter-active-tasks", + NULL, + N_("Active Tasks"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_ACTIVE_TASKS }, + + { "task-filter-any-category", + NULL, + N_("Any Category"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_ANY_CATEGORY }, + + { "task-filter-completed-tasks", + NULL, + N_("Completed Tasks"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_COMPLETED_TASKS }, + + { "task-filter-next-7-days-tasks", + NULL, + N_("Next 7 Days' Tasks"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_NEXT_7_DAYS_TASKS }, + + { "task-filter-overdue-tasks", + NULL, + N_("Overdue Tasks"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_OVERDUE_TASKS }, + + { "task-filter-tasks-with-attachments", + NULL, + N_("Tasks with Attachments"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_TASKS_WITH_ATTACHMENTS }, + + { "task-filter-unmatched", + NULL, + N_("Unmatched"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_FILTER_UNMATCHED } +}; + +static GtkRadioActionEntry task_search_entries[] = { + + { "task-search-any-field-contains", + NULL, + N_("Any field contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_SEARCH_ANY_FIELD_CONTAINS }, + + { "task-search-description-contains", + NULL, + N_("Description contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_SEARCH_DESCRIPTION_CONTAINS }, + + { "task-search-summary-contains", + NULL, + N_("Summary contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + TASK_SEARCH_SUMMARY_CONTAINS } +}; + +static GtkActionEntry lockdown_printing_entries[] = { + + { "task-list-print", + GTK_STOCK_PRINT, + NULL, + "<Control>p", + N_("Print the list of tasks"), + G_CALLBACK (action_task_list_print_cb) }, + + { "task-list-print-preview", + GTK_STOCK_PRINT_PREVIEW, + NULL, + NULL, + N_("Preview the list of tasks to be printed"), + G_CALLBACK (action_task_list_print_preview_cb) }, + + { "task-print", + GTK_STOCK_PRINT, + NULL, + NULL, + N_("Print the selected task"), + G_CALLBACK (action_task_print_cb) } +}; + +static EPopupActionEntry lockdown_printing_popup_entries[] = { + + { "task-popup-print", + NULL, + "task-print" } +}; + +void +e_task_shell_view_actions_init (ETaskShellView *task_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkActionGroup *action_group; + GConfBridge *bridge; + GtkAction *action; + GObject *object; + const gchar *key; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + /* Task Actions */ + action_group = ACTION_GROUP (TASKS); + gtk_action_group_add_actions ( + action_group, task_entries, + G_N_ELEMENTS (task_entries), task_shell_view); + e_action_group_add_popup_actions ( + action_group, task_popup_entries, + G_N_ELEMENTS (task_popup_entries)); + gtk_action_group_add_toggle_actions ( + action_group, task_toggle_entries, + G_N_ELEMENTS (task_toggle_entries), task_shell_view); + gtk_action_group_add_radio_actions ( + action_group, task_view_entries, + G_N_ELEMENTS (task_view_entries), -1, + G_CALLBACK (action_task_view_cb), task_shell_view); + gtk_action_group_add_radio_actions ( + action_group, task_search_entries, + G_N_ELEMENTS (task_search_entries), + TASK_SEARCH_SUMMARY_CONTAINS, + G_CALLBACK (action_task_search_cb), task_shell_view); + + /* Lockdown Printing Actions */ + action_group = ACTION_GROUP (LOCKDOWN_PRINTING); + gtk_action_group_add_actions ( + action_group, lockdown_printing_entries, + G_N_ELEMENTS (lockdown_printing_entries), task_shell_view); + e_action_group_add_popup_actions ( + action_group, lockdown_printing_popup_entries, + G_N_ELEMENTS (lockdown_printing_popup_entries)); + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (ACTION (TASK_PREVIEW)); + key = "/apps/evolution/calendar/display/show_task_preview"; + gconf_bridge_bind_property (bridge, key, object, "active"); + + object = G_OBJECT (ACTION (TASK_VIEW_VERTICAL)); + key = "/apps/evolution/calendar/display/task_layout"; + gconf_bridge_bind_property (bridge, key, object, "current-value"); + + /* Fine tuning. */ + + action = ACTION (TASK_DELETE); + g_object_set (action, "short-label", _("Delete"), NULL); + + g_signal_connect ( + ACTION (GAL_SAVE_CUSTOM_VIEW), "activate", + G_CALLBACK (action_gal_save_custom_view_cb), task_shell_view); + + g_signal_connect ( + ACTION (SEARCH_EXECUTE), "activate", + G_CALLBACK (action_search_execute_cb), task_shell_view); +} + +void +e_task_shell_view_update_search_filter (ETaskShellView *task_shell_view) +{ + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + GtkActionGroup *action_group; + GtkRadioAction *radio_action; + GList *list, *iter; + GSList *group; + gint ii; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + action_group = ACTION_GROUP (TASKS_FILTER); + e_action_group_remove_all_actions (action_group); + + /* Add the standard filter actions. */ + gtk_action_group_add_radio_actions ( + action_group, task_filter_entries, + G_N_ELEMENTS (task_filter_entries), + TASK_FILTER_ANY_CATEGORY, + G_CALLBACK (action_search_filter_cb), + task_shell_view); + + /* Retrieve the radio group from an action we just added. */ + list = gtk_action_group_list_actions (action_group); + radio_action = GTK_RADIO_ACTION (list->data); + group = gtk_radio_action_get_group (radio_action); + g_list_free (list); + + /* Build the category actions. */ + + list = e_categories_get_list (); + for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) { + const gchar *category_name = iter->data; + const gchar *filename; + GtkAction *action; + gchar *action_name; + + action_name = g_strdup_printf ( + "task-filter-category-%d", ii); + radio_action = gtk_radio_action_new ( + action_name, category_name, NULL, NULL, ii); + g_free (action_name); + + /* Convert the category icon file to a themed icon name. */ + filename = e_categories_get_icon_file_for (category_name); + if (filename != NULL && *filename != '\0') { + gchar *basename; + gchar *cp; + + basename = g_path_get_basename (filename); + + /* Lose the file extension. */ + if ((cp = strrchr (basename, '.')) != NULL) + *cp = '\0'; + + g_object_set ( + radio_action, "icon-name", basename, NULL); + + g_free (basename); + } + + gtk_radio_action_set_group (radio_action, group); + group = gtk_radio_action_get_group (radio_action); + + /* The action group takes ownership of the action. */ + action = GTK_ACTION (radio_action); + gtk_action_group_add_action (action_group, action); + g_object_unref (radio_action); + } + g_list_free (list); + + /* Use any action in the group; doesn't matter which. */ + e_shell_content_set_filter_action (shell_content, radio_action); + + ii = TASK_FILTER_UNMATCHED; + e_shell_content_add_filter_separator_after (shell_content, ii); + + ii = TASK_FILTER_TASKS_WITH_ATTACHMENTS; + e_shell_content_add_filter_separator_after (shell_content, ii); +} diff --git a/modules/calendar/e-task-shell-view-actions.h b/modules/calendar/e-task-shell-view-actions.h new file mode 100644 index 0000000000..daa70c36da --- /dev/null +++ b/modules/calendar/e-task-shell-view-actions.h @@ -0,0 +1,109 @@ +/* + * e-task-shell-view-actions.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_VIEW_ACTIONS_H +#define E_TASK_SHELL_VIEW_ACTIONS_H + +#include <shell/e-shell-window-actions.h> + +/* Task Actions */ +#define E_SHELL_WINDOW_ACTION_TASK_ASSIGN(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-assign") +#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-clipboard-copy") +#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_CUT(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-clipboard-cut") +#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_PASTE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-clipboard-paste") +#define E_SHELL_WINDOW_ACTION_TASK_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-delete") +#define E_SHELL_WINDOW_ACTION_TASK_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-forward") +#define E_SHELL_WINDOW_ACTION_TASK_MARK_COMPLETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-mark-complete") +#define E_SHELL_WINDOW_ACTION_TASK_MARK_INCOMPLETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-mark-incomplete") +#define E_SHELL_WINDOW_ACTION_TASK_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-new") +#define E_SHELL_WINDOW_ACTION_TASK_OPEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-open") +#define E_SHELL_WINDOW_ACTION_TASK_OPEN_URL(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-open-url") +#define E_SHELL_WINDOW_ACTION_TASK_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-preview") +#define E_SHELL_WINDOW_ACTION_TASK_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-print") +#define E_SHELL_WINDOW_ACTION_TASK_PURGE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-purge") +#define E_SHELL_WINDOW_ACTION_TASK_SAVE_AS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-save-as") +#define E_SHELL_WINDOW_ACTION_TASK_VIEW_CLASSIC(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-view-classic") +#define E_SHELL_WINDOW_ACTION_TASK_VIEW_VERTICAL(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-view-vertical") + +/* Task List Actions */ +#define E_SHELL_WINDOW_ACTION_TASK_LIST_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-copy") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-delete") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-new") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-print") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_PRINT_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-print-preview") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_PROPERTIES(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-properties") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_RENAME(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-rename") +#define E_SHELL_WINDOW_ACTION_TASK_LIST_SELECT_ONE(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-list-select-one") + +/* Task Query Actions */ +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_ACTIVE_TASKS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-active-tasks") +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_ANY_CATEGORY(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-any-category") +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_COMPLETED_TASKS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-completed-tasks") +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_NEXT_7_DAYS_TASKS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-next-7-days-tasks") +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_OVERDUE_TASKS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-overdue-tasks") +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_TASKS_WITH_ATTACHMENTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-tasks-with-attachments") +#define E_SHELL_WINDOW_ACTION_TASK_FILTER_UNMATCHED(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-filter-unmatched") +#define E_SHELL_WINDOW_ACTION_TASK_SEARCH_ANY_FIELD_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-search-any-field-contains") +#define E_SHELL_WINDOW_ACTION_TASK_SEARCH_DESCRIPTION_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-search-description-contains") +#define E_SHELL_WINDOW_ACTION_TASK_SEARCH_SUMMARY_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "task-search-summary-contains") + +/* Action Groups */ +#define E_SHELL_WINDOW_ACTION_GROUP_TASKS(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "tasks") +#define E_SHELL_WINDOW_ACTION_GROUP_TASKS_FILTER(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "tasks-filter") + +#endif /* E_TASK_SHELL_VIEW_ACTIONS_H */ diff --git a/modules/calendar/e-task-shell-view-private.c b/modules/calendar/e-task-shell-view-private.c new file mode 100644 index 0000000000..a0ca5ee978 --- /dev/null +++ b/modules/calendar/e-task-shell-view-private.c @@ -0,0 +1,726 @@ +/* + * e-task-shell-view-private.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-view-private.h" + +#include "widgets/menus/gal-view-factory-etable.h" + +static void +task_shell_view_process_completed_tasks (ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ETaskShellSidebar *task_shell_sidebar; + ECalendarTable *task_table; + GList *clients; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + clients = e_task_shell_sidebar_get_clients (task_shell_sidebar); + + e_calendar_table_process_completed_tasks (task_table, clients, TRUE); + + /* Search query takes whether to show completed tasks into account, + * so if the preference has changed we need to update the query. */ + e_task_shell_view_execute_search (task_shell_view); + + g_list_free (clients); +} + +static void +task_shell_view_table_popup_event_cb (EShellView *shell_view, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/task-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); +} + +static void +task_shell_view_table_user_created_cb (ETaskShellView *task_shell_view, + ECalendarTable *task_table) +{ + ETaskShellSidebar *task_shell_sidebar; + ECalModel *model; + ECal *client; + ESource *source; + + /* This is the "Click to Add" handler. */ + + model = e_calendar_table_get_model (task_table); + client = e_cal_model_get_default_client (model); + source = e_cal_get_source (client); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + e_task_shell_sidebar_add_source (task_shell_sidebar, source); + + e_cal_model_add_client (model, client); +} + +static void +task_shell_view_selector_client_added_cb (ETaskShellView *task_shell_view, + ECal *client) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModel *model; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + model = e_calendar_table_get_model (task_table); + + e_cal_model_add_client (model, client); + e_task_shell_view_update_timezone (task_shell_view); +} + +static void +task_shell_view_selector_client_removed_cb (ETaskShellView *task_shell_view, + ECal *client) +{ + ETaskShellContent *task_shell_content; + ECalendarTable *task_table; + ECalModel *model; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + model = e_calendar_table_get_model (task_table); + + e_cal_model_remove_client (model, client); +} + +static gboolean +task_shell_view_selector_popup_event_cb (EShellView *shell_view, + ESource *primary_source, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/task-list-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); + + return TRUE; +} + +static gboolean +task_shell_view_update_timeout_cb (ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ETaskShellSidebar *task_shell_sidebar; + ECalendarTable *task_table; + ECalModel *model; + GList *clients; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + model = e_calendar_table_get_model (task_table); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + clients = e_task_shell_sidebar_get_clients (task_shell_sidebar); + + e_calendar_table_process_completed_tasks (task_table, clients, FALSE); + e_cal_model_tasks_update_due_tasks (E_CAL_MODEL_TASKS (model)); + + g_list_free (clients); + + return TRUE; +} + +static void +task_shell_view_load_view_collection (EShellViewClass *shell_view_class) +{ + GalViewCollection *collection; + GalViewFactory *factory; + ETableSpecification *spec; + const gchar *base_dir; + gchar *filename; + + collection = shell_view_class->view_collection; + + base_dir = EVOLUTION_ETSPECDIR; + spec = e_table_specification_new (); + filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL); + if (!e_table_specification_load_from_file (spec, filename)) + g_critical ("Unable to load ETable specification file " + "for tasks"); + g_free (filename); + + factory = gal_view_factory_etable_new (spec); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + g_object_unref (spec); + + gal_view_collection_load (collection); +} + +static void +task_shell_view_notify_view_id_cb (ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + GalViewInstance *view_instance; + const gchar *view_id; + + task_shell_content = task_shell_view->priv->task_shell_content; + view_instance = + e_task_shell_content_get_view_instance (task_shell_content); + view_id = e_shell_view_get_view_id (E_SHELL_VIEW (task_shell_view)); + + /* A NULL view ID implies we're in a custom view. But you can + * only get to a custom view via the "Define Views" dialog, which + * would have already modified the view instance appropriately. + * Furthermore, there's no way to refer to a custom view by ID + * anyway, since custom views have no IDs. */ + if (view_id == NULL) + return; + + gal_view_instance_set_current_view_id (view_instance, view_id); +} + +void +e_task_shell_view_private_init (ETaskShellView *task_shell_view, + EShellViewClass *shell_view_class) +{ + if (!gal_view_collection_loaded (shell_view_class->view_collection)) + task_shell_view_load_view_collection (shell_view_class); + + g_signal_connect ( + task_shell_view, "notify::view-id", + G_CALLBACK (task_shell_view_notify_view_id_cb), NULL); +} + +void +e_task_shell_view_private_constructed (ETaskShellView *task_shell_view) +{ + ETaskShellViewPrivate *priv = task_shell_view->priv; + ETaskShellContent *task_shell_content; + ETaskShellSidebar *task_shell_sidebar; + EShell *shell; + EShellBackend *shell_backend; + EShellContent *shell_content; + EShellSettings *shell_settings; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + EShellView *shell_view; + ECalendarTable *task_table; + ECalModel *model; + ETable *table; + ESourceSelector *selector; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + e_shell_window_add_action_group (shell_window, "tasks"); + e_shell_window_add_action_group (shell_window, "tasks-filter"); + + /* Cache these to avoid lots of awkward casting. */ + priv->task_shell_backend = g_object_ref (shell_backend); + priv->task_shell_content = g_object_ref (shell_content); + priv->task_shell_sidebar = g_object_ref (shell_sidebar); + + task_shell_content = E_TASK_SHELL_CONTENT (shell_content); + task_table = e_task_shell_content_get_task_table (task_shell_content); + model = e_calendar_table_get_model (task_table); + table = e_calendar_table_get_table (task_table); + + task_shell_sidebar = E_TASK_SHELL_SIDEBAR (shell_sidebar); + selector = e_task_shell_sidebar_get_selector (task_shell_sidebar); + + g_signal_connect_swapped ( + model, "notify::timezone", + G_CALLBACK (e_task_shell_view_update_timezone), + task_shell_view); + + g_signal_connect_swapped ( + task_table, "open-component", + G_CALLBACK (e_task_shell_view_open_task), + task_shell_view); + + g_signal_connect_swapped ( + task_table, "popup-event", + G_CALLBACK (task_shell_view_table_popup_event_cb), + task_shell_view); + + g_signal_connect_swapped ( + task_table, "status-message", + G_CALLBACK (e_task_shell_view_set_status_message), + task_shell_view); + + g_signal_connect_swapped ( + task_table, "user-created", + G_CALLBACK (task_shell_view_table_user_created_cb), + task_shell_view); + + g_signal_connect_swapped ( + model, "model-changed", + G_CALLBACK (e_task_shell_view_update_sidebar), + task_shell_view); + + g_signal_connect_swapped ( + model, "model-rows-deleted", + G_CALLBACK (e_task_shell_view_update_sidebar), + task_shell_view); + + g_signal_connect_swapped ( + model, "model-rows-inserted", + G_CALLBACK (e_task_shell_view_update_sidebar), + task_shell_view); + + g_signal_connect_swapped ( + table, "selection-change", + G_CALLBACK (e_task_shell_view_update_sidebar), + task_shell_view); + + g_signal_connect_swapped ( + task_shell_sidebar, "client-added", + G_CALLBACK (task_shell_view_selector_client_added_cb), + task_shell_view); + + g_signal_connect_swapped ( + task_shell_sidebar, "client-removed", + G_CALLBACK (task_shell_view_selector_client_removed_cb), + task_shell_view); + + g_signal_connect_swapped ( + task_shell_sidebar, "status-message", + G_CALLBACK (e_task_shell_view_set_status_message), + task_shell_view); + + g_signal_connect_swapped ( + selector, "popup-event", + G_CALLBACK (task_shell_view_selector_popup_event_cb), + task_shell_view); + + g_signal_connect_swapped ( + selector, "primary-selection-changed", + G_CALLBACK (e_shell_view_update_actions), + task_shell_view); + + e_categories_register_change_listener ( + G_CALLBACK (e_task_shell_view_update_search_filter), + task_shell_view); + + task_shell_view_update_timeout_cb (task_shell_view); + priv->update_timeout = g_timeout_add_full ( + G_PRIORITY_LOW, 60000, (GSourceFunc) + task_shell_view_update_timeout_cb, + task_shell_view, NULL); + + /* Listen for configuration changes. */ + + e_mutual_binding_new ( + G_OBJECT (shell_settings), "cal-confirm-purge", + G_OBJECT (task_shell_view), "confirm-purge"); + + /* Hide Completed Tasks (enable/units/value) */ + g_signal_connect_swapped ( + shell_settings, "notify::cal-hide-completed-tasks", + G_CALLBACK (task_shell_view_process_completed_tasks), + task_shell_view); + g_signal_connect_swapped ( + shell_settings, "notify::cal-hide-completed-tasks-units", + G_CALLBACK (task_shell_view_process_completed_tasks), + task_shell_view); + g_signal_connect_swapped ( + shell_settings, "notify::cal-hide-completed-tasks-value", + G_CALLBACK (task_shell_view_process_completed_tasks), + task_shell_view); + + e_task_shell_view_actions_init (task_shell_view); + e_task_shell_view_update_sidebar (task_shell_view); + e_task_shell_view_update_search_filter (task_shell_view); + e_task_shell_view_update_timezone (task_shell_view); + + e_task_shell_view_execute_search (task_shell_view); +} + +void +e_task_shell_view_private_dispose (ETaskShellView *task_shell_view) +{ + ETaskShellViewPrivate *priv = task_shell_view->priv; + + DISPOSE (priv->task_shell_backend); + DISPOSE (priv->task_shell_content); + DISPOSE (priv->task_shell_sidebar); + + if (task_shell_view->priv->activity != NULL) { + /* XXX Activity is no cancellable. */ + e_activity_complete (task_shell_view->priv->activity); + g_object_unref (task_shell_view->priv->activity); + task_shell_view->priv->activity = NULL; + } + + if (priv->update_timeout > 0) { + g_source_remove (priv->update_timeout); + priv->update_timeout = 0; + } +} + +void +e_task_shell_view_private_finalize (ETaskShellView *task_shell_view) +{ + /* XXX Nothing to do? */ +} + +void +e_task_shell_view_execute_search (ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + GtkRadioAction *action; + GString *string; + ECalComponentPreview *task_preview; + ECalendarTable *task_table; + ECalModel *model; + FilterRule *rule; + const gchar *format; + const gchar *text; + time_t start_range; + time_t end_range; + gchar *start, *end; + gchar *query; + gchar *temp; + gint value; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + text = e_shell_content_get_search_text (shell_content); + + shell_window = e_shell_view_get_shell_window (shell_view); + action = GTK_RADIO_ACTION (ACTION (TASK_SEARCH_ANY_FIELD_CONTAINS)); + value = gtk_radio_action_get_current_value (action); + + if (text == NULL || *text == '\0') { + text = ""; + value = TASK_SEARCH_SUMMARY_CONTAINS; + } + + switch (value) { + default: + text = ""; + /* fall through */ + + case TASK_SEARCH_SUMMARY_CONTAINS: + format = "(contains? \"summary\" %s)"; + break; + + case TASK_SEARCH_DESCRIPTION_CONTAINS: + format = "(contains? \"description\" %s)"; + break; + + case TASK_SEARCH_ANY_FIELD_CONTAINS: + format = "(contains? \"any\" %s)"; + break; + } + + /* Build the query. */ + string = g_string_new (""); + e_sexp_encode_string (string, text); + query = g_strdup_printf (format, string->str); + g_string_free (string, TRUE); + + /* Apply selected filter. */ + value = e_shell_content_get_filter_value (shell_content); + switch (value) { + case TASK_FILTER_ANY_CATEGORY: + break; + + case TASK_FILTER_UNMATCHED: + temp = g_strdup_printf ( + "(and (has-categories? #f) %s)", query); + g_free (query); + query = temp; + break; + + case TASK_FILTER_NEXT_7_DAYS_TASKS: + start_range = time (NULL); + end_range = time_add_day (start_range, 7); + start = isodate_from_time_t (start_range); + end = isodate_from_time_t (end_range); + + temp = g_strdup_printf ( + "(and %s (due-in-time-range? " + "(make-time \"%s\") (make-time \"%s\")))", + query, start, end); + g_free (query); + query = temp; + break; + + case TASK_FILTER_ACTIVE_TASKS: + start_range = time (NULL); + end_range = time_add_day (start_range, 365); + start = isodate_from_time_t (start_range); + end = isodate_from_time_t (end_range); + + temp = g_strdup_printf ( + "(and %s (due-in-time-range? " + "(make-time \"%s\") (make-time \"%s\")) " + "(not (is-completed?)))", + query, start, end); + g_free (query); + query = temp; + break; + + case TASK_FILTER_OVERDUE_TASKS: + start_range = 0; + end_range = time (NULL); + start = isodate_from_time_t (start_range); + end = isodate_from_time_t (end_range); + + temp = g_strdup_printf ( + "(and %s (due-in-time-range? " + "(make-time \"%s\") (make-time \"%s\")) " + "(not (is-completed?)))", + query, start, end); + g_free (query); + query = temp; + break; + + case TASK_FILTER_COMPLETED_TASKS: + temp = g_strdup_printf ( + "(and (is-completed?) %s)", query); + g_free (query); + query = temp; + break; + + case TASK_FILTER_TASKS_WITH_ATTACHMENTS: + temp = g_strdup_printf ( + "(and (has-attachments?) %s)", query); + g_free (query); + query = temp; + break; + + default: + { + GList *categories; + const gchar *category_name; + + categories = e_categories_get_list (); + category_name = g_list_nth_data (categories, value); + g_list_free (categories); + + temp = g_strdup_printf ( + "(and (has-categories? \"%s\") %s)", + category_name, query); + g_free (query); + query = temp; + break; + } + } + + /* Honor the user's preference to hide completed tasks. */ + temp = calendar_config_get_hide_completed_tasks_sexp (FALSE); + if (temp != NULL) { + gchar *temp2; + + temp2 = g_strdup_printf ("(and %s %s)", temp, query); + g_free (query); + g_free (temp); + query = temp2; + } + + /* XXX This is wrong. We need to programmatically construct a + * FilterRule, tell it to build code, and pass the resulting + * expression string to ECalModel. */ + rule = filter_rule_new (); + e_shell_content_set_search_rule (shell_content, rule); + g_object_unref (rule); + + /* Submit the query. */ + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + model = e_calendar_table_get_model (task_table); + e_cal_model_set_search_query (model, query); + g_free (query); + + task_preview = + e_task_shell_content_get_task_preview (task_shell_content); + e_cal_component_preview_clear (task_preview); +} + +void +e_task_shell_view_open_task (ETaskShellView *task_shell_view, + ECalModelComponent *comp_data) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + CompEditor *editor; + CompEditorFlags flags = 0; + ECalComponent *comp; + icalcomponent *clone; + icalproperty *prop; + const gchar *uid; + + g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view)); + g_return_if_fail (E_IS_CAL_MODEL_COMPONENT (comp_data)); + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell = e_shell_window_get_shell (shell_window); + + uid = icalcomponent_get_uid (comp_data->icalcomp); + editor = comp_editor_find_instance (uid); + + if (editor != NULL) + goto exit; + + comp = e_cal_component_new (); + clone = icalcomponent_new_clone (comp_data->icalcomp); + e_cal_component_set_icalcomponent (comp, clone); + + prop = icalcomponent_get_first_property ( + comp_data->icalcomp, ICAL_ATTENDEE_PROPERTY); + if (prop != NULL) + flags |= COMP_EDITOR_IS_ASSIGNED; + + if (itip_organizer_is_user (comp, comp_data->client)) + flags |= COMP_EDITOR_USER_ORG; + + if (!e_cal_component_has_attendees (comp)) + flags |= COMP_EDITOR_USER_ORG; + + editor = task_editor_new (comp_data->client, shell, flags); + comp_editor_edit_comp (editor, comp); + + g_object_ref (comp); + + if (flags & COMP_EDITOR_IS_ASSIGNED) + task_editor_show_assignment (TASK_EDITOR (editor)); + +exit: + gtk_window_present (GTK_WINDOW (editor)); +} + +void +e_task_shell_view_set_status_message (ETaskShellView *task_shell_view, + const gchar *status_message, + gdouble percent) +{ + EActivity *activity; + EShellView *shell_view; + EShellBackend *shell_backend; + + g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view)); + + activity = task_shell_view->priv->activity; + shell_view = E_SHELL_VIEW (task_shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + + if (status_message == NULL || *status_message == '\0') { + if (activity != NULL) { + e_activity_complete (activity); + g_object_unref (activity); + activity = NULL; + } + + } else if (activity == NULL) { + activity = e_activity_new (status_message); + e_activity_set_percent (activity, percent); + e_shell_backend_add_activity (shell_backend, activity); + + } else { + e_activity_set_percent (activity, percent); + e_activity_set_primary_text (activity, status_message); + } + + task_shell_view->priv->activity = activity; +} + +void +e_task_shell_view_update_sidebar (ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + EShellView *shell_view; + EShellSidebar *shell_sidebar; + ECalendarTable *task_table; + ECalModel *model; + ETable *table; + GString *string; + const gchar *format; + gint n_rows; + gint n_selected; + + shell_view = E_SHELL_VIEW (task_shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + + task_shell_content = task_shell_view->priv->task_shell_content; + task_table = e_task_shell_content_get_task_table (task_shell_content); + + model = e_calendar_table_get_model (task_table); + table = e_calendar_table_get_table (task_table); + + n_rows = e_table_model_row_count (E_TABLE_MODEL (model)); + n_selected = e_table_selected_count (table); + + string = g_string_sized_new (64); + + format = ngettext ("%d task", "%d tasks", n_rows); + g_string_append_printf (string, format, n_rows); + + if (n_selected > 0) { + format = _("%d selected"); + g_string_append_len (string, ", ", 2); + g_string_append_printf (string, format, n_selected); + } + + e_shell_sidebar_set_secondary_text (shell_sidebar, string->str); + + g_string_free (string, TRUE); +} + +void +e_task_shell_view_update_timezone (ETaskShellView *task_shell_view) +{ + ETaskShellContent *task_shell_content; + ETaskShellSidebar *task_shell_sidebar; + ECalComponentPreview *task_preview; + icaltimezone *timezone; + ECalModel *model; + GList *clients, *iter; + + task_shell_content = task_shell_view->priv->task_shell_content; + task_preview = e_task_shell_content_get_task_preview (task_shell_content); + model = e_task_shell_content_get_task_model (task_shell_content); + timezone = e_cal_model_get_timezone (model); + + task_shell_sidebar = task_shell_view->priv->task_shell_sidebar; + clients = e_task_shell_sidebar_get_clients (task_shell_sidebar); + + for (iter = clients; iter != NULL; iter = iter->next) { + ECal *client = iter->data; + + if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED) + e_cal_set_default_timezone (client, timezone, NULL); + } + + e_cal_component_preview_set_default_timezone (task_preview, timezone); + + g_list_free (clients); +} diff --git a/modules/calendar/e-task-shell-view-private.h b/modules/calendar/e-task-shell-view-private.h new file mode 100644 index 0000000000..e612069bed --- /dev/null +++ b/modules/calendar/e-task-shell-view-private.h @@ -0,0 +1,141 @@ +/* + * e-task-shell-view-private.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_VIEW_PRIVATE_H +#define E_TASK_SHELL_VIEW_PRIVATE_H + +#include "e-task-shell-view.h" + +#include <string.h> +#include <glib/gi18n.h> +#include <libecal/e-cal-time-util.h> +#include <libedataserver/e-categories.h> +#include <libedataserver/e-sexp.h> + +#include "e-util/e-binding.h" +#include "e-util/e-dialog-utils.h" +#include "e-util/e-error.h" +#include "e-util/e-util.h" +#include "e-util/gconf-bridge.h" +#include "widgets/misc/e-popup-action.h" + +#include "calendar/common/authentication.h" +#include "calendar/gui/calendar-config.h" +#include "calendar/gui/comp-util.h" +#include "calendar/gui/e-cal-component-preview.h" +#include "calendar/gui/e-cal-model-tasks.h" +#include "calendar/gui/e-calendar-selector.h" +#include "calendar/gui/print.h" +#include "calendar/gui/dialogs/calendar-setup.h" +#include "calendar/gui/dialogs/copy-source-dialog.h" +#include "calendar/gui/dialogs/task-editor.h" + +#include "e-task-shell-backend.h" +#include "e-task-shell-content.h" +#include "e-task-shell-sidebar.h" +#include "e-task-shell-view-actions.h" + +#define E_TASK_SHELL_VIEW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewPrivate)) + +/* Shorthand, requires a variable named "shell_window". */ +#define ACTION(name) \ + (E_SHELL_WINDOW_ACTION_##name (shell_window)) +#define ACTION_GROUP(name) \ + (E_SHELL_WINDOW_ACTION_GROUP_##name (shell_window)) + +/* For use in dispose() methods. */ +#define DISPOSE(obj) \ + G_STMT_START { \ + if ((obj) != NULL) { g_object_unref (obj); (obj) = NULL; } \ + } G_STMT_END + +/* ETable Specifications */ +#define ETSPEC_FILENAME "e-calendar-table.etspec" + +G_BEGIN_DECLS + +/* Filter items are displayed in ascending order. + * Non-negative values are reserved for categories. */ +enum { + TASK_FILTER_ANY_CATEGORY = -7, + TASK_FILTER_UNMATCHED = -6, + TASK_FILTER_NEXT_7_DAYS_TASKS = -5, + TASK_FILTER_ACTIVE_TASKS = -4, + TASK_FILTER_OVERDUE_TASKS = -3, + TASK_FILTER_COMPLETED_TASKS = -2, + TASK_FILTER_TASKS_WITH_ATTACHMENTS = -1 +}; + +/* Search items are displayed in ascending order. */ +enum { + TASK_SEARCH_SUMMARY_CONTAINS, + TASK_SEARCH_DESCRIPTION_CONTAINS, + TASK_SEARCH_ANY_FIELD_CONTAINS +}; + +struct _ETaskShellViewPrivate { + + /* These are just for convenience. */ + ETaskShellBackend *task_shell_backend; + ETaskShellContent *task_shell_content; + ETaskShellSidebar *task_shell_sidebar; + + EActivity *activity; + guint update_timeout; + + guint confirm_purge : 1; +}; + +void e_task_shell_view_private_init + (ETaskShellView *task_shell_view, + EShellViewClass *shell_view_class); +void e_task_shell_view_private_constructed + (ETaskShellView *task_shell_view); +void e_task_shell_view_private_dispose + (ETaskShellView *task_shell_view); +void e_task_shell_view_private_finalize + (ETaskShellView *task_shell_view); + +/* Private Utilities */ + +void e_task_shell_view_actions_init + (ETaskShellView *task_shell_view); +void e_task_shell_view_execute_search + (ETaskShellView *task_shell_view); +void e_task_shell_view_open_task + (ETaskShellView *task_shell_view, + ECalModelComponent *comp_data); +void e_task_shell_view_set_status_message + (ETaskShellView *task_shell_view, + const gchar *status_message, + gdouble percent); +void e_task_shell_view_update_sidebar + (ETaskShellView *task_shell_view); +void e_task_shell_view_update_search_filter + (ETaskShellView *task_shell_view); +void e_task_shell_view_update_timezone + (ETaskShellView *task_shell_view); + +G_END_DECLS + +#endif /* E_TASK_SHELL_VIEW_PRIVATE_H */ diff --git a/modules/calendar/e-task-shell-view.c b/modules/calendar/e-task-shell-view.c new file mode 100644 index 0000000000..b1298eaa29 --- /dev/null +++ b/modules/calendar/e-task-shell-view.c @@ -0,0 +1,325 @@ +/* + * e-task-shell-view.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-task-shell-view-private.h" + +enum { + PROP_0, + PROP_CONFIRM_PURGE +}; + +static gpointer parent_class; +static GType task_shell_view_type; + +static void +task_shell_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CONFIRM_PURGE: + e_task_shell_view_set_confirm_purge ( + E_TASK_SHELL_VIEW (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +task_shell_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CONFIRM_PURGE: + g_value_set_boolean ( + value, e_task_shell_view_get_confirm_purge ( + E_TASK_SHELL_VIEW (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +task_shell_view_dispose (GObject *object) +{ + e_task_shell_view_private_dispose (E_TASK_SHELL_VIEW (object)); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +task_shell_view_finalize (GObject *object) +{ + e_task_shell_view_private_finalize (E_TASK_SHELL_VIEW (object)); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +task_shell_view_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + e_task_shell_view_private_constructed (E_TASK_SHELL_VIEW (object)); +} + +static void +task_shell_view_update_actions (EShellView *shell_view) +{ + ETaskShellViewPrivate *priv; + EShellContent *shell_content; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + GtkAction *action; + const gchar *label; + gboolean sensitive; + guint32 state; + + /* Be descriptive. */ + gboolean any_tasks_selected; + gboolean has_primary_source; + gboolean multiple_tasks_selected; + gboolean primary_source_is_system; + gboolean selection_has_url; + gboolean selection_is_assignable; + gboolean single_task_selected; + gboolean some_tasks_complete; + gboolean some_tasks_incomplete; + gboolean sources_are_editable; + + priv = E_TASK_SHELL_VIEW_GET_PRIVATE (shell_view); + + shell_window = e_shell_view_get_shell_window (shell_view); + + shell_content = e_shell_view_get_shell_content (shell_view); + state = e_shell_content_check_state (shell_content); + + single_task_selected = + (state & E_TASK_SHELL_CONTENT_SELECTION_SINGLE); + multiple_tasks_selected = + (state & E_TASK_SHELL_CONTENT_SELECTION_MULTIPLE); + selection_is_assignable = + (state & E_TASK_SHELL_CONTENT_SELECTION_CAN_ASSIGN); + sources_are_editable = + (state & E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT); + some_tasks_complete = + (state & E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE); + some_tasks_incomplete = + (state & E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE); + selection_has_url = + (state & E_TASK_SHELL_CONTENT_SELECTION_HAS_URL); + + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + state = e_shell_sidebar_check_state (shell_sidebar); + + has_primary_source = + (state & E_TASK_SHELL_SIDEBAR_HAS_PRIMARY_SOURCE); + primary_source_is_system = + (state & E_TASK_SHELL_SIDEBAR_PRIMARY_SOURCE_IS_SYSTEM); + + any_tasks_selected = + (single_task_selected || multiple_tasks_selected); + + action = ACTION (TASK_ASSIGN); + sensitive = + single_task_selected && sources_are_editable && + selection_is_assignable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_CLIPBOARD_COPY); + sensitive = any_tasks_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_CLIPBOARD_CUT); + sensitive = any_tasks_selected && sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_CLIPBOARD_PASTE); + sensitive = sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_DELETE); + sensitive = any_tasks_selected && sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + if (multiple_tasks_selected) + label = _("Delete Tasks"); + else + label = _("Delete Task"); + g_object_set (action, "label", label, NULL); + + action = ACTION (TASK_FORWARD); + sensitive = single_task_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_LIST_COPY); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_LIST_DELETE); + sensitive = has_primary_source && !primary_source_is_system; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_LIST_PROPERTIES); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_LIST_RENAME); + sensitive = has_primary_source; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_MARK_COMPLETE); + sensitive = + any_tasks_selected && + sources_are_editable && + some_tasks_incomplete; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_MARK_INCOMPLETE); + sensitive = + any_tasks_selected && + sources_are_editable && + some_tasks_complete; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_OPEN); + sensitive = single_task_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_OPEN_URL); + sensitive = single_task_selected && selection_has_url; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_PRINT); + sensitive = single_task_selected; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_PURGE); + sensitive = sources_are_editable; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (TASK_SAVE_AS); + sensitive = single_task_selected; + gtk_action_set_sensitive (action, sensitive); +} + +static void +task_shell_view_class_init (ETaskShellViewClass *class, + GTypeModule *type_module) +{ + GObjectClass *object_class; + EShellViewClass *shell_view_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (ETaskShellViewPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = task_shell_view_set_property; + object_class->get_property = task_shell_view_get_property; + object_class->dispose = task_shell_view_dispose; + object_class->finalize = task_shell_view_finalize; + object_class->constructed = task_shell_view_constructed; + + shell_view_class = E_SHELL_VIEW_CLASS (class); + shell_view_class->label = _("Tasks"); + shell_view_class->icon_name = "evolution-tasks"; + shell_view_class->ui_definition = "evolution-tasks.ui"; + shell_view_class->ui_manager_id = "org.gnome.evolution.tasks"; + shell_view_class->search_options = "/task-search-options"; + shell_view_class->search_rules = "tasktypes.xml"; + shell_view_class->new_shell_content = e_task_shell_content_new; + shell_view_class->new_shell_sidebar = e_task_shell_sidebar_new; + shell_view_class->update_actions = task_shell_view_update_actions; + + g_object_class_install_property ( + object_class, + PROP_CONFIRM_PURGE, + g_param_spec_boolean ( + "confirm-purge", + "Confirm Purge", + NULL, + TRUE, + G_PARAM_READWRITE)); +} + +static void +task_shell_view_init (ETaskShellView *task_shell_view, + EShellViewClass *shell_view_class) +{ + task_shell_view->priv = + E_TASK_SHELL_VIEW_GET_PRIVATE (task_shell_view); + + e_task_shell_view_private_init (task_shell_view, shell_view_class); +} + +GType +e_task_shell_view_get_type (void) +{ + return task_shell_view_type; +} + +void +e_task_shell_view_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (ETaskShellViewClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) task_shell_view_class_init, + (GClassFinalizeFunc) NULL, + type_module, + sizeof (ETaskShellView), + 0, /* n_preallocs */ + (GInstanceInitFunc) task_shell_view_init, + NULL /* value_table */ + }; + + task_shell_view_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_VIEW, + "ETaskShellView", &type_info, 0); +} + +gboolean +e_task_shell_view_get_confirm_purge (ETaskShellView *task_shell_view) +{ + g_return_val_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view), FALSE); + + return task_shell_view->priv->confirm_purge; +} + +void +e_task_shell_view_set_confirm_purge (ETaskShellView *task_shell_view, + gboolean confirm_purge) +{ + g_return_if_fail (E_IS_TASK_SHELL_VIEW (task_shell_view)); + + task_shell_view->priv->confirm_purge = confirm_purge; + + g_object_notify (G_OBJECT (task_shell_view), "confirm-purge"); +} diff --git a/modules/calendar/e-task-shell-view.h b/modules/calendar/e-task-shell-view.h new file mode 100644 index 0000000000..853d90cac1 --- /dev/null +++ b/modules/calendar/e-task-shell-view.h @@ -0,0 +1,72 @@ +/* + * e-task-shell-view.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_TASK_SHELL_VIEW_H +#define E_TASK_SHELL_VIEW_H + +#include <shell/e-shell-view.h> +#include <libedataserver/e-source-list.h> + +/* Standard GObject macros */ +#define E_TYPE_TASK_SHELL_VIEW \ + (e_task_shell_view_get_type ()) +#define E_TASK_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellView)) +#define E_TASK_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewClass)) +#define E_IS_TASK_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_TASK_SHELL_VIEW)) +#define E_IS_TASK_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_TASK_SHELL_VIEW)) +#define E_TASK_SHELL_VIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_TASK_SHELL_VIEW, ETaskShellViewClass)) + +G_BEGIN_DECLS + +typedef struct _ETaskShellView ETaskShellView; +typedef struct _ETaskShellViewClass ETaskShellViewClass; +typedef struct _ETaskShellViewPrivate ETaskShellViewPrivate; + +struct _ETaskShellView { + EShellView parent; + ETaskShellViewPrivate *priv; +}; + +struct _ETaskShellViewClass { + EShellViewClass parent_class; +}; + +GType e_task_shell_view_get_type (void); +void e_task_shell_view_register_type (GTypeModule *type_module); +gboolean e_task_shell_view_get_confirm_purge + (ETaskShellView *task_shell_view); +void e_task_shell_view_set_confirm_purge + (ETaskShellView *task_shell_view, + gboolean confirm_purge); + +G_END_DECLS + +#endif /* E_TASK_SHELL_VIEW_H */ diff --git a/modules/calendar/evolution-module-calendar.c b/modules/calendar/evolution-module-calendar.c new file mode 100644 index 0000000000..f72e8a97e4 --- /dev/null +++ b/modules/calendar/evolution-module-calendar.c @@ -0,0 +1,75 @@ +/* + * evolution-module-calendar.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; 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 + * 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/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-cal-attachment-handler.h" + +#include "e-cal-config-hook.h" +#include "e-cal-event-hook.h" + +#include "e-cal-shell-backend.h" +#include "e-cal-shell-content.h" +#include "e-cal-shell-sidebar.h" +#include "e-cal-shell-view.h" + +#include "e-memo-shell-backend.h" +#include "e-memo-shell-content.h" +#include "e-memo-shell-sidebar.h" +#include "e-memo-shell-view.h" + +#include "e-task-shell-backend.h" +#include "e-task-shell-content.h" +#include "e-task-shell-sidebar.h" +#include "e-task-shell-view.h" + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + /* Register dynamically loaded types. */ + + e_cal_attachment_handler_register_type (type_module); + + e_cal_config_hook_register_type (type_module); + e_cal_event_hook_register_type (type_module); + + e_cal_shell_backend_register_type (type_module); + e_cal_shell_content_register_type (type_module); + e_cal_shell_sidebar_register_type (type_module); + e_cal_shell_view_register_type (type_module); + + e_memo_shell_backend_register_type (type_module); + e_memo_shell_content_register_type (type_module); + e_memo_shell_sidebar_register_type (type_module); + e_memo_shell_view_register_type (type_module); + + e_task_shell_backend_register_type (type_module); + e_task_shell_content_register_type (type_module); + e_task_shell_sidebar_register_type (type_module); + e_task_shell_view_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} |