diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2009-06-25 00:59:33 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2009-06-25 06:29:22 +0800 |
commit | f0d3f3afdfa314e1e8cd7d8da790878008a46aad (patch) | |
tree | 7116e2a18c7bd50633b2f8de42b5377af1e8172a /modules/mail | |
parent | 94302ea73cde0b470faad653f752406f19f202d7 (diff) | |
download | gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.gz gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.tar.zst gsoc2013-evolution-f0d3f3afdfa314e1e8cd7d8da790878008a46aad.zip |
Radically reorganize source code.
- Collect all shell modules into a new top-level 'modules' directory:
$(top_srcdir)/modules/addressbook
$(top_srcdir)/modules/calendar
$(top_srcdir)/modules/mail
Nothing is allowed to link to these, not plugins nor other modules.
THIS SOLVES BUG #571275 AND OPENS THE DOOR TO PORTING TO MAC OS X.
- Mimic the libevolution-mail-shared library from master (except drop
the "shared" suffix) and have libevolution-mail-importers and all
mail-related plugins link to it.
- Discard the a11y subdirectories and have the files live alongside
their counterpart widgets.
Diffstat (limited to 'modules/mail')
30 files changed, 16993 insertions, 0 deletions
diff --git a/modules/mail/Makefile.am b/modules/mail/Makefile.am new file mode 100644 index 0000000000..ab73a5d542 --- /dev/null +++ b/modules/mail/Makefile.am @@ -0,0 +1,56 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/mail \ + -I$(top_srcdir)/widgets \ + $(EVOLUTION_MAIL_CFLAGS) \ + -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ + -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \ + -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \ + -DG_LOG_DOMAIN=\"evolution-module-mail\" + +module_LTLIBRARIES = libevolution-module-mail.la + +libevolution_module_mail_la_SOURCES = \ + evolution-module-mail.c \ + e-attachment-handler-mail.c \ + e-attachment-handler-mail.h \ + e-mail-shell-backend.c \ + e-mail-shell-backend.h \ + e-mail-shell-content.c \ + e-mail-shell-content.h \ + e-mail-shell-migrate.c \ + e-mail-shell-migrate.h \ + e-mail-shell-settings.c \ + e-mail-shell-settings.h \ + e-mail-shell-sidebar.c \ + e-mail-shell-sidebar.h \ + e-mail-shell-view.c \ + e-mail-shell-view.h \ + e-mail-shell-view-actions.c \ + e-mail-shell-view-actions.h \ + e-mail-shell-view-private.c \ + e-mail-shell-view-private.h \ + em-account-editor.c \ + em-account-editor.h \ + em-account-prefs.c \ + em-account-prefs.h \ + em-composer-prefs.c \ + em-composer-prefs.h \ + em-mailer-prefs.c \ + em-mailer-prefs.h \ + em-network-prefs.c \ + em-network-prefs.h + +libevolution_module_mail_la_LIBADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/shell/libeshell.la \ + $(top_builddir)/composer/libcomposer.la \ + $(top_builddir)/widgets/table/libetable.la \ + $(top_builddir)/widgets/text/libetext.la \ + $(top_builddir)/widgets/misc/libemiscwidgets.la \ + $(top_builddir)/mail/importers/libevolution-mail-importers.la + +libevolution_module_mail_la_LDFLAGS = \ + -avoid-version -module $(NO_UNDEFINED) + +-include $(top_srcdir)/git.mk diff --git a/modules/mail/e-attachment-handler-mail.c b/modules/mail/e-attachment-handler-mail.c new file mode 100644 index 0000000000..c17c97d8ca --- /dev/null +++ b/modules/mail/e-attachment-handler-mail.c @@ -0,0 +1,524 @@ +/* + * e-attachment-handler-mail.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-attachment-handler-mail.h" + +#include <glib/gi18n.h> +#include <camel/camel-folder.h> +#include <camel/camel-stream-mem.h> + +#include "e-util/e-error.h" +#include "mail/em-composer-utils.h" +#include "mail/mail-tools.h" + +#define E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailPrivate)) + +struct _EAttachmentHandlerMailPrivate { + gint placeholder; +}; + +static gpointer parent_class; + +static const gchar *ui = +"<ui>" +" <popup name='context'>" +" <placeholder name='custom-actions'>" +" <menuitem action='mail-reply-sender'/>" +" <menuitem action='mail-reply-all'/>" +" <menuitem action='mail-forward'/>" +" </placeholder>" +" </popup>" +"</ui>"; + +/* Note: Do not use the info field. */ +static GtkTargetEntry target_table[] = { + { (gchar *) "message/rfc822", 0, 0 }, + { (gchar *) "x-uid-list", 0, 0 } +}; + +static void +attachment_handler_mail_forward (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GList *selected; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + em_utils_forward_message (CAMEL_MIME_MESSAGE (wrapper), NULL); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +attachment_handler_mail_reply_all (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GList *selected; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + em_utils_reply_to_message ( + NULL, NULL, CAMEL_MIME_MESSAGE (wrapper), + REPLY_MODE_ALL, NULL); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +attachment_handler_mail_reply_sender (GtkAction *action, + EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GList *selected; + + selected = e_attachment_view_get_selected_attachments (view); + g_return_if_fail (g_list_length (selected) == 1); + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + em_utils_reply_to_message ( + NULL, NULL, CAMEL_MIME_MESSAGE (wrapper), + REPLY_MODE_SENDER, NULL); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static GtkActionEntry standard_entries[] = { + + { "mail-forward", + "mail-forward", + N_("_Forward"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_mail_forward) }, + + { "mail-reply-all", + "mail-reply-all", + N_("Reply to _All"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_mail_reply_all) }, + + { "mail-reply-sender", + "mail-reply-sender", + N_("_Reply to Sender"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (attachment_handler_mail_reply_sender) } +}; + +static void +attachment_handler_mail_message_rfc822 (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + static GdkAtom atom = GDK_NONE; + EAttachmentStore *store; + EAttachment *attachment; + CamelMimeMessage *message; + CamelDataWrapper *wrapper; + CamelStream *stream; + const gchar *data; + gboolean success = FALSE; + gpointer parent; + gint length; + + if (G_UNLIKELY (atom == GDK_NONE)) + atom = gdk_atom_intern_static_string ("message/rfc822"); + + if (gtk_selection_data_get_target (selection_data) != atom) + return; + + g_signal_stop_emission_by_name (view, "drag-data-received"); + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + + stream = camel_stream_mem_new (); + camel_stream_write (stream, data, length); + camel_stream_reset (stream); + + message = camel_mime_message_new (); + wrapper = CAMEL_DATA_WRAPPER (message); + + if (camel_data_wrapper_construct_from_stream (wrapper, stream) == -1) + goto exit; + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + attachment = e_attachment_new_for_message (message); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + success = TRUE; + +exit: + camel_object_unref (message); + camel_object_unref (stream); + + gtk_drag_finish (drag_context, success, FALSE, time); +} + +static void +attachment_handler_mail_x_uid_list (EAttachmentView *view, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + static GdkAtom atom = GDK_NONE; + CamelException ex = CAMEL_EXCEPTION_INITIALISER; + CamelDataWrapper *wrapper; + CamelMimeMessage *message; + CamelMultipart *multipart; + CamelMimePart *mime_part; + CamelFolder *folder = NULL; + EAttachment *attachment; + EAttachmentStore *store; + GPtrArray *uids; + const gchar *data; + const gchar *cp, *end; + gchar *description; + gpointer parent; + gint length; + guint ii; + + if (G_UNLIKELY (atom == GDK_NONE)) + atom = gdk_atom_intern_static_string ("x-uid-list"); + + if (gtk_selection_data_get_target (selection_data) != atom) + return; + + store = e_attachment_view_get_store (view); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + uids = g_ptr_array_new (); + + data = (const gchar *) gtk_selection_data_get_data (selection_data); + length = gtk_selection_data_get_length (selection_data); + + /* The UID list is delimited by NUL characters. + * Brilliant. So we can't use g_strsplit(). */ + + cp = data; + end = data + length; + + while (cp < end) { + const gchar *start = cp; + + while (cp < end && *cp != '\0') + cp++; + + /* Skip the first string. */ + if (start > data) + g_ptr_array_add (uids, g_strndup (start, cp - start)); + + cp++; + } + + if (uids->len == 0) + goto exit; + + /* The first string is the folder URI. */ + folder = mail_tool_uri_to_folder (data, 0, &ex); + if (folder == NULL) + goto exit; + + /* Handle one message. */ + if (uids->len == 1) { + message = camel_folder_get_message ( + folder, uids->pdata[0], &ex); + if (message == NULL) + goto exit; + + attachment = e_attachment_new_for_message (message); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + camel_object_unref (message); + goto exit; + } + + /* Build a multipart/digest message out of the UIDs. */ + + multipart = camel_multipart_new (); + wrapper = CAMEL_DATA_WRAPPER (multipart); + camel_data_wrapper_set_mime_type (wrapper, "multipart/digest"); + camel_multipart_set_boundary (multipart, NULL); + + for (ii = 0; ii < uids->len; ii++) { + message = camel_folder_get_message ( + folder, uids->pdata[ii], &ex); + if (message == NULL) { + camel_object_unref (multipart); + goto exit; + } + + mime_part = camel_mime_part_new (); + wrapper = CAMEL_DATA_WRAPPER (message); + camel_mime_part_set_disposition (mime_part, "inline"); + camel_medium_set_content_object ( + CAMEL_MEDIUM (mime_part), wrapper); + camel_mime_part_set_content_type (mime_part, "message/rfc822"); + camel_multipart_add_part (multipart, mime_part); + camel_object_unref (mime_part); + + camel_object_unref (message); + } + + mime_part = camel_mime_part_new (); + wrapper = CAMEL_DATA_WRAPPER (multipart); + camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper); + + /* Translators: This is only for multiple messages. */ + description = g_strdup_printf (_("%d attached messages"), uids->len); + camel_mime_part_set_description (mime_part, description); + g_free (description); + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + e_attachment_load_handle_error, parent); + g_object_unref (attachment); + + camel_object_unref (mime_part); + camel_object_unref (multipart); + +exit: + if (camel_exception_is_set (&ex)) { + gchar *folder_name; + + if (folder != NULL) + camel_object_get ( + folder, NULL, CAMEL_FOLDER_NAME, + &folder_name, NULL); + else + folder_name = g_strdup (data); + + e_error_run ( + parent, "mail-composer:attach-nomessages", + folder_name, camel_exception_get_description (&ex), + NULL); + + if (folder != NULL) + camel_object_free ( + folder, CAMEL_FOLDER_NAME, folder_name); + else + g_free (folder_name); + + camel_exception_clear (&ex); + } + + if (folder != NULL) + camel_object_unref (folder); + + g_ptr_array_free (uids, TRUE); + + g_signal_stop_emission_by_name (view, "drag-data-received"); +} + +static void +attachment_handler_mail_update_actions (EAttachmentView *view) +{ + EAttachment *attachment; + CamelMimePart *mime_part; + CamelDataWrapper *wrapper; + GtkActionGroup *action_group; + GList *selected; + gboolean visible = FALSE; + + selected = e_attachment_view_get_selected_attachments (view); + + if (g_list_length (selected) != 1) + goto exit; + + attachment = E_ATTACHMENT (selected->data); + mime_part = e_attachment_get_mime_part (attachment); + + if (!CAMEL_IS_MIME_PART (mime_part)) + goto exit; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + + visible = CAMEL_IS_MIME_MESSAGE (wrapper); + +exit: + action_group = e_attachment_view_get_action_group (view, "mail"); + gtk_action_group_set_visible (action_group, visible); + + g_list_foreach (selected, (GFunc) g_object_unref, NULL); + g_list_free (selected); +} + +static void +attachment_handler_mail_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, "mail"); + gtk_action_group_add_actions ( + action_group, standard_entries, + G_N_ELEMENTS (standard_entries), view); + + 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 (attachment_handler_mail_update_actions), + NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_handler_mail_message_rfc822), + NULL); + + g_signal_connect ( + view, "drag-data-received", + G_CALLBACK (attachment_handler_mail_x_uid_list), + NULL); +} + +static GdkDragAction +attachment_handler_mail_get_drag_actions (EAttachmentHandler *handler) +{ + return GDK_ACTION_COPY; +} + +static const GtkTargetEntry * +attachment_handler_mail_get_target_table (EAttachmentHandler *handler, + guint *n_targets) +{ + if (n_targets != NULL) + *n_targets = G_N_ELEMENTS (target_table); + + return target_table; +} + +static void +attachment_handler_mail_class_init (EAttachmentHandlerMailClass *class) +{ + GObjectClass *object_class; + EAttachmentHandlerClass *handler_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EAttachmentHandlerMailPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = attachment_handler_mail_constructed; + + handler_class = E_ATTACHMENT_HANDLER_CLASS (class); + handler_class->get_drag_actions = attachment_handler_mail_get_drag_actions; + handler_class->get_target_table = attachment_handler_mail_get_target_table; +} + +static void +attachment_handler_mail_init (EAttachmentHandlerMail *handler) +{ + handler->priv = E_ATTACHMENT_HANDLER_MAIL_GET_PRIVATE (handler); +} + +GType +e_attachment_handler_mail_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EAttachmentHandlerMailClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) attachment_handler_mail_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EAttachmentHandlerMail), + 0, /* n_preallocs */ + (GInstanceInitFunc) attachment_handler_mail_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + E_TYPE_ATTACHMENT_HANDLER, + "EAttachmentHandlerMail", &type_info, 0); + } + + return type; +} diff --git a/modules/mail/e-attachment-handler-mail.h b/modules/mail/e-attachment-handler-mail.h new file mode 100644 index 0000000000..c62ea99cab --- /dev/null +++ b/modules/mail/e-attachment-handler-mail.h @@ -0,0 +1,65 @@ +/* + * e-attachment-handler-mail.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_ATTACHMENT_HANDLER_MAIL_H +#define E_ATTACHMENT_HANDLER_MAIL_H + +#include <widgets/misc/e-attachment-handler.h> + +/* Standard GObject macros */ +#define E_TYPE_ATTACHMENT_HANDLER_MAIL \ + (e_attachment_handler_mail_get_type ()) +#define E_ATTACHMENT_HANDLER_MAIL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMail)) +#define E_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass)) +#define E_IS_ATTACHMENT_HANDLER_MAIL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL)) +#define E_IS_ATTACHMENT_HANDLER_MAIL_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_ATTACHMENT_HANDLER_MAIL)) +#define E_ATTACHMENT_HANDLER_MAIL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_ATTACHMENT_HANDLER_MAIL, EAttachmentHandlerMailClass)) + +G_BEGIN_DECLS + +typedef struct _EAttachmentHandlerMail EAttachmentHandlerMail; +typedef struct _EAttachmentHandlerMailClass EAttachmentHandlerMailClass; +typedef struct _EAttachmentHandlerMailPrivate EAttachmentHandlerMailPrivate; + +struct _EAttachmentHandlerMail { + EAttachmentHandler parent; + EAttachmentHandlerMailPrivate *priv; +}; + +struct _EAttachmentHandlerMailClass { + EAttachmentHandlerClass parent_class; +}; + +GType e_attachment_handler_mail_get_type (void); + +G_END_DECLS + +#endif /* E_ATTACHMENT_HANDLER_MAIL_H */ diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c new file mode 100644 index 0000000000..3dcb7b1391 --- /dev/null +++ b/modules/mail/e-mail-shell-backend.c @@ -0,0 +1,787 @@ +/* + * e-mail-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-mail-shell-backend.h" + +#include <glib/gi18n.h> +#include <camel/camel-disco-store.h> +#include <camel/camel-offline-store.h> +#include <camel/camel-session.h> +#include <camel/camel-url.h> + +#include "e-util/e-account-utils.h" +#include "e-util/e-binding.h" +#include "e-util/e-import.h" +#include "e-util/e-util.h" +#include "shell/e-shell.h" +#include "shell/e-shell-window.h" +#include "composer/e-msg-composer.h" +#include "widgets/misc/e-preferences-window.h" + +#include "e-mail-shell-migrate.h" +#include "e-mail-shell-settings.h" +#include "e-mail-shell-sidebar.h" +#include "e-mail-shell-view.h" + +#include "e-attachment-handler-mail.h" +#include "e-mail-browser.h" +#include "e-mail-reader.h" +#include "e-mail-store.h" +#include "em-account-editor.h" +#include "em-account-prefs.h" +#include "em-composer-prefs.h" +#include "em-composer-utils.h" +#include "em-config.h" +#include "em-event.h" +#include "em-folder-utils.h" +#include "em-format-hook.h" +#include "em-format-html-display.h" +#include "em-junk-hook.h" +#include "em-mailer-prefs.h" +#include "em-network-prefs.h" +#include "em-utils.h" +#include "mail-config.h" +#include "mail-ops.h" +#include "mail-send-recv.h" +#include "mail-session.h" +#include "mail-vfolder.h" +#include "importers/mail-importer.h" + +#define E_MAIL_SHELL_BACKEND_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackendPrivate)) + +#define BACKEND_NAME "mail" + +struct _EMailShellBackendPrivate { + gint mail_sync_in_progress; + guint mail_sync_timeout_source_id; +}; + +static gpointer parent_class; +static GType mail_shell_backend_type; + +/* XXX So many things need the shell backend that it's + * just easier for now to make it globally available. + * We should fix this, though. */ +EMailShellBackend *global_mail_shell_backend = NULL; + +extern gint camel_application_is_exiting; + +static gboolean +mail_shell_backend_run_account_druid (GtkWindow *parent) +{ + EAccountList *account_list; + EMAccountEditor *account_editor; + + account_editor = em_account_editor_new ( + NULL, EMAE_DRUID, + "org.gnome.evolution.mail.config.accountDruid"); + if (GTK_IS_WINDOW (parent)) + gtk_window_set_transient_for ( + GTK_WINDOW (account_editor->editor), parent); + g_object_weak_ref ( + G_OBJECT (account_editor->editor), + (GWeakNotify) gtk_main_quit, NULL); + gtk_widget_show (account_editor->editor); + gtk_grab_add (account_editor->editor); + gtk_main (); + + account_list = e_get_account_list (); + + return (e_list_length ((EList *) account_list) > 0); +} + +static void +mail_shell_backend_init_hooks (void) +{ + e_plugin_hook_register_type (em_config_hook_get_type ()); + e_plugin_hook_register_type (em_event_hook_get_type ()); + e_plugin_hook_register_type (em_junk_hook_get_type ()); + + /* EMFormat classes must be registered before EMFormatHook. */ + em_format_hook_register_type (em_format_get_type ()); + em_format_hook_register_type (em_format_html_get_type ()); + em_format_hook_register_type (em_format_html_display_get_type ()); + e_plugin_hook_register_type (em_format_hook_get_type ()); + + em_junk_hook_register_type (emj_get_type ()); +} + +static void +mail_shell_backend_init_importers (void) +{ + EImportClass *import_class; + EImportImporter *importer; + + import_class = g_type_class_ref (e_import_get_type ()); + + importer = mbox_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = elm_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = pine_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); +} + +static void +mail_shell_backend_mail_icon_cb (EShellWindow *shell_window, + const gchar *icon_name) +{ + GtkAction *action; + + action = e_shell_window_get_shell_view_action ( + shell_window, BACKEND_NAME); + g_object_set (action, "icon-name", icon_name, NULL); +} + +static void +action_mail_folder_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + EMFolderTree *folder_tree = NULL; + EMailShellSidebar *mail_shell_sidebar; + EShellSidebar *shell_sidebar; + EShellView *shell_view; + const gchar *view_name; + + /* Take care not to unnecessarily load the mail shell view. */ + view_name = e_shell_window_get_active_view (shell_window); + if (g_strcmp0 (view_name, BACKEND_NAME) != 0) + goto exit; + + shell_view = e_shell_window_get_shell_view (shell_window, view_name); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + + mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar); + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + +exit: + em_folder_utils_create_folder ( + NULL, folder_tree, GTK_WINDOW (shell_window)); +} + +static void +action_mail_message_new_cb (GtkAction *action, + EShellWindow *shell_window) +{ + EMailShellSidebar *mail_shell_sidebar; + EShellSidebar *shell_sidebar; + EShellView *shell_view; + EMFolderTree *folder_tree; + const gchar *view_name; + gchar *uri = NULL; + + if (!em_utils_check_user_can_send_mail ()) + return; + + /* Take care not to unnecessarily load the mail shell view. */ + view_name = e_shell_window_get_active_view (shell_window); + if (g_strcmp0 (view_name, BACKEND_NAME) != 0) + goto exit; + + shell_view = e_shell_window_get_shell_view (shell_window, view_name); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + + mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar); + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + uri = em_folder_tree_get_selected_uri (folder_tree); + +exit: + em_utils_compose_new_message (uri); + + g_free (uri); +} + +static GtkActionEntry item_entries[] = { + + { "mail-message-new", + "mail-message-new", + NC_("New", "_Mail Message"), + "<Shift><Control>m", + N_("Compose a new mail message"), + G_CALLBACK (action_mail_message_new_cb) } +}; + +static GtkActionEntry source_entries[] = { + + { "mail-folder-new", + "folder-new", + NC_("New", "Mail _Folder"), + NULL, + N_("Create a new mail folder"), + G_CALLBACK (action_mail_folder_new_cb) } +}; + +static void +mail_shell_backend_init_preferences (EShell *shell) +{ + EAccountList *account_list; + GtkWidget *preferences_window; + + account_list = e_get_account_list (); + preferences_window = e_shell_get_preferences_window (shell); + + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "mail-accounts", + "preferences-mail-accounts", + _("Mail Accounts"), + em_account_prefs_new (account_list), + 100); + + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "mail", + "preferences-mail", + _("Mail Preferences"), + em_mailer_prefs_new (shell), + 300); + + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "composer", + "preferences-composer", + _("Composer Preferences"), + em_composer_prefs_new (shell), + 400); + + e_preferences_window_add_page ( + E_PREFERENCES_WINDOW (preferences_window), + "system-network-proxy", + "preferences-system-network-proxy", + _("Network Preferences"), + em_network_prefs_new (), + 500); +} + +static void +mail_shell_backend_sync_store_done_cb (CamelStore *store, + gpointer user_data) +{ + EMailShellBackend *mail_shell_backend = user_data; + + mail_shell_backend->priv->mail_sync_in_progress--; +} + +static void +mail_shell_backend_sync_store_cb (CamelStore *store, + EMailShellBackend *mail_shell_backend) +{ + if (!camel_application_is_exiting) { + mail_shell_backend->priv->mail_sync_in_progress++; + mail_sync_store ( + store, FALSE, + mail_shell_backend_sync_store_done_cb, + mail_shell_backend); + } +} + +static gboolean +mail_shell_backend_mail_sync (EMailShellBackend *mail_shell_backend) +{ + if (camel_application_is_exiting) + return FALSE; + + if (mail_shell_backend->priv->mail_sync_in_progress) + goto exit; + + if (session == NULL || !camel_session_is_online (session)) + goto exit; + + e_mail_store_foreach ( + (GHFunc) mail_shell_backend_sync_store_cb, + mail_shell_backend); + +exit: + return !camel_application_is_exiting; +} + +static void +mail_shell_backend_notify_online_cb (EShell *shell, + GParamSpec *pspec, + EShellBackend *shell_backend) +{ + gboolean online; + + online = e_shell_get_online (shell); + camel_session_set_online (session, online); +} + +static void +mail_shell_backend_handle_email_uri_cb (gchar *folder_uri, + CamelFolder *folder, + gpointer user_data) +{ + EShellBackend *shell_backend = user_data; + CamelURL *url = user_data; + const gchar *forward; + const gchar *reply; + const gchar *uid; + + if (folder == NULL) { + g_warning ("Could not open folder '%s'", folder_uri); + goto exit; + } + + forward = camel_url_get_param (url, "forward"); + reply = camel_url_get_param (url, "reply"); + uid = camel_url_get_param (url, "uid"); + + if (reply != NULL) { + gint mode; + + if (g_strcmp0 (reply, "all") == 0) + mode = REPLY_MODE_ALL; + else if (g_strcmp0 (reply, "list") == 0) + mode = REPLY_MODE_LIST; + else + mode = REPLY_MODE_SENDER; + + em_utils_reply_to_message (folder, uid, NULL, mode, NULL); + + } else if (forward != NULL) { + GPtrArray *uids; + + uids = g_ptr_array_new (); + g_ptr_array_add (uids, g_strdup (uid)); + + if (g_strcmp0 (forward, "attached") == 0) + em_utils_forward_attached (folder, uids, folder_uri); + else if (g_strcmp0 (forward, "inline") == 0) + em_utils_forward_inline (folder, uids, folder_uri); + else if (g_strcmp0 (forward, "quoted") == 0) + em_utils_forward_quoted (folder, uids, folder_uri); + else + em_utils_forward_messages (folder, uids, folder_uri); + + } else { + GtkWidget *browser; + + /* FIXME Should pass in the shell module. */ + browser = e_mail_browser_new (shell_backend); + e_mail_reader_set_folder ( + E_MAIL_READER (browser), folder, folder_uri); + e_mail_reader_set_message ( + E_MAIL_READER (browser), uid, FALSE); + gtk_widget_show (browser); + } + +exit: + camel_url_free (url); +} + +static gboolean +mail_shell_backend_handle_uri_cb (EShell *shell, + const gchar *uri, + EMailShellBackend *mail_shell_backend) +{ + gboolean handled = TRUE; + + if (g_str_has_prefix (uri, "mailto:")) { + if (em_utils_check_user_can_send_mail ()) + em_utils_compose_new_message_with_mailto (uri, NULL); + + } else if (g_str_has_prefix (uri, "email:")) { + CamelURL *url; + + url = camel_url_new (uri, NULL); + if (camel_url_get_param (url, "uid") != NULL) { + gchar *curi = em_uri_to_camel (uri); + + mail_get_folder ( + curi, 0, + mail_shell_backend_handle_email_uri_cb, + mail_shell_backend, mail_msg_unordered_push); + g_free (curi); + + } else { + g_warning ("Email URI's must include a uid parameter"); + camel_url_free (url); + } + } else + handled = FALSE; + + return TRUE; +} + +/* Helper for mail_shell_backend_prepare_for_[off|on]line_cb() */ +static void +mail_shell_store_line_transition_done_cb (CamelStore *store, + gpointer user_data) +{ + EActivity *activity = user_data; + + g_object_unref (activity); +} + +/* Helper for mail_shell_backend_prepare_for_offline_cb() */ +static void +mail_shell_store_prepare_for_offline_cb (CamelService *service, + gpointer unused, + EActivity *activity) +{ + if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service)) + mail_store_set_offline ( + CAMEL_STORE (service), TRUE, + mail_shell_store_line_transition_done_cb, + g_object_ref (activity)); +} + +static void +mail_shell_backend_prepare_for_offline_cb (EShell *shell, + EActivity *activity, + EMailShellBackend *mail_shell_backend) +{ + GList *watched_windows; + GtkWidget *parent = NULL; + gboolean synchronize = FALSE; + + watched_windows = e_shell_get_watched_windows (shell); + if (watched_windows != NULL) + parent = GTK_WIDGET (watched_windows->data); + + if (e_shell_get_network_available (shell)) + synchronize = em_utils_prompt_user ( + GTK_WINDOW (parent), + "/apps/evolution/mail/prompts/quick_offline", + "mail:ask-quick-offline", NULL); + + if (!synchronize) { + mail_cancel_all (); + camel_session_set_network_state (session, FALSE); + } + + e_mail_store_foreach ( + (GHFunc) mail_shell_store_prepare_for_offline_cb, activity); +} + +/* Helper for mail_shell_backend_prepare_for_online_cb() */ +static void +mail_shell_store_prepare_for_online_cb (CamelService *service, + gpointer unused, + EActivity *activity) +{ + if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service)) + mail_store_set_offline ( + CAMEL_STORE (service), FALSE, + mail_shell_store_line_transition_done_cb, + g_object_ref (activity)); +} + +static void +mail_shell_backend_prepare_for_online_cb (EShell *shell, + EActivity *activity, + EMailShellBackend *mail_shell_backend) +{ + camel_session_set_online (session, TRUE); + + e_mail_store_foreach ( + (GHFunc) mail_shell_store_prepare_for_online_cb, activity); +} + +static void +mail_shell_backend_send_receive_cb (EShell *shell, + GtkWindow *parent, + EShellBackend *shell_backend) +{ + em_utils_clear_get_password_canceled_accounts_flag (); + mail_send_receive (parent); +} + +static void +mail_shell_backend_window_weak_notify_cb (EShell *shell, + GObject *where_the_object_was) +{ + g_signal_handlers_disconnect_by_func ( + shell, mail_shell_backend_mail_icon_cb, + where_the_object_was); +} + +static void +mail_shell_backend_window_created_cb (EShell *shell, + GtkWindow *window, + EShellBackend *shell_backend) +{ + EShellSettings *shell_settings; + static gboolean first_time = TRUE; + const gchar *backend_name; + + shell_settings = e_shell_get_shell_settings (shell); + + /* This applies to both the composer and signature editor. */ + if (GTKHTML_IS_EDITOR (window)) { + GList *spell_languages; + + e_binding_new ( + G_OBJECT (shell_settings), "composer-inline-spelling", + G_OBJECT (window), "inline-spelling"); + + e_binding_new ( + G_OBJECT (shell_settings), "composer-magic-links", + G_OBJECT (window), "magic-links"); + + e_binding_new ( + G_OBJECT (shell_settings), "composer-magic-smileys", + G_OBJECT (window), "magic-smileys"); + + spell_languages = e_load_spell_languages (); + gtkhtml_editor_set_spell_languages ( + GTKHTML_EDITOR (window), spell_languages); + g_list_free (spell_languages); + } + + if (E_IS_MSG_COMPOSER (window)) { + /* Integrate the new composer into the mail module. */ + em_configure_new_composer (E_MSG_COMPOSER (window)); + return; + } + + 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)); + + g_signal_connect_swapped ( + shell, "event::mail-icon", + G_CALLBACK (mail_shell_backend_mail_icon_cb), window); + + g_object_weak_ref ( + G_OBJECT (window), (GWeakNotify) + mail_shell_backend_window_weak_notify_cb, shell); + + if (first_time) { + g_signal_connect ( + window, "map-event", + G_CALLBACK (e_msg_composer_check_autosave), NULL); + first_time = FALSE; + } +} + +static void +mail_shell_backend_constructed (GObject *object) +{ + EMailShellBackendPrivate *priv; + EShell *shell; + EShellBackend *shell_backend; + const gchar *data_dir; + + priv = E_MAIL_SHELL_BACKEND_GET_PRIVATE (object); + + shell_backend = E_SHELL_BACKEND (object); + shell = e_shell_backend_get_shell (shell_backend); + + /* This also initializes Camel, so it needs to happen early. */ + mail_session_init (shell_backend); + + mail_shell_backend_init_hooks (); + mail_shell_backend_init_importers (); + + e_attachment_handler_mail_get_type (); + + /* XXX This never gets unreffed. */ + global_mail_shell_backend = g_object_ref (shell_backend); + + g_signal_connect ( + shell, "notify::online", + G_CALLBACK (mail_shell_backend_notify_online_cb), + shell_backend); + + g_signal_connect ( + shell, "handle-uri", + G_CALLBACK (mail_shell_backend_handle_uri_cb), + shell_backend); + + g_signal_connect ( + shell, "prepare-for-offline", + G_CALLBACK (mail_shell_backend_prepare_for_offline_cb), + shell_backend); + + g_signal_connect ( + shell, "prepare-for-online", + G_CALLBACK (mail_shell_backend_prepare_for_online_cb), + shell_backend); + + g_signal_connect ( + shell, "send-receive", + G_CALLBACK (mail_shell_backend_send_receive_cb), + shell_backend); + + g_signal_connect ( + shell, "window-created", + G_CALLBACK (mail_shell_backend_window_created_cb), + shell_backend); + + mail_config_init (); + mail_msg_init (); + + data_dir = e_shell_backend_get_data_dir (shell_backend); + e_mail_store_init (data_dir); + + /* Initialize settings before initializing preferences, + * since the preferences bind to the shell settings. */ + e_mail_shell_settings_init (shell); + mail_shell_backend_init_preferences (shell); +} + +static void +mail_shell_backend_start (EShellBackend *shell_backend) +{ + EMailShellBackendPrivate *priv; + EShell *shell; + EShellSettings *shell_settings; + gboolean enable_search_folders; + + priv = E_MAIL_SHELL_BACKEND_GET_PRIVATE (shell_backend); + + shell = e_shell_backend_get_shell (shell_backend); + shell_settings = e_shell_get_shell_settings (shell); + + /* XXX Do we really still need this flag? */ + mail_session_set_interactive (TRUE); + + enable_search_folders = e_shell_settings_get_boolean ( + shell_settings, "mail-enable-search-folders"); + if (enable_search_folders) + vfolder_load_storage (); + + mail_autoreceive_init (shell_backend, session); + + if (g_getenv ("CAMEL_FLUSH_CHANGES") != NULL) + priv->mail_sync_timeout_source_id = g_timeout_add_seconds ( + mail_config_get_sync_timeout (), + (GSourceFunc) mail_shell_backend_mail_sync, + shell_backend); +} + +static void +mail_shell_backend_class_init (EMailShellBackendClass *class) +{ + GObjectClass *object_class; + EShellBackendClass *shell_backend_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailShellBackendPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = mail_shell_backend_constructed; + + shell_backend_class = E_SHELL_BACKEND_CLASS (class); + shell_backend_class->shell_view_type = E_TYPE_MAIL_SHELL_VIEW; + shell_backend_class->name = BACKEND_NAME; + shell_backend_class->aliases = ""; + shell_backend_class->schemes = "mailto:email"; + shell_backend_class->sort_order = 200; + shell_backend_class->start = mail_shell_backend_start; + shell_backend_class->is_busy = NULL; + shell_backend_class->shutdown = NULL; + shell_backend_class->migrate = e_mail_shell_migrate; +} + +static void +mail_shell_backend_init (EMailShellBackend *mail_shell_backend) +{ + mail_shell_backend->priv = + E_MAIL_SHELL_BACKEND_GET_PRIVATE (mail_shell_backend); +} + +GType +e_mail_shell_backend_get_type (void) +{ + return mail_shell_backend_type; +} + +void +e_mail_shell_backend_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EMailShellBackendClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) mail_shell_backend_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailShellBackend), + 0, /* n_preallocs */ + (GInstanceInitFunc) mail_shell_backend_init, + NULL /* value_table */ + }; + + mail_shell_backend_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_BACKEND, + "EMailShellBackend", &type_info, 0); +} + +/******************* Code below here belongs elsewhere. *******************/ + +#include "filter/filter-option.h" +#include "shell/e-shell-settings.h" +#include "mail/e-mail-label-list-store.h" + +GSList * +e_mail_labels_get_filter_options (void) +{ + EShell *shell; + EShellSettings *shell_settings; + EMailLabelListStore *list_store; + GtkTreeModel *model; + GtkTreeIter iter; + GSList *list = NULL; + gboolean valid; + + shell = e_shell_get_default (); + shell_settings = e_shell_get_shell_settings (shell); + list_store = e_shell_settings_get_object ( + shell_settings, "mail-label-list-store"); + + model = GTK_TREE_MODEL (list_store); + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) { + struct _filter_option *option; + gchar *name, *tag; + + name = e_mail_label_list_store_get_name (list_store, &iter); + tag = e_mail_label_list_store_get_tag (list_store, &iter); + + option = g_new0 (struct _filter_option, 1); + option->title = e_str_without_underscores (name); + option->value = tag; /* takes ownership */ + + g_free (name); + + valid = gtk_tree_model_iter_next (model, &iter); + } + + g_object_unref (list_store); + + return list; +} diff --git a/modules/mail/e-mail-shell-backend.h b/modules/mail/e-mail-shell-backend.h new file mode 100644 index 0000000000..4bc1a36706 --- /dev/null +++ b/modules/mail/e-mail-shell-backend.h @@ -0,0 +1,82 @@ +/* + * e-mail-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_MAIL_SHELL_BACKEND_H +#define E_MAIL_SHELL_BACKEND_H + +#include <shell/e-shell-backend.h> + +#include <camel/camel-folder.h> +#include <camel/camel-store.h> +#include <e-util/e-signature-list.h> +#include <libedataserver/e-account-list.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_SHELL_BACKEND \ + (e_mail_shell_backend_get_type ()) +#define E_MAIL_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackend)) +#define E_MAIL_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackendClass)) +#define E_IS_MAIL_SHELL_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_SHELL_BACKEND)) +#define E_IS_MAIL_SHELL_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_SHELL_BACKEND)) +#define E_MAIL_SHELL_BACKEND_GET_CLASS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_SHELL_BACKEND, EMailShellBackendClass)) + +G_BEGIN_DECLS + +typedef struct _EMailShellBackend EMailShellBackend; +typedef struct _EMailShellBackendClass EMailShellBackendClass; +typedef struct _EMailShellBackendPrivate EMailShellBackendPrivate; + +struct _EMailShellBackend { + EShellBackend parent; + EMailShellBackendPrivate *priv; +}; + +struct _EMailShellBackendClass { + EShellBackendClass parent_class; +}; + +/* Globally available shell backend. + * + * XXX I don't like having this globally available but passing it around + * to all the various utilities that need to access the backend's data + * directory is too much of a pain for now. */ +extern EMailShellBackend *global_mail_shell_backend; + +GType e_mail_shell_backend_get_type (void); +void e_mail_shell_backend_register_type + (GTypeModule *type_module); + +/* XXX Find a better place for this function. */ +GSList * e_mail_labels_get_filter_options(void); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_BACKEND_H */ diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c new file mode 100644 index 0000000000..b801093116 --- /dev/null +++ b/modules/mail/e-mail-shell-content.c @@ -0,0 +1,1053 @@ +/* + * e-mail-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-mail-shell-content.h" + +#include <glib/gi18n.h> +#include <camel/camel-store.h> +#include <libedataserver/e-data-server-util.h> + +#include "e-util/gconf-bridge.h" +#include "widgets/menus/gal-view-etable.h" +#include "widgets/menus/gal-view-instance.h" + +#include "em-search-context.h" +#include "em-utils.h" +#include "mail-config.h" +#include "mail-ops.h" + +#include "e-mail-reader.h" +#include "e-mail-search-bar.h" +#include "e-mail-shell-backend.h" +#include "e-mail-shell-view-actions.h" + +#define E_MAIL_SHELL_CONTENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentPrivate)) + +#define STATE_KEY_SCROLLBAR_POSITION "ScrollbarPosition" +#define STATE_KEY_SELECTED_MESSAGE "SelectedMessage" + +struct _EMailShellContentPrivate { + GtkWidget *paned; + GtkWidget *message_list; + GtkWidget *search_bar; + + EMFormatHTMLDisplay *html_display; + GalViewInstance *view_instance; + + /* ETable scrolling hack */ + gdouble default_scrollbar_position; + + guint paned_binding_id; + guint scroll_timeout_id; + + /* Signal handler IDs */ + guint message_list_built_id; + guint message_list_scrolled_id; + + guint preview_visible : 1; + guint suppress_message_selection : 1; + guint vertical_view : 1; + guint show_deleted : 1; +}; + +enum { + PROP_0, + PROP_PREVIEW_VISIBLE, + PROP_SHOW_DELETED, + PROP_VERTICAL_VIEW +}; + +static gpointer parent_class; +static GType mail_shell_content_type; + +static void +mail_shell_content_etree_unfreeze (MessageList *message_list, + GdkEvent *event) +{ + ETableItem *item; + GObject *object; + + item = e_tree_get_item (message_list->tree); + object = G_OBJECT (((GnomeCanvasItem *) item)->canvas); + + g_object_set_data (object, "freeze-cursor", 0); +} + +static void +mail_shell_content_message_list_scrolled_cb (EMailShellContent *mail_shell_content, + MessageList *message_list) +{ + EShellContent *shell_content; + EShellView *shell_view; + GKeyFile *key_file; + const gchar *folder_uri; + const gchar *key; + gchar *group_name; + gdouble position; + + /* Save the scrollbar position for the current folder. */ + + folder_uri = message_list->folder_uri; + + if (folder_uri == NULL) + return; + + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + key = STATE_KEY_SCROLLBAR_POSITION; + group_name = g_strdup_printf ("Folder %s", folder_uri); + position = message_list_get_scrollbar_position (message_list); + + g_key_file_set_double (key_file, group_name, key, position); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); +} + +static gboolean +mail_shell_content_scroll_timeout_cb (EMailShellContent *mail_shell_content) +{ + EMailShellContentPrivate *priv = mail_shell_content->priv; + EShellContent *shell_content; + EShellView *shell_view; + MessageList *message_list; + EMailReader *reader; + GKeyFile *key_file; + const gchar *folder_uri; + const gchar *key; + gchar *group_name; + + /* Initialize the scrollbar position for the current folder + * and setup a callback to handle scrollbar position changes. */ + + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + reader = E_MAIL_READER (mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri == NULL) + goto skip; + + /* Restore the message list scrollbar position. */ + + key = STATE_KEY_SCROLLBAR_POSITION; + group_name = g_strdup_printf ("Folder %s", folder_uri); + + if (g_key_file_has_key (key_file, group_name, key, NULL)) { + gdouble position; + + position = g_key_file_get_double ( + key_file, group_name, key, NULL); + message_list_set_scrollbar_position (message_list, position); + } + + g_free (group_name); + +skip: + priv->message_list_scrolled_id = g_signal_connect_swapped ( + message_list, "message-list-scrolled", + G_CALLBACK (mail_shell_content_message_list_scrolled_cb), + mail_shell_content); + + priv->scroll_timeout_id = 0; + + return FALSE; +} + +static void +mail_shell_content_message_list_built_cb (EMailShellContent *mail_shell_content, + MessageList *message_list) +{ + EMailShellContentPrivate *priv = mail_shell_content->priv; + EShellContent *shell_content; + EShellView *shell_view; + GtkScrolledWindow *scrolled_window; + GtkWidget *vscrollbar; + GKeyFile *key_file; + gchar *uid; + + g_signal_handler_disconnect ( + message_list, priv->message_list_built_id); + priv->message_list_built_id = 0; + + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + if (message_list->cursor_uid != NULL) + uid = NULL; + + else if (message_list->folder_uri == NULL) + uid = NULL; + + else if (mail_shell_content->priv->suppress_message_selection) + uid = NULL; + + else { + const gchar *folder_uri; + const gchar *key; + gchar *group_name; + + key = STATE_KEY_SELECTED_MESSAGE; + folder_uri = message_list->folder_uri; + group_name = g_strdup_printf ("Folder %s", folder_uri); + uid = g_key_file_get_string (key_file, group_name, key, NULL); + g_free (group_name); + } + + if (uid != NULL) { + CamelFolder *folder; + CamelMessageInfo *info; + + folder = message_list->folder; + info = camel_folder_get_message_info (folder, uid); + if (info != NULL) { + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_content); + e_mail_reader_set_message (reader, uid, TRUE); + camel_folder_free_message_info (folder, info); + } + + g_free (uid); + } + + /* FIXME This is a gross workaround for an ETable bug that I can't + * fix (Ximian bug #55303). + * + * Since e_canvas_item_region_show_relay() uses a timeout, + * we have to use a timeout of the same interval but a lower + * priority. */ + priv->scroll_timeout_id = g_timeout_add_full ( + G_PRIORITY_LOW, 250, (GSourceFunc) + mail_shell_content_scroll_timeout_cb, + mail_shell_content, NULL); + + /* FIXME This is another ugly hack to hide a side-effect of the + * previous workaround. */ + scrolled_window = GTK_SCROLLED_WINDOW (message_list); + vscrollbar = gtk_scrolled_window_get_vscrollbar (scrolled_window); + g_signal_connect_swapped ( + vscrollbar, "button-press-event", + G_CALLBACK (mail_shell_content_etree_unfreeze), + message_list); +} + +static void +mail_shell_content_display_view_cb (EMailShellContent *mail_shell_content, + GalView *gal_view) +{ + EMailReader *reader; + MessageList *message_list; + + reader = E_MAIL_READER (mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + if (GAL_IS_VIEW_ETABLE (gal_view)) + gal_view_etable_attach_tree ( + GAL_VIEW_ETABLE (gal_view), message_list->tree); +} + +static void +mail_shell_content_message_selected_cb (EMailShellContent *mail_shell_content, + const gchar *message_uid, + MessageList *message_list) +{ + EShellContent *shell_content; + EShellView *shell_view; + GKeyFile *key_file; + const gchar *folder_uri; + const gchar *key; + gchar *group_name; + + folder_uri = message_list->folder_uri; + + /* This also gets triggered when selecting a store name on + * the sidebar such as "On This Computer", in which case + * 'folder_uri' will be NULL. */ + if (folder_uri == NULL) + return; + + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + key_file = e_shell_view_get_state_key_file (shell_view); + + key = STATE_KEY_SELECTED_MESSAGE; + group_name = g_strdup_printf ("Folder %s", folder_uri); + + if (message_uid != NULL) + g_key_file_set_string (key_file, group_name, key, message_uid); + else + g_key_file_remove_key (key_file, group_name, key, NULL); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); +} + +static void +mail_shell_content_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_PREVIEW_VISIBLE: + e_mail_shell_content_set_preview_visible ( + E_MAIL_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + + case PROP_SHOW_DELETED: + e_mail_shell_content_set_show_deleted ( + E_MAIL_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + + case PROP_VERTICAL_VIEW: + e_mail_shell_content_set_vertical_view ( + E_MAIL_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_shell_content_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_PREVIEW_VISIBLE: + g_value_set_boolean ( + value, + e_mail_shell_content_get_preview_visible ( + E_MAIL_SHELL_CONTENT (object))); + return; + + case PROP_SHOW_DELETED: + g_value_set_boolean ( + value, + e_mail_shell_content_get_show_deleted ( + E_MAIL_SHELL_CONTENT (object))); + return; + + case PROP_VERTICAL_VIEW: + g_value_set_boolean ( + value, + e_mail_shell_content_get_vertical_view ( + E_MAIL_SHELL_CONTENT (object))); + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_shell_content_dispose (GObject *object) +{ + EMailShellContentPrivate *priv; + + priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object); + + if (priv->paned != NULL) { + g_object_unref (priv->paned); + priv->paned = NULL; + } + + if (priv->message_list != NULL) { + g_object_unref (priv->message_list); + priv->message_list = NULL; + } + + if (priv->search_bar != NULL) { + g_object_unref (priv->search_bar); + priv->search_bar = NULL; + } + + if (priv->html_display != NULL) { + g_object_unref (priv->html_display); + priv->html_display = 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 +mail_shell_content_constructed (GObject *object) +{ + EMailShellContentPrivate *priv; + EShellContent *shell_content; + EShellBackend *shell_backend; + EShellView *shell_view; + EShellViewClass *shell_view_class; + EMailReader *reader; + MessageList *message_list; + GConfBridge *bridge; + GtkWidget *container; + GtkWidget *widget; + GtkHTML *html; + GalViewCollection *view_collection; + const gchar *key; + + priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (object); + priv->html_display = em_format_html_display_new (); + + /* 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_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + shell_backend = e_shell_view_get_shell_backend (shell_view); + view_collection = shell_view_class->view_collection; + + html = EM_FORMAT_HTML (priv->html_display)->html; + + /* Build content widgets. */ + + container = GTK_WIDGET (object); + + widget = gtk_vpaned_new (); + gtk_container_add (GTK_CONTAINER (container), widget); + priv->paned = g_object_ref (widget); + gtk_widget_show (widget); + + container = widget; + + widget = message_list_new (shell_backend); + gtk_paned_add1 (GTK_PANED (container), widget); + priv->message_list = g_object_ref (widget); + gtk_widget_show (widget); + + widget = gtk_vbox_new (FALSE, 1); + gtk_paned_add2 (GTK_PANED (container), 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_container_add (GTK_CONTAINER (widget), GTK_WIDGET (html)); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + gtk_widget_show (GTK_WIDGET (html)); + gtk_widget_show (widget); + + widget = e_mail_search_bar_new (html); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + priv->search_bar = g_object_ref (widget); + gtk_widget_hide (widget); + + g_signal_connect_swapped ( + widget, "changed", + G_CALLBACK (em_format_redraw), priv->html_display); + + /* Load the view instance. */ + + e_mail_shell_content_update_view_instance ( + E_MAIL_SHELL_CONTENT (shell_content)); + + /* Bind GObject properties to GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (priv->paned); + key = "/apps/evolution/mail/display/paned_size"; + gconf_bridge_bind_property_delayed (bridge, key, object, "position"); + + object = G_OBJECT (shell_content); + key = "/apps/evolution/mail/display/show_deleted"; + gconf_bridge_bind_property (bridge, key, object, "show-deleted"); + + /* Message list customizations. */ + + reader = E_MAIL_READER (shell_content); + message_list = e_mail_reader_get_message_list (reader); + + g_signal_connect_swapped ( + message_list, "message-selected", + G_CALLBACK (mail_shell_content_message_selected_cb), + shell_content); +} + +static guint32 +mail_shell_content_check_state (EShellContent *shell_content) +{ + return e_mail_reader_check_state (E_MAIL_READER (shell_content)); +} + +static GtkActionGroup * +mail_shell_content_get_action_group (EMailReader *reader) +{ + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + + shell_content = E_SHELL_CONTENT (reader); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + return E_SHELL_WINDOW_ACTION_GROUP_MAIL (shell_window); +} + +static gboolean +mail_shell_content_get_hide_deleted (EMailReader *reader) +{ + EMailShellContent *mail_shell_content; + + mail_shell_content = E_MAIL_SHELL_CONTENT (reader); + + return !e_mail_shell_content_get_show_deleted (mail_shell_content); +} + +static EMFormatHTMLDisplay * +mail_shell_content_get_html_display (EMailReader *reader) +{ + EMailShellContentPrivate *priv; + + priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader); + + return priv->html_display; +} + +static MessageList * +mail_shell_content_get_message_list (EMailReader *reader) +{ + EMailShellContentPrivate *priv; + + priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader); + + return MESSAGE_LIST (priv->message_list); +} + +static EShellBackend * +mail_shell_content_get_shell_backend (EMailReader *reader) +{ + EShellContent *shell_content; + EShellView *shell_view; + + shell_content = E_SHELL_CONTENT (reader); + shell_view = e_shell_content_get_shell_view (shell_content); + + return e_shell_view_get_shell_backend (shell_view); +} + +static GtkWindow * +mail_shell_content_get_window (EMailReader *reader) +{ + EShellContent *shell_content; + EShellWindow *shell_window; + EShellView *shell_view; + + shell_content = E_SHELL_CONTENT (reader); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + return GTK_WINDOW (shell_window); +} + +static void +mail_shell_content_set_folder (EMailReader *reader, + CamelFolder *folder, + const gchar *folder_uri) +{ + EMailShellContentPrivate *priv; + EMailReaderIface *default_iface; + MessageList *message_list; + gboolean different_folder; + + priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader); + + message_list = e_mail_reader_get_message_list (reader); + + message_list_freeze (message_list); + + different_folder = + message_list->folder != NULL && + folder != message_list->folder; + + /* Chain up to interface's default set_folder() method. */ + default_iface = g_type_default_interface_peek (E_TYPE_MAIL_READER); + default_iface->set_folder (reader, folder, folder_uri); + + if (folder == NULL) + goto exit; + + mail_refresh_folder (folder, NULL, NULL); + + /* This function gets triggered several times at startup, + * so we don't want to reset the message suppression state + * unless we're actually switching to a different folder. */ + if (different_folder) + priv->suppress_message_selection = FALSE; + + /* This is a one-time-only callback. */ + if (message_list->cursor_uid == NULL && priv->message_list_built_id == 0) + priv->message_list_built_id = g_signal_connect_swapped ( + message_list, "message-list-built", + G_CALLBACK (mail_shell_content_message_list_built_cb), + reader); + +exit: + message_list_thaw (message_list); +} + +static void +mail_shell_content_show_search_bar (EMailReader *reader) +{ + EMailShellContentPrivate *priv; + + priv = E_MAIL_SHELL_CONTENT_GET_PRIVATE (reader); + + gtk_widget_show (priv->search_bar); +} + +static void +mail_shell_content_class_init (EMailShellContentClass *class) +{ + GObjectClass *object_class; + EShellContentClass *shell_content_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailShellContentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = mail_shell_content_set_property; + object_class->get_property = mail_shell_content_get_property; + object_class->dispose = mail_shell_content_dispose; + object_class->constructed = mail_shell_content_constructed; + + shell_content_class = E_SHELL_CONTENT_CLASS (class); + shell_content_class->new_search_context = em_search_context_new; + shell_content_class->check_state = mail_shell_content_check_state; + + 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_object_class_install_property ( + object_class, + PROP_SHOW_DELETED, + g_param_spec_boolean ( + "show-deleted", + "Show Deleted", + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_VERTICAL_VIEW, + g_param_spec_boolean ( + "vertical-view", + _("Vertical View"), + _("Whether vertical view is enabled"), + FALSE, + G_PARAM_READWRITE)); +} + +static void +mail_shell_content_iface_init (EMailReaderIface *iface) +{ + iface->get_action_group = mail_shell_content_get_action_group; + iface->get_hide_deleted = mail_shell_content_get_hide_deleted; + iface->get_html_display = mail_shell_content_get_html_display; + iface->get_message_list = mail_shell_content_get_message_list; + iface->get_shell_backend = mail_shell_content_get_shell_backend; + iface->get_window = mail_shell_content_get_window; + iface->set_folder = mail_shell_content_set_folder; + iface->show_search_bar = mail_shell_content_show_search_bar; +} + +static void +mail_shell_content_init (EMailShellContent *mail_shell_content) +{ + mail_shell_content->priv = + E_MAIL_SHELL_CONTENT_GET_PRIVATE (mail_shell_content); + + mail_shell_content->priv->preview_visible = TRUE; + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_mail_shell_content_get_type (void) +{ + return mail_shell_content_type; +} + +void +e_mail_shell_content_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (EMailShellContentClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) mail_shell_content_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailShellContent), + 0, /* n_preallocs */ + (GInstanceInitFunc) mail_shell_content_init, + NULL /* value_table */ + }; + + static const GInterfaceInfo iface_info = { + (GInterfaceInitFunc) mail_shell_content_iface_init, + (GInterfaceFinalizeFunc) NULL, + NULL /* interface_data */ + }; + + mail_shell_content_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_CONTENT, + "EMailShellContent", &type_info, 0); + + g_type_module_add_interface ( + type_module, mail_shell_content_type, + E_TYPE_MAIL_READER, &iface_info); +} + +GtkWidget * +e_mail_shell_content_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_MAIL_SHELL_CONTENT, + "shell-view", shell_view, NULL); +} + +gboolean +e_mail_shell_content_get_preview_visible (EMailShellContent *mail_shell_content) +{ + g_return_val_if_fail ( + E_IS_MAIL_SHELL_CONTENT (mail_shell_content), FALSE); + + return mail_shell_content->priv->preview_visible; +} + +void +e_mail_shell_content_set_preview_visible (EMailShellContent *mail_shell_content, + gboolean preview_visible) +{ + GtkPaned *paned; + GtkWidget *child; + + g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content)); + + if (preview_visible == mail_shell_content->priv->preview_visible) + return; + + paned = GTK_PANED (mail_shell_content->priv->paned); + child = gtk_paned_get_child2 (paned); + + if (preview_visible) + gtk_widget_show (child); + else + gtk_widget_hide (child); + + mail_shell_content->priv->preview_visible = preview_visible; + + g_object_notify (G_OBJECT (mail_shell_content), "preview-visible"); +} + +gboolean +e_mail_shell_content_get_show_deleted (EMailShellContent *mail_shell_content) +{ + g_return_val_if_fail ( + E_IS_MAIL_SHELL_CONTENT (mail_shell_content), FALSE); + + return mail_shell_content->priv->show_deleted; +} + +void +e_mail_shell_content_set_show_deleted (EMailShellContent *mail_shell_content, + gboolean show_deleted) +{ + g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content)); + + mail_shell_content->priv->show_deleted = show_deleted; + + g_object_notify (G_OBJECT (mail_shell_content), "show-deleted"); +} + +gboolean +e_mail_shell_content_get_vertical_view (EMailShellContent *mail_shell_content) +{ + g_return_val_if_fail ( + E_IS_MAIL_SHELL_CONTENT (mail_shell_content), FALSE); + + return mail_shell_content->priv->vertical_view; +} + +void +e_mail_shell_content_set_vertical_view (EMailShellContent *mail_shell_content, + gboolean vertical_view) +{ + GConfBridge *bridge; + GtkWidget *old_paned; + GtkWidget *new_paned; + GtkWidget *child1; + GtkWidget *child2; + guint binding_id; + const gchar *key; + + g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content)); + + if (vertical_view == mail_shell_content->priv->vertical_view) + return; + + bridge = gconf_bridge_get (); + old_paned = mail_shell_content->priv->paned; + binding_id = mail_shell_content->priv->paned_binding_id; + + child1 = gtk_paned_get_child1 (GTK_PANED (old_paned)); + child2 = gtk_paned_get_child2 (GTK_PANED (old_paned)); + + if (binding_id > 0) + gconf_bridge_unbind (bridge, binding_id); + + if (vertical_view) { + new_paned = gtk_hpaned_new (); + key = "/apps/evolution/mail/display/hpaned_size"; + } else { + new_paned = gtk_vpaned_new (); + key = "/apps/evolution/mail/display/paned_size"; + } + + gtk_widget_reparent (child1, new_paned); + gtk_widget_reparent (child2, new_paned); + gtk_widget_show (new_paned); + + gtk_widget_destroy (old_paned); + gtk_container_add (GTK_CONTAINER (mail_shell_content), new_paned); + + binding_id = gconf_bridge_bind_property_delayed ( + bridge, key, G_OBJECT (new_paned), "position"); + + mail_shell_content->priv->vertical_view = vertical_view; + mail_shell_content->priv->paned_binding_id = binding_id; + mail_shell_content->priv->paned = g_object_ref (new_paned); + + e_mail_shell_content_update_view_instance (mail_shell_content); + + g_object_notify (G_OBJECT (mail_shell_content), "vertical-view"); +} + +GalViewInstance * +e_mail_shell_content_get_view_instance (EMailShellContent *mail_shell_content) +{ + g_return_val_if_fail ( + E_IS_MAIL_SHELL_CONTENT (mail_shell_content), NULL); + + return mail_shell_content->priv->view_instance; +} + +void +e_mail_shell_content_set_search_strings (EMailShellContent *mail_shell_content, + GSList *search_strings) +{ + EMailSearchBar *search_bar; + ESearchingTokenizer *tokenizer; + + g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content)); + + search_bar = E_MAIL_SEARCH_BAR (mail_shell_content->priv->search_bar); + tokenizer = e_mail_search_bar_get_tokenizer (search_bar); + + e_searching_tokenizer_set_secondary_case_sensitivity (tokenizer, FALSE); + e_searching_tokenizer_set_secondary_search_string (tokenizer, NULL); + + while (search_strings != NULL) { + e_searching_tokenizer_add_secondary_search_string ( + tokenizer, search_strings->data); + search_strings = g_slist_next (search_strings); + } + + e_mail_search_bar_changed (search_bar); +} + +void +e_mail_shell_content_update_view_instance (EMailShellContent *mail_shell_content) +{ + EMailReader *reader; + EShellContent *shell_content; + EShellView *shell_view; + EShellViewClass *shell_view_class; + GalViewCollection *view_collection; + GalViewInstance *view_instance; + MessageList *message_list; + gboolean outgoing_folder; + gboolean show_vertical_view; + gchar *view_id; + + g_return_if_fail (E_IS_MAIL_SHELL_CONTENT (mail_shell_content)); + + shell_content = E_SHELL_CONTENT (mail_shell_content); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + view_collection = shell_view_class->view_collection; + + reader = E_MAIL_READER (mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + /* If no folder is selected, return silently. */ + if (message_list->folder == NULL) + return; + + /* If we have a folder, we should also have a URI. */ + g_return_if_fail (message_list->folder_uri != NULL); + + if (mail_shell_content->priv->view_instance != NULL) { + g_object_unref (mail_shell_content->priv->view_instance); + mail_shell_content->priv->view_instance = NULL; + } + + view_id = mail_config_folder_to_safe_url (message_list->folder); + view_instance = e_shell_view_new_view_instance (shell_view, view_id); + mail_shell_content->priv->view_instance = view_instance; + + show_vertical_view = + e_mail_shell_content_get_vertical_view (mail_shell_content); + + if (show_vertical_view) { + gchar *filename; + gchar *safe_view_id; + + /* Force the view instance into vertical view. */ + + g_free (view_instance->custom_filename); + g_free (view_instance->current_view_filename); + + safe_view_id = g_strdup (view_id); + e_filename_make_safe (safe_view_id); + + filename = g_strdup_printf ( + "custom_wide_view-%s.xml", safe_view_id); + view_instance->custom_filename = g_build_filename ( + view_collection->local_dir, filename, NULL); + g_free (filename); + + filename = g_strdup_printf ( + "current_wide_view-%s.xml", safe_view_id); + view_instance->current_view_filename = g_build_filename ( + view_collection->local_dir, filename, NULL); + g_free (filename); + + g_free (safe_view_id); + } + + g_free (view_id); + + outgoing_folder = + em_utils_folder_is_drafts ( + message_list->folder, message_list->folder_uri) || + em_utils_folder_is_outbox ( + message_list->folder, message_list->folder_uri) || + em_utils_folder_is_sent ( + message_list->folder, message_list->folder_uri); + + if (outgoing_folder) { + if (show_vertical_view) + gal_view_instance_set_default_view ( + view_instance, "Wide_View_Sent"); + else + gal_view_instance_set_default_view ( + view_instance, "As_Sent_Folder"); + } else if (show_vertical_view) { + gal_view_instance_set_default_view ( + view_instance, "Wide_View_Normal"); + } + + gal_view_instance_load (view_instance); + + if (!gal_view_instance_exists (view_instance)) { + gchar *state_filename; + + state_filename = mail_config_folder_to_cachename ( + message_list->folder, "et-header-"); + + if (g_file_test (state_filename, G_FILE_TEST_IS_REGULAR)) { + ETableSpecification *spec; + ETableState *state; + GalView *view; + gchar *spec_filename; + + spec = e_table_specification_new (); + spec_filename = g_build_filename ( + EVOLUTION_ETSPECDIR, + "message-list.etspec", + NULL); + e_table_specification_load_from_file ( + spec, spec_filename); + g_free (spec_filename); + + state = e_table_state_new (); + view = gal_view_etable_new (spec, ""); + + e_table_state_load_from_file ( + state, state_filename); + gal_view_etable_set_state ( + GAL_VIEW_ETABLE (view), state); + gal_view_instance_set_custom_view ( + view_instance, view); + + g_object_unref (state); + g_object_unref (view); + g_object_unref (spec); + } + + g_free (state_filename); + } + + g_signal_connect ( + view_instance, "display-view", + G_CALLBACK (mail_shell_content_display_view_cb), + mail_shell_content); + + mail_shell_content_display_view_cb ( + mail_shell_content, + gal_view_instance_get_current_view (view_instance)); +} diff --git a/modules/mail/e-mail-shell-content.h b/modules/mail/e-mail-shell-content.h new file mode 100644 index 0000000000..57d2438705 --- /dev/null +++ b/modules/mail/e-mail-shell-content.h @@ -0,0 +1,94 @@ +/* + * e-mail-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_MAIL_SHELL_CONTENT_H +#define E_MAIL_SHELL_CONTENT_H + +#include <shell/e-shell-content.h> +#include <shell/e-shell-view.h> + +#include <mail/em-format-html-display.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_SHELL_CONTENT \ + (e_mail_shell_content_get_type ()) +#define E_MAIL_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContent)) +#define E_MAIL_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentClass)) +#define E_IS_MAIL_SHELL_CONTENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_SHELL_CONTENT)) +#define E_IS_MAIL_SHELL_CONTENT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_SHELL_CONTENT)) +#define E_MAIL_SHELL_CONTENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_SHELL_CONTENT, EMailShellContentClass)) + +G_BEGIN_DECLS + +typedef struct _EMailShellContent EMailShellContent; +typedef struct _EMailShellContentClass EMailShellContentClass; +typedef struct _EMailShellContentPrivate EMailShellContentPrivate; + +struct _EMailShellContent { + EShellContent parent; + EMailShellContentPrivate *priv; +}; + +struct _EMailShellContentClass { + EShellContentClass parent_class; +}; + +GType e_mail_shell_content_get_type (void); +void e_mail_shell_content_register_type + (GTypeModule *type_module); +GtkWidget * e_mail_shell_content_new(EShellView *shell_view); +gboolean e_mail_shell_content_get_preview_visible + (EMailShellContent *mail_shell_content); +void e_mail_shell_content_set_preview_visible + (EMailShellContent *mail_shell_content, + gboolean preview_visible); +gboolean e_mail_shell_content_get_show_deleted + (EMailShellContent *mail_shell_content); +void e_mail_shell_content_set_show_deleted + (EMailShellContent *mail_shell_content, + gboolean show_deleted); +gboolean e_mail_shell_content_get_vertical_view + (EMailShellContent *mail_shell_content); +void e_mail_shell_content_set_vertical_view + (EMailShellContent *mail_shell_content, + gboolean vertical_view); +GalViewInstance * + e_mail_shell_content_get_view_instance + (EMailShellContent *mail_shell_content); +void e_mail_shell_content_set_search_strings + (EMailShellContent *mail_shell_content, + GSList *search_strings); +void e_mail_shell_content_update_view_instance + (EMailShellContent *mail_shell_content); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_CONTENT_H */ diff --git a/modules/mail/e-mail-shell-migrate.c b/modules/mail/e-mail-shell-migrate.c new file mode 100644 index 0000000000..2158580bf9 --- /dev/null +++ b/modules/mail/e-mail-shell-migrate.c @@ -0,0 +1,3104 @@ +/* + * e-mail-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-mail-shell-migrate.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <utime.h> +#include <unistd.h> +#include <dirent.h> +#include <regex.h> +#include <errno.h> +#include <ctype.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <gtk/gtk.h> + +#include <gconf/gconf-client.h> + +#include <camel/camel.h> +#include <camel/camel-store.h> +#include <camel/camel-session.h> +#include <camel/camel-file-utils.h> +#include <camel/camel-disco-folder.h> + +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#include <e-util/e-util.h> +#include <libedataserver/e-xml-utils.h> +#include <libedataserver/e-data-server-util.h> +#include <e-util/e-xml-utils.h> + +#include "e-util/e-account-utils.h" +#include "e-util/e-bconf-map.h" +#include "e-util/e-error.h" +#include "e-util/e-util-private.h" +#include "e-util/e-plugin.h" +#include "e-util/e-signature-utils.h" + +#include "e-mail-shell-backend.h" +#include "shell/e-shell-migrate.h" + +#include "e-mail-store.h" +#include "mail-config.h" +#include "em-utils.h" + +#define d(x) x + +#ifndef G_OS_WIN32 +/* No versions previous to 2.8 or thereabouts have been available on + * Windows, so don't bother with upgrade support from earlier versions + * on Win32. Do try to support upgrades from 2.12 and later to the + * current version. + */ + +/* upgrade helper functions */ +static xmlDocPtr +emm_load_xml (const gchar *dirname, const gchar *filename) +{ + xmlDocPtr doc; + struct stat st; + gchar *path; + + path = g_strdup_printf ("%s/%s", dirname, filename); + if (stat (path, &st) == -1 || !(doc = xmlParseFile (path))) { + g_free (path); + return NULL; + } + + g_free (path); + + return doc; +} + +static gint +emm_save_xml (xmlDocPtr doc, const gchar *dirname, const gchar *filename) +{ + gchar *path; + gint retval; + + path = g_strdup_printf ("%s/%s", dirname, filename); + retval = e_xml_save_file (path, doc); + g_free (path); + + return retval; +} + +static xmlNodePtr +xml_find_node (xmlNodePtr parent, const gchar *name) +{ + xmlNodePtr node; + + node = parent->children; + while (node != NULL) { + if (node->name && !strcmp ((gchar *)node->name, name)) + return node; + + node = node->next; + } + + return NULL; +} + +static void +upgrade_xml_uris (xmlDocPtr doc, gchar * (* upgrade_uri) (const gchar *uri)) +{ + xmlNodePtr root, node; + gchar *uri, *new; + + if (!doc || !(root = xmlDocGetRootElement (doc))) + return; + + if (!root->name || strcmp ((gchar *)root->name, "filteroptions") != 0) { + /* root node is not <filteroptions>, nothing to upgrade */ + return; + } + + if (!(node = xml_find_node (root, "ruleset"))) { + /* no ruleset node, nothing to upgrade */ + return; + } + + node = node->children; + while (node != NULL) { + if (node->name && !strcmp ((gchar *)node->name, "rule")) { + xmlNodePtr actionset, part, val, n; + + if ((actionset = xml_find_node (node, "actionset"))) { + /* filters.xml */ + part = actionset->children; + while (part != NULL) { + if (part->name && !strcmp ((gchar *)part->name, "part")) { + val = part->children; + while (val != NULL) { + if (val->name && !strcmp ((gchar *)val->name, "value")) { + gchar *type; + + type = (gchar *)xmlGetProp (val, (const guchar *)"type"); + if (type && !strcmp ((gchar *)type, "folder")) { + if ((n = xml_find_node (val, "folder"))) { + uri = (gchar *)xmlGetProp (n, (const guchar *)"uri"); + new = upgrade_uri (uri); + xmlFree (uri); + + xmlSetProp (n, (const guchar *)"uri", (guchar *)new); + g_free (new); + } + } + + xmlFree (type); + } + + val = val->next; + } + } + + part = part->next; + } + } else if ((actionset = xml_find_node (node, "sources"))) { + /* vfolders.xml */ + n = actionset->children; + while (n != NULL) { + if (n->name && !strcmp ((gchar *)n->name, "folder")) { + uri = (gchar *)xmlGetProp (n, (const guchar *)"uri"); + new = upgrade_uri (uri); + xmlFree (uri); + + xmlSetProp (n, (const guchar *)"uri", (guchar *)new); + g_free (new); + } + + n = n->next; + } + } + } + + node = node->next; + } +} + +/* 1.0 upgrade functions & data */ + +/* as much info as we have on a given account */ +struct _account_info_1_0 { + gchar *name; + gchar *uri; + gchar *base_uri; + union { + struct { + /* for imap */ + gchar *namespace; + gchar *namespace_full; + guint32 capabilities; + GHashTable *folders; + gchar dir_sep; + } imap; + } u; +}; + +struct _imap_folder_info_1_0 { + gchar *folder; + /* encoded? decoded? canonicalised? */ + gchar dir_sep; +}; + +static GHashTable *accounts_1_0 = NULL; +static GHashTable *accounts_name_1_0 = NULL; + +static void +imap_folder_info_1_0_free (struct _imap_folder_info_1_0 *fi) +{ + g_free(fi->folder); + g_free(fi); +} + +static void +account_info_1_0_free (struct _account_info_1_0 *ai) +{ + g_free(ai->name); + g_free(ai->uri); + g_free(ai->base_uri); + g_free(ai->u.imap.namespace); + g_free(ai->u.imap.namespace_full); + g_hash_table_destroy(ai->u.imap.folders); + g_free(ai); +} + +static gchar * +get_base_uri(const gchar *val) +{ + const gchar *tmp; + + tmp = strchr(val, ':'); + if (tmp) { + tmp++; + if (strncmp(tmp, "//", 2) == 0) + tmp += 2; + tmp = strchr(tmp, '/'); + } + + if (tmp) + return g_strndup(val, tmp-val); + else + return g_strdup(val); +} + +static gchar * +upgrade_xml_uris_1_0 (const gchar *uri) +{ + gchar *out = NULL; + + /* upgrades camel uri's */ + if (strncmp (uri, "imap:", 5) == 0) { + gchar *base_uri, dir_sep, *folder, *p; + struct _account_info_1_0 *ai; + + /* add namespace, canonicalise dir_sep to / */ + base_uri = get_base_uri (uri); + ai = g_hash_table_lookup (accounts_1_0, base_uri); + + if (ai == NULL) { + g_free (base_uri); + return NULL; + } + + dir_sep = ai->u.imap.dir_sep; + if (dir_sep == 0) { + /* no dir_sep listed, try get it from the namespace, if set */ + if (ai->u.imap.namespace != NULL) { + p = ai->u.imap.namespace; + while ((dir_sep = *p++)) { + if (dir_sep < '0' + || (dir_sep > '9' && dir_sep < 'A') + || (dir_sep > 'Z' && dir_sep < 'a') + || (dir_sep > 'z')) { + break; + } + p++; + } + } + + /* give up ... */ + if (dir_sep == 0) { + g_free (base_uri); + return NULL; + } + } + + folder = g_strdup (uri + strlen (base_uri) + 1); + + /* Add the namespace before the mailbox name, unless the mailbox is INBOX */ + if (ai->u.imap.namespace && strcmp ((gchar *)folder, "INBOX") != 0) + out = g_strdup_printf ("%s/%s/%s", base_uri, ai->u.imap.namespace, folder); + else + out = g_strdup_printf ("%s/%s", base_uri, folder); + + p = out; + while (*p) { + if (*p == dir_sep) + *p = '/'; + p++; + } + + g_free (folder); + g_free (base_uri); + } else if (strncmp (uri, "exchange:", 9) == 0) { + gchar *base_uri, *folder, *p; + + /* exchange://user@host/exchange/ * -> exchange://user@host/personal/ * */ + /* Any url encoding (%xx) in the folder name is also removed */ + base_uri = get_base_uri (uri); + uri += strlen (base_uri) + 1; + if (strncmp (uri, "exchange/", 9) == 0) { + folder = e_bconf_url_decode (uri + 9); + p = strchr (folder, '/'); + out = g_strdup_printf ("%s/personal%s", base_uri, p ? p : "/"); + g_free (folder); + } + } else if (strncmp (uri, "exchanget:", 10) == 0) { + /* these should be converted in the accounts table when it is loaded */ + g_warning ("exchanget: uri not converted: '%s'", uri); + } + + return out; +} + +static gchar * +parse_lsub (const gchar *lsub, gchar *dir_sep) +{ + static gint comp; + static regex_t pat; + regmatch_t match[3]; + const gchar *m = "^\\* LSUB \\([^)]*\\) \"?([^\" ]+)\"? \"?(.*)\"?$"; + + if (!comp) { + if (regcomp (&pat, m, REG_EXTENDED|REG_ICASE) == -1) { + g_warning ("reg comp '%s' failed: %s", m, g_strerror (errno)); + return NULL; + } + comp = 1; + } + + if (regexec (&pat, lsub, 3, match, 0) == 0) { + if (match[1].rm_so != -1 && match[2].rm_so != -1) { + if (dir_sep) + *dir_sep = (match[1].rm_eo - match[1].rm_so == 1) ? lsub[match[1].rm_so] : 0; + return g_strndup (lsub + match[2].rm_so, match[2].rm_eo - match[2].rm_so); + } + } + + return NULL; +} + +static gboolean +read_imap_storeinfo (struct _account_info_1_0 *si) +{ + FILE *storeinfo; + guint32 tmp; + gchar *buf, *folder, dir_sep, *path, *name, *p; + struct _imap_folder_info_1_0 *fi; + + si->u.imap.folders = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) imap_folder_info_1_0_free); + + /* get details from uri first */ + name = strstr (si->uri, ";override_namespace"); + if (name) { + name = strstr (si->uri, ";namespace="); + if (name) { + gchar *end; + + name += strlen (";namespace="); + if (*name == '\"') { + name++; + end = strchr (name, '\"'); + } else { + end = strchr (name, ';'); + } + + if (end) { + /* try get the dir_sep from the namespace */ + si->u.imap.namespace = g_strndup (name, end-name); + + p = si->u.imap.namespace; + while ((dir_sep = *p++)) { + if (dir_sep < '0' + || (dir_sep > '9' && dir_sep < 'A') + || (dir_sep > 'Z' && dir_sep < 'a') + || (dir_sep > 'z')) { + si->u.imap.dir_sep = dir_sep; + break; + } + p++; + } + } + } + } + + /* now load storeinfo if it exists */ + path = g_build_filename (g_get_home_dir (), "evolution", "mail", "imap", si->base_uri + 7, "storeinfo", NULL); + storeinfo = fopen (path, "r"); + g_free (path); + if (storeinfo == NULL) { + g_warning ("could not find imap store info '%s'", path); + return FALSE; + } + + /* ignore version */ + camel_file_util_decode_uint32 (storeinfo, &tmp); + camel_file_util_decode_uint32 (storeinfo, &si->u.imap.capabilities); + g_free (si->u.imap.namespace); + camel_file_util_decode_string (storeinfo, &si->u.imap.namespace); + camel_file_util_decode_uint32 (storeinfo, &tmp); + si->u.imap.dir_sep = tmp; + /* strip trailing dir_sep or / */ + if (si->u.imap.namespace + && (si->u.imap.namespace[strlen (si->u.imap.namespace) - 1] == si->u.imap.dir_sep + || si->u.imap.namespace[strlen (si->u.imap.namespace) - 1] == '/')) { + si->u.imap.namespace[strlen (si->u.imap.namespace) - 1] = 0; + } + + d(printf ("namespace '%s' dir_sep '%c'\n", si->u.imap.namespace, si->u.imap.dir_sep ? si->u.imap.dir_sep : '?')); + + while (camel_file_util_decode_string (storeinfo, &buf) == 0) { + folder = parse_lsub (buf, &dir_sep); + if (folder) { + fi = g_new0 (struct _imap_folder_info_1_0, 1); + fi->folder = folder; + fi->dir_sep = dir_sep; +#if d(!)0 + printf (" add folder '%s' ", folder); + if (dir_sep) + printf ("'%c'\n", dir_sep); + else + printf ("NIL\n"); +#endif + g_hash_table_insert (si->u.imap.folders, fi->folder, fi); + } else { + g_warning ("Could not parse LIST result '%s'\n", buf); + } + } + + fclose (storeinfo); + + return TRUE; +} + +static gboolean +load_accounts_1_0 (xmlDocPtr doc) +{ + xmlNodePtr source; + gchar *val, *tmp; + gint count = 0, i; + gchar key[32]; + + if (!(source = e_bconf_get_path (doc, "/Mail/Accounts"))) + return TRUE; + + if ((val = e_bconf_get_value (source, "num"))) { + count = atoi (val); + xmlFree (val); + } + + /* load account upgrade info for each account */ + for (i = 0; i < count; i++) { + struct _account_info_1_0 *ai; + gchar *rawuri; + + sprintf (key, "source_url_%d", i); + if (!(rawuri = e_bconf_get_value (source, key))) + continue; + + ai = g_malloc0 (sizeof (struct _account_info_1_0)); + ai->uri = e_bconf_hex_decode (rawuri); + ai->base_uri = get_base_uri (ai->uri); + sprintf (key, "account_name_%d", i); + ai->name = e_bconf_get_string (source, key); + + d(printf("load account '%s'\n", ai->uri)); + + if (!strncmp (ai->uri, "imap:", 5)) { + read_imap_storeinfo (ai); + } else if (!strncmp (ai->uri, "exchange:", 9)) { + xmlNodePtr node; + + d(printf (" upgrade exchange account\n")); + /* small hack, poke the source_url into the transport_url for exchanget: transports + - this will be picked up later in the conversion */ + sprintf (key, "transport_url_%d", i); + node = e_bconf_get_entry (source, key); + if (node && (val = (gchar *)xmlGetProp (node, (const guchar *)"value"))) { + tmp = e_bconf_hex_decode (val); + xmlFree (val); + if (strncmp (tmp, "exchanget:", 10) == 0) + xmlSetProp (node, (const guchar *)"value", (guchar *)rawuri); + g_free (tmp); + } else { + d(printf (" couldn't find transport uri?\n")); + } + } + xmlFree (rawuri); + + g_hash_table_insert (accounts_1_0, ai->base_uri, ai); + if (ai->name) + g_hash_table_insert (accounts_name_1_0, ai->name, ai); + } + + return TRUE; +} + +static gboolean +em_migrate_1_0 (const gchar *data_dir, xmlDocPtr config_xmldb, xmlDocPtr filters, xmlDocPtr vfolders, GError **error) +{ + accounts_1_0 = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) account_info_1_0_free); + accounts_name_1_0 = g_hash_table_new (g_str_hash, g_str_equal); + load_accounts_1_0 (config_xmldb); + + upgrade_xml_uris(filters, upgrade_xml_uris_1_0); + upgrade_xml_uris(vfolders, upgrade_xml_uris_1_0); + + g_hash_table_destroy (accounts_1_0); + g_hash_table_destroy (accounts_name_1_0); + + return TRUE; +} + +/* 1.2 upgrade functions */ +static gboolean +is_xml1encoded (const gchar *txt) +{ + const guchar *p; + gint isxml1 = FALSE; + gint is8bit = FALSE; + + p = (const guchar *)txt; + while (*p) { + if (p[0] == '\\' && p[1] == 'U' && p[2] == '+' + && isxdigit (p[3]) && isxdigit (p[4]) && isxdigit (p[5]) && isxdigit (p[6]) + && p[7] == '\\') { + isxml1 = TRUE; + p+=7; + } else if (p[0] >= 0x80) + is8bit = TRUE; + p++; + } + + /* check for invalid utf8 that needs cleaning */ + if (is8bit && !isxml1) + isxml1 = !g_utf8_validate (txt, -1, NULL); + + return isxml1; +} + +static gchar * +decode_xml1 (const gchar *txt) +{ + GString *out = g_string_new (""); + const guchar *p; + gchar *res; + + /* convert: + \U+XXXX\ -> utf8 + 8 bit characters -> utf8 (iso-8859-1) */ + + p = (const guchar *) txt; + while (*p) { + if (p[0] > 0x80 + || (p[0] == '\\' && p[1] == 'U' && p[2] == '+' + && isxdigit (p[3]) && isxdigit (p[4]) && isxdigit (p[5]) && isxdigit (p[6]) + && p[7] == '\\')) { + gchar utf8[8]; + gunichar u; + + if (p[0] == '\\') { + memcpy (utf8, p + 3, 4); + utf8[4] = 0; + u = strtoul (utf8, NULL, 16); + p+=7; + } else + u = p[0]; + utf8[g_unichar_to_utf8 (u, utf8)] = 0; + g_string_append (out, utf8); + } else { + g_string_append_c (out, *p); + } + p++; + } + + res = out->str; + g_string_free (out, FALSE); + + return res; +} + +static gchar * +utf8_reencode (const gchar *txt) +{ + GString *out = g_string_new (""); + gchar *p; + gchar *res; + + /* convert: + libxml1 8 bit utf8 converted to xml entities byte-by-byte chars -> utf8 */ + + p = (gchar *)txt; + + while (*p) { + g_string_append_c (out, (gchar)g_utf8_get_char ((const gchar *)p)); + p = (gchar *)g_utf8_next_char (p); + } + + res = out->str; + if (g_utf8_validate (res, -1, NULL)) { + g_string_free (out, FALSE); + return res; + } else { + g_string_free (out, TRUE); + return g_strdup (txt); + } +} + +static gboolean +upgrade_xml_1_2_rec (xmlNodePtr node) +{ + const gchar *value_tags[] = { "string", "address", "regex", "file", "command", NULL }; + const gchar *rule_tags[] = { "title", NULL }; + const gchar *item_props[] = { "name", NULL }; + struct { + const gchar *name; + const gchar **tags; + const gchar **props; + } tags[] = { + { "value", value_tags, NULL }, + { "rule", rule_tags, NULL }, + { "item", NULL, item_props }, + { 0 }, + }; + xmlNodePtr work; + gint i,j; + gchar *txt, *tmp; + + /* upgrades the content of a node, if the node has a specific parent/node name */ + + for (i = 0; tags[i].name; i++) { + if (!strcmp ((gchar *)node->name, tags[i].name)) { + if (tags[i].tags != NULL) { + work = node->children; + while (work) { + for (j = 0; tags[i].tags[j]; j++) { + if (!strcmp ((gchar *)work->name, tags[i].tags[j])) { + txt = (gchar *)xmlNodeGetContent (work); + if (is_xml1encoded (txt)) { + tmp = decode_xml1 (txt); + d(printf ("upgrading xml node %s/%s '%s' -> '%s'\n", + tags[i].name, tags[i].tags[j], txt, tmp)); + xmlNodeSetContent (work, (guchar *)tmp); + g_free (tmp); + } + xmlFree (txt); + } + } + work = work->next; + } + break; + } + + if (tags[i].props != NULL) { + for (j = 0; tags[i].props[j]; j++) { + txt = (gchar *)xmlGetProp (node, (guchar *)tags[i].props[j]); + tmp = utf8_reencode (txt); + d(printf ("upgrading xml property %s on node %s '%s' -> '%s'\n", + tags[i].props[j], tags[i].name, txt, tmp)); + xmlSetProp (node, (const guchar *)tags[i].props[j], (guchar *)tmp); + g_free (tmp); + xmlFree (txt); + } + } + } + } + + node = node->children; + while (node) { + upgrade_xml_1_2_rec (node); + node = node->next; + } + + return TRUE; +} + +static gboolean +em_upgrade_xml_1_2 (xmlDocPtr doc) +{ + xmlNodePtr root; + + if (!doc || !(root = xmlDocGetRootElement (doc))) + return TRUE; + + return upgrade_xml_1_2_rec (root); +} + +/* ********************************************************************** */ +/* Tables for converting flat bonobo conf -> gconf xml blob */ +/* ********************************************************************** */ + +/* Mail/Accounts/ * */ +static e_bconf_map_t cc_map[] = { + { "account_always_cc_%i", "always", E_BCONF_MAP_BOOL }, + { "account_always_cc_addrs_%i", "recipients", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +static e_bconf_map_t bcc_map[] = { + { "account_always_cc_%i", "always", E_BCONF_MAP_BOOL }, + { "account_always_bcc_addrs_%i", "recipients", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +static e_bconf_map_t pgp_map[] = { + { "account_pgp_encrypt_to_self_%i", "encrypt-to-self", E_BCONF_MAP_BOOL }, + { "account_pgp_always_trust_%i", "always-trust", E_BCONF_MAP_BOOL }, + { "account_pgp_always_sign_%i", "always-sign", E_BCONF_MAP_BOOL }, + { "account_pgp_no_imip_sign_%i", "no-imip-sign", E_BCONF_MAP_BOOL }, + { "account_pgp_key_%i", "key-id", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +static e_bconf_map_t smime_map[] = { + { "account_smime_encrypt_to_self_%i", "encrypt-to-self", E_BCONF_MAP_BOOL }, + { "account_smime_always_sign_%i", "always-sign", E_BCONF_MAP_BOOL }, + { "account_smime_key_%i", "key-id", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +static e_bconf_map_t identity_sig_map[] = { + { "identity_autogenerated_signature_%i", "auto", E_BCONF_MAP_BOOL }, + { "identity_def_signature_%i", "default", E_BCONF_MAP_LONG }, + { NULL }, +}; + +static e_bconf_map_t identity_map[] = { + { "identity_name_%i", "name", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { "identity_address_%i", "addr-spec", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { "identity_reply_to_%i", "reply-to", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { "identity_organization_%i", "organization", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL, "signature", E_BCONF_MAP_CHILD, identity_sig_map }, + { NULL }, +}; + +static e_bconf_map_t source_map[] = { + { "source_save_passwd_%i", "save-passwd", E_BCONF_MAP_BOOL }, + { "source_keep_on_server_%i", "keep-on-server", E_BCONF_MAP_BOOL }, + { "source_auto_check_%i", "auto-check", E_BCONF_MAP_BOOL }, + { "source_auto_check_time_%i", "auto-check-timeout", E_BCONF_MAP_LONG }, + { "source_url_%i", "url", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +static e_bconf_map_t transport_map[] = { + { "transport_save_passwd_%i", "save-passwd", E_BCONF_MAP_BOOL }, + { "transport_url_%i", "url", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +static e_bconf_map_t account_map[] = { + { "account_name_%i", "name", E_BCONF_MAP_STRING }, + { "source_enabled_%i", "enabled", E_BCONF_MAP_BOOL }, + { NULL, "identity", E_BCONF_MAP_CHILD, identity_map }, + { NULL, "source", E_BCONF_MAP_CHILD, source_map }, + { NULL, "transport", E_BCONF_MAP_CHILD, transport_map }, + { "account_drafts_folder_uri_%i", "drafts-folder", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { "account_sent_folder_uri_%i", "sent-folder", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL, "auto-cc", E_BCONF_MAP_CHILD, cc_map }, + { NULL, "auto-bcc", E_BCONF_MAP_CHILD, bcc_map }, + { NULL, "pgp", E_BCONF_MAP_CHILD, pgp_map }, + { NULL, "smime", E_BCONF_MAP_CHILD, smime_map }, + { NULL }, +}; + +/* /Mail/Signatures/ * */ +static e_bconf_map_t signature_format_map[] = { + { "text/plain", }, + { "text/html", }, + { NULL } +}; + +static e_bconf_map_t signature_map[] = { + { "name_%i", "name", E_BCONF_MAP_STRING }, + { "html_%i", "format", E_BCONF_MAP_ENUM, signature_format_map }, + { "filename_%i", "filename", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { "script_%i", "script", E_BCONF_MAP_STRING|E_BCONF_MAP_CONTENT }, + { NULL }, +}; + +/* ********************************************************************** */ +/* Tables for bonobo conf -> gconf conversion */ +/* ********************************************************************** */ + +static e_gconf_map_t mail_accounts_map[] = { + /* /Mail/Accounts - most entries are processed via the xml blob routine */ + /* This also works because the initial uid mapping is 1:1 with the list order */ + { "default_account", "mail/default_account", E_GCONF_MAP_SIMPLESTRING }, + { 0 }, +}; + +static e_gconf_map_t mail_display_map[] = { + /* /Mail/Display */ + { "side_bar_search", "mail/display/side_bar_search", E_GCONF_MAP_BOOL }, + { "thread_list", "mail/display/thread_list", E_GCONF_MAP_BOOL }, + { "thread_subject", "mail/display/thread_subject", E_GCONF_MAP_BOOL }, + { "hide_deleted", "mail/display/show_deleted", E_GCONF_MAP_BOOLNOT }, + { "preview_pane", "mail/display/show_preview", E_GCONF_MAP_BOOL }, + { "paned_size", "mail/display/paned_size", E_GCONF_MAP_INT }, + { "seen_timeout", "mail/display/mark_seen_timeout", E_GCONF_MAP_INT }, + { "do_seen_timeout", "mail/display/mark_seen", E_GCONF_MAP_BOOL }, + { "http_images", "mail/display/load_http_images", E_GCONF_MAP_INT }, + { "citation_highlight", "mail/display/mark_citations", E_GCONF_MAP_BOOL }, + { "citation_color", "mail/display/citation_colour", E_GCONF_MAP_COLOUR }, + { 0 }, +}; + +static e_gconf_map_t mail_format_map[] = { + /* /Mail/Format */ + { "message_display_style", "mail/display/message_style", E_GCONF_MAP_INT }, + { "send_html", "mail/composer/send_html", E_GCONF_MAP_BOOL }, + { "default_reply_style", "mail/format/reply_style", E_GCONF_MAP_INT }, + { "default_forward_style", "mail/format/forward_style", E_GCONF_MAP_INT }, + { "default_charset", "mail/composer/charset", E_GCONF_MAP_STRING }, + { "confirm_unwanted_html", "mail/prompts/unwanted_html", E_GCONF_MAP_BOOL }, + { 0 }, +}; + +static e_gconf_map_t mail_trash_map[] = { + /* /Mail/Trash */ + { "empty_on_exit", "mail/trash/empty_on_exit", E_GCONF_MAP_BOOL }, + { 0 }, +}; + +static e_gconf_map_t mail_prompts_map[] = { + /* /Mail/Prompts */ + { "confirm_expunge", "mail/prompts/expunge", E_GCONF_MAP_BOOL }, + { "empty_subject", "mail/prompts/empty_subject", E_GCONF_MAP_BOOL }, + { "only_bcc", "mail/prompts/only_bcc", E_GCONF_MAP_BOOL }, + { 0 } +}; + +static e_gconf_map_t mail_filters_map[] = { + /* /Mail/Filters */ + { "log", "mail/filters/log", E_GCONF_MAP_BOOL }, + { "log_path", "mail/filters/logfile", E_GCONF_MAP_STRING }, + { 0 } +}; + +static e_gconf_map_t mail_notify_map[] = { + /* /Mail/Notify */ + { "new_mail_notification", "mail/notify/type", E_GCONF_MAP_INT }, + { "new_mail_notification_sound_file", "mail/notify/sound", E_GCONF_MAP_STRING }, + { 0 } +}; + +static e_gconf_map_t mail_filesel_map[] = { + /* /Mail/Filesel */ + { "last_filesel_dir", "mail/save_dir", E_GCONF_MAP_STRING }, + { 0 } +}; + +static e_gconf_map_t mail_composer_map[] = { + /* /Mail/Composer */ + { "ViewFrom", "mail/composer/view/From", E_GCONF_MAP_BOOL }, + { "ViewReplyTo", "mail/composer/view/ReplyTo", E_GCONF_MAP_BOOL }, + { "ViewCC", "mail/composer/view/Cc", E_GCONF_MAP_BOOL }, + { "ViewBCC", "mail/composer/view/Bcc", E_GCONF_MAP_BOOL }, + { "ViewSubject", "mail/composer/view/Subject", E_GCONF_MAP_BOOL }, + { 0 }, +}; + +/* ********************************************************************** */ + +static e_gconf_map_t importer_elm_map[] = { + /* /Importer/Elm */ + { "mail", "importer/elm/mail", E_GCONF_MAP_BOOL }, + { "mail-imported", "importer/elm/mail-imported", E_GCONF_MAP_BOOL }, + { 0 }, +}; + +static e_gconf_map_t importer_pine_map[] = { + /* /Importer/Pine */ + { "mail", "importer/elm/mail", E_GCONF_MAP_BOOL }, + { "address", "importer/elm/address", E_GCONF_MAP_BOOL }, + { 0 }, +}; + +static e_gconf_map_t importer_netscape_map[] = { + /* /Importer/Netscape */ + { "mail", "importer/netscape/mail", E_GCONF_MAP_BOOL }, + { "settings", "importer/netscape/settings", E_GCONF_MAP_BOOL }, + { "filters", "importer/netscape/filters", E_GCONF_MAP_BOOL }, + { 0 }, +}; + +/* ********************************************************************** */ + +static e_gconf_map_list_t gconf_remap_list[] = { + { "/Mail/Accounts", mail_accounts_map }, + { "/Mail/Display", mail_display_map }, + { "/Mail/Format", mail_format_map }, + { "/Mail/Trash", mail_trash_map }, + { "/Mail/Prompts", mail_prompts_map }, + { "/Mail/Filters", mail_filters_map }, + { "/Mail/Notify", mail_notify_map }, + { "/Mail/Filesel", mail_filesel_map }, + { "/Mail/Composer", mail_composer_map }, + + { "/Importer/Elm", importer_elm_map }, + { "/Importer/Pine", importer_pine_map }, + { "/Importer/Netscape", importer_netscape_map }, + + { 0 }, +}; + +static struct { + const gchar *label; + const gchar *colour; +} label_default[5] = { + { N_("Important"), "#EF2929" }, /* red */ + { N_("Work"), "#F57900" }, /* orange */ + { N_("Personal"), "#4E9A06" }, /* green */ + { N_("To Do"), "#3465A4" }, /* blue */ + { N_("Later"), "#75507B" } /* purple */ +}; + +/* remaps mail config from bconf to gconf */ +static gboolean +bconf_import(GConfClient *gconf, xmlDocPtr config_xmldb) +{ + xmlNodePtr source; + gchar labx[16], colx[16]; + gchar *val, *lab, *col; + GSList *list, *l; + gint i; + + e_bconf_import(gconf, config_xmldb, gconf_remap_list); + + /* Labels: + label string + label colour as integer + -> label string:# colour as hex */ + source = e_bconf_get_path(config_xmldb, "/Mail/Labels"); + if (source) { + list = NULL; + for (i = 0; i < 5; i++) { + sprintf(labx, "label_%d", i); + sprintf(colx, "color_%d", i); + lab = e_bconf_get_string(source, labx); + if ((col = e_bconf_get_value(source, colx))) { + sprintf(colx, "#%06x", atoi(col) & 0xffffff); + g_free(col); + } else + strcpy(colx, label_default[i].colour); + + val = g_strdup_printf("%s:%s", lab ? lab : label_default[i].label, colx); + list = g_slist_append(list, val); + g_free(lab); + } + + gconf_client_set_list(gconf, "/apps/evolution/mail/labels", GCONF_VALUE_STRING, list, NULL); + while (list) { + l = list->next; + g_free(list->data); + g_slist_free_1(list); + list = l; + } + } else { + g_warning("could not find /Mail/Labels in old config database, skipping"); + } + + /* Accounts: The flat bonobo-config structure is remapped to a list of xml blobs. Upgrades as necessary */ + e_bconf_import_xml_blob(gconf, config_xmldb, account_map, "/Mail/Accounts", + "/apps/evolution/mail/accounts", "account", "uid"); + + /* Same for signatures */ + e_bconf_import_xml_blob(gconf, config_xmldb, signature_map, "/Mail/Signatures", + "/apps/evolution/mail/signatures", "signature", NULL); + + return TRUE; +} + +static gboolean +em_migrate_1_2(const gchar *data_dir, xmlDocPtr config_xmldb, xmlDocPtr filters, xmlDocPtr vfolders, GError **error) +{ + GConfClient *gconf; + + gconf = gconf_client_get_default(); + bconf_import(gconf, config_xmldb); + g_object_unref(gconf); + + em_upgrade_xml_1_2(filters); + em_upgrade_xml_1_2(vfolders); + + return TRUE; +} + +/* 1.4 upgrade functions */ + +#define EM_MIGRATE_SESSION_TYPE (em_migrate_session_get_type ()) +#define EM_MIGRATE_SESSION(obj) (CAMEL_CHECK_CAST((obj), EM_MIGRATE_SESSION_TYPE, EMMigrateSession)) +#define EM_MIGRATE_SESSION_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_MIGRATE_SESSION_TYPE, EMMigrateSessionClass)) +#define EM_MIGRATE_IS_SESSION(o) (CAMEL_CHECK_TYPE((o), EM_MIGRATE_SESSION_TYPE)) + +typedef struct _EMMigrateSession { + CamelSession parent_object; + + CamelStore *store; /* new folder tree store */ + gchar *srcdir; /* old folder tree path */ +} EMMigrateSession; + +typedef struct _EMMigrateSessionClass { + CamelSessionClass parent_class; + +} EMMigrateSessionClass; + +static CamelType em_migrate_session_get_type (void); +static CamelSession *em_migrate_session_new (const gchar *path); + +static void +class_init (EMMigrateSessionClass *klass) +{ + ; +} + +static CamelType +em_migrate_session_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + type = camel_type_register ( + camel_session_get_type (), + "EMMigrateSession", + sizeof (EMMigrateSession), + sizeof (EMMigrateSessionClass), + (CamelObjectClassInitFunc) class_init, + NULL, + NULL, + NULL); + } + + return type; +} + +static CamelSession * +em_migrate_session_new (const gchar *path) +{ + CamelSession *session; + + session = CAMEL_SESSION (camel_object_new (EM_MIGRATE_SESSION_TYPE)); + + camel_session_construct (session, path); + + return session; +} + + +#endif /* !G_OS_WIN32 */ + +static GtkWidget *window; +static GtkLabel *label; +static GtkProgressBar *progress; + +static void +em_migrate_setup_progress_dialog (const gchar *title, const gchar *desc) +{ + GtkWidget *vbox, *hbox, *w; + gchar *markup; + + 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 (desc); + + gtk_label_set_line_wrap ((GtkLabel *) w, TRUE); + gtk_widget_show (w); + gtk_box_pack_start_defaults ((GtkBox *) vbox, w); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox); + gtk_box_pack_start_defaults ((GtkBox *) vbox, hbox); + + label = (GtkLabel *) gtk_label_new (""); + gtk_widget_show ((GtkWidget *) label); + gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) label); + + progress = (GtkProgressBar *) gtk_progress_bar_new (); + gtk_widget_show ((GtkWidget *) progress); + gtk_box_pack_start_defaults ((GtkBox *) hbox, (GtkWidget *) progress); + + /* Prepare the message */ + vbox = gtk_vbox_new (FALSE, 12); + gtk_widget_show (vbox); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + + w = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0); + markup = g_strconcat ("<big><b>", title ? title : _("Migration"), "</b></big>", NULL); + gtk_label_set_markup (GTK_LABEL (w), markup); + gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0); + g_free (markup); + + w = gtk_label_new (desc); + gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0); + gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0); + + /* Progress bar */ + w = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0); + + label = GTK_LABEL (gtk_label_new ("")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0); + gtk_label_set_line_wrap (label, TRUE); + gtk_widget_show (GTK_WIDGET (label)); + gtk_box_pack_start (GTK_BOX (w), GTK_WIDGET (label), TRUE, TRUE, 0); + + progress = GTK_PROGRESS_BAR (gtk_progress_bar_new ()); + gtk_widget_show (GTK_WIDGET (progress)); + gtk_box_pack_start (GTK_BOX (w), GTK_WIDGET (progress), TRUE, TRUE, 0); + + gtk_container_add (GTK_CONTAINER (window), hbox); + gtk_widget_show_all (hbox); + gtk_widget_show (window); +} + +static void +em_migrate_close_progress_dialog (void) +{ + gtk_widget_destroy ((GtkWidget *) window); +} + +static void +em_migrate_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 +em_migrate_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 (); +} + +#ifndef G_OS_WIN32 + +static gboolean +is_mail_folder (const gchar *metadata) +{ + xmlNodePtr node; + xmlDocPtr doc; + gchar *type; + + if (!(doc = xmlParseFile (metadata))) { + g_warning ("Cannot parse `%s'", metadata); + return FALSE; + } + + if (!(node = xmlDocGetRootElement (doc))) { + g_warning ("`%s' corrupt: document contains no root node", metadata); + xmlFreeDoc (doc); + return FALSE; + } + + if (!node->name || strcmp ((gchar *)node->name, "efolder") != 0) { + g_warning ("`%s' corrupt: root node is not 'efolder'", metadata); + xmlFreeDoc (doc); + return FALSE; + } + + node = node->children; + while (node != NULL) { + if (node->name && !strcmp ((gchar *)node->name, "type")) { + type = (gchar *)xmlNodeGetContent (node); + if (!strcmp ((gchar *)type, "mail")) { + xmlFreeDoc (doc); + xmlFree (type); + + return TRUE; + } + + xmlFree (type); + + break; + } + + node = node->next; + } + + xmlFreeDoc (doc); + + return FALSE; +} + +static gboolean +get_local_et_expanded (const gchar *dirname) +{ + xmlNodePtr node; + xmlDocPtr doc; + struct stat st; + gchar *buf, *p; + gint thread_list; + + buf = g_strdup_printf ("%s/evolution/config/file:%s", g_get_home_dir (), dirname); + p = buf + strlen (g_get_home_dir ()) + strlen ("/evolution/config/file:"); + e_filename_make_safe (p); + + if (stat (buf, &st) == -1) { + g_free (buf); + return FALSE; + } + + if (!(doc = xmlParseFile (buf))) { + g_free (buf); + return FALSE; + } + + g_free (buf); + + if (!(node = xmlDocGetRootElement (doc)) || strcmp ((gchar *)node->name, "expanded_state") != 0) { + xmlFreeDoc (doc); + return FALSE; + } + + if (!(buf = (gchar *)xmlGetProp (node, (const guchar *)"default"))) { + xmlFreeDoc (doc); + return FALSE; + } + + thread_list = strcmp (buf, "0") == 0 ? 0 : 1; + xmlFree (buf); + + xmlFreeDoc (doc); + + return thread_list; +} + +static gchar * +get_local_store_uri (const gchar *dirname, gchar **namep, gint *indexp) +{ + gchar *name, *protocol, *metadata, *tmp; + gint index; + struct stat st; + xmlNodePtr node; + xmlDocPtr doc; + + metadata = g_build_filename(dirname, "local-metadata.xml", NULL); + + /* in 1.4, any errors are treated as defaults, this function cannot fail */ + + /* defaults */ + name = (gchar *) "mbox"; + protocol = (gchar *) "mbox"; + index = TRUE; + + if (stat (metadata, &st) == -1 || !S_ISREG (st.st_mode)) + goto nofile; + + doc = xmlParseFile(metadata); + if (doc == NULL) + goto nofile; + + node = doc->children; + if (strcmp((gchar *)node->name, "folderinfo")) + goto dodefault; + + for (node = node->children; node; node = node->next) { + if (node->name && !strcmp ((gchar *)node->name, "folder")) { + tmp = (gchar *)xmlGetProp (node, (const guchar *)"type"); + if (tmp) { + protocol = alloca(strlen(tmp)+1); + strcpy(protocol, tmp); + xmlFree(tmp); + } + tmp = (gchar *)xmlGetProp (node, (const guchar *)"name"); + if (tmp) { + name = alloca(strlen(tmp)+1); + strcpy(name, tmp); + xmlFree(tmp); + } + tmp = (gchar *)xmlGetProp (node, (const guchar *)"index"); + if (tmp) { + index = atoi(tmp); + xmlFree(tmp); + } + } + } +dodefault: + xmlFreeDoc (doc); +nofile: + g_free(metadata); + + *namep = g_strdup(name); + *indexp = index; + + return g_strdup_printf("%s:%s", protocol, dirname); +} + +#endif /* !G_OS_WIN32 */ + +enum { + CP_UNIQUE = 0, + CP_OVERWRITE, + CP_APPEND +}; + +static gint open_flags[3] = { + O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT | O_APPEND, +}; + +static gboolean +cp (const gchar *src, const gchar *dest, gboolean show_progress, gint mode) +{ + guchar readbuf[65536]; + gssize nread, nwritten; + gint errnosav, readfd, writefd; + gsize total = 0; + struct stat st; + struct utimbuf ut; + + /* if the dest file exists and has content, abort - we don't + * want to corrupt their existing data */ + if (g_stat (dest, &st) == 0 && st.st_size > 0 && mode == CP_UNIQUE) { + errno = EEXIST; + return FALSE; + } + + if (g_stat (src, &st) == -1 + || (readfd = g_open (src, O_RDONLY | O_BINARY, 0)) == -1) + return FALSE; + + if ((writefd = g_open (dest, open_flags[mode] | O_BINARY, 0666)) == -1) { + errnosav = errno; + close (readfd); + errno = errnosav; + return FALSE; + } + + do { + do { + nread = read (readfd, readbuf, sizeof (readbuf)); + } while (nread == -1 && errno == EINTR); + + if (nread == 0) + break; + else if (nread < 0) + goto exception; + + do { + nwritten = write (writefd, readbuf, nread); + } while (nwritten == -1 && errno == EINTR); + + if (nwritten < nread) + goto exception; + + total += nwritten; + if (show_progress) + em_migrate_set_progress (((double) total) / ((double) st.st_size)); + } while (total < st.st_size); + + if (fsync (writefd) == -1) + goto exception; + + close (readfd); + if (close (writefd) == -1) + goto failclose; + + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + utime (dest, &ut); + chmod (dest, st.st_mode); + + return TRUE; + + exception: + + errnosav = errno; + close (readfd); + close (writefd); + errno = errnosav; + + failclose: + + errnosav = errno; + unlink (dest); + errno = errnosav; + + return FALSE; +} + +#ifndef G_OS_WIN32 + +static gboolean +cp_r (const gchar *src, const gchar *dest, const gchar *pattern, gint mode) +{ + GString *srcpath, *destpath; + struct dirent *dent; + gsize slen, dlen; + struct stat st; + DIR *dir; + + if (g_mkdir_with_parents (dest, 0777) == -1) + return FALSE; + + if (!(dir = opendir (src))) + return FALSE; + + srcpath = g_string_new (src); + g_string_append_c (srcpath, '/'); + slen = srcpath->len; + + destpath = g_string_new (dest); + g_string_append_c (destpath, '/'); + dlen = destpath->len; + + while ((dent = readdir (dir))) { + if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, "..")) + continue; + + g_string_truncate (srcpath, slen); + g_string_truncate (destpath, dlen); + + g_string_append (srcpath, dent->d_name); + g_string_append (destpath, dent->d_name); + + if (stat (srcpath->str, &st) == -1) + continue; + + if (S_ISDIR (st.st_mode)) { + cp_r (srcpath->str, destpath->str, pattern, mode); + } else if (!pattern || !strcmp (dent->d_name, pattern)) { + cp (srcpath->str, destpath->str, FALSE, mode); + } + } + + closedir (dir); + + g_string_free (destpath, TRUE); + g_string_free (srcpath, TRUE); + + return TRUE; +} + +static void +mbox_build_filename (GString *path, const gchar *toplevel_dir, const gchar *full_name) +{ + const gchar *start, *inptr = full_name; + gint subdirs = 0; + + while (*inptr != '\0') { + if (*inptr == '/') + subdirs++; + inptr++; + } + + g_string_assign(path, toplevel_dir); + g_string_append_c (path, '/'); + + inptr = full_name; + while (*inptr != '\0') { + start = inptr; + while (*inptr != '/' && *inptr != '\0') + inptr++; + + g_string_append_len (path, start, inptr - start); + + if (*inptr == '/') { + g_string_append (path, ".sbd/"); + inptr++; + + /* strip extranaeous '/'s */ + while (*inptr == '/') + inptr++; + } + } +} + +static gboolean +em_migrate_folder(EMMigrateSession *session, const gchar *dirname, const gchar *full_name, GError **error) +{ + CamelFolder *old_folder = NULL, *new_folder = NULL; + CamelStore *local_store = NULL; + CamelException ex; + gchar *name, *uri; + GPtrArray *uids; + struct stat st; + gboolean thread_list; + gint index, i; + GString *src, *dest; + gboolean success = FALSE; + + camel_exception_init (&ex); + + src = g_string_new(""); + + g_string_printf(src, "%s/folder-metadata.xml", dirname); + if (stat (src->str, &st) == -1 + || !S_ISREG (st.st_mode) + || !is_mail_folder(src->str)) { + /* Not an evolution mail folder */ + g_string_free(src, TRUE); + return TRUE; + } + + dest = g_string_new(""); + uri = get_local_store_uri(dirname, &name, &index); + em_migrate_set_folder_name (full_name); + thread_list = get_local_et_expanded (dirname); + + /* Manually copy local mbox files, its much faster */ + if (!strncmp (uri, "mbox:", 5)) { + static const gchar *meta_ext[] = { ".summary", ".ibex.index", ".ibex.index.data" }; + gsize slen, dlen; + FILE *fp; + gchar *p; + gint mode; + + g_string_printf (src, "%s/%s", uri + 5, name); + mbox_build_filename (dest, ((CamelService *)session->store)->url->path, full_name); + p = strrchr (dest->str, '/'); + *p = '\0'; + + slen = src->len; + dlen = dest->len; + + if (g_mkdir_with_parents (dest->str, 0777) == -1 && errno != EEXIST) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to create new folder `%s': %s"), + dest->str, g_strerror (errno)); + goto fatal; + } + + *p = '/'; + mode = CP_UNIQUE; + retry_copy: + if (!cp (src->str, dest->str, TRUE, mode)) { + if (errno == EEXIST) { + gint save = errno; + + switch (e_error_run(NULL, "mail:ask-migrate-existing", src->str, dest->str, NULL)) { + case GTK_RESPONSE_ACCEPT: + mode = CP_OVERWRITE; + goto retry_copy; + case GTK_RESPONSE_OK: + mode = CP_APPEND; + goto retry_copy; + case GTK_RESPONSE_REJECT: + goto ignore; + } + + errno = save; + } + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to copy folder `%s' to `%s': %s"), + src->str, dest->str, g_strerror (errno)); + goto fatal; + } + ignore: + + /* create a .cmeta file specifying to index and/or thread the folder */ + g_string_truncate (dest, dlen); + g_string_append (dest, ".cmeta"); + if ((fp = fopen (dest->str, "w")) != NULL) { + gint fd = fileno (fp); + + /* write the magic string */ + if (fwrite ("CLMD", 4, 1, fp) != 1) + goto cmeta_err; + + /* write the version (1) */ + if (camel_file_util_encode_uint32 (fp, 1) == -1) + goto cmeta_err; + + /* write the meta count */ + if (camel_file_util_encode_uint32 (fp, thread_list ? 1 : 0) == -1) + goto cmeta_err; + + if (!thread_list) { + if (camel_file_util_encode_string (fp, "evolution:thread_list") == -1) + goto cmeta_err; + + if (camel_file_util_encode_string (fp, !thread_list ? "1" : "0") == -1) + goto cmeta_err; + } + + /* write the prop count (only prop is the index prop) */ + if (camel_file_util_encode_uint32 (fp, 1) == -1) + goto cmeta_err; + + /* write the index prop tag (== CAMEL_FOLDER_ARG_LAST|CAMEL_ARG_BOO) */ + if (camel_file_util_encode_uint32 (fp, CAMEL_FOLDER_ARG_LAST|CAMEL_ARG_BOO) == -1) + goto cmeta_err; + + /* write the index prop value */ + if (camel_file_util_encode_uint32 (fp, 1) == -1) + goto cmeta_err; + + fflush (fp); + + if (fsync (fd) == -1) { + cmeta_err: + fclose (fp); + unlink (dest->str); + } else { + fclose (fp); + } + } + + /* copy over the metadata files */ + for (i = 0; i < sizeof(meta_ext)/sizeof(meta_ext[0]); i++) { + g_string_truncate (src, slen); + g_string_truncate (dest, dlen); + + g_string_append (src, meta_ext[i]); + g_string_append (dest, meta_ext[i]); + cp (src->str, dest->str, FALSE, CP_OVERWRITE); + } + } else { + guint32 flags = CAMEL_STORE_FOLDER_CREATE; + + if (!(local_store = camel_session_get_store ((CamelSession *) session, uri, &ex)) + || !(old_folder = camel_store_get_folder (local_store, name, 0, &ex))) + goto fatal; + + flags |= (index ? CAMEL_STORE_FOLDER_BODY_INDEX : 0); + if (!(new_folder = camel_store_get_folder (session->store, full_name, flags, &ex))) + goto fatal; + + if (!thread_list) { + camel_object_meta_set (new_folder, "evolution:thread_list", !thread_list ? "1" : "0"); + camel_object_state_write (new_folder); + } + + uids = camel_folder_get_uids (old_folder); + for (i = 0; i < uids->len; i++) { + CamelMimeMessage *message; + CamelMessageInfo *info; + + if (!(info = camel_folder_get_message_info (old_folder, uids->pdata[i]))) + continue; + + if (!(message = camel_folder_get_message (old_folder, uids->pdata[i], &ex))) { + camel_folder_free_message_info (old_folder, info); + camel_folder_free_uids (old_folder, uids); + goto fatal; + } + + camel_folder_append_message (new_folder, message, info, NULL, &ex); + camel_folder_free_message_info (old_folder, info); + camel_object_unref (message); + + if (camel_exception_is_set (&ex)) + break; + + em_migrate_set_progress (((double) i + 1) / ((double) uids->len)); + } + + camel_folder_free_uids (old_folder, uids); + + if (camel_exception_is_set (&ex)) + goto fatal; + } + success = TRUE; +fatal: + g_free (uri); + g_free (name); + g_string_free(src, TRUE); + g_string_free(dest, TRUE); + if (local_store) + camel_object_unref(local_store); + if (old_folder) + camel_object_unref(old_folder); + if (new_folder) + camel_object_unref(new_folder); + + if (camel_exception_is_set (&ex)) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + "%s", camel_exception_get_description (&ex)); + camel_exception_clear (&ex); + } + + return success; +} + +static gboolean +em_migrate_dir (EMMigrateSession *session, const gchar *dirname, const gchar *full_name, GError **error) +{ + gchar *path; + DIR *dir; + struct stat st; + struct dirent *dent; + gboolean success = TRUE; + + if (!em_migrate_folder(session, dirname, full_name, error)) + return FALSE; + + /* no subfolders, not readable, don't care */ + path = g_strdup_printf ("%s/subfolders", dirname); + if (stat (path, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_free (path); + return TRUE; + } + + if (!(dir = opendir (path))) { + g_free (path); + return TRUE; + } + + while (success && (dent = readdir (dir))) { + gchar *full_path; + gchar *name; + + if (dent->d_name[0] == '.') + continue; + + full_path = g_strdup_printf ("%s/%s", path, dent->d_name); + if (stat (full_path, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_free (full_path); + continue; + } + + name = g_strdup_printf ("%s/%s", full_name, dent->d_name); + success = em_migrate_dir (session, full_path, name, error); + g_free (full_path); + g_free (name); + } + + closedir (dir); + + g_free (path); + + return success; +} + +static gboolean +em_migrate_local_folders_1_4 (EMMigrateSession *session, GError **error) +{ + struct dirent *dent; + struct stat st; + DIR *dir; + gboolean success = TRUE; + + if (!(dir = opendir (session->srcdir))) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to scan for existing mailboxes at " + "`%s': %s"), session->srcdir, g_strerror (errno)); + return FALSE; + } + + em_migrate_setup_progress_dialog ( + _("Migrating Folders"), + _("The location and hierarchy of the Evolution mailbox " + "folders has changed since Evolution 1.x.\n\nPlease be " + "patient while Evolution migrates your folders...")); + + while (success && (dent = readdir (dir))) { + gchar *full_path; + + if (dent->d_name[0] == '.') + continue; + + full_path = g_strdup_printf ("%s/%s", session->srcdir, dent->d_name); + if (stat (full_path, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_free (full_path); + continue; + } + + success = em_migrate_dir (session, full_path, dent->d_name, error); + g_free (full_path); + } + + closedir (dir); + + em_migrate_close_progress_dialog (); + + return success; +} + +static gchar * +upgrade_xml_uris_1_4 (const gchar *uri) +{ + gchar *path, *prefix, *p; + CamelURL *url; + + if (!strncmp (uri, "file:", 5)) { + url = camel_url_new (uri, NULL); + camel_url_set_protocol (url, "email"); + camel_url_set_user (url, "local"); + camel_url_set_host (url, "local"); + + prefix = g_build_filename (g_get_home_dir (), "evolution", "local", NULL); + if (strncmp (url->path, prefix, strlen (prefix)) != 0) { + /* uri is busticated - user probably copied from another user's home directory */ + camel_url_free (url); + g_free (prefix); + + return g_strdup (uri); + } + path = g_strdup (url->path + strlen (prefix)); + g_free (prefix); + + /* modify the path in-place */ + p = path + strlen (path) - 12; + while (p > path) { + if (!strncmp (p, "/subfolders/", 12)) + memmove (p, p + 11, strlen (p + 11) + 1); + + p--; + } + + camel_url_set_path (url, path); + g_free (path); + + path = camel_url_to_string (url, 0); + camel_url_free (url); + + return path; + } else { + return em_uri_from_camel (uri); + } +} + +static void +upgrade_vfolder_sources_1_4 (xmlDocPtr doc) +{ + xmlNodePtr root, node; + + if (!doc || !(root = xmlDocGetRootElement (doc))) + return; + + if (!root->name || strcmp ((gchar *)root->name, "filteroptions") != 0) { + /* root node is not <filteroptions>, nothing to upgrade */ + return; + } + + if (!(node = xml_find_node (root, "ruleset"))) { + /* no ruleset node, nothing to upgrade */ + return; + } + + node = node->children; + while (node != NULL) { + if (node->name && !strcmp ((gchar *)node->name, "rule")) { + xmlNodePtr sources; + gchar *src; + + if (!(src = (gchar *)xmlGetProp (node, (const guchar *)"source"))) + src = (gchar *)xmlStrdup ((const guchar *)"local"); /* default to all local folders? */ + + xmlSetProp (node, (const guchar *)"source", (const guchar *)"incoming"); + + if (!(sources = xml_find_node (node, "sources"))) + sources = xmlNewChild (node, NULL, (const guchar *)"sources", NULL); + + xmlSetProp (sources, (const guchar *)"with", (guchar *)src); + xmlFree (src); + } + + node = node->next; + } +} + +static gchar * +get_nth_sig (gint id) +{ + ESignatureList *list; + ESignature *sig; + EIterator *iter; + gchar *uid = NULL; + gint i = 0; + + list = e_get_signature_list (); + iter = e_list_get_iterator ((EList *) list); + + while (e_iterator_is_valid (iter) && i < id) { + e_iterator_next (iter); + i++; + } + + if (i == id && e_iterator_is_valid (iter)) { + sig = (ESignature *) e_iterator_get (iter); + uid = g_strdup (sig->uid); + } + + g_object_unref (iter); + + return uid; +} + +static void +em_upgrade_accounts_1_4 (void) +{ + EAccountList *accounts; + EIterator *iter; + + if (!(accounts = e_get_account_list ())) + return; + + iter = e_list_get_iterator ((EList *) accounts); + while (e_iterator_is_valid (iter)) { + EAccount *account = (EAccount *) e_iterator_get (iter); + gchar *url; + + if (account->drafts_folder_uri) { + url = upgrade_xml_uris_1_4 (account->drafts_folder_uri); + g_free (account->drafts_folder_uri); + account->drafts_folder_uri = url; + } + + if (account->sent_folder_uri) { + url = upgrade_xml_uris_1_4 (account->sent_folder_uri); + g_free (account->sent_folder_uri); + account->sent_folder_uri = url; + } + + if (account->id->sig_uid && !strncmp (account->id->sig_uid, "::", 2)) { + gint sig_id; + + sig_id = strtol (account->id->sig_uid + 2, NULL, 10); + g_free (account->id->sig_uid); + account->id->sig_uid = get_nth_sig (sig_id); + } + + e_iterator_next (iter); + } + + g_object_unref (iter); + + e_account_list_save (accounts); +} + +static gboolean +em_migrate_pop_uid_caches_1_4 (const gchar *data_dir, GError **error) +{ + GString *oldpath, *newpath; + struct dirent *dent; + gsize olen, nlen; + gchar *cache_dir; + DIR *dir; + gboolean success = TRUE; + + /* Sigh, too many unique strings to translate, for cases which shouldn't ever happen */ + + /* open the old cache dir */ + cache_dir = g_build_filename (g_get_home_dir (), "evolution", "mail", "pop3", NULL); + if (!(dir = opendir (cache_dir))) { + if (errno == ENOENT) { + g_free(cache_dir); + return TRUE; + } + + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to open old POP keep-on-server data " + "`%s': %s"), cache_dir, g_strerror (errno)); + g_free (cache_dir); + return FALSE; + } + + oldpath = g_string_new (cache_dir); + g_string_append_c (oldpath, '/'); + olen = oldpath->len; + g_free (cache_dir); + + cache_dir = g_build_filename (data_dir, "pop", NULL); + if (g_mkdir_with_parents (cache_dir, 0777) == -1) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to create POP3 keep-on-server data " + "directory `%s': %s"), cache_dir, + g_strerror (errno)); + g_string_free (oldpath, TRUE); + g_free (cache_dir); + closedir (dir); + return FALSE; + } + + newpath = g_string_new (cache_dir); + g_string_append_c (newpath, '/'); + nlen = newpath->len; + g_free (cache_dir); + + while (success && (dent = readdir (dir))) { + if (strncmp (dent->d_name, "cache-pop:__", 12) != 0) + continue; + + g_string_truncate (oldpath, olen); + g_string_truncate (newpath, nlen); + + g_string_append (oldpath, dent->d_name); + g_string_append (newpath, dent->d_name + 12); + + /* strip the trailing '_' */ + g_string_truncate (newpath, newpath->len - 1); + + if (g_mkdir_with_parents (newpath->str, 0777) == -1 + || !cp(oldpath->str, (g_string_append(newpath, "/uid-cache"))->str, FALSE, CP_UNIQUE)) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to copy POP3 keep-on-server data " + "`%s': %s"), oldpath->str, + g_strerror (errno)); + success = FALSE; + } + + } + + g_string_free (oldpath, TRUE); + g_string_free (newpath, TRUE); + + closedir (dir); + + return success; +} + +static gboolean +em_migrate_imap_caches_1_4 (const gchar *data_dir, GError **error) +{ + gchar *src, *dest; + struct stat st; + + src = g_build_filename (g_get_home_dir (), "evolution", "mail", "imap", NULL); + if (stat (src, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_free (src); + return TRUE; + } + + dest = g_build_filename (data_dir, "imap", NULL); + + /* we don't care if this fails, it's only a cache... */ + cp_r (src, dest, "summary", CP_OVERWRITE); + + g_free (dest); + g_free (src); + + return TRUE; +} + +static gboolean +em_migrate_folder_expand_state_1_4 (const gchar *data_dir, GError **error) +{ + GString *srcpath, *destpath; + gsize slen, dlen, rlen; + gchar *evo14_mbox_root; + struct dirent *dent; + struct stat st; + DIR *dir; + + srcpath = g_string_new (g_get_home_dir ()); + g_string_append (srcpath, "/evolution/config"); + if (stat (srcpath->str, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_string_free (srcpath, TRUE); + return TRUE; + } + + destpath = g_string_new (data_dir); + g_string_append (destpath, "/config"); + if (g_mkdir_with_parents (destpath->str, 0777) == -1 || !(dir = opendir (srcpath->str))) { + g_string_free (destpath, TRUE); + g_string_free (srcpath, TRUE); + return TRUE; + } + + g_string_append (srcpath, "/et-expanded-"); + slen = srcpath->len; + g_string_append (destpath, "/et-expanded-"); + dlen = destpath->len; + + evo14_mbox_root = g_build_filename (g_get_home_dir (), "evolution", "local", NULL); + e_filename_make_safe (evo14_mbox_root); + rlen = strlen (evo14_mbox_root); + evo14_mbox_root = g_realloc (evo14_mbox_root, rlen + 2); + evo14_mbox_root[rlen++] = '_'; + evo14_mbox_root[rlen] = '\0'; + + while ((dent = readdir (dir))) { + gchar *full_name, *inptr, *buf = NULL; + const gchar *filename; + GString *new; + + if (strncmp (dent->d_name, "et-expanded-", 12) != 0) + continue; + + if (!strncmp (dent->d_name + 12, "file:", 5)) { + /* need to munge the filename */ + inptr = dent->d_name + 17; + + if (!strncmp (inptr, evo14_mbox_root, rlen)) { + /* this should always be the case afaik... */ + inptr += rlen; + new = g_string_new ("mbox:"); + g_string_append_printf (new, "%s/local#", data_dir); + + full_name = g_strdup (inptr); + inptr = full_name + strlen (full_name) - 12; + while (inptr > full_name) { + if (!strncmp (inptr, "_subfolders_", 12)) + memmove (inptr, inptr + 11, strlen (inptr + 11) + 1); + + inptr--; + } + + g_string_append (new, full_name); + g_free (full_name); + + filename = buf = new->str; + g_string_free (new, FALSE); + e_filename_make_safe (buf); + } else { + /* but just in case... */ + filename = dent->d_name + 12; + } + } else { + /* no munging needed */ + filename = dent->d_name + 12; + } + + g_string_append (srcpath, dent->d_name + 12); + g_string_append (destpath, filename); + g_free (buf); + + cp (srcpath->str, destpath->str, FALSE, CP_UNIQUE); + + g_string_truncate (srcpath, slen); + g_string_truncate (destpath, dlen); + } + + closedir (dir); + + g_free (evo14_mbox_root); + g_string_free (destpath, TRUE); + g_string_free (srcpath, TRUE); + + return TRUE; +} + +static gboolean +em_migrate_folder_view_settings_1_4 (const gchar *data_dir, GError **error) +{ + GString *srcpath, *destpath; + gsize slen, dlen, rlen; + gchar *evo14_mbox_root; + struct dirent *dent; + struct stat st; + DIR *dir; + + srcpath = g_string_new (g_get_home_dir ()); + g_string_append (srcpath, "/evolution/views/mail"); + if (stat (srcpath->str, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_string_free (srcpath, TRUE); + return TRUE; + } + + destpath = g_string_new (data_dir); + g_string_append (destpath, "/views"); + if (g_mkdir_with_parents (destpath->str, 0777) == -1 || !(dir = opendir (srcpath->str))) { + g_string_free (destpath, TRUE); + g_string_free (srcpath, TRUE); + return TRUE; + } + + g_string_append_c (srcpath, '/'); + slen = srcpath->len; + g_string_append_c (destpath, '/'); + dlen = destpath->len; + + evo14_mbox_root = g_build_filename (g_get_home_dir (), "evolution", "local", NULL); + e_filename_make_safe (evo14_mbox_root); + rlen = strlen (evo14_mbox_root); + evo14_mbox_root = g_realloc (evo14_mbox_root, rlen + 2); + evo14_mbox_root[rlen++] = '_'; + evo14_mbox_root[rlen] = '\0'; + + while ((dent = readdir (dir))) { + gchar *full_name, *inptr, *buf = NULL; + const gchar *filename, *ext; + gsize prelen = 0; + GString *new; + + if (dent->d_name[0] == '.') + continue; + + if (!(ext = strrchr (dent->d_name, '.'))) + continue; + + if (!strcmp (ext, ".galview") || !strcmp ((gchar *)dent->d_name, "galview.xml")) { + /* just copy the file */ + filename = dent->d_name; + goto copy; + } else if (strcmp (ext, ".xml") != 0) { + continue; + } + + if (!strncmp ((const gchar *)dent->d_name, "current_view-", 13)) { + prelen = 13; + } else if (!strncmp ((const gchar *)dent->d_name, "custom_view-", 12)) { + prelen = 12; + } else { + /* huh? wtf is this file? */ + continue; + } + + if (!strncmp (dent->d_name + prelen, "file:", 5)) { + /* need to munge the filename */ + inptr = dent->d_name + prelen + 5; + + if (!strncmp (inptr, evo14_mbox_root, rlen)) { + /* this should always be the case afaik... */ + inptr += rlen; + new = g_string_new ("mbox:"); + g_string_append_printf (new, "%s/local#", data_dir); + + full_name = g_strdup (inptr); + inptr = full_name + strlen (full_name) - 12; + while (inptr > full_name) { + if (!strncmp (inptr, "_subfolders_", 12)) + memmove (inptr, inptr + 11, strlen (inptr + 11) + 1); + + inptr--; + } + + g_string_append (new, full_name); + g_free (full_name); + + filename = buf = new->str; + g_string_free (new, FALSE); + e_filename_make_safe (buf); + } else { + /* but just in case... */ + filename = dent->d_name + prelen; + } + } else { + /* no munging needed */ + filename = dent->d_name + prelen; + } + + copy: + g_string_append (srcpath, dent->d_name); + if (prelen > 0) + g_string_append_len (destpath, dent->d_name, prelen); + g_string_append (destpath, filename); + g_free (buf); + + cp (srcpath->str, destpath->str, FALSE, CP_UNIQUE); + + g_string_truncate (srcpath, slen); + g_string_truncate (destpath, dlen); + } + + closedir (dir); + + g_free (evo14_mbox_root); + g_string_free (destpath, TRUE); + g_string_free (srcpath, TRUE); + + return TRUE; +} + +#define SUBFOLDER_DIR_NAME "subfolders" +#define SUBFOLDER_DIR_NAME_LEN 10 + +static gchar * +e_path_to_physical (const gchar *prefix, const gchar *vpath) +{ + const gchar *p, *newp; + gchar *dp; + gchar *ppath; + gint ppath_len; + gint prefix_len; + + while (*vpath == '/') + vpath++; + if (!prefix) + prefix = ""; + + /* Calculate the length of the real path. */ + ppath_len = strlen (vpath); + ppath_len++; /* For the ending zero. */ + + prefix_len = strlen (prefix); + ppath_len += prefix_len; + ppath_len++; /* For the separating slash. */ + + /* Take account of the fact that we need to translate every + * separator into `subfolders/'. + */ + p = vpath; + while (1) { + newp = strchr (p, '/'); + if (newp == NULL) + break; + + ppath_len += SUBFOLDER_DIR_NAME_LEN; + ppath_len++; /* For the separating slash. */ + + /* Skip consecutive slashes. */ + while (*newp == '/') + newp++; + + p = newp; + }; + + ppath = g_malloc (ppath_len); + dp = ppath; + + memcpy (dp, prefix, prefix_len); + dp += prefix_len; + *(dp++) = '/'; + + /* Copy the mangled path. */ + p = vpath; + while (1) { + newp = strchr (p, '/'); + if (newp == NULL) { + strcpy (dp, p); + break; + } + + memcpy (dp, p, newp - p + 1); /* `+ 1' to copy the slash too. */ + dp += newp - p + 1; + + memcpy (dp, SUBFOLDER_DIR_NAME, SUBFOLDER_DIR_NAME_LEN); + dp += SUBFOLDER_DIR_NAME_LEN; + + *(dp++) = '/'; + + /* Skip consecutive slashes. */ + while (*newp == '/') + newp++; + + p = newp; + } + + return ppath; +} + +static gboolean +em_migrate_imap_cmeta_1_4(const gchar *data_dir, GError **error) +{ + GConfClient *gconf; + GSList *paths, *p; + EAccountList *accounts; + const EAccount *account; + + if (!(accounts = e_get_account_list ())) + return TRUE; + + gconf = gconf_client_get_default(); + paths = gconf_client_get_list(gconf, "/apps/evolution/shell/offline/folder_paths", GCONF_VALUE_STRING, NULL); + for (p = paths;p;p = g_slist_next(p)) { + gchar *name, *path; + + name = p->data; + if (*name) + name++; + path = strchr(name, '/'); + if (path) { + *path++ = 0; + account = e_account_list_find(accounts, E_ACCOUNT_FIND_NAME, name); + if (account && !strncmp(account->source->url, "imap:", 5)) { + CamelURL *url = camel_url_new(account->source->url, NULL); + + if (url) { + gchar *dir, *base; + + base = g_strdup_printf("%s/imap/%s@%s/folders", + data_dir, + url->user?url->user:"", + url->host?url->host:""); + + dir = e_path_to_physical(base, path); + if (g_mkdir_with_parents(dir, 0777) == 0) { + gchar *cmeta; + FILE *fp; + + cmeta = g_build_filename(dir, "cmeta", NULL); + fp = fopen(cmeta, "w"); + if (fp) { + /* header/version */ + fwrite("CLMD", 4, 1, fp); + camel_file_util_encode_uint32(fp, 1); + /* meta count, do we have any metadata? */ + camel_file_util_encode_uint32(fp, 0); + /* prop count */ + camel_file_util_encode_uint32(fp, 1); + /* sync offline property */ + camel_file_util_encode_uint32(fp, CAMEL_DISCO_FOLDER_OFFLINE_SYNC); + camel_file_util_encode_uint32(fp, 1); + fclose(fp); + } else { + g_warning("couldn't create imap folder cmeta file '%s'", cmeta); + } + g_free(cmeta); + } else { + g_warning("couldn't create imap folder directory '%s'", dir); + } + g_free(dir); + g_free(base); + camel_url_free(url); + } + } else + g_warning("can't find offline folder '%s' '%s'", name, path); + } + g_free(p->data); + } + g_slist_free(paths); + g_object_unref(gconf); + + /* we couldn't care less if this doesn't work */ + + return TRUE; +} + +static void +remove_system_searches(xmlDocPtr searches) +{ + xmlNodePtr node; + + /* in pre 2.0, system searches were stored in the user + * searches.xml file with the source set to 'demand'. In 2.0+ + * the system searches are stored in the system + * searchtypes.xml file instead */ + + node = xmlDocGetRootElement(searches); + if (!node->name || strcmp((gchar *)node->name, "filteroptions")) + return; + + if (!(node = xml_find_node(node, "ruleset"))) + return; + + node = node->children; + while (node != NULL) { + xmlNodePtr nnode = node->next; + + if (node->name && !strcmp ((gchar *)node->name, "rule")) { + gchar *src; + + src = (gchar *)xmlGetProp(node, (guchar *)"source"); + if (src && !strcmp((gchar *)src, "demand")) { + xmlUnlinkNode(node); + xmlFreeNodeList(node); + } + xmlFree (src); + } + + node = nnode; + } +} + +static gboolean +em_migrate_1_4 (const gchar *data_dir, xmlDocPtr filters, xmlDocPtr vfolders, GError **error) +{ + EMMigrateSession *session; + CamelException lex; + struct stat st; + gchar *path; + xmlDocPtr searches; + + camel_init (data_dir, TRUE); + camel_provider_init(); + session = (EMMigrateSession *) em_migrate_session_new (data_dir); + + session->srcdir = g_build_filename (g_get_home_dir (), "evolution", "local", NULL); + + path = g_strdup_printf ("mbox:%s/.evolution/mail/local", g_get_home_dir ()); + if (stat (path + 5, &st) == -1) { + if (errno != ENOENT || g_mkdir_with_parents (path + 5, 0777) == -1) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Failed to create local mail storage " + "`%s': %s"), path + 5, g_strerror (errno)); + g_free (session->srcdir); + camel_object_unref (session); + g_free (path); + return FALSE; + } + } + + camel_exception_init (&lex); + if (!(session->store = camel_session_get_store ((CamelSession *) session, path, &lex))) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Failed to create local mail storage `%s': %s"), + path, lex.desc); + g_free (session->srcdir); + camel_object_unref (session); + camel_exception_clear (&lex); + g_free (path); + return FALSE; + } + g_free (path); + + if (!em_migrate_local_folders_1_4 (session, error)) + return FALSE; + + camel_object_unref (session->store); + g_free (session->srcdir); + + camel_object_unref (session); + + em_upgrade_accounts_1_4(); + + upgrade_xml_uris(filters, upgrade_xml_uris_1_4); + upgrade_vfolder_sources_1_4(vfolders); + upgrade_xml_uris(vfolders, upgrade_xml_uris_1_4); + + path = g_build_filename(g_get_home_dir(), "evolution", NULL); + searches = emm_load_xml(path, "searches.xml"); + g_free(path); + if (searches) { + remove_system_searches(searches); + emm_save_xml(searches, data_dir, "searches.xml"); + xmlFreeDoc(searches); + } + + if (!em_migrate_pop_uid_caches_1_4 (data_dir, error)) + return FALSE; + + /* these are non-fatal */ + em_migrate_imap_caches_1_4 (data_dir, error); + g_clear_error (error); + em_migrate_folder_expand_state_1_4 (data_dir, error); + g_clear_error (error); + em_migrate_folder_view_settings_1_4 (data_dir, error); + g_clear_error (error); + em_migrate_imap_cmeta_1_4 (data_dir, error); + g_clear_error (error); + + return TRUE; +} + +static void +em_update_accounts_2_11 (void) +{ + EAccountList *accounts; + EIterator *iter; + gboolean changed = FALSE; + + if (!(accounts = e_get_account_list ())) + return; + + iter = e_list_get_iterator ((EList *) accounts); + while (e_iterator_is_valid (iter)) { + EAccount *account = (EAccount *) e_iterator_get (iter); + + if (g_str_has_prefix (account->source->url, "spool://")) { + if (g_file_test (account->source->url + 8, G_FILE_TEST_IS_DIR)) { + gchar *str = g_strdup_printf ("spooldir://%s", account->source->url + 8); + + g_free (account->source->url); + account->source->url = str; + changed = TRUE; + } + } + + e_iterator_next (iter); + } + + g_object_unref (iter); + + if (changed) + e_account_list_save (accounts); +} + +#endif /* !G_OS_WIN32 */ + +static gboolean +emm_setup_initial(const gchar *data_dir) +{ + GDir *dir; + const gchar *d; + gchar *local = NULL, *base; + const gchar * const *language_names; + + /* special-case - this means brand new install of evolution */ + /* FIXME: create default folders and stuff... */ + + d(printf("Setting up initial mail tree\n")); + + base = g_build_filename(data_dir, "local", NULL); + if (g_mkdir_with_parents(base, 0777) == -1 && errno != EEXIST) { + g_free(base); + return FALSE; + } + + /* e.g. try en-AU then en, etc */ + language_names = g_get_language_names (); + while (*language_names != NULL) { + local = g_build_filename ( + EVOLUTION_PRIVDATADIR, "default", + *language_names, "mail", "local", NULL); + if (g_file_test (local, G_FILE_TEST_EXISTS)) + break; + g_free (local); + language_names++; + } + + /* Make sure we found one. */ + g_return_val_if_fail (*language_names != NULL, FALSE); + + dir = g_dir_open(local, 0, NULL); + if (dir) { + while ((d = g_dir_read_name(dir))) { + gchar *src, *dest; + + src = g_build_filename(local, d, NULL); + dest = g_build_filename(base, d, NULL); + + cp(src, dest, FALSE, CP_UNIQUE); + g_free(dest); + g_free(src); + } + g_dir_close(dir); + } + + g_free(local); + g_free(base); + + return TRUE; +} + +static gboolean +is_in_plugs_list (GSList *list, const gchar *value) +{ + GSList *l; + + for (l = list; l; l = l->next) { + if (l->data && !strcmp (l->data, value)) + return TRUE; + } + + return FALSE; +} + +/* + * em_update_message_notify_settings_2_21 + * DBus plugin and sound email notification was merged to mail-notification plugin, + * so move these options to new locations. + */ +static void +em_update_message_notify_settings_2_21 (void) +{ + GConfClient *client; + GConfValue *is_key; + gboolean dbus, status; + GSList *list; + gchar *str; + gint val; + + client = gconf_client_get_default (); + + is_key = gconf_client_get (client, "/apps/evolution/eplugin/mail-notification/dbus-enabled", NULL); + if (is_key) { + /* already migrated, so do not migrate again */ + gconf_value_free (is_key); + g_object_unref (client); + + return; + } + + gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-blink-icon", + gconf_client_get_bool (client, "/apps/evolution/mail/notification/blink-status-icon", NULL), NULL); + gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-notification", + gconf_client_get_bool (client, "/apps/evolution/mail/notification/notification", NULL), NULL); + + list = gconf_client_get_list (client, "/apps/evolution/eplugin/disabled", GCONF_VALUE_STRING, NULL); + dbus = !is_in_plugs_list (list, "org.gnome.evolution.new_mail_notify"); + status = !is_in_plugs_list (list, "org.gnome.evolution.mail_notification"); + + gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/dbus-enabled", dbus, NULL); + gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-enabled", status, NULL); + + if (!status) { + /* enable this plugin, because it holds all those other things */ + GSList *plugins, *l; + + plugins = e_plugin_list_plugins (); + + for (l = plugins; l; l = l->next) { + EPlugin *p = l->data; + + if (p && p->id && !strcmp (p->id, "org.gnome.evolution.mail_notification")) { + e_plugin_enable (p, 1); + break; + } + } + + g_slist_foreach (plugins, (GFunc)g_object_unref, NULL); + g_slist_free (plugins); + } + + g_slist_foreach (list, (GFunc) g_free, NULL); + g_slist_free (list); + + val = gconf_client_get_int (client, "/apps/evolution/mail/notify/type", NULL); + gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/sound-enabled", val == 1 || val == 2, NULL); + gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/sound-beep", val == 0 || val == 1, NULL); + + str = gconf_client_get_string (client, "/apps/evolution/mail/notify/sound", NULL); + gconf_client_set_string (client, "/apps/evolution/eplugin/mail-notification/sound-file", str ? str : "", NULL); + g_free (str); + + g_object_unref (client); +} + +/* fixing typo in SpamAssassin name */ +static void +em_update_sa_junk_setting_2_23 (void) +{ + GConfClient *client; + GConfValue *key; + + client = gconf_client_get_default (); + + key = gconf_client_get (client, "/apps/evolution/mail/junk/default_plugin", NULL); + if (key) { + const gchar *str = gconf_value_get_string (key); + + if (str && strcmp (str, "Spamassasin") == 0) + gconf_client_set_string (client, "/apps/evolution/mail/junk/default_plugin", "SpamAssassin", NULL); + + gconf_value_free (key); + g_object_unref (client); + + return; + } + + g_object_unref (client); +} + +static gboolean +update_progress_in_main_thread (double *progress) +{ + em_migrate_set_progress (*progress); + return FALSE; +} + +static void +migrate_folders(CamelStore *store, gboolean is_local, CamelFolderInfo *fi, const gchar *acc, CamelException *ex, gboolean *done, gint *nth_folder, gint total_folders) +{ + CamelFolder *folder; + + while (fi) { + double progress; + gchar *tmp; + + *nth_folder = *nth_folder + 1; + + tmp = g_strdup_printf ("%s/%s", acc, fi->full_name); + em_migrate_set_folder_name (tmp); + g_free (tmp); + + progress = (double) (*nth_folder) / total_folders; + g_idle_add ((GSourceFunc) update_progress_in_main_thread, &progress); + + if (is_local) + folder = camel_store_get_folder (store, fi->full_name, CAMEL_STORE_IS_MIGRATING, ex); + else + folder = camel_store_get_folder (store, fi->full_name, 0, ex); + + if (folder != NULL) + camel_folder_summary_migrate_infos (folder->summary); + migrate_folders(store, is_local, fi->child, acc, ex, done, nth_folder, total_folders); + fi = fi->next; + } + + if ((*nth_folder) == (total_folders - 1)) + *done = TRUE; +} + +/* This could be in CamelStore.ch */ +static void +count_folders (CamelFolderInfo *fi, gint *count) +{ + while (fi) { + *count = *count + 1; + count_folders (fi->child, count); + fi = fi->next; + } +} + +static CamelStore * +setup_local_store (EShellBackend *shell_backend, + EMMigrateSession *session) +{ + CamelURL *url; + const gchar *data_dir; + gchar *tmp; + CamelStore *store; + + url = camel_url_new("mbox:", NULL); + data_dir = e_shell_backend_get_data_dir (shell_backend); + tmp = g_build_filename (data_dir, "local", NULL); + camel_url_set_path(url, tmp); + g_free(tmp); + tmp = camel_url_to_string(url, 0); + store = (CamelStore *)camel_session_get_service(CAMEL_SESSION (session), tmp, CAMEL_PROVIDER_STORE, NULL); + g_free(tmp); + + return store; +} + +struct migrate_folders_to_db_structure { + gchar *account_name; + CamelException ex; + CamelStore *store; + CamelFolderInfo *info; + gboolean done; + gboolean is_local_store; +}; + +static void +migrate_folders_to_db_thread (struct migrate_folders_to_db_structure *migrate_dbs) +{ + gint num_of_folders = 0, nth_folder = 0; + count_folders (migrate_dbs->info, &num_of_folders); + migrate_folders (migrate_dbs->store, migrate_dbs->is_local_store, migrate_dbs->info, + migrate_dbs->account_name, &(migrate_dbs->ex), &(migrate_dbs->done), + &nth_folder, num_of_folders); +} + +static void +migrate_to_db (EShellBackend *shell_backend) +{ + EMMigrateSession *session; + EAccountList *accounts; + EIterator *iter; + gint i=0, len; + CamelStore *store = NULL; + CamelFolderInfo *info; + const gchar *data_dir; + + if (!(accounts = e_get_account_list ())) + return; + + iter = e_list_get_iterator ((EList *) accounts); + len = e_list_length ((EList *) accounts); + + data_dir = e_shell_backend_get_data_dir (shell_backend); + session = (EMMigrateSession *) em_migrate_session_new (data_dir); + camel_session_set_online ((CamelSession *) session, FALSE); + em_migrate_setup_progress_dialog ( + _("Migrating Folders"), + _("The summary format of the Evolution mailbox " + "folders has been moved to SQLite since Evolution 2.24.\n\nPlease be " + "patient while Evolution migrates your folders...")); + + em_migrate_set_progress ( (double)i/(len+1)); + store = setup_local_store (shell_backend, session); + info = camel_store_get_folder_info (store, NULL, CAMEL_STORE_FOLDER_INFO_RECURSIVE|CAMEL_STORE_FOLDER_INFO_FAST|CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, NULL); + if (info) { + GThread *thread; + struct migrate_folders_to_db_structure migrate_dbs; + + if (g_str_has_suffix (((CamelService *)store)->url->path, ".evolution/mail/local")) + migrate_dbs.is_local_store = TRUE; + else + migrate_dbs.is_local_store = FALSE; + camel_exception_init (&migrate_dbs.ex); + migrate_dbs.account_name = _("On This Computer"); + migrate_dbs.info = info; + migrate_dbs.store = store; + migrate_dbs.done = FALSE; + + thread = g_thread_create ((GThreadFunc) migrate_folders_to_db_thread, &migrate_dbs, TRUE, NULL); + while (!migrate_dbs.done) + g_main_context_iteration (NULL, TRUE); + } + i++; + em_migrate_set_progress ( (double)i/(len+1)); + + + while (e_iterator_is_valid (iter)) { + EAccount *account = (EAccount *) e_iterator_get (iter); + EAccountService *service; + const gchar *name; + + + service = account->source; + name = account->name; + em_migrate_set_progress ( (double)i/(len+1)); + if (account->enabled + && service->url != NULL + && service->url[0] + && strncmp(service->url, "mbox:", 5) != 0) { + + CamelException ex; + + camel_exception_init (&ex); + e_mail_store_add_by_uri (service->url, name); + + store = (CamelStore *) camel_session_get_service (CAMEL_SESSION (session), service->url, CAMEL_PROVIDER_STORE, &ex); + info = camel_store_get_folder_info (store, NULL, CAMEL_STORE_FOLDER_INFO_RECURSIVE|CAMEL_STORE_FOLDER_INFO_FAST|CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, &ex); + if (info) { + GThread *thread; + struct migrate_folders_to_db_structure migrate_dbs; + + migrate_dbs.ex = ex; + migrate_dbs.account_name = account->name; + migrate_dbs.info = info; + migrate_dbs.store = store; + migrate_dbs.done = FALSE; + + thread = g_thread_create ((GThreadFunc) migrate_folders_to_db_thread, &migrate_dbs, TRUE, NULL); + while (!migrate_dbs.done) + g_main_context_iteration (NULL, TRUE); + } else + printf("%s:%s: failed to get folder infos \n", G_STRLOC, G_STRFUNC); + camel_exception_clear(&ex); + + } + i++; + e_iterator_next (iter); + + } + + //camel_session_set_online ((CamelSession *) session, TRUE); + + g_object_unref (iter); + em_migrate_close_progress_dialog (); + + g_object_unref (session); +} + +gboolean +e_mail_shell_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error) +{ + struct stat st; + const gchar *data_dir; + gchar *path; + + /* make sure ~/.evolution/mail exists */ + data_dir = e_shell_backend_get_data_dir (shell_backend); + if (g_stat (data_dir, &st) == -1) { + if (errno != ENOENT || g_mkdir_with_parents (data_dir, 0777) == -1) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to create local mail folders at " + "`%s': %s"), data_dir, g_strerror (errno)); + return FALSE; + } + } + + if (major == 0) + return emm_setup_initial (data_dir); + + if (major == 1 && minor < 5) { +#ifndef G_OS_WIN32 + xmlDocPtr config_xmldb = NULL, filters, vfolders; + + path = g_build_filename (g_get_home_dir (), "evolution", NULL); + if (minor <= 2 && !(config_xmldb = emm_load_xml (path, "config.xmldb"))) { + g_set_error ( + error, E_SHELL_MIGRATE_ERROR, + E_SHELL_MIGRATE_ERROR_FAILED, + _("Unable to read settings from previous " + "Evolution install, `evolution/config.xmldb' " + "does not exist or is corrupt.")); + return FALSE; + } + filters = emm_load_xml (path, "filters.xml"); + vfolders = emm_load_xml (path, "vfolders.xml"); + g_free (path); + + if (minor == 0) { + if (!em_migrate_1_0 (data_dir, config_xmldb, filters, vfolders, error)) { + xmlFreeDoc (config_xmldb); + xmlFreeDoc (filters); + xmlFreeDoc (vfolders); + return FALSE; + } + } + + if (minor <= 2) { + if (!em_migrate_1_2 (data_dir, config_xmldb, filters, vfolders, error)) { + xmlFreeDoc (config_xmldb); + xmlFreeDoc (filters); + xmlFreeDoc (vfolders); + return FALSE; + } + + xmlFreeDoc (config_xmldb); + } + + if (minor <= 4) { + if (!em_migrate_1_4 (data_dir, filters, vfolders, error)) { + xmlFreeDoc (filters); + xmlFreeDoc (vfolders); + return FALSE; + } + } + + if (filters) { + emm_save_xml (filters, path, "filters.xml"); + xmlFreeDoc (filters); + } + + if (vfolders) { + emm_save_xml (vfolders, path, "vfolders.xml"); + xmlFreeDoc (vfolders); + } + + g_free (path); +#else + g_error ("Upgrading from ancient versions not supported on Windows"); +#endif + } + + if (major < 2 || (major == 2 && minor < 12)) { +#ifndef G_OS_WIN32 + em_update_accounts_2_11 (); +#else + g_error ("Upgrading from ancient versions not supported on Windows"); +#endif + } + + + if (major < 2 || (major == 2 && minor < 22)) + em_update_message_notify_settings_2_21 (); + + if (major < 2 || (major == 2 && minor < 24)) { + em_update_sa_junk_setting_2_23 (); + migrate_to_db (shell_backend); + } + + return TRUE; +} diff --git a/modules/mail/e-mail-shell-migrate.h b/modules/mail/e-mail-shell-migrate.h new file mode 100644 index 0000000000..8f3057ec0d --- /dev/null +++ b/modules/mail/e-mail-shell-migrate.h @@ -0,0 +1,38 @@ +/* + * e-mail-shell-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_MAIL_SHELL_BACKEND_MIGRATE_H +#define E_MAIL_SHELL_BACKEND_MIGRATE_H + +#include <glib.h> +#include <shell/e-shell-backend.h> + +G_BEGIN_DECLS + +gboolean e_mail_shell_migrate (EShellBackend *shell_backend, + gint major, + gint minor, + gint micro, + GError **error); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_BACKEND_MIGRATE_H */ diff --git a/modules/mail/e-mail-shell-settings.c b/modules/mail/e-mail-shell-settings.c new file mode 100644 index 0000000000..8237924e3c --- /dev/null +++ b/modules/mail/e-mail-shell-settings.c @@ -0,0 +1,521 @@ +/* + * e-mail-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-mail-shell-settings.h" + +#include <gconf/gconf-client.h> +#include <libedataserver/e-account-list.h> + +#include "e-util/e-signature-list.h" +#include "mail/e-mail-label-list-store.h" +#include "mail/mail-session.h" + +void +e_mail_shell_settings_init (EShell *shell) +{ + EShellSettings *shell_settings; + gpointer object; + + shell_settings = e_shell_get_shell_settings (shell); + + /* XXX Default values should match the GConf schema. + * Yes it's redundant, but we're stuck with GConf. */ + + /*** Global Objects ***/ + + e_shell_settings_install_property ( + g_param_spec_object ( + "mail-label-list-store", + NULL, + NULL, + E_TYPE_MAIL_LABEL_LIST_STORE, + G_PARAM_READWRITE)); + + object = e_mail_label_list_store_new (); + e_shell_settings_set_object ( + shell_settings, "mail-label-list-store", object); + g_object_unref (object); + + e_shell_settings_install_property ( + g_param_spec_pointer ( + "mail-session", + NULL, + NULL, + G_PARAM_READWRITE)); + + camel_object_ref (session); + e_shell_settings_set_pointer ( + shell_settings, "mail-session", session); + + /*** Mail Preferences ***/ + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-address-compress", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-address-compress", + "/apps/evolution/mail/display/address_compress"); + + e_shell_settings_install_property ( + g_param_spec_int ( + "mail-address-count", + NULL, + NULL, + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-address-count", + "/apps/evolution/mail/display/address_count"); + + e_shell_settings_install_property ( + g_param_spec_string ( + "mail-charset-default", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-charset-default", + "/apps/evolution/mail/display/charset"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-check-for-junk", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-check-for-junk", + "/apps/evolution/mail/junk/check_incoming"); + + e_shell_settings_install_property ( + g_param_spec_string ( + "mail-citation-color", + NULL, + NULL, + "#737373", + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-citation-color", + "/apps/evolution/mail/display/citation_colour"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-confirm-expunge", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-confirm-expunge", + "/apps/evolution/mail/prompts/expunge"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-confirm-unwanted-html", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-confirm-unwanted-html", + "/apps/evolution/mail/prompts/unwanted_html"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-empty-trash-on-exit", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-empty-trash-on-exit", + "/apps/evolution/mail/trash/empty_on_exit"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-enable-search-folders", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-enable-search-folders", + "/apps/evolution/mail/display/enable_vfolders"); + + e_shell_settings_install_property ( + g_param_spec_string ( + "mail-font-monospace", + NULL, + NULL, + "", + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-font-monospace", + "/apps/evolution/mail/display/fonts/monospace"); + + e_shell_settings_install_property ( + g_param_spec_string ( + "mail-font-variable", + NULL, + NULL, + "", + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-font-variable", + "/apps/evolution/mail/display/fonts/variable"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-force-message-limit", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-force-message-limit", + "/apps/evolution/mail/display/force_message_limit"); + + /* This value corresponds to MailConfigForwardStyle enum. */ + e_shell_settings_install_property ( + g_param_spec_int ( + "mail-forward-style", + NULL, + NULL, + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-forward-style", + "/apps/evolution/mail/format/forward_style"); + + /* This value corresponds to MailConfigHTTPMode enum. */ + e_shell_settings_install_property ( + g_param_spec_int ( + "mail-image-loading-policy", + NULL, + NULL, + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-image-loading-policy", + "/apps/evolution/mail/display/load_http_images"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-magic-spacebar", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-magic-spacebar", + "/apps/evolution/mail/display/magic_spacebar"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-mark-citations", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-mark-citations", + "/apps/evolution/mail/display/mark_citations"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-mark-seen", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-mark-seen", + "/apps/evolution/mail/display/mark_seen"); + + e_shell_settings_install_property ( + g_param_spec_int ( + "mail-mark-seen-timeout", + NULL, + NULL, + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-mark-seen-timeout", + "/apps/evolution/mail/display/mark_seen_timeout"); + + e_shell_settings_install_property ( + g_param_spec_int ( + "mail-message-text-part-limit", + NULL, + NULL, + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-message-text-part-limit", + "/apps/evolution/mail/display/message_text_part_limit"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-only-local-photos", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-only-local-photos", + "/apps/evolution/mail/display/photo_local"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-prompt-delete-in-vfolder", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-prompt-delete-in-vfolder", + "/apps/evolution/mail/prompts/delete_in_vfolder"); + + /* This value corresponds to MailConfigReplyStyle enum, + * but the ordering of the combo box items in preferences + * has changed. We use transformation functions there. */ + e_shell_settings_install_property ( + g_param_spec_int ( + "mail-reply-style", + NULL, + NULL, + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-reply-style", + "/apps/evolution/mail/format/reply_style"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-show-animated-images", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-show-animated-images", + "/apps/evolution/mail/display/animated_images"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-show-sender-photo", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-show-sender-photo", + "/apps/evolution/mail/display/sender_photo"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "mail-use-custom-fonts", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "mail-use-custom-fonts", + "/apps/evolution/mail/display/fonts/use_custom"); + + + /*** Composer Preferences ***/ + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-format-html", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-format-html", + "/apps/evolution/mail/composer/send_html"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-inline-spelling", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-inline-spelling", + "/apps/evolution/mail/composer/inline_spelling"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-magic-links", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-magic-links", + "/apps/evolution/mail/composer/magic_links"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-magic-smileys", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-magic-smileys", + "/apps/evolution/mail/composer/magic_smileys"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-outlook-filenames", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-outlook-filenames", + "/apps/evolution/mail/composer/outlook_filenames"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-prompt-only-bcc", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-prompt-only-bcc", + "/apps/evolution/mail/prompts/only_bcc"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-prompt-empty-subject", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-prompt-empty-subject", + "/apps/evolution/mail/prompts/empty_subject"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-reply-start-bottom", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-reply-start-bottom", + "/apps/evolution/mail/composer/reply_start_bottom"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-request-receipt", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-request-receipt", + "/apps/evolution/mail/composer/request_receipt"); + + e_shell_settings_install_property ( + g_param_spec_string ( + "composer-spell-color", + NULL, + NULL, + "#ff0000", + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-spell-color", + "/apps/evolution/mail/composer/spell_color"); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "composer-top-signature", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_bind_to_gconf ( + shell_settings, "composer-top-signature", + "/apps/evolution/mail/composer/top_signature"); +} diff --git a/modules/mail/e-mail-shell-settings.h b/modules/mail/e-mail-shell-settings.h new file mode 100644 index 0000000000..4267fd8a60 --- /dev/null +++ b/modules/mail/e-mail-shell-settings.h @@ -0,0 +1,33 @@ +/* + * e-mail-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_MAIL_SHELL_SETTINGS_H +#define E_MAIL_SHELL_SETTINGS_H + +#include <shell/e-shell.h> + +G_BEGIN_DECLS + +void e_mail_shell_settings_init (EShell *shell); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_SETTINGS_H */ diff --git a/modules/mail/e-mail-shell-sidebar.c b/modules/mail/e-mail-shell-sidebar.c new file mode 100644 index 0000000000..2e5dffcb96 --- /dev/null +++ b/modules/mail/e-mail-shell-sidebar.c @@ -0,0 +1,644 @@ +/* + * e-mail-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-mail-shell-sidebar.h" + +#include <string.h> +#include <camel/camel.h> + +#include "em-utils.h" +#include "em-folder-utils.h" + +#include "e-mail-local.h" +#include "e-mail-store.h" + +#define E_MAIL_SHELL_SIDEBAR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarPrivate)) + +#define STATE_KEY_EXPANDED "Expanded" + +struct _EMailShellSidebarPrivate { + GtkWidget *folder_tree; +}; + +enum { + PROP_0, + PROP_FOLDER_TREE +}; + +static gpointer parent_class; +static GType mail_shell_sidebar_type; + +static void +mail_shell_sidebar_restore_state (EMailShellSidebar *mail_shell_sidebar) +{ + EShellView *shell_view; + EShellSidebar *shell_sidebar; + EMFolderTree *folder_tree; + GtkTreeModel *tree_model; + GtkTreeView *tree_view; + GtkTreeIter iter; + GKeyFile *key_file; + gboolean valid; + gchar *selected; + gchar **groups; + gint ii; + + shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + + tree_view = GTK_TREE_VIEW (folder_tree); + tree_model = gtk_tree_view_get_model (tree_view); + + /* Restore selected folder. */ + + selected = g_key_file_get_string ( + key_file, "Folder Tree", "Selected", NULL); + if (selected != NULL) { + em_folder_tree_set_selected (folder_tree, selected, FALSE); + g_free (selected); + } + + /* Set the initial folder tree expanded state in two stages: + * + * 1) Iterate over the "Store" and "Folder" state file groups + * and apply the "Expanded" keys where possible. + * + * 2) Iterate over the top-level nodes in the folder tree + * (these are all stores) and expand those that have no + * corresponding "Expanded" key in the state file. This + * ensures that new stores are expanded by default. + */ + + /* Stage 1 */ + + groups = g_key_file_get_groups (key_file, NULL); + + for (ii = 0; groups[ii] != NULL; ii++) { + GtkTreeRowReference *reference; + GtkTreePath *path; + GtkTreeIter iter; + const gchar *group_name = groups[ii]; + const gchar *key = STATE_KEY_EXPANDED; + const gchar *uri; + gboolean expanded; + + if (g_str_has_prefix (group_name, "Store ")) { + uri = group_name + 6; + expanded = TRUE; + } else if (g_str_has_prefix (group_name, "Folder ")) { + uri = group_name + 7; + expanded = FALSE; + } else + continue; + + if (g_key_file_has_key (key_file, group_name, key, NULL)) + expanded = g_key_file_get_boolean ( + key_file, group_name, key, NULL); + + if (!expanded) + continue; + + reference = em_folder_tree_model_lookup_uri ( + EM_FOLDER_TREE_MODEL (tree_model), uri); + if (reference == NULL) + continue; + + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (tree_model, &iter, path); + gtk_tree_view_expand_row (tree_view, path, FALSE); + gtk_tree_path_free (path); + } + + g_strfreev (groups); + + /* Stage 2 */ + + valid = gtk_tree_model_get_iter_first (tree_model, &iter); + + while (valid) { + const gchar *key = STATE_KEY_EXPANDED; + gchar *group_name; + gchar *uri; + + gtk_tree_model_get ( + tree_model, &iter, COL_STRING_URI, &uri, -1); + + if (uri == NULL) + goto next; + + group_name = g_strdup_printf ("Store %s", uri); + + if (!g_key_file_has_key (key_file, group_name, key, NULL)) { + GtkTreePath *path; + + path = gtk_tree_model_get_path (tree_model, &iter); + gtk_tree_view_expand_row (tree_view, path, FALSE); + gtk_tree_path_free (path); + } + + g_free (group_name); + g_free (uri); + + next: + valid = gtk_tree_model_iter_next (tree_model, &iter); + } +} + +static void +mail_shell_sidebar_row_collapsed_cb (EShellSidebar *shell_sidebar, + GtkTreeIter *iter, + GtkTreePath *path, + GtkTreeView *tree_view) +{ + EShellView *shell_view; + GtkTreeModel *model; + GKeyFile *key_file; + const gchar *key; + gboolean is_folder; + gboolean is_store; + gchar *group_name; + gchar *uri; + + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_model_get ( + model, iter, + COL_STRING_URI, &uri, + COL_BOOL_IS_STORE, &is_store, + COL_BOOL_IS_FOLDER, &is_folder, -1); + + g_return_if_fail (is_store || is_folder); + + key = STATE_KEY_EXPANDED; + if (is_store) + group_name = g_strdup_printf ("Store %s", uri); + else + group_name = g_strdup_printf ("Folder %s", uri); + + g_key_file_set_boolean (key_file, group_name, key, FALSE); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + g_free (uri); +} + +static void +mail_shell_sidebar_row_expanded_cb (EShellSidebar *shell_sidebar, + GtkTreeIter *unused, + GtkTreePath *path, + GtkTreeView *tree_view) +{ + EShellView *shell_view; + GtkTreeModel *model; + GKeyFile *key_file; + const gchar *key; + gboolean is_folder; + gboolean is_store; + gchar *group_name; + gchar *uri; + + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + path = gtk_tree_path_copy (path); + model = gtk_tree_view_get_model (tree_view); + + /* Expand the node and all ancestors. */ + while (gtk_tree_path_get_depth (path) > 0) { + GtkTreeIter iter; + + gtk_tree_model_get_iter (model, &iter, path); + + gtk_tree_model_get ( + model, &iter, + COL_STRING_URI, &uri, + COL_BOOL_IS_STORE, &is_store, + COL_BOOL_IS_FOLDER, &is_folder, -1); + + g_return_if_fail (is_store || is_folder); + + key = STATE_KEY_EXPANDED; + if (is_store) + group_name = g_strdup_printf ("Store %s", uri); + else + group_name = g_strdup_printf ("Folder %s", uri); + + g_key_file_set_boolean (key_file, group_name, key, TRUE); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + g_free (uri); + + gtk_tree_path_up (path); + } + + gtk_tree_path_free (path); +} + +static void +mail_shell_sidebar_model_loaded_row_cb (EMailShellSidebar *mail_shell_sidebar, + GtkTreePath *path, + GtkTreeIter *iter, + GtkTreeModel *model) +{ + EShellSidebar *shell_sidebar; + EShellView *shell_view; + GtkTreeView *tree_view; + GKeyFile *key_file; + gboolean is_folder; + gboolean is_store; + const gchar *key; + gchar *group_name; + gchar *uri; + gboolean expanded; + + shell_sidebar = E_SHELL_SIDEBAR (mail_shell_sidebar); + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + key_file = e_shell_view_get_state_key_file (shell_view); + + tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree); + + gtk_tree_model_get ( + model, iter, + COL_STRING_URI, &uri, + COL_BOOL_IS_STORE, &is_store, + COL_BOOL_IS_FOLDER, &is_folder, -1); + + g_return_if_fail (is_store || is_folder); + + key = STATE_KEY_EXPANDED; + if (is_store) { + group_name = g_strdup_printf ("Store %s", uri); + expanded = TRUE; + } else { + group_name = g_strdup_printf ("Folder %s", uri); + expanded = FALSE; + } + + if (g_key_file_has_key (key_file, group_name, key, NULL)) + expanded = g_key_file_get_boolean ( + key_file, group_name, key, NULL); + + if (expanded) + gtk_tree_view_expand_row (tree_view, path, FALSE); + + g_free (group_name); + g_free (uri); +} + +static void +mail_shell_sidebar_selection_changed_cb (EShellSidebar *shell_sidebar, + GtkTreeSelection *selection) +{ + EShellView *shell_view; + EShellViewClass *shell_view_class; + GtkTreeModel *model; + GtkTreeIter iter; + GKeyFile *key_file; + const gchar *icon_name; + gchar *display_name = NULL; + gchar *uri = NULL; + gboolean is_folder = FALSE; + guint flags = 0; + + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + gtk_tree_model_get ( + model, &iter, + COL_STRING_DISPLAY_NAME, &display_name, + COL_STRING_URI, &uri, + COL_BOOL_IS_FOLDER, &is_folder, + COL_UINT_FLAGS, &flags, -1); + + if (uri != NULL) + g_key_file_set_string ( + key_file, "Folder Tree", "Selected", uri); + else + g_key_file_remove_key ( + key_file, "Folder Tree", "Selected", NULL); + + e_shell_view_set_state_dirty (shell_view); + + if (is_folder) + icon_name = em_folder_utils_get_icon_name (flags); + else { + icon_name = shell_view_class->icon_name; + display_name = g_strdup (shell_view_class->label); + } + + e_shell_sidebar_set_icon_name (shell_sidebar, icon_name); + e_shell_sidebar_set_primary_text (shell_sidebar, display_name); + + g_free (display_name); + g_free (uri); +} + +static void +mail_shell_sidebar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FOLDER_TREE: + g_value_set_object ( + value, e_mail_shell_sidebar_get_folder_tree ( + E_MAIL_SHELL_SIDEBAR (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_shell_sidebar_dispose (GObject *object) +{ + EMailShellSidebarPrivate *priv; + + priv = E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (object); + + if (priv->folder_tree != NULL) { + g_object_unref (priv->folder_tree); + priv->folder_tree = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +mail_shell_sidebar_finalize (GObject *object) +{ + EMailShellSidebarPrivate *priv; + + priv = E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (object); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +mail_shell_sidebar_constructed (GObject *object) +{ + EMailShellSidebar *mail_shell_sidebar; + EShellSidebar *shell_sidebar; + EShellView *shell_view; + GtkTreeSelection *selection; + GtkTreeView *tree_view; + GtkTreeModel *model; + GtkWidget *container; + GtkWidget *widget; + + /* 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); + + mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (object); + + /* Build sidebar widgets. */ + + container = GTK_WIDGET (object); + + 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 (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + container = widget; + + widget = em_folder_tree_new (); + em_folder_tree_set_excluded (EM_FOLDER_TREE (widget), 0); + em_folder_tree_enable_drag_and_drop (EM_FOLDER_TREE (widget)); + gtk_container_add (GTK_CONTAINER (container), widget); + mail_shell_sidebar->priv->folder_tree = g_object_ref (widget); + gtk_widget_show (widget); + + tree_view = GTK_TREE_VIEW (mail_shell_sidebar->priv->folder_tree); + selection = gtk_tree_view_get_selection (tree_view); + model = gtk_tree_view_get_model (tree_view); + + if (em_folder_tree_model_get_selection ( + EM_FOLDER_TREE_MODEL (model)) == NULL) + mail_shell_sidebar_restore_state (mail_shell_sidebar); + + em_folder_tree_model_set_selection ( + EM_FOLDER_TREE_MODEL (model), selection); + + g_signal_connect_swapped ( + tree_view, "row-collapsed", + G_CALLBACK (mail_shell_sidebar_row_collapsed_cb), + shell_sidebar); + + g_signal_connect_swapped ( + tree_view, "row-expanded", + G_CALLBACK (mail_shell_sidebar_row_expanded_cb), + shell_sidebar); + + g_signal_connect_swapped ( + model, "loaded-row", + G_CALLBACK (mail_shell_sidebar_model_loaded_row_cb), + shell_sidebar); + + g_signal_connect_swapped ( + selection, "changed", + G_CALLBACK (mail_shell_sidebar_selection_changed_cb), + shell_sidebar); +} + +static guint32 +mail_shell_sidebar_check_state (EShellSidebar *shell_sidebar) +{ + EMailShellSidebar *mail_shell_sidebar; + EShellView *shell_view; + EMFolderTree *folder_tree; + GtkTreeSelection *selection; + GtkTreeView *tree_view; + GtkTreeModel *model; + GtkTreeIter iter; + CamelFolder *folder; + CamelStore *local_store; + CamelStore *store; + gchar *full_name; + gchar *uri; + gboolean allows_children = TRUE; + gboolean can_delete = TRUE; + gboolean is_junk = FALSE; + gboolean is_outbox = FALSE; + gboolean is_store; + gboolean is_trash = FALSE; + guint32 folder_flags = 0; + guint32 state = 0; + + shell_view = e_shell_sidebar_get_shell_view (shell_sidebar); + + local_store = e_mail_local_get_store (); + + mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar); + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + tree_view = GTK_TREE_VIEW (folder_tree); + + selection = gtk_tree_view_get_selection (tree_view); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return 0; + + gtk_tree_model_get ( + model, &iter, + COL_POINTER_CAMEL_STORE, &store, + COL_STRING_FULL_NAME, &full_name, + COL_BOOL_IS_STORE, &is_store, + COL_UINT_FLAGS, &folder_flags, + COL_STRING_URI, &uri, -1); + + if (!is_store) { + is_junk = (strcmp (full_name, CAMEL_VJUNK_NAME) == 0); + is_trash = (strcmp (full_name, CAMEL_VTRASH_NAME) == 0); + allows_children = !(is_junk || is_trash); + + /* Don't allow deletion of special local folders. */ + if (store == local_store) + can_delete = + (strcmp (full_name, "Drafts") != 0) && + (strcmp (full_name, "Inbox") != 0) && + (strcmp (full_name, "Outbox") != 0) && + (strcmp (full_name, "Sent") != 0) && + (strcmp (full_name, "Templates") != 0); + + folder = em_folder_tree_get_selected_folder (folder_tree); + is_outbox = em_utils_folder_is_outbox (folder, NULL); + can_delete &= !(folder_flags & CAMEL_FOLDER_SYSTEM); + } + + if (allows_children) + state |= E_MAIL_SHELL_SIDEBAR_FOLDER_ALLOWS_CHILDREN; + if (can_delete) + state |= E_MAIL_SHELL_SIDEBAR_FOLDER_CAN_DELETE; + if (is_junk) + state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_JUNK; + if (is_outbox) + state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_OUTBOX; + if (is_store) + state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_STORE; + if (is_trash) + state |= E_MAIL_SHELL_SIDEBAR_FOLDER_IS_TRASH; + + return state; +} + +static void +mail_shell_sidebar_class_init (EMailShellSidebarClass *class) +{ + GObjectClass *object_class; + EShellSidebarClass *shell_sidebar_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMailShellSidebarPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = mail_shell_sidebar_get_property; + object_class->dispose = mail_shell_sidebar_dispose; + object_class->finalize = mail_shell_sidebar_finalize; + object_class->constructed = mail_shell_sidebar_constructed; + + shell_sidebar_class = E_SHELL_SIDEBAR_CLASS (class); + shell_sidebar_class->check_state = mail_shell_sidebar_check_state; + + g_object_class_install_property ( + object_class, + PROP_FOLDER_TREE, + g_param_spec_object ( + "folder-tree", + NULL, + NULL, + EM_TYPE_FOLDER_TREE, + G_PARAM_READABLE)); +} + +static void +mail_shell_sidebar_init (EMailShellSidebar *mail_shell_sidebar) +{ + mail_shell_sidebar->priv = + E_MAIL_SHELL_SIDEBAR_GET_PRIVATE (mail_shell_sidebar); + + /* Postpone widget construction until we have a shell view. */ +} + +GType +e_mail_shell_sidebar_get_type (void) +{ + return mail_shell_sidebar_type; +} + +void +e_mail_shell_sidebar_register_type (GTypeModule *type_module) +{ + static const GTypeInfo type_info = { + sizeof (EMailShellSidebarClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) mail_shell_sidebar_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailShellSidebar), + 0, /* n_preallocs */ + (GInstanceInitFunc) mail_shell_sidebar_init, + NULL /* value_table */ + }; + + mail_shell_sidebar_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_SIDEBAR, + "EMailShellSidebar", &type_info, 0); +} + +GtkWidget * +e_mail_shell_sidebar_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_MAIL_SHELL_SIDEBAR, + "shell-view", shell_view, NULL); +} + +EMFolderTree * +e_mail_shell_sidebar_get_folder_tree (EMailShellSidebar *mail_shell_sidebar) +{ + g_return_val_if_fail ( + E_IS_MAIL_SHELL_SIDEBAR (mail_shell_sidebar), NULL); + + return EM_FOLDER_TREE (mail_shell_sidebar->priv->folder_tree); +} diff --git a/modules/mail/e-mail-shell-sidebar.h b/modules/mail/e-mail-shell-sidebar.h new file mode 100644 index 0000000000..10a2ff6a2a --- /dev/null +++ b/modules/mail/e-mail-shell-sidebar.h @@ -0,0 +1,81 @@ +/* + * e-mail-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_MAIL_SHELL_SIDEBAR_H +#define E_MAIL_SHELL_SIDEBAR_H + +#include <shell/e-shell-sidebar.h> +#include <shell/e-shell-view.h> +#include <mail/em-folder-tree.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_SHELL_SIDEBAR \ + (e_mail_shell_sidebar_get_type ()) +#define E_MAIL_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebar)) +#define E_MAIL_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarClass)) +#define E_IS_MAIL_SHELL_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_SHELL_SIDEBAR)) +#define E_IS_MAIL_SHELL_SIDEBAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_SHELL_SIDEBAR)) +#define E_MAIL_SHELL_SIDEBAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_SHELL_SIDEBAR, EMailShellSidebarClass)) + +G_BEGIN_DECLS + +typedef struct _EMailShellSidebar EMailShellSidebar; +typedef struct _EMailShellSidebarClass EMailShellSidebarClass; +typedef struct _EMailShellSidebarPrivate EMailShellSidebarPrivate; + +enum { + E_MAIL_SHELL_SIDEBAR_FOLDER_ALLOWS_CHILDREN = 1 << 0, + E_MAIL_SHELL_SIDEBAR_FOLDER_CAN_DELETE = 1 << 1, + E_MAIL_SHELL_SIDEBAR_FOLDER_IS_JUNK = 1 << 2, + E_MAIL_SHELL_SIDEBAR_FOLDER_IS_OUTBOX = 1 << 3, + E_MAIL_SHELL_SIDEBAR_FOLDER_IS_STORE = 1 << 4, + E_MAIL_SHELL_SIDEBAR_FOLDER_IS_TRASH = 1 << 5 +}; + +struct _EMailShellSidebar { + EShellSidebar parent; + EMailShellSidebarPrivate *priv; +}; + +struct _EMailShellSidebarClass { + EShellSidebarClass parent_class; +}; + +GType e_mail_shell_sidebar_get_type (void); +void e_mail_shell_sidebar_register_type + (GTypeModule *type_module); +GtkWidget * e_mail_shell_sidebar_new(EShellView *shell_view); +EMFolderTree * e_mail_shell_sidebar_get_folder_tree + (EMailShellSidebar *mail_shell_sidebar); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_SIDEBAR_H */ diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c new file mode 100644 index 0000000000..25e7d80da3 --- /dev/null +++ b/modules/mail/e-mail-shell-view-actions.c @@ -0,0 +1,1806 @@ +/* + * e-mail-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-mail-shell-view-private.h" + +#define STATE_KEY_SEARCH_FILTER "SearchFilter" +#define STATE_KEY_SEARCH_SCOPE "SearchScope" +#define STATE_KEY_SEARCH_TEXT "SearchText" + +static void +action_gal_save_custom_view_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellContent *mail_shell_content; + EShellView *shell_view; + GalViewInstance *view_instance; + + /* All shell views repond 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 (mail_shell_view); + if (!e_shell_view_is_active (shell_view)) + return; + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + view_instance = e_mail_shell_content_get_view_instance (mail_shell_content); + gal_view_instance_save_as (view_instance); +} + +static void +action_mail_account_disable_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + EAccountList *account_list; + EAccount *account; + gchar *folder_uri; + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder_uri = em_folder_tree_get_selected_uri (folder_tree); + g_return_if_fail (folder_uri != NULL); + + account_list = e_get_account_list (); + account = mail_config_get_account_by_source_url (folder_uri); + g_return_if_fail (account != NULL); + + if (e_account_list_account_has_proxies (account_list, account)) + e_account_list_remove_account_proxies (account_list, account); + + account->enabled = !account->enabled; + e_account_list_change (account_list, account); + e_mail_store_remove_by_uri (folder_uri); + + if (account->parent_uid != NULL) + e_account_list_remove (account_list, account); + + e_account_list_save (account_list); + + g_free (folder_uri); +} + +static void +action_mail_create_search_folder_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + /* FIXME */ + g_print ("Action: %s\n", gtk_action_get_name (GTK_ACTION (action))); +} + +static void +action_mail_download_foreach_cb (CamelService *service) +{ + if (CAMEL_IS_DISCO_STORE (service) || CAMEL_IS_OFFLINE_STORE (service)) + mail_store_prepare_offline (CAMEL_STORE (service)); +} + +static void +action_mail_download_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + e_mail_store_foreach ((GHFunc) action_mail_download_foreach_cb, NULL); +} + +static void +action_mail_empty_trash_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShellWindow *shell_window; + EShellView *shell_view; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + em_utils_empty_trash (GTK_WIDGET (shell_window)); +} + +static void +action_mail_flush_outbox_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + mail_send (); +} + +static void +action_mail_folder_copy_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + CamelFolderInfo *folder_info; + EMFolderTree *folder_tree; + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder_info = em_folder_tree_get_selected_folder_info (folder_tree); + g_return_if_fail (folder_info != NULL); + + /* XXX Leaking folder_info? */ + em_folder_utils_copy_folder (folder_info, FALSE); +} + +static void +action_mail_folder_delete_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + CamelFolder *folder; + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder = em_folder_tree_get_selected_folder (folder_tree); + g_return_if_fail (folder != NULL); + + em_folder_utils_delete_folder (folder); +} + +static void +action_mail_folder_expunge_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailReader *reader; + MessageList *message_list; + EShellWindow *shell_window; + EShellView *shell_view; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + g_return_if_fail (message_list->folder != NULL); + + em_utils_expunge_folder ( + GTK_WIDGET (shell_window), message_list->folder); +} + +static void +action_mail_folder_mark_all_as_read_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailReader *reader; + MessageList *message_list; + EShellWindow *shell_window; + EShellView *shell_view; + CamelFolder *folder; + GtkWindow *parent; + GPtrArray *uids; + const gchar *key; + const gchar *prompt; + guint ii; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + parent = GTK_WINDOW (shell_window); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder = message_list->folder; + g_return_if_fail (folder != NULL); + + key = "/apps/evolution/mail/prompts/mark_all_read"; + prompt = "mail:ask-mark-all-read"; + + if (!em_utils_prompt_user (parent, key, prompt, NULL)) + return; + + uids = message_list_get_uids (message_list); + + camel_folder_freeze (folder); + for (ii = 0; ii < uids->len; ii++) + camel_folder_set_message_flags ( + folder, uids->pdata[ii], + CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN); + camel_folder_thaw (folder); + + message_list_free_uids (message_list, uids); +} + +static void +action_mail_folder_move_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + CamelFolderInfo *folder_info; + EMFolderTree *folder_tree; + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder_info = em_folder_tree_get_selected_folder_info (folder_tree); + g_return_if_fail (folder_info != NULL); + + /* XXX Leaking folder_info? */ + em_folder_utils_copy_folder (folder_info, TRUE); +} + +static void +action_mail_folder_new_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EMailShellSidebar *mail_shell_sidebar; + CamelFolderInfo *folder_info; + EMFolderTree *folder_tree; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder_info = em_folder_tree_get_selected_folder_info (folder_tree); + g_return_if_fail (folder_info != NULL); + + em_folder_utils_create_folder ( + folder_info, folder_tree, GTK_WINDOW (shell_window)); + camel_folder_info_free (folder_info); +} + +static void +action_mail_folder_properties_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + EShellView *shell_view; + GtkTreeSelection *selection; + GtkTreeView *tree_view; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + + tree_view = GTK_TREE_VIEW (folder_tree); + selection = gtk_tree_view_get_selection (tree_view); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return; + + gtk_tree_model_get (model, &iter, COL_STRING_URI, &uri, -1); + em_folder_properties_show (shell_view, NULL, uri); + g_free (uri); +} + +static void +action_mail_folder_refresh_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + CamelFolder *folder; + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder = em_folder_tree_get_selected_folder (folder_tree); + g_return_if_fail (folder != NULL); + + mail_refresh_folder (folder, NULL, NULL); +} + +static void +action_mail_folder_rename_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + CamelFolder *folder; + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + folder = em_folder_tree_get_selected_folder (folder_tree); + g_return_if_fail (folder != NULL); + + em_folder_utils_rename_folder (folder); +} + +/* Helper for action_mail_folder_select_all_cb() */ +static gboolean +action_mail_folder_select_all_timeout_cb (MessageList *message_list) +{ + message_list_select_all (message_list); + gtk_widget_grab_focus (GTK_WIDGET (message_list)); + + return FALSE; +} + +static void +action_mail_folder_select_all_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EMailReader *reader; + MessageList *message_list; + EShellWindow *shell_window; + EShellView *shell_view; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + if (message_list->threaded) { + gtk_action_activate (ACTION (MAIL_THREADS_EXPAND_ALL)); + + /* XXX The timeout below is added so that the execution + * thread to expand all conversation threads would + * have completed. The timeout 505 is just to ensure + * that the value is a small delta more than the + * timeout value in mail_regen_list(). */ + g_timeout_add ( + 505, (GSourceFunc) + action_mail_folder_select_all_timeout_cb, + message_list); + } else + /* If there is no threading, just select all immediately. */ + action_mail_folder_select_all_timeout_cb (message_list); +} + +static void +action_mail_folder_select_thread_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_select_thread (message_list); +} + +static void +action_mail_folder_select_subthread_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_select_subthread (message_list); +} + +static void +action_mail_hide_deleted_cb (GtkToggleAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + gboolean active; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + active = gtk_toggle_action_get_active (action); + message_list_set_hidedeleted (message_list, active); +} + +static void +action_mail_hide_read_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_hide_add ( + message_list, + "(match-all (system-flag \"seen\"))", + ML_HIDE_SAME, ML_HIDE_SAME); +} + +static void +action_mail_hide_selected_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + GPtrArray *uids; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + uids = message_list_get_selected (message_list); + message_list_hide_uids (message_list, uids); + message_list_free_uids (message_list, uids); +} + +static void +action_mail_label_cb (GtkToggleAction *action, + EMailShellView *mail_shell_view) +{ + EMailReader *reader; + MessageList *message_list; + CamelFolder *folder; + GPtrArray *uids; + const gchar *tag; + gint ii; + + tag = g_object_get_data (G_OBJECT (action), "tag"); + g_return_if_fail (tag != NULL); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder = message_list->folder; + + uids = message_list_get_selected (message_list); + + for (ii = 0; ii < uids->len; ii++) { + if (gtk_toggle_action_get_active (action)) + camel_folder_set_message_user_flag ( + folder, uids->pdata[ii], tag, TRUE); + else { + camel_folder_set_message_user_flag ( + folder, uids->pdata[ii], tag, FALSE); + camel_folder_set_message_user_tag ( + folder, uids->pdata[ii], "label", NULL); + } + } + + message_list_free_uids (message_list, uids); +} + +static void +action_mail_label_new_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellSettings *shell_settings; + EShellWindow *shell_window; + EShellView *shell_view; + EMailLabelDialog *label_dialog; + EMailLabelListStore *store; + EMailReader *reader; + MessageList *message_list; + CamelFolder *folder; + GtkTreeModel *model; + GtkTreeIter iter; + GtkWidget *dialog; + GPtrArray *uids; + GdkColor label_color; + const gchar *property_name; + const gchar *label_name; + gchar *label_tag; + gint n_children; + guint ii; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + dialog = e_mail_label_dialog_new (GTK_WINDOW (shell_window)); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Add Label")); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_OK) + goto exit; + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + label_dialog = E_MAIL_LABEL_DIALOG (dialog); + label_name = e_mail_label_dialog_get_label_name (label_dialog); + e_mail_label_dialog_get_label_color (label_dialog, &label_color); + + property_name = "mail-label-list-store"; + store = e_shell_settings_get_object (shell_settings, property_name); + e_mail_label_list_store_set (store, NULL, label_name, &label_color); + g_object_unref (store); + + /* XXX This is awkward. We've added a new label to the list store + * but we don't have the new label's tag nor an iterator to use + * to fetch it. We know the label was appended to the store, + * so we have to dig it out manually. EMailLabelListStore API + * probably needs some rethinking. */ + model = GTK_TREE_MODEL (store); + n_children = gtk_tree_model_iter_n_children (model, NULL); + gtk_tree_model_iter_nth_child (model, &iter, NULL, n_children - 1); + label_tag = e_mail_label_list_store_get_tag (store, &iter); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder = message_list->folder; + + uids = message_list_get_selected (message_list); + + for (ii = 0; ii < uids->len; ii++) + camel_folder_set_message_user_flag ( + folder, uids->pdata[ii], label_tag, TRUE); + + message_list_free_uids (message_list, uids); + + g_free (label_tag); + +exit: + gtk_widget_destroy (dialog); +} + +static void +action_mail_label_none_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellSettings *shell_settings; + EShellWindow *shell_window; + EMailReader *reader; + MessageList *message_list; + GtkTreeModel *tree_model; + CamelFolder *folder; + GtkTreeIter iter; + GPtrArray *uids; + gboolean valid; + guint ii; + + shell_view = E_SHELL_VIEW (mail_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); + + tree_model = e_shell_settings_get_object ( + shell_settings, "mail-label-list-store"); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + uids = message_list_get_selected (message_list); + folder = message_list->folder; + + valid = gtk_tree_model_get_iter_first (tree_model, &iter); + + while (valid) { + gchar *tag; + + tag = e_mail_label_list_store_get_tag ( + E_MAIL_LABEL_LIST_STORE (tree_model), &iter); + + for (ii = 0; ii < uids->len; ii++) { + camel_folder_set_message_user_flag ( + folder, uids->pdata[ii], tag, FALSE); + camel_folder_set_message_user_tag ( + folder, uids->pdata[ii], "label", NULL); + } + + g_free (tag); + + valid = gtk_tree_model_iter_next (tree_model, &iter); + } + + message_list_free_uids (message_list, uids); +} + +static void +action_mail_search_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMailShellView *mail_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 (mail_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_mail_show_hidden_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_hide_clear (message_list); +} + +static void +action_mail_smart_backward_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSettings *shell_settings; + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + EMFormatHTMLDisplay *html_display; + EMailReader *reader; + MessageList *message_list; + GtkToggleAction *toggle_action; + GtkHTML *html; + gboolean caret_mode; + gboolean magic_spacebar; + + /* This implements the so-called "Magic Backspace". */ + + shell_view = E_SHELL_VIEW (mail_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); + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + html_display = e_mail_reader_get_html_display (reader); + message_list = e_mail_reader_get_message_list (reader); + + magic_spacebar = e_shell_settings_get_boolean ( + shell_settings, "mail-magic-spacebar"); + + toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE)); + caret_mode = gtk_toggle_action_get_active (toggle_action); + + html = EM_FORMAT_HTML (html_display)->html; + + if (gtk_html_command (html, "scroll-backward")) + return; + + if (caret_mode || !magic_spacebar) + return; + + /* XXX Are two separate calls really necessary? */ + + if (message_list_select ( + message_list, MESSAGE_LIST_SELECT_PREVIOUS, + 0, CAMEL_MESSAGE_SEEN)) + return; + + if (message_list_select ( + message_list, MESSAGE_LIST_SELECT_PREVIOUS | + MESSAGE_LIST_SELECT_WRAP, 0, CAMEL_MESSAGE_SEEN)) + return; + + em_folder_tree_select_prev_path (folder_tree, TRUE); + + gtk_widget_grab_focus (GTK_WIDGET (message_list)); +} + +static void +action_mail_smart_forward_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellSettings *shell_settings; + EMailShellSidebar *mail_shell_sidebar; + EMFolderTree *folder_tree; + EMFormatHTMLDisplay *html_display; + EMailReader *reader; + MessageList *message_list; + GtkToggleAction *toggle_action; + GtkHTML *html; + gboolean caret_mode; + gboolean magic_spacebar; + + /* This implements the so-called "Magic Spacebar". */ + + shell_view = E_SHELL_VIEW (mail_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); + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + html_display = e_mail_reader_get_html_display (reader); + message_list = e_mail_reader_get_message_list (reader); + + magic_spacebar = e_shell_settings_get_boolean ( + shell_settings, "mail-magic-spacebar"); + + toggle_action = GTK_TOGGLE_ACTION (ACTION (MAIL_CARET_MODE)); + caret_mode = gtk_toggle_action_get_active (toggle_action); + + html = EM_FORMAT_HTML (html_display)->html; + + if (gtk_html_command (html, "scroll-forward")) + return; + + if (caret_mode || !magic_spacebar) + return; + + /* XXX Are two separate calls really necessary? */ + + if (message_list_select ( + message_list, MESSAGE_LIST_SELECT_NEXT, + 0, CAMEL_MESSAGE_SEEN)) + return; + + if (message_list_select ( + message_list, MESSAGE_LIST_SELECT_NEXT | + MESSAGE_LIST_SELECT_WRAP, 0, CAMEL_MESSAGE_SEEN)) + return; + + em_folder_tree_select_next_path (folder_tree, TRUE); + + gtk_widget_grab_focus (GTK_WIDGET (message_list)); +} + +static void +action_mail_stop_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + mail_cancel_all (); +} + +static void +action_mail_threads_collapse_all_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_set_threaded_collapse_all (message_list); +} + +static void +action_mail_threads_expand_all_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + MessageList *message_list; + EMailReader *reader; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_set_threaded_expand_all (message_list); +} + +static void +action_mail_threads_group_by_cb (GtkToggleAction *action, + EMailShellView *mail_shell_view) +{ + EMailShellContent *mail_shell_content; + MessageList *message_list; + EMailReader *reader; + gboolean active; + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + active = gtk_toggle_action_get_active (action); + + reader = E_MAIL_READER (mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + + message_list_set_threaded (message_list, active); +} + +static void +action_mail_tools_filters_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShellWindow *shell_window; + EShellView *shell_view; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + em_utils_edit_filters (GTK_WIDGET (shell_window)); +} + +static void +action_mail_tools_search_folders_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + vfolder_edit (E_SHELL_VIEW (mail_shell_view)); +} + +static void +action_mail_tools_subscriptions_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShellWindow *shell_window; + EShellView *shell_view; + GtkWidget *dialog; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + dialog = em_subscribe_editor_new (); + gtk_window_set_transient_for ( + GTK_WINDOW (dialog), GTK_WINDOW (shell_window)); + gtk_dialog_run (GTK_DIALOG (dialog)); + /* XXX Dialog destroys itself. */ +} + +static void +action_mail_view_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMailShellView *mail_shell_view) +{ + EMailShellContent *mail_shell_content; + gboolean vertical_view; + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + vertical_view = (gtk_radio_action_get_current_value (action) == 1); + + e_mail_shell_content_set_vertical_view ( + mail_shell_content, vertical_view); +} + +static void +action_search_execute_cb (GtkAction *action, + EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellContent *shell_content; + EMailReader *reader; + MessageList *message_list; + GKeyFile *key_file; + const gchar *folder_uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + key_file = e_shell_view_get_state_key_file (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. */ + if (!e_shell_view_is_active (shell_view)) + return; + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri != NULL) { + const gchar *key; + const gchar *string; + gchar *group_name; + + key = STATE_KEY_SEARCH_TEXT; + string = e_shell_content_get_search_text (shell_content); + group_name = g_strdup_printf ("Folder %s", folder_uri); + + if (string != NULL && *string != '\0') + g_key_file_set_string ( + key_file, group_name, key, string); + else + g_key_file_remove_key ( + key_file, group_name, key, NULL); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + } + + e_mail_shell_view_execute_search (mail_shell_view); +} + +static void +action_search_filter_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EMailReader *reader; + MessageList *message_list; + GKeyFile *key_file; + const gchar *folder_uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri != NULL) { + const gchar *key; + const gchar *string; + gchar *group_name; + + key = STATE_KEY_SEARCH_FILTER; + string = gtk_action_get_name (GTK_ACTION (current)); + group_name = g_strdup_printf ("Folder %s", folder_uri); + + g_key_file_set_string (key_file, group_name, key, string); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + } + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); +} + +static void +action_search_scope_cb (GtkRadioAction *action, + GtkRadioAction *current, + EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EMailReader *reader; + MessageList *message_list; + GKeyFile *key_file; + const gchar *folder_uri; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + + if (folder_uri != NULL) { + const gchar *key; + const gchar *string; + gchar *group_name; + + key = STATE_KEY_SEARCH_SCOPE; + string = gtk_action_get_name (GTK_ACTION (current)); + group_name = g_strdup_printf ("Folder %s", folder_uri); + + g_key_file_set_string (key_file, group_name, key, string); + e_shell_view_set_state_dirty (shell_view); + + g_free (group_name); + } + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); +} + +static GtkActionEntry mail_entries[] = { + + { "mail-account-disable", + NULL, + N_("_Disable Account"), + NULL, + N_("Disable this account"), + G_CALLBACK (action_mail_account_disable_cb) }, + + { "mail-create-search-folder", + NULL, + N_("C_reate Search Folder From Search..."), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_mail_create_search_folder_cb) }, + + { "mail-download", + NULL, + N_("_Download Messages for Offline Usage"), + NULL, + N_("Download messages of accounts and folders marked for offline"), + G_CALLBACK (action_mail_download_cb) }, + + { "mail-empty-trash", + NULL, + N_("Empty _Trash"), + NULL, + N_("Permanently remove all the deleted messages from all folders"), + G_CALLBACK (action_mail_empty_trash_cb) }, + + { "mail-flush-outbox", + "mail-send", + N_("Fl_ush Outbox"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_mail_flush_outbox_cb) }, + + { "mail-folder-copy", + "folder-copy", + N_("_Copy Folder To..."), + NULL, + N_("Copy the selected folder into another folder"), + G_CALLBACK (action_mail_folder_copy_cb) }, + + { "mail-folder-delete", + GTK_STOCK_DELETE, + NULL, + NULL, + N_("Permanently remove this folder"), + G_CALLBACK (action_mail_folder_delete_cb) }, + + { "mail-folder-expunge", + NULL, + N_("E_xpunge"), + "<Control>e", + N_("Permanently remove all deleted messages from this folder"), + G_CALLBACK (action_mail_folder_expunge_cb) }, + + { "mail-folder-mark-all-as-read", + "mail-read", + N_("Mar_k All Messages as Read"), + NULL, + N_("Mark all messages in the folder as read"), + G_CALLBACK (action_mail_folder_mark_all_as_read_cb) }, + + { "mail-folder-move", + "folder-move", + N_("_Move Folder To..."), + NULL, + N_("Move the selected folder into another folder"), + G_CALLBACK (action_mail_folder_move_cb) }, + + { "mail-folder-new", + "folder-new", + N_("_New..."), + NULL, + N_("Create a new folder for storing mail"), + G_CALLBACK (action_mail_folder_new_cb) }, + + { "mail-folder-properties", + GTK_STOCK_PROPERTIES, + NULL, + NULL, + N_("Change the properties of this folder"), + G_CALLBACK (action_mail_folder_properties_cb) }, + + { "mail-folder-refresh", + GTK_STOCK_REFRESH, + NULL, + "F5", + N_("Refresh the folder"), + G_CALLBACK (action_mail_folder_refresh_cb) }, + + { "mail-folder-rename", + NULL, + N_("_Rename..."), + "F2", + N_("Change the name of this folder"), + G_CALLBACK (action_mail_folder_rename_cb) }, + + { "mail-folder-select-all", + NULL, + N_("Select _All Messages"), + "<Control>a", + N_("Select all visible messages"), + G_CALLBACK (action_mail_folder_select_all_cb) }, + + { "mail-folder-select-thread", + NULL, + N_("Select Message _Thread"), + "<Control>h", + N_("Select all messages in the same thread as the selected message"), + G_CALLBACK (action_mail_folder_select_thread_cb) }, + + { "mail-folder-select-subthread", + NULL, + N_("Select Message S_ubthread"), + "<Shift><Control>h", + N_("Select all replies to the currently selected message"), + G_CALLBACK (action_mail_folder_select_subthread_cb) }, + + { "mail-label-new", + NULL, + N_("_New Label"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_mail_label_new_cb) }, + + { "mail-label-none", + NULL, + N_("N_one"), + NULL, + NULL, /* XXX Add a tooltip! */ + G_CALLBACK (action_mail_label_none_cb) }, + + { "mail-hide-read", + NULL, + N_("Hide _Read Messages"), + NULL, + N_("Temporarily hide all messages that have already been read"), + G_CALLBACK (action_mail_hide_read_cb) }, + + { "mail-hide-selected", + NULL, + N_("Hide S_elected Messages"), + NULL, + N_("Temporarily hide the selected messages"), + G_CALLBACK (action_mail_hide_selected_cb) }, + + { "mail-show-hidden", + NULL, + N_("Show Hidde_n Messages"), + NULL, + N_("Show messages that have been temporarily hidden"), + G_CALLBACK (action_mail_show_hidden_cb) }, + + { "mail-smart-backward", + NULL, + NULL, /* No menu item; key press only */ + NULL, + NULL, + G_CALLBACK (action_mail_smart_backward_cb) }, + + { "mail-smart-forward", + NULL, + NULL, /* No menu item; key press only */ + NULL, + NULL, + G_CALLBACK (action_mail_smart_forward_cb) }, + + { "mail-stop", + GTK_STOCK_STOP, + N_("Cancel"), + NULL, + N_("Cancel the current mail operation"), + G_CALLBACK (action_mail_stop_cb) }, + + { "mail-threads-collapse-all", + NULL, + N_("Collapse All _Threads"), + "<Shift><Control>b", + N_("Collapse all message threads"), + G_CALLBACK (action_mail_threads_collapse_all_cb) }, + + { "mail-threads-expand-all", + NULL, + N_("E_xpand All Threads"), + NULL, + N_("Expand all message threads"), + G_CALLBACK (action_mail_threads_expand_all_cb) }, + + { "mail-tools-filters", + NULL, + N_("_Message Filters"), + NULL, + N_("Create or edit rules for filtering new mail"), + G_CALLBACK (action_mail_tools_filters_cb) }, + + { "mail-tools-search-folders", + NULL, + N_("Search F_olders"), + NULL, + N_("Create or edit search folder definitions"), + G_CALLBACK (action_mail_tools_search_folders_cb) }, + + { "mail-tools-subscriptions", + NULL, + N_("_Subscriptions..."), + NULL, + N_("Subscribe or unsubscribe to folders on remote servers"), + G_CALLBACK (action_mail_tools_subscriptions_cb) }, + + /*** Menus ***/ + + { "mail-folder-menu", + NULL, + N_("F_older"), + NULL, + NULL, + NULL }, + + { "mail-label-menu", + NULL, + N_("_Label"), + NULL, + NULL, + NULL }, + + { "mail-preview-menu", + NULL, + N_("_Preview"), + NULL, + NULL, + NULL } +}; + +static EPopupActionEntry mail_popup_entries[] = { + + { "mail-popup-account-disable", + NULL, + "mail-account-disable" }, + + { "mail-popup-empty-trash", + NULL, + "mail-empty-trash" }, + + { "mail-popup-flush-outbox", + NULL, + "mail-flush-outbox" }, + + { "mail-popup-folder-copy", + NULL, + "mail-folder-copy" }, + + { "mail-popup-folder-delete", + NULL, + "mail-folder-delete" }, + + { "mail-popup-folder-move", + NULL, + "mail-folder-move" }, + + { "mail-popup-folder-new", + N_("_New Folder..."), + "mail-folder-new" }, + + { "mail-popup-folder-properties", + NULL, + "mail-folder-properties" }, + + { "mail-popup-folder-refresh", + NULL, + "mail-folder-refresh" }, + + { "mail-popup-folder-rename", + NULL, + "mail-folder-rename" } +}; + +static GtkToggleActionEntry mail_toggle_entries[] = { + + { "mail-hide-deleted", + NULL, + N_("Hide _Deleted Messages"), + NULL, + N_("Hide deleted messages rather than displaying " + "them with a line through them"), + G_CALLBACK (action_mail_hide_deleted_cb), + TRUE }, + + { "mail-preview", + NULL, + N_("Show Message _Preview"), + "<Control>m", + N_("Show message preview pane"), + NULL, /* Handled by property bindings */ + TRUE }, + + { "mail-threads-group-by", + NULL, + N_("_Group By Threads"), + "<Control>t", + N_("Threaded message list"), + G_CALLBACK (action_mail_threads_group_by_cb), + FALSE } +}; + +static GtkRadioActionEntry mail_view_entries[] = { + + /* This action represents the initial active mail view. + * It should not be visible in the UI, nor should it be + * possible to switch to it from another shell view. */ + { "mail-view-internal", + NULL, + NULL, + NULL, + NULL, + -1 }, + + { "mail-view-classic", + NULL, + N_("_Classic View"), + NULL, + N_("Show message preview below the message list"), + 0 }, + + { "mail-view-vertical", + NULL, + N_("_Vertical View"), + NULL, + N_("Show message preview alongside the message list"), + 1 } +}; + +static GtkRadioActionEntry mail_filter_entries[] = { + + { "mail-filter-all-messages", + NULL, + N_("All Messages"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_ALL_MESSAGES }, + + { "mail-filter-important-messages", + "emblem-important", + N_("Important Messages"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_IMPORTANT_MESSAGES }, + + { "mail-filter-last-5-days-messages", + NULL, + N_("Last 5 Days' Messages"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_LAST_5_DAYS_MESSAGES }, + + { "mail-filter-messages-not-junk", + "mail-mark-notjunk", + N_("Messages Not Junk"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_MESSAGES_NOT_JUNK }, + + { "mail-filter-messages-with-attachments", + "mail-attachment", + N_("Messages with Attachments"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS }, + + { "mail-filter-no-label", + NULL, + N_("No Label"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_NO_LABEL }, + + { "mail-filter-read-messages", + "mail-read", + N_("Read Messages"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_READ_MESSAGES }, + + { "mail-filter-recent-messages", + NULL, + N_("Recent Messages"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_RECENT_MESSAGES }, + + { "mail-filter-unread-messages", + "mail-unread", + N_("Unread Messages"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_FILTER_UNREAD_MESSAGES } +}; + +static GtkRadioActionEntry mail_search_entries[] = { + + { "mail-search-body-contains", + NULL, + N_("Body contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_BODY_CONTAINS }, + + { "mail-search-message-contains", + NULL, + N_("Message contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_MESSAGE_CONTAINS }, + + { "mail-search-recipients-contain", + NULL, + N_("Recipients contain"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_RECIPIENTS_CONTAIN }, + + { "mail-search-sender-contains", + NULL, + N_("Sender contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_SENDER_CONTAINS }, + + { "mail-search-subject-contains", + NULL, + N_("Subject contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_SUBJECT_CONTAINS }, + + { "mail-search-subject-or-recipients-contains", + NULL, + N_("Subject or Recipients contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_SUBJECT_OR_RECIPIENTS_CONTAINS }, + + { "mail-search-subject-or-sender-contains", + NULL, + N_("Subject or Sender contains"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS } +}; + +static GtkRadioActionEntry mail_scope_entries[] = { + + { "mail-scope-all-accounts", + NULL, + N_("All Accounts"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SCOPE_ALL_ACCOUNTS }, + + { "mail-scope-current-account", + NULL, + N_("Current Account"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SCOPE_CURRENT_ACCOUNT }, + + { "mail-scope-current-folder", + NULL, + N_("Current Folder"), + NULL, + NULL, /* XXX Add a tooltip! */ + MAIL_SCOPE_CURRENT_FOLDER } +}; + +void +e_mail_shell_view_actions_init (EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + GtkActionGroup *action_group; + GtkRadioAction *radio_action; + GConfBridge *bridge; + GObject *object; + GObject *src_object; + GObject *dst_object; + const gchar *key; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + /* Mail Actions */ + action_group = ACTION_GROUP (MAIL); + gtk_action_group_add_actions ( + action_group, mail_entries, + G_N_ELEMENTS (mail_entries), mail_shell_view); + e_action_group_add_popup_actions ( + action_group, mail_popup_entries, + G_N_ELEMENTS (mail_popup_entries)); + gtk_action_group_add_toggle_actions ( + action_group, mail_toggle_entries, + G_N_ELEMENTS (mail_toggle_entries), mail_shell_view); + gtk_action_group_add_radio_actions ( + action_group, mail_view_entries, + G_N_ELEMENTS (mail_view_entries), -1, + G_CALLBACK (action_mail_view_cb), mail_shell_view); + gtk_action_group_add_radio_actions ( + action_group, mail_search_entries, + G_N_ELEMENTS (mail_search_entries), + MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS, + G_CALLBACK (action_mail_search_cb), mail_shell_view); + gtk_action_group_add_radio_actions ( + action_group, mail_scope_entries, + G_N_ELEMENTS (mail_scope_entries), + MAIL_SCOPE_CURRENT_FOLDER, + G_CALLBACK (action_search_scope_cb), mail_shell_view); + + radio_action = GTK_RADIO_ACTION (ACTION (MAIL_SCOPE_ALL_ACCOUNTS)); + e_shell_content_set_scope_action (shell_content, radio_action); + e_shell_content_set_scope_visible (shell_content, TRUE); + + /* Bind GObject properties for GConf keys. */ + + bridge = gconf_bridge_get (); + + object = G_OBJECT (ACTION (MAIL_PREVIEW)); + key = "/apps/evolution/mail/display/show_preview"; + gconf_bridge_bind_property (bridge, key, object, "active"); + + object = G_OBJECT (ACTION (MAIL_THREADS_GROUP_BY)); + key = "/apps/evolution/mail/display/thread_list"; + gconf_bridge_bind_property (bridge, key, object, "active"); + + object = G_OBJECT (ACTION (MAIL_VIEW_VERTICAL)); + key = "/apps/evolution/mail/display/layout"; + gconf_bridge_bind_property (bridge, key, object, "current-value"); + + /* Fine tuning. */ + + src_object = G_OBJECT (ACTION (MAIL_THREADS_GROUP_BY)); + + dst_object = G_OBJECT (ACTION (MAIL_FOLDER_SELECT_THREAD)); + e_binding_new (src_object, "active", dst_object, "sensitive"); + + dst_object = G_OBJECT (ACTION (MAIL_FOLDER_SELECT_SUBTHREAD)); + e_binding_new (src_object, "active", dst_object, "sensitive"); + + dst_object = G_OBJECT (ACTION (MAIL_THREADS_COLLAPSE_ALL)); + e_binding_new (src_object, "active", dst_object, "sensitive"); + + dst_object = G_OBJECT (ACTION (MAIL_THREADS_EXPAND_ALL)); + e_binding_new (src_object, "active", dst_object, "sensitive"); + + e_mutual_binding_new ( + G_OBJECT (ACTION (MAIL_PREVIEW)), "active", + G_OBJECT (shell_content), "preview-visible"); + + /* XXX The boolean sense of the GConf key is the inverse of + * the menu item, so we have to maintain two properties. */ + e_mutual_binding_new_with_negation ( + G_OBJECT (shell_content), "show-deleted", + G_OBJECT (ACTION (MAIL_HIDE_DELETED)), "active"); + + g_signal_connect ( + ACTION (GAL_SAVE_CUSTOM_VIEW), "activate", + G_CALLBACK (action_gal_save_custom_view_cb), mail_shell_view); + + g_signal_connect ( + ACTION (SEARCH_EXECUTE), "activate", + G_CALLBACK (action_search_execute_cb), mail_shell_view); +} + +/* Helper for e_mail_shell_view_update_popup_labels() */ +static void +mail_shell_view_update_label_action (GtkToggleAction *action, + MessageList *message_list, + GPtrArray *uids, + const gchar *label_tag) +{ + CamelFolder *folder; + gboolean exists = FALSE; + gboolean not_exists = FALSE; + gboolean sensitive; + guint ii; + + folder = message_list->folder; + + /* Figure out the proper label action state for the selected + * messages. If all the selected messages have the given label, + * make the toggle action active. If all the selected message + * DO NOT have the given label, make the toggle action inactive. + * If some do and some don't, make the action insensitive. */ + + for (ii = 0; ii < uids->len && (!exists || !not_exists); ii++) { + const gchar *old_label; + gchar *new_label; + + /* Check for new-style labels. */ + if (camel_folder_get_message_user_flag ( + folder, uids->pdata[ii], label_tag)) { + exists = TRUE; + continue; + } + + /* Check for old-style labels. */ + old_label = camel_folder_get_message_user_tag ( + folder, uids->pdata[ii], "label"); + if (old_label == NULL) { + not_exists = TRUE; + continue; + } + + /* Convert old-style labels ("<name>") to "$Label<name>". */ + new_label = g_alloca (strlen (old_label) + 10); + g_stpcpy (g_stpcpy (new_label, "$Label"), old_label); + + if (strcmp (new_label, label_tag) == 0) + exists = TRUE; + else + not_exists = TRUE; + } + + sensitive = !(exists && not_exists); + gtk_toggle_action_set_active (action, exists); + gtk_action_set_sensitive (GTK_ACTION (action), sensitive); +} + +void +e_mail_shell_view_update_popup_labels (EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellSettings *shell_settings; + EShellWindow *shell_window; + EShellView *shell_view; + EMailReader *reader; + MessageList *message_list; + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + GtkTreeModel *tree_model; + GtkTreeIter iter; + GPtrArray *uids; + const gchar *path; + gboolean valid; + guint merge_id; + gint ii = 0; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + ui_manager = e_shell_window_get_ui_manager (shell_window); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + tree_model = e_shell_settings_get_object ( + shell_settings, "mail-label-list-store"); + + action_group = ACTION_GROUP (MAIL_LABEL); + merge_id = mail_shell_view->priv->label_merge_id; + path = "/mail-message-popup/mail-label-menu/mail-label-actions"; + + /* Unmerge the previous menu items. */ + gtk_ui_manager_remove_ui (ui_manager, merge_id); + e_action_group_remove_all_actions (action_group); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + uids = message_list_get_selected (message_list); + + valid = gtk_tree_model_get_iter_first (tree_model, &iter); + + while (valid) { + GtkToggleAction *toggle_action; + GtkAction *action; + gchar *action_name; + gchar *stock_id; + gchar *label; + gchar *tag; + + label = e_mail_label_list_store_get_name ( + E_MAIL_LABEL_LIST_STORE (tree_model), &iter); + stock_id = e_mail_label_list_store_get_stock_id ( + E_MAIL_LABEL_LIST_STORE (tree_model), &iter); + tag = e_mail_label_list_store_get_tag ( + E_MAIL_LABEL_LIST_STORE (tree_model), &iter); + action_name = g_strdup_printf ("mail-label-%d", ii); + + /* XXX Add a tooltip! */ + toggle_action = gtk_toggle_action_new ( + action_name, label, NULL, stock_id); + + g_object_set_data_full ( + G_OBJECT (toggle_action), "tag", + tag, (GDestroyNotify) g_free); + + /* Configure the action before we connect to signals. */ + mail_shell_view_update_label_action ( + toggle_action, message_list, uids, tag); + + g_signal_connect ( + toggle_action, "toggled", + G_CALLBACK (action_mail_label_cb), mail_shell_view); + + /* The action group takes ownership of the action. */ + action = GTK_ACTION (toggle_action); + gtk_action_group_add_action (action_group, action); + g_object_unref (toggle_action); + + gtk_ui_manager_add_ui ( + ui_manager, merge_id, path, action_name, + action_name, GTK_UI_MANAGER_AUTO, FALSE); + + g_free (label); + g_free (stock_id); + g_free (action_name); + + valid = gtk_tree_model_iter_next (tree_model, &iter); + ii++; + } + + message_list_free_uids (message_list, uids); + + g_object_unref (tree_model); +} + +void +e_mail_shell_view_update_search_filter (EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellContent *shell_content; + EShellSettings *shell_settings; + EShellWindow *shell_window; + EShellView *shell_view; + GtkActionGroup *action_group; + GtkRadioAction *radio_action; + GtkTreeModel *tree_model; + GtkTreeIter iter; + GList *list; + GSList *group; + gboolean valid; + gint ii = 0; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_content = e_shell_view_get_shell_content (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); + + tree_model = e_shell_settings_get_object ( + shell_settings, "mail-label-list-store"); + + action_group = ACTION_GROUP (MAIL_FILTER); + e_action_group_remove_all_actions (action_group); + + /* Add the standard filter actions. */ + gtk_action_group_add_radio_actions ( + action_group, mail_filter_entries, + G_N_ELEMENTS (mail_filter_entries), + MAIL_FILTER_ALL_MESSAGES, + G_CALLBACK (action_search_filter_cb), + mail_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); + + valid = gtk_tree_model_get_iter_first (tree_model, &iter); + + while (valid) { + GtkAction *action; + gchar *action_name; + gchar *stock_id; + gchar *label; + + label = e_mail_label_list_store_get_name ( + E_MAIL_LABEL_LIST_STORE (tree_model), &iter); + stock_id = e_mail_label_list_store_get_stock_id ( + E_MAIL_LABEL_LIST_STORE (tree_model), &iter); + + action_name = g_strdup_printf ("mail-filter-label-%d", ii); + radio_action = gtk_radio_action_new ( + action_name, label, NULL, stock_id, ii); + g_free (action_name); + + 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_free (label); + g_free (stock_id); + + valid = gtk_tree_model_iter_next (tree_model, &iter); + ii++; + } + + /* Use any action in the group; doesn't matter which. */ + e_shell_content_set_filter_action (shell_content, radio_action); + + ii = MAIL_FILTER_UNREAD_MESSAGES; + e_shell_content_add_filter_separator_after (shell_content, ii); + + ii = MAIL_FILTER_READ_MESSAGES; + e_shell_content_add_filter_separator_before (shell_content, ii); + + g_object_unref (tree_model); +} diff --git a/modules/mail/e-mail-shell-view-actions.h b/modules/mail/e-mail-shell-view-actions.h new file mode 100644 index 0000000000..2a05582df8 --- /dev/null +++ b/modules/mail/e-mail-shell-view-actions.h @@ -0,0 +1,257 @@ +/* + * e-mail-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_MAIL_SHELL_VIEW_ACTIONS_H +#define E_MAIL_SHELL_VIEW_ACTIONS_H + +#include <shell/e-shell-window-actions.h> + +/* Mail Actions */ +#define E_SHELL_WINDOW_ACTION_MAIL_ACCOUNT_DISABLE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-account-disable") +#define E_SHELL_WINDOW_ACTION_MAIL_ADD_SENDER(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-add-sender") +#define E_SHELL_WINDOW_ACTION_MAIL_CARET_MODE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-caret-mode") +#define E_SHELL_WINDOW_ACTION_MAIL_CHECK_FOR_JUNK(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-check-for-junk") +#define E_SHELL_WINDOW_ACTION_MAIL_CLIPBOARD_COPY(window) \ + E_SHELL_WINDOw_ACTION ((window), "mail-clipboard-copy") +#define E_SHELL_WINDOW_ACTION_MAIL_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-copy") +#define E_SHELL_WINDOW_ACTION_MAIL_CREATE_SEARCH_FOLDER(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-create-search-folder") +#define E_SHELL_WINDOW_ACTION_MAIL_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-delete") +#define E_SHELL_WINDOW_ACTION_MAIL_DOWNLOAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-download") +#define E_SHELL_WINDOW_ACTION_MAIL_EMPTY_TRASH(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-empty-trash") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_MAILING_LIST(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-mailing-list") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_RECIPIENTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-recipients") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_SENDER(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-sender") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ON_SUBJECT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-on-subject") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTERS_APPLY(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filters-apply") +#define E_SHELL_WINDOW_ACTION_MAIL_FIND(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-find") +#define E_SHELL_WINDOW_ACTION_MAIL_FLAG_CLEAR(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-flag-clear") +#define E_SHELL_WINDOW_ACTION_MAIL_FLAG_COMPLETED(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-flag-completed") +#define E_SHELL_WINDOW_ACTION_MAIL_FLAG_FOR_FOLLOWUP(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-flag-for-followup") +#define E_SHELL_WINDOW_ACTION_MAIL_FLUSH_OUTBOX(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-flush-outbox") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_COPY(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-copy") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_DELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-delete") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_EXPUNGE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-expunge") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_MARK_ALL_READ(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-mark-all-read") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_MOVE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-move") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-new") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_PROPERTIES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-properties") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_REFRESH(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-refresh") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_RENAME(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-rename") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_ALL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-all") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_THREAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-thread") +#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_SUBTHREAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-subthread") +#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-forward") +#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD_ATTACHED(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-forward-attached") +#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD_INLINE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-forward-inline") +#define E_SHELL_WINDOW_ACTION_MAIL_FORWARD_QUOTED(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-forward-quoted") +#define E_SHELL_WINDOW_ACTION_MAIL_HIDE_DELETED(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-hide-deleted") +#define E_SHELL_WINDOW_ACTION_MAIL_HIDE_READ(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-hide-read") +#define E_SHELL_WINDOW_ACTION_MAIL_HIDE_SELECTED(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-hide-selected") +#define E_SHELL_WINDOW_ACTION_MAIL_LABEL_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-label-new") +#define E_SHELL_WINDOW_ACTION_MAIL_LABEL_NONE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-label-none") +#define E_SHELL_WINDOW_ACTION_MAIL_LOAD_IMAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-load-images") +#define E_SHELL_WINDOW_ACTION_MAIL_MARK_IMPORTANT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-mark-important") +#define E_SHELL_WINDOW_ACTION_MAIL_MARK_JUNK(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-mark-junk") +#define E_SHELL_WINDOW_ACTION_MAIL_MARK_NOTJUNK(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-mark-notjunk") +#define E_SHELL_WINDOW_ACTION_MAIL_MARK_READ(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-mark-read") +#define E_SHELL_WINDOW_ACTION_MAIL_MARK_UNIMPORTANT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-mark-unimportant") +#define E_SHELL_WINDOW_ACTION_MAIL_MARK_UNREAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-mark-unread") +#define E_SHELL_WINDOW_ACTION_MAIL_MESSAGE_EDIT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-message-edit") +#define E_SHELL_WINDOW_ACTION_MAIL_MESSAGE_NEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-message-new") +#define E_SHELL_WINDOW_ACTION_MAIL_MESSAGE_OPEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-message-open") +#define E_SHELL_WINDOW_ACTION_MAIL_MOVE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-move") +#define E_SHELL_WINDOW_ACTION_MAIL_NEXT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-next") +#define E_SHELL_WINDOW_ACTION_MAIL_NEXT_IMPORTANT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-next-important") +#define E_SHELL_WINDOW_ACTION_MAIL_NEXT_THREAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-next-thread") +#define E_SHELL_WINDOW_ACTION_MAIL_NEXT_UNREAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-next-unread") +#define E_SHELL_WINDOW_ACTION_MAIL_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-preview") +#define E_SHELL_WINDOW_ACTION_MAIL_PREVIOUS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-previous") +#define E_SHELL_WINDOW_ACTION_MAIL_PREVIOUS_IMPORTANT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-previous-important") +#define E_SHELL_WINDOW_ACTION_MAIL_PREVIOUS_UNREAD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-previous-unread") +#define E_SHELL_WINDOW_ACTION_MAIL_PRINT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-print") +#define E_SHELL_WINDOW_ACTION_MAIL_PRINT_PREVIEW(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-print-preview") +#define E_SHELL_WINDOW_ACTION_MAIL_REDIRECT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-redirect") +#define E_SHELL_WINDOW_ACTION_MAIL_REPLY_ALL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-reply-all") +#define E_SHELL_WINDOW_ACTION_MAIL_REPLY_LIST(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-reply-list") +#define E_SHELL_WINDOW_ACTION_MAIL_REPLY_SENDER(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-reply-sender") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_MAILING_LIST(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-mailing-list") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_RECIPIENTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-recipients") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_SENDER(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-sender") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_FOLDER_FROM_SUBJECT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-folder-from-subject") +#define E_SHELL_WINDOW_ACTION_MAIL_SELECT_ALL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-select-all") +#define E_SHELL_WINDOW_ACTION_MAIL_SHOW_ALL_HEADERS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-show-all-headers") +#define E_SHELL_WINDOW_ACTION_MAIL_SHOW_HIDDEN(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-show-hidden") +#define E_SHELL_WINDOW_ACTION_MAIL_SHOW_SOURCE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-show-source") +#define E_SHELL_WINDOW_ACTION_MAIL_SMART_BACKWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-smart-backward") +#define E_SHELL_WINDOW_ACTION_MAIL_SMART_FORWARD(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-smart-forward") +#define E_SHELL_WINDOW_ACTION_MAIL_STOP(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-stop") +#define E_SHELL_WINDOW_ACTION_MAIL_THREADS_COLLAPSE_ALL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-threads-collapse-all") +#define E_SHELL_WINDOW_ACTION_MAIL_THREADS_EXPAND_ALL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-threads-expand-all") +#define E_SHELL_WINDOW_ACTION_MAIL_THREADS_GROUP_BY(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-threads-group-by") +#define E_SHELL_WINDOW_ACTION_MAIL_TOOLS_FILTERS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-tools-filters") +#define E_SHELL_WINDOW_ACTION_MAIL_TOOLS_SEARCH_FOLDERS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-tools-search-folders") +#define E_SHELL_WINDOW_ACTION_MAIL_TOOLS_SUBSCRIPTIONS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-tools-subscriptions") +#define E_SHELL_WINDOW_ACTION_MAIL_UNDELETE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-undelete") +#define E_SHELL_WINDOW_ACTION_MAIL_VIEW_CLASSIC(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-view-classic") +#define E_SHELL_WINDOW_ACTION_MAIL_VIEW_VERTICAL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-view-vertical") +#define E_SHELL_WINDOW_ACTION_MAIL_ZOOM_100(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-zoom-100") +#define E_SHELL_WINDOW_ACTION_MAIL_ZOOM_IN(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-zoom-in") +#define E_SHELL_WINDOW_ACTION_MAIL_ZOOM_OUT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-zoom-out") + +/* Mail Query Actions */ +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_ALL_MESSAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-all-messages") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_IMPORTANT_MESSAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-important-messages") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_LAST_5_DAYS_MESSAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-last-5-days-messages") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_MESSAGES_NOT_JUNK(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-not-junk") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-with-attachments") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_NO_LABEL(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-no-label") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_READ_MESSAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-read-messages") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_RECENT_MESSAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-recent-messages") +#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_UNREAD_MESSAGES(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-filter-unread-messages") +#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_ALL_ACCOUNTS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-scope-all-accounts") +#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_CURRENT_ACCOUNT(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-account") +#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_CURRENT_FOLDER(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-folder") +#define E_SHELL_WINDOW_ACTION_MAIL_SCOPE_CURRENT_MESSAGE(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-scope-current-message") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_BODY_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-body-contains") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_MESSAGE_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-message-contains") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_RECIPIENTS_CONTAIN(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-recipients-contain") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SENDER_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-sender-contains") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SUBJECT_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-subject-contains") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SUBJECT_OR_RECIPIENTS_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-subject-or-recipients-contains") +#define E_SHELL_WINDOW_ACTION_MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS(window) \ + E_SHELL_WINDOW_ACTION ((window), "mail-search-subject-or-sender-contains") + +/* Action Groups */ +#define E_SHELL_WINDOW_ACTION_GROUP_MAIL(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "mail") +#define E_SHELL_WINDOW_ACTION_GROUP_MAIL_FILTER(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "mail-filter") +#define E_SHELL_WINDOW_ACTION_GROUP_MAIL_LABEL(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "mail-label") + +#endif /* E_MAIL_SHELL_VIEW_ACTIONS_H */ diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c new file mode 100644 index 0000000000..f3b3b5a513 --- /dev/null +++ b/modules/mail/e-mail-shell-view-private.c @@ -0,0 +1,905 @@ +/* + * e-mail-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-mail-shell-view-private.h" + +#include "widgets/menus/gal-view-factory-etable.h" + +static void +mail_shell_view_folder_tree_selected_cb (EMailShellView *mail_shell_view, + const gchar *full_name, + const gchar *uri, + guint32 flags, + EMFolderTree *folder_tree) +{ + EShellView *shell_view; + EMailReader *reader; + gboolean folder_selected; + + shell_view = E_SHELL_VIEW (mail_shell_view); + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + + folder_selected = + !(flags & CAMEL_FOLDER_NOSELECT) && + full_name != NULL; + + if (folder_selected) + e_mail_reader_set_folder_uri (reader, uri); + else + e_mail_reader_set_folder (reader, NULL, NULL); + + e_shell_view_update_actions (shell_view); +} + +static void +mail_shell_view_folder_tree_popup_event_cb (EShellView *shell_view, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/mail-folder-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); +} + +static gboolean +mail_shell_view_key_press_event_cb (EMailShellView *mail_shell_view, + GdkEventKey *event) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkAction *action; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + + if ((event->state & GDK_CONTROL_MASK) != 0) + return FALSE; + + switch (event->keyval) { + case GDK_space: + action = ACTION (MAIL_SMART_FORWARD); + break; + + case GDK_BackSpace: + action = ACTION (MAIL_SMART_BACKWARD); + break; + + default: + return FALSE; + } + + gtk_action_activate (action); + + return TRUE; +} + +static gint +mail_shell_view_message_list_key_press_cb (EMailShellView *mail_shell_view, + gint row, + ETreePath path, + gint col, + GdkEvent *event) +{ + return mail_shell_view_key_press_event_cb ( + mail_shell_view, &event->key); +} + +static gboolean +mail_shell_view_message_list_right_click_cb (EShellView *shell_view, + gint row, + ETreePath path, + gint col, + GdkEventButton *event) +{ + const gchar *widget_path; + + widget_path = "/mail-message-popup"; + e_shell_view_show_popup_menu (shell_view, widget_path, event); + + return TRUE; +} + +static void +mail_shell_view_reader_changed_cb (EMailShellView *mail_shell_view, + EMailReader *reader) +{ + EMailShellContent *mail_shell_content; + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + e_mail_shell_content_update_view_instance (mail_shell_content); + e_mail_shell_view_update_sidebar (mail_shell_view); +} + +static void +mail_shell_view_reader_status_message_cb (EMailShellView *mail_shell_view, + const gchar *status_message) +{ + EShellView *shell_view; + EShellTaskbar *shell_taskbar; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_taskbar = e_shell_view_get_shell_taskbar (shell_view); + + e_shell_taskbar_set_message (shell_taskbar, status_message); +} + +static void +mail_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 mail"); + 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 +mail_shell_view_notify_view_id_cb (EMailShellView *mail_shell_view) +{ + EMailShellContent *mail_shell_content; + GalViewInstance *view_instance; + const gchar *view_id; + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + view_instance = NULL; /* FIXME */ + view_id = e_shell_view_get_view_id (E_SHELL_VIEW (mail_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_mail_shell_view_private_init (EMailShellView *mail_shell_view, + EShellViewClass *shell_view_class) +{ + if (!gal_view_collection_loaded (shell_view_class->view_collection)) + mail_shell_view_load_view_collection (shell_view_class); + + g_signal_connect ( + mail_shell_view, "notify::view-id", + G_CALLBACK (mail_shell_view_notify_view_id_cb), NULL); +} + +void +e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view) +{ + EMailShellViewPrivate *priv = mail_shell_view->priv; + EMailShellSidebar *mail_shell_sidebar; + EShell *shell; + EShellView *shell_view; + EShellBackend *shell_backend; + EShellContent *shell_content; + EShellSettings *shell_settings; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + EMFormatHTMLDisplay *html_display; + EMFolderTree *folder_tree; + RuleContext *context; + FilterRule *rule = NULL; + GtkTreeModel *tree_model; + GtkUIManager *ui_manager; + MessageList *message_list; + EMailReader *reader; + GtkHTML *html; + const gchar *source; + guint merge_id; + gint ii = 0; + + shell_view = E_SHELL_VIEW (mail_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); + ui_manager = e_shell_window_get_ui_manager (shell_window); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + tree_model = e_shell_settings_get_object ( + shell_settings, "mail-label-list-store"); + + e_shell_window_add_action_group (shell_window, "mail"); + e_shell_window_add_action_group (shell_window, "mail-filter"); + e_shell_window_add_action_group (shell_window, "mail-label"); + + merge_id = gtk_ui_manager_new_merge_id (ui_manager); + priv->label_merge_id = merge_id; + + /* Cache these to avoid lots of awkward casting. */ + priv->mail_shell_backend = g_object_ref (shell_backend); + priv->mail_shell_content = g_object_ref (shell_content); + priv->mail_shell_sidebar = g_object_ref (shell_sidebar); + + reader = E_MAIL_READER (shell_content); + html_display = e_mail_reader_get_html_display (reader); + message_list = e_mail_reader_get_message_list (reader); + + mail_shell_sidebar = E_MAIL_SHELL_SIDEBAR (shell_sidebar); + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + + html = EM_FORMAT_HTML (html_display)->html; + + g_signal_connect_swapped ( + folder_tree, "folder-selected", + G_CALLBACK (mail_shell_view_folder_tree_selected_cb), + mail_shell_view); + + g_signal_connect_swapped ( + folder_tree, "popup-event", + G_CALLBACK (mail_shell_view_folder_tree_popup_event_cb), + mail_shell_view); + + g_signal_connect_swapped ( + message_list->tree, "key-press", + G_CALLBACK (mail_shell_view_message_list_key_press_cb), + mail_shell_view); + + g_signal_connect_swapped ( + message_list->tree, "right-click", + G_CALLBACK (mail_shell_view_message_list_right_click_cb), + mail_shell_view); + + g_signal_connect_swapped ( + reader, "changed", + G_CALLBACK (mail_shell_view_reader_changed_cb), + mail_shell_view); + + /* Use the same callback as "changed". */ + g_signal_connect_swapped ( + reader, "folder-loaded", + G_CALLBACK (mail_shell_view_reader_changed_cb), + mail_shell_view); + + g_signal_connect_swapped ( + reader, "folder-loaded", + G_CALLBACK (e_mail_shell_view_restore_state), + mail_shell_view); + + g_signal_connect_swapped ( + tree_model, "row-changed", + G_CALLBACK (e_mail_shell_view_update_search_filter), + mail_shell_view); + + g_signal_connect_swapped ( + tree_model, "row-deleted", + G_CALLBACK (e_mail_shell_view_update_search_filter), + mail_shell_view); + + g_signal_connect_swapped ( + tree_model, "row-inserted", + G_CALLBACK (e_mail_shell_view_update_search_filter), + mail_shell_view); + + g_signal_connect_swapped ( + html, "key-press-event", + G_CALLBACK (mail_shell_view_key_press_event_cb), + mail_shell_view); + + g_signal_connect_swapped ( + html, "status-message", + G_CALLBACK (mail_shell_view_reader_status_message_cb), + mail_shell_view); + + e_mail_shell_view_actions_init (mail_shell_view); + e_mail_shell_view_update_search_filter (mail_shell_view); + e_mail_reader_init (reader); + + /* Populate built-in rules for search entry popup menu. + * Keep the assertions, please. If the conditions aren't + * met we're going to crash anyway, just more mysteriously. */ + context = e_shell_content_get_search_context (shell_content); + source = FILTER_SOURCE_DEMAND; + while ((rule = rule_context_next_rule (context, rule, source))) { + g_assert (ii < MAIL_NUM_SEARCH_RULES); + priv->search_rules[ii++] = g_object_ref (rule); + } + g_assert (ii == MAIL_NUM_SEARCH_RULES); +} + +void +e_mail_shell_view_private_dispose (EMailShellView *mail_shell_view) +{ + EMailShellViewPrivate *priv = mail_shell_view->priv; + gint ii; + + DISPOSE (priv->mail_shell_backend); + DISPOSE (priv->mail_shell_content); + DISPOSE (priv->mail_shell_sidebar); + + for (ii = 0; ii < MAIL_NUM_SEARCH_RULES; ii++) + DISPOSE (priv->search_rules[ii]); +} + +void +e_mail_shell_view_private_finalize (EMailShellView *mail_shell_view) +{ + /* XXX Nothing to do? */ +} + +void +e_mail_shell_view_restore_state (EMailShellView *mail_shell_view) +{ + EShellView *shell_view; + EShellContent *shell_content; + EMailReader *reader; + MessageList *message_list; + const gchar *folder_uri; + gchar *group_name; + + /* XXX Move this to EMailShellContent. */ + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + reader = E_MAIL_READER (shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + g_return_if_fail (folder_uri != NULL); + + group_name = g_strdup_printf ("Folder %s", folder_uri); + e_shell_content_restore_state (shell_content, group_name); + g_free (group_name); +} + +void +e_mail_shell_view_execute_search (EMailShellView *mail_shell_view) +{ + EShell *shell; + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + EShellSettings *shell_settings; + EMFormatHTMLDisplay *html_display; + EMailShellContent *mail_shell_content; + MessageList *message_list; + FilterRule *rule; + EMailReader *reader; + CamelFolder *folder; + GtkAction *action; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter tree_iter; + GString *string; + GList *iter; + GSList *search_strings = NULL; + const gchar *folder_uri; + const gchar *text; + gboolean valid; + gchar *query; + gchar *temp; + gchar *tag; + gint value; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_window = e_shell_view_get_shell_window (shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + + shell = e_shell_window_get_shell (shell_window); + shell_settings = e_shell_get_shell_settings (shell); + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + + reader = E_MAIL_READER (shell_content); + html_display = e_mail_reader_get_html_display (reader); + message_list = e_mail_reader_get_message_list (reader); + + folder_uri = message_list->folder_uri; + folder = message_list->folder; + + /* This returns a new object reference. */ + model = e_shell_settings_get_object ( + shell_settings, "mail-label-list-store"); + + text = e_shell_content_get_search_text (shell_content); + if (text == NULL || *text == '\0') { + query = g_strdup (""); + goto filter; + } + + /* Replace variables in the selected rule with the + * current search text and extract a query string. */ + + action = ACTION (MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS); + value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action)); + g_return_if_fail (value >= 0 && value < MAIL_NUM_SEARCH_RULES); + rule = mail_shell_view->priv->search_rules[value]; + + for (iter = rule->parts; iter != NULL; iter = iter->next) { + FilterPart *part = iter->data; + FilterElement *element = NULL; + + if (strcmp (part->name, "subject") == 0) + element = filter_part_find_element (part, "subject"); + else if (strcmp (part->name, "body") == 0) + element = filter_part_find_element (part, "word"); + else if (strcmp (part->name, "sender") == 0) + element = filter_part_find_element (part, "sender"); + else if (strcmp (part->name, "to") == 0) + element = filter_part_find_element (part, "recipient"); + + if (strcmp (part->name, "body") == 0) { + struct _camel_search_words *words; + gint ii; + + words = camel_search_words_split ((guchar *) text); + for (ii = 0; ii < words->len; ii++) + search_strings = g_slist_prepend ( + search_strings, g_strdup ( + words->words[ii]->word)); + camel_search_words_free (words); + } + + if (element != NULL) { + FilterInput *input = FILTER_INPUT (element); + filter_input_set_value (input, text); + } + } + + string = g_string_sized_new (1024); + filter_rule_build_code (rule, string); + query = g_string_free (string, FALSE); + +filter: + + /* Apply selected filter. */ + + value = e_shell_content_get_filter_value (shell_content); + switch (value) { + case MAIL_FILTER_ALL_MESSAGES: + break; + + case MAIL_FILTER_UNREAD_MESSAGES: + temp = g_strdup_printf ( + "(and %s (match-all (not " + "(system-flag \"Seen\"))))", query); + g_free (query); + query = temp; + break; + + case MAIL_FILTER_NO_LABEL: + string = g_string_sized_new (1024); + g_string_append_printf ( + string, "(and %s (and ", query); + valid = gtk_tree_model_get_iter_first ( + model, &tree_iter); + while (valid) { + tag = e_mail_label_list_store_get_tag ( + E_MAIL_LABEL_LIST_STORE (model), + &tree_iter); + g_string_append_printf ( + string, " (match-all (not (or " + "(= (user-tag \"label\") \"%s\") " + "(user-flag \"$Label%s\") " + "(user-flag \"%s\"))))", + tag, tag, tag); + g_free (tag); + + valid = gtk_tree_model_iter_next ( + model, &tree_iter); + } + g_string_append_len (string, "))", 2); + g_free (query); + query = g_string_free (string, FALSE); + break; + + case MAIL_FILTER_READ_MESSAGES: + temp = g_strdup_printf ( + "(and %s (match-all " + "(system-flag \"Seen\")))", query); + g_free (query); + query = temp; + break; + + case MAIL_FILTER_RECENT_MESSAGES: + if (em_utils_folder_is_sent (folder, folder_uri)) + temp = g_strdup_printf ( + "(and %s (match-all " + "(> (get-sent-date) " + "(- (get-current-date) 86400))))", + query); + else + temp = g_strdup_printf ( + "(and %s (match-all " + "(> (get-received-date) " + "(- (get-current-date) 86400))))", + query); + g_free (query); + query = temp; + break; + + case MAIL_FILTER_LAST_5_DAYS_MESSAGES: + if (em_utils_folder_is_sent (folder, folder_uri)) + temp = g_strdup_printf ( + "(and %s (match-all " + "(> (get-sent-date) " + "(- (get-current-date) 432000))))", + query); + else + temp = g_strdup_printf ( + "(and %s (match-all " + "(> (get-received-date) " + "(- (get-current-date) 432000))))", + query); + g_free (query); + query = temp; + break; + + case MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS: + temp = g_strdup_printf ( + "(and %s (match-all " + "(system-flag \"Attachments\")))", query); + g_free (query); + query = temp; + break; + + case MAIL_FILTER_IMPORTANT_MESSAGES: + temp = g_strdup_printf ( + "(and %s (match-all " + "(system-flag \"Flagged\")))", query); + g_free (query); + query = temp; + break; + + case MAIL_FILTER_MESSAGES_NOT_JUNK: + temp = g_strdup_printf ( + "(and %s (match-all (not " + "(system-flag \"junk\"))))", query); + g_free (query); + query = temp; + break; + + default: + /* The action value also serves as a path for + * the label list store. That's why we number + * the label actions from zero. */ + path = gtk_tree_path_new_from_indices (value, -1); + gtk_tree_model_get_iter (model, &tree_iter, path); + gtk_tree_path_free (path); + + tag = e_mail_label_list_store_get_tag ( + E_MAIL_LABEL_LIST_STORE (model), &tree_iter); + temp = g_strdup_printf ( + "(and %s (match-all (or " + "(= (user-tag \"label\") \"%s\") " + "(user-flag \"$Label%s\") " + "(user-flag \"%s\"))))", + query, tag, tag, tag); + g_free (tag); + + g_free (query); + query = temp; + break; + } + + message_list_set_search (message_list, query); + + e_mail_shell_content_set_search_strings ( + mail_shell_content, search_strings); + + g_slist_foreach (search_strings, (GFunc) g_free, NULL); + g_slist_free (search_strings); + + g_object_unref (model); + g_free (query); +} + +/* Helper for e_mail_shell_view_create_filter_from_selected() */ +static void +mail_shell_view_create_filter_cb (CamelFolder *folder, + const gchar *uid, + CamelMimeMessage *message, + gpointer user_data) +{ + struct { + const gchar *source; + gint type; + } *filter_data = user_data; + + if (message != NULL) + filter_gui_add_from_message ( + message, filter_data->source, filter_data->type); + + g_free (filter_data); +} + +void +e_mail_shell_view_create_filter_from_selected (EMailShellView *mail_shell_view, + gint filter_type) +{ + EMailReader *reader; + MessageList *message_list; + CamelFolder *folder; + const gchar *filter_source; + const gchar *folder_uri; + GPtrArray *uids; + + struct { + const gchar *source; + gint type; + } *filter_data; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + folder = message_list->folder; + + if (em_utils_folder_is_sent (folder, folder_uri)) + filter_source = FILTER_SOURCE_OUTGOING; + else if (em_utils_folder_is_outbox (folder, folder_uri)) + filter_source = FILTER_SOURCE_OUTGOING; + else + filter_source = FILTER_SOURCE_INCOMING; + + uids = message_list_get_selected (message_list); + + if (uids->len == 1) { + filter_data = g_malloc (sizeof (*filter_data)); + filter_data->source = filter_source; + filter_data->type = filter_type; + + mail_get_message ( + folder, uids->pdata[0], + mail_shell_view_create_filter_cb, + filter_data, mail_msg_unordered_push); + } + + em_utils_uids_free (uids); +} + +/* Helper for e_mail_shell_view_create_vfolder_from_selected() */ +static void +mail_shell_view_create_vfolder_cb (CamelFolder *folder, + const gchar *uid, + CamelMimeMessage *message, + gpointer user_data) +{ + struct { + gchar *uri; + gint type; + } *vfolder_data = user_data; + + if (message != NULL) + vfolder_gui_add_from_message ( + message, vfolder_data->type, vfolder_data->uri); + + g_free (vfolder_data->uri); + g_free (vfolder_data); +} + +void +e_mail_shell_view_create_vfolder_from_selected (EMailShellView *mail_shell_view, + gint vfolder_type) +{ + EMailReader *reader; + MessageList *message_list; + CamelFolder *folder; + const gchar *folder_uri; + GPtrArray *uids; + + struct { + gchar *uri; + gint type; + } *vfolder_data; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + folder = message_list->folder; + + uids = message_list_get_selected (message_list); + + if (uids->len == 1) { + vfolder_data = g_malloc (sizeof (*vfolder_data)); + vfolder_data->uri = g_strdup (folder_uri); + vfolder_data->type = vfolder_type; + + mail_get_message ( + folder, uids->pdata[0], + mail_shell_view_create_vfolder_cb, + vfolder_data, mail_msg_unordered_push); + } + + em_utils_uids_free (uids); +} + +void +e_mail_shell_view_update_sidebar (EMailShellView *mail_shell_view) +{ + EMailShellContent *mail_shell_content; + EShellSidebar *shell_sidebar; + EShellView *shell_view; + EMailReader *reader; + MessageList *message_list; + CamelStore *local_store; + CamelFolder *folder; + GPtrArray *selected; + GString *buffer; + const gchar *display_name; + const gchar *folder_uri; + gchar *folder_name; + gchar *title; + guint32 num_deleted; + guint32 num_junked; + guint32 num_junked_not_deleted; + guint32 num_unread; + guint32 num_visible; + + g_return_if_fail (E_IS_MAIL_SHELL_VIEW (mail_shell_view)); + + mail_shell_content = mail_shell_view->priv->mail_shell_content; + + shell_view = E_SHELL_VIEW (mail_shell_view); + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + + reader = E_MAIL_READER (mail_shell_content); + message_list = e_mail_reader_get_message_list (reader); + folder_uri = message_list->folder_uri; + folder = message_list->folder; + + local_store = e_mail_local_get_store (); + + /* If no folder is selected, reset the sidebar banners + * to their default values and stop. */ + if (folder == NULL) { + GtkAction *action; + gchar *label; + + action = e_shell_view_get_action (shell_view); + + g_object_get (action, "label", &label, NULL); + e_shell_sidebar_set_secondary_text (shell_sidebar, NULL); + e_shell_view_set_title (shell_view, label); + g_free (label); + + return; + } + + camel_object_get ( + folder, NULL, + CAMEL_FOLDER_NAME, &folder_name, + CAMEL_FOLDER_DELETED, &num_deleted, + CAMEL_FOLDER_JUNKED, &num_junked, + CAMEL_FOLDER_JUNKED_NOT_DELETED, &num_junked_not_deleted, + CAMEL_FOLDER_UNREAD, &num_unread, + CAMEL_FOLDER_VISIBLE, &num_visible, + NULL); + + buffer = g_string_sized_new (256); + selected = message_list_get_selected (message_list); + + if (selected->len > 1) + g_string_append_printf ( + buffer, ngettext ("%d selected, ", "%d selected, ", + selected->len), selected->len); + + if (CAMEL_IS_VTRASH_FOLDER (folder)) { + CamelVTrashFolder *trash_folder; + + trash_folder = (CamelVTrashFolder *) folder; + + /* "Trash" folder */ + if (trash_folder->type == CAMEL_VTRASH_FOLDER_TRASH) + g_string_append_printf ( + buffer, ngettext ("%d deleted", + "%d deleted", num_deleted), num_deleted); + + /* "Junk" folder (hide deleted messages) */ + else if (e_mail_reader_get_hide_deleted (reader)) + g_string_append_printf ( + buffer, ngettext ("%d junk", + "%d junk", num_junked_not_deleted), + num_junked_not_deleted); + + /* "Junk" folder (show deleted messages) */ + else + g_string_append_printf ( + buffer, ngettext ("%d junk", "%d junk", + num_junked), num_junked); + + /* "Drafts" folder */ + } else if (em_utils_folder_is_drafts (folder, folder_uri)) { + g_string_append_printf ( + buffer, ngettext ("%d draft", "%d drafts", + num_visible), num_visible); + + /* "Outbox" folder */ + } else if (em_utils_folder_is_outbox (folder, folder_uri)) { + g_string_append_printf ( + buffer, ngettext ("%d unsent", "%d unsent", + num_visible), num_visible); + + /* "Sent" folder */ + } else if (em_utils_folder_is_sent (folder, folder_uri)) { + g_string_append_printf ( + buffer, ngettext ("%d sent", "%d sent", + num_visible), num_visible); + + /* Normal folder */ + } else { + if (!e_mail_reader_get_hide_deleted (reader)) + num_visible += + num_deleted - num_junked + + num_junked_not_deleted; + + if (num_unread > 0 && selected->len <= 1) + g_string_append_printf ( + buffer, ngettext ("%d unread, ", + "%d unread, ", num_unread), num_unread); + g_string_append_printf ( + buffer, ngettext ("%d total", "%d total", + num_visible), num_visible); + } + + message_list_free_uids (message_list, selected); + + /* Choose a suitable folder name for displaying. */ + if (folder->parent_store == local_store && ( + strcmp (folder_name, "Drafts") == 0 || + strcmp (folder_name, "Inbox") == 0 || + strcmp (folder_name, "Outbox") == 0 || + strcmp (folder_name, "Sent") == 0 || + strcmp (folder_name, "Templates") == 0)) + display_name = _(folder_name); + else if (strcmp (folder_name, "INBOX") == 0) + display_name = _("Inbox"); + else + display_name = folder_name; + + title = g_strdup_printf ("%s (%s)", display_name, buffer->str); + e_shell_sidebar_set_secondary_text (shell_sidebar, buffer->str); + e_shell_view_set_title (shell_view, title); + g_free (title); + + camel_object_free (folder, CAMEL_FOLDER_NAME, folder_name); + g_string_free (buffer, TRUE); +} diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h new file mode 100644 index 0000000000..988d494219 --- /dev/null +++ b/modules/mail/e-mail-shell-view-private.h @@ -0,0 +1,173 @@ +/* + * e-mail-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_MAIL_SHELL_VIEW_PRIVATE_H +#define E_MAIL_SHELL_VIEW_PRIVATE_H + +#include "e-mail-shell-view.h" + +#include <glib/gi18n.h> +#include <gtkhtml/gtkhtml.h> +#include <camel/camel-disco-store.h> +#include <camel/camel-offline-store.h> +#include <camel/camel-vtrash-folder.h> +#include <camel/camel-search-private.h> /* for camel_search_word */ + +#include "e-util/e-util.h" +#include "e-util/e-binding.h" +#include "e-util/gconf-bridge.h" +#include "e-util/e-account-utils.h" +#include "filter/filter-part.h" +#include "widgets/misc/e-popup-action.h" +#include "widgets/menus/gal-view-instance.h" + +#include "e-mail-label-dialog.h" +#include "e-mail-label-list-store.h" +#include "e-mail-local.h" +#include "e-mail-reader.h" +#include "e-mail-store.h" +#include "em-composer-utils.h" +#include "em-folder-properties.h" +#include "em-folder-selector.h" +#include "em-folder-utils.h" +#include "em-subscribe-editor.h" +#include "em-utils.h" +#include "mail-autofilter.h" +#include "mail-config.h" +#include "mail-ops.h" +#include "mail-send-recv.h" +#include "mail-vfolder.h" + +#include "e-mail-shell-backend.h" +#include "e-mail-shell-content.h" +#include "e-mail-shell-sidebar.h" +#include "e-mail-shell-view-actions.h" + +#define E_MAIL_SHELL_VIEW_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_SHELL_VIEW, EMailShellViewPrivate)) + +/* 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 "message-list.etspec" + +G_BEGIN_DECLS + +/* Filter items are displayed in ascending order. + * Labels are numbered from zero, so subsequent items must have + * sufficiently large values. Unfortunately this introduces an + * arbitrary upper bound on labels. */ +enum { + MAIL_FILTER_ALL_MESSAGES = -3, + MAIL_FILTER_UNREAD_MESSAGES = -2, + MAIL_FILTER_NO_LABEL = -1, + /* Labels go here */ + MAIL_FILTER_READ_MESSAGES = 5000, + MAIL_FILTER_RECENT_MESSAGES = 5001, + MAIL_FILTER_LAST_5_DAYS_MESSAGES = 5002, + MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS = 5003, + MAIL_FILTER_IMPORTANT_MESSAGES = 5004, + MAIL_FILTER_MESSAGES_NOT_JUNK = 5005 +}; + +/* Search items are displayed in ascending order. */ +enum { + MAIL_SEARCH_SUBJECT_OR_SENDER_CONTAINS, + MAIL_SEARCH_SUBJECT_OR_RECIPIENTS_CONTAINS, + MAIL_SEARCH_RECIPIENTS_CONTAIN, + MAIL_SEARCH_MESSAGE_CONTAINS, + MAIL_SEARCH_SUBJECT_CONTAINS, + MAIL_SEARCH_SENDER_CONTAINS, + MAIL_SEARCH_BODY_CONTAINS, + MAIL_NUM_SEARCH_RULES +}; + +/* Scope items are displayed in ascending order. */ +enum { + MAIL_SCOPE_CURRENT_FOLDER, + MAIL_SCOPE_CURRENT_ACCOUNT, + MAIL_SCOPE_ALL_ACCOUNTS +}; + +struct _EMailShellViewPrivate { + + /*** Other Stuff ***/ + + /* These are just for convenience. */ + EMailShellBackend *mail_shell_backend; + EMailShellContent *mail_shell_content; + EMailShellSidebar *mail_shell_sidebar; + + /* For UI merging and unmerging. */ + guint merge_id; + guint label_merge_id; + + /* Filter rules correspond to the search entry menu. */ + FilterRule *search_rules[MAIL_NUM_SEARCH_RULES]; + + guint show_deleted : 1; +}; + +void e_mail_shell_view_private_init + (EMailShellView *mail_shell_view, + EShellViewClass *shell_view_class); +void e_mail_shell_view_private_constructed + (EMailShellView *mail_shell_view); +void e_mail_shell_view_private_dispose + (EMailShellView *mail_shell_view); +void e_mail_shell_view_private_finalize + (EMailShellView *mail_shell_view); + +/* Private Utilities */ + +void e_mail_shell_view_actions_init + (EMailShellView *mail_shell_view); +void e_mail_shell_view_restore_state + (EMailShellView *mail_shell_view); +void e_mail_shell_view_execute_search + (EMailShellView *mail_shell_view); +void e_mail_shell_view_create_filter_from_selected + (EMailShellView *mail_shell_view, + gint filter_type); +void e_mail_shell_view_create_vfolder_from_selected + (EMailShellView *mail_shell_view, + gint vfolder_type); +void e_mail_shell_view_update_popup_labels + (EMailShellView *mail_shell_view); +void e_mail_shell_view_update_search_filter + (EMailShellView *mail_shell_view); +void e_mail_shell_view_update_sidebar + (EMailShellView *mail_shell_view); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_VIEW_PRIVATE_H */ diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c new file mode 100644 index 0000000000..8d8b4aa2b3 --- /dev/null +++ b/modules/mail/e-mail-shell-view.c @@ -0,0 +1,260 @@ +/* + * e-mail-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-mail-shell-view-private.h" + +static gpointer parent_class; +static GType mail_shell_view_type; + +static void +mail_shell_view_dispose (GObject *object) +{ + e_mail_shell_view_private_dispose (E_MAIL_SHELL_VIEW (object)); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +mail_shell_view_finalize (GObject *object) +{ + e_mail_shell_view_private_finalize (E_MAIL_SHELL_VIEW (object)); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +mail_shell_view_constructed (GObject *object) +{ + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (parent_class)->constructed (object); + + e_mail_shell_view_private_constructed (E_MAIL_SHELL_VIEW (object)); +} + +static void +mail_shell_view_toggled (EShellView *shell_view) +{ + EMailShellViewPrivate *priv; + EShellWindow *shell_window; + GtkUIManager *ui_manager; + const gchar *basename; + gboolean view_is_active; + + /* Chain up to parent's toggled() method. */ + E_SHELL_VIEW_CLASS (parent_class)->toggled (shell_view); + + priv = E_MAIL_SHELL_VIEW_GET_PRIVATE (shell_view); + + shell_window = e_shell_view_get_shell_window (shell_view); + ui_manager = e_shell_window_get_ui_manager (shell_window); + view_is_active = e_shell_view_is_active (shell_view); + basename = E_MAIL_READER_UI_DEFINITION; + + if (view_is_active && priv->merge_id == 0) { + priv->merge_id = e_load_ui_definition (ui_manager, basename); + e_mail_reader_create_charset_menu ( + E_MAIL_READER (priv->mail_shell_content), + ui_manager, priv->merge_id); + } else if (!view_is_active && priv->merge_id != 0) { + gtk_ui_manager_remove_ui (ui_manager, priv->merge_id); + priv->merge_id = 0; + } + + gtk_ui_manager_ensure_update (ui_manager); +} + +static void +mail_shell_view_update_actions (EShellView *shell_view) +{ + EMailShellView *mail_shell_view; + EMailShellSidebar *mail_shell_sidebar; + EShellContent *shell_content; + EShellSidebar *shell_sidebar; + EShellWindow *shell_window; + EMFolderTree *folder_tree; + EAccount *account = NULL; + GtkAction *action; + const gchar *label; + gchar *uri; + gboolean sensitive; + guint32 state; + + /* Be descriptive. */ + gboolean account_is_groupwise; + gboolean folder_allows_children; + gboolean folder_can_be_deleted; + gboolean folder_is_junk; + gboolean folder_is_outbox; + gboolean folder_is_store; + gboolean folder_is_trash; + + mail_shell_view = E_MAIL_SHELL_VIEW (shell_view); + + shell_window = e_shell_view_get_shell_window (shell_view); + + shell_content = e_shell_view_get_shell_content (shell_view); + e_mail_reader_update_actions (E_MAIL_READER (shell_content)); + + mail_shell_sidebar = mail_shell_view->priv->mail_shell_sidebar; + folder_tree = e_mail_shell_sidebar_get_folder_tree (mail_shell_sidebar); + + shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); + state = e_shell_sidebar_check_state (shell_sidebar); + + folder_allows_children = + (state & E_MAIL_SHELL_SIDEBAR_FOLDER_ALLOWS_CHILDREN); + folder_can_be_deleted = + (state & E_MAIL_SHELL_SIDEBAR_FOLDER_CAN_DELETE); + folder_is_junk = + (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_JUNK); + folder_is_outbox = + (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_OUTBOX); + folder_is_store = + (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_STORE); + folder_is_trash = + (state & E_MAIL_SHELL_SIDEBAR_FOLDER_IS_TRASH); + + uri = em_folder_tree_get_selected_uri (folder_tree); + if (uri != NULL) { + account = mail_config_get_account_by_source_url (uri); + + /* FIXME This belongs in a GroupWise plugin. */ + account_is_groupwise = + (g_strrstr (uri, "groupwise://") != NULL) && + account != NULL && account->parent_uid != NULL; + + g_free (uri); + } + + action = ACTION (MAIL_ACCOUNT_DISABLE); + sensitive = (account != NULL) && folder_is_store; + if (account_is_groupwise) + label = _("Proxy _Logout"); + else + label = _("_Disable Account"); + gtk_action_set_sensitive (action, sensitive); + g_object_set (action, "label", label, NULL); + + action = ACTION (MAIL_EMPTY_TRASH); + sensitive = folder_is_trash; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FLUSH_OUTBOX); + sensitive = folder_is_outbox; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_COPY); + sensitive = !folder_is_store; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_DELETE); + sensitive = !folder_is_store && folder_can_be_deleted; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_MOVE); + sensitive = !folder_is_store && folder_can_be_deleted; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_NEW); + sensitive = folder_allows_children; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_PROPERTIES); + sensitive = !folder_is_store; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_REFRESH); + sensitive = !folder_is_store; + gtk_action_set_sensitive (action, sensitive); + + action = ACTION (MAIL_FOLDER_RENAME); + sensitive = !folder_is_store && folder_can_be_deleted; + gtk_action_set_sensitive (action, sensitive); + + e_mail_shell_view_update_popup_labels (mail_shell_view); +} + +static void +mail_shell_view_class_init (EMailShellViewClass *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 (EMailShellViewPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = mail_shell_view_dispose; + object_class->finalize = mail_shell_view_finalize; + object_class->constructed = mail_shell_view_constructed; + + shell_view_class = E_SHELL_VIEW_CLASS (class); + shell_view_class->label = _("Mail"); + shell_view_class->icon_name = "evolution-mail"; + shell_view_class->ui_definition = "evolution-mail.ui"; + shell_view_class->ui_manager_id = "org.gnome.evolution.mail"; + shell_view_class->search_options = "/mail-search-options"; + shell_view_class->search_rules = "searchtypes.xml"; + shell_view_class->new_shell_content = e_mail_shell_content_new; + shell_view_class->new_shell_sidebar = e_mail_shell_sidebar_new; + shell_view_class->toggled = mail_shell_view_toggled; + shell_view_class->update_actions = mail_shell_view_update_actions; +} + +static void +mail_shell_view_init (EMailShellView *mail_shell_view, + EShellViewClass *shell_view_class) +{ + mail_shell_view->priv = + E_MAIL_SHELL_VIEW_GET_PRIVATE (mail_shell_view); + + e_mail_shell_view_private_init (mail_shell_view, shell_view_class); +} + +GType +e_mail_shell_view_get_type (void) +{ + return mail_shell_view_type; +} + +void +e_mail_shell_view_register_type (GTypeModule *type_module) +{ + const GTypeInfo type_info = { + sizeof (EMailShellViewClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) mail_shell_view_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMailShellView), + 0, /* n_preallocs */ + (GInstanceInitFunc) mail_shell_view_init, + NULL /* value_table */ + }; + + mail_shell_view_type = g_type_module_register_type ( + type_module, E_TYPE_SHELL_VIEW, + "EMailShellView", &type_info, 0); +} diff --git a/modules/mail/e-mail-shell-view.h b/modules/mail/e-mail-shell-view.h new file mode 100644 index 0000000000..d20bde74a6 --- /dev/null +++ b/modules/mail/e-mail-shell-view.h @@ -0,0 +1,72 @@ +/* + * e-mail-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_MAIL_SHELL_VIEW_H +#define E_MAIL_SHELL_VIEW_H + +#include <shell/e-shell-view.h> + +/* Standard GObject macros */ +#define E_TYPE_MAIL_SHELL_VIEW \ + (e_mail_shell_view_get_type ()) +#define E_MAIL_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_SHELL_VIEW, EMailShellView)) +#define E_MAIL_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_SHELL_VIEW, EMailShellViewClass)) +#define E_IS_MAIL_SHELL_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_SHELL_VIEW)) +#define E_IS_MAIL_SHELL_VIEW_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_SHELL_VIEW)) +#define E_MAIL_SHELL_VIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_SHELL_VIEW, EMailShellViewClass)) + +G_BEGIN_DECLS + +typedef struct _EMailShellView EMailShellView; +typedef struct _EMailShellViewClass EMailShellViewClass; +typedef struct _EMailShellViewPrivate EMailShellViewPrivate; + +struct _EMailShellView { + EShellView parent; + EMailShellViewPrivate *priv; +}; + +struct _EMailShellViewClass { + EShellViewClass parent_class; +}; + +GType e_mail_shell_view_get_type (void); +void e_mail_shell_view_register_type + (GTypeModule *type_module); +gboolean e_mail_shell_view_get_show_deleted + (EMailShellView *mail_shell_view); +void e_mail_shell_view_set_show_deleted + (EMailShellView *mail_shell_view, + gboolean show_deleted); + +G_END_DECLS + +#endif /* E_MAIL_SHELL_VIEW_H */ diff --git a/modules/mail/em-account-editor.c b/modules/mail/em-account-editor.c new file mode 100644 index 0000000000..188a2d475f --- /dev/null +++ b/modules/mail/em-account-editor.c @@ -0,0 +1,3113 @@ +/* + * 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/> + * + * + * Authors: + * Dan Winship <danw@ximian.com> + * Jeffrey Stedfast <fejj@ximian.com> + * Michael Zucchi <notzed@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +/* + work before merge can occur: + + verify behaviour. + work out what to do with the startup druid. + + also need to work out: + how to remove unecessary items from a service url once + configured (removing settings from other types). + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <string.h> +#include <stdarg.h> + +#include <gconf/gconf-client.h> + +#include <glade/glade.h> + +#include <libgnomeui/gnome-druid.h> +#include <libgnomeui/gnome-druid-page-standard.h> + +#include "e-util/e-error.h" +#include "e-util/e-account-utils.h" +#include "e-util/e-signature-list.h" +#include "e-util/e-signature-utils.h" +#include "e-util/e-util-private.h" +#include "misc/e-signature-editor.h" + +#include "e-mail-local.h" +#include "em-config.h" +#include "em-folder-selection-button.h" +#include "em-account-editor.h" +#include "mail-session.h" +#include "mail-send-recv.h" +#include "em-utils.h" +#include "em-composer-prefs.h" +#include "mail-config.h" +#include "mail-ops.h" +#include "mail-mt.h" + +#if defined (HAVE_NSS) +#include "smime/gui/e-cert-selector.h" +#endif + +#define d(x) + +/* econfig item for the extra config hings */ +struct _receive_options_item { + EMConfigItem item; + + /* Only CAMEL_PROVIDER_CONF_ENTRYs GtkEntrys are stored here. + The auto-detect camel provider code will probably be removed */ + GHashTable *extra_table; +}; + +typedef struct _EMAccountEditorService { + EMAccountEditor *emae; /* parent pointer, for callbacks */ + + /* NOTE: keep all widgets together, first frame last check_dialog */ + GtkWidget *frame; + GtkWidget *container; + + GtkComboBox *providers; + + GtkLabel *description; + GtkLabel *hostlabel; + GtkEntry *hostname; + GtkLabel *userlabel; + GtkEntry *username; + GtkEntry *path; + GtkLabel *pathlabel; + GtkWidget *pathentry; + + GtkWidget *ssl_frame; + GtkComboBox *use_ssl; + GtkWidget *ssl_hbox; + GtkWidget *no_ssl; + + GtkWidget *auth_frame; + GtkComboBox *authtype; + + GtkWidget *authitem; + GtkToggleButton *remember; + GtkButton *check_supported; + GtkToggleButton *needs_auth; + + GtkWidget *check_dialog; + gint check_id; + + GList *authtypes; /* if "Check supported" */ + CamelProvider *provider; + CamelProviderType type; + + gint auth_changed_id; +} EMAccountEditorService; + +struct _EMAccountEditorPrivate { + struct _EMConfig *config; + GList *providers; + + /* signatures */ + GtkComboBox *signatures_dropdown; + guint sig_added_id; + guint sig_removed_id; + guint sig_changed_id; + const gchar *sig_uid; + + /* incoming mail */ + EMAccountEditorService source; + + /* extra incoming config */ + CamelProvider *extra_provider; + GSList *extra_items; /* this is freed by the econfig automatically */ + + /* outgoing mail */ + EMAccountEditorService transport; + + /* account management */ + GtkEntry *identity_entries[5]; + GtkToggleButton *default_account; + GtkWidget *management_frame; + + /* special folders */ + GtkButton *drafts_folder_button; + GtkButton *sent_folder_button; + GtkButton *restore_folders_button; + + /* Security */ + GtkEntry *pgp_key; + GtkToggleButton *pgp_encrypt_to_self; + GtkToggleButton *pgp_always_sign; + GtkToggleButton *pgp_no_imip_sign; + GtkToggleButton *pgp_always_trust; + + GtkToggleButton *smime_sign_default; + GtkEntry *smime_sign_key; + GtkButton *smime_sign_key_select; + GtkButton *smime_sign_key_clear; + GtkButton *smime_sign_select; + GtkToggleButton *smime_encrypt_default; + GtkToggleButton *smime_encrypt_to_self; + GtkEntry *smime_encrypt_key; + GtkButton *smime_encrypt_key_select; + GtkButton *smime_encrypt_key_clear; + + /* for e-config callbacks, each page sets up its widgets, then they are dealed out by the get_widget callback in order*/ + GtkWidget *widgets[5]; + const gchar *widgets_name[5]; + gint widgets_index; + + /* for druid page preparation */ + guint identity_set:1; + guint receive_set:1; + guint management_set:1; +}; + +static void emae_refresh_authtype(EMAccountEditor *emae, EMAccountEditorService *service); +static void em_account_editor_construct(EMAccountEditor *emae, EAccount *account, em_account_editor_t type, const gchar *id); +static void emae_account_folder_changed(EMFolderSelectionButton *folder, EMAccountEditor *emae); +static GtkVBoxClass *emae_parent; + +static void +emae_init(GObject *o) +{ + EMAccountEditor *emae = (EMAccountEditor *)o; + + emae->priv = g_malloc0(sizeof(*emae->priv)); + + emae->priv->source.emae = emae; + emae->priv->transport.emae = emae; +} + +static void +emae_finalise(GObject *o) +{ + EMAccountEditor *emae = (EMAccountEditor *)o; + EMAccountEditorPrivate *p = emae->priv; + + if (p->sig_added_id) { + ESignatureList *signatures = e_get_signature_list (); + + g_signal_handler_disconnect(signatures, p->sig_added_id); + g_signal_handler_disconnect(signatures, p->sig_removed_id); + g_signal_handler_disconnect(signatures, p->sig_changed_id); + } + + g_list_free(p->source.authtypes); + g_list_free(p->transport.authtypes); + + g_list_free(p->providers); + g_free(p); + + g_object_unref(emae->account); + if (emae->original) + g_object_unref(emae->original); + + ((GObjectClass *)emae_parent)->finalize(o); +} + +static void +emae_class_init(GObjectClass *klass) +{ + klass->finalize = emae_finalise; +} + +GType +em_account_editor_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(EMAccountEditorClass), + NULL, NULL, + (GClassInitFunc)emae_class_init, + NULL, NULL, + sizeof(EMAccountEditor), 0, + (GInstanceInitFunc)emae_init + }; + emae_parent = g_type_class_ref(G_TYPE_OBJECT); + type = g_type_register_static(G_TYPE_OBJECT, "EMAccountEditor", &info, 0); + } + + return type; +} + +/** + * em_account_editor_new: + * @account: + * @type: + * + * Create a new account editor. If @account is NULL then this is to + * create a new account, else @account is copied to a working + * structure and is for editing an existing account. + * + * Return value: + **/ +EMAccountEditor *em_account_editor_new(EAccount *account, em_account_editor_t type, const gchar *id) +{ + EMAccountEditor *emae = g_object_new(em_account_editor_get_type(), NULL); + + em_account_editor_construct(emae, account, type, id); + + return emae; +} + +/** + * em_account_editor_new_for_pages: + * @account: + * @type: + * + * Create a new account editor. If @account is NULL then this is to + * create a new account, else @account is copied to a working + * structure and is for editing an existing account. + * + * Return value: + **/ +EMAccountEditor *em_account_editor_new_for_pages(EAccount *account, em_account_editor_t type, gchar *id, GtkWidget **pages) +{ + EMAccountEditor *emae = g_object_new(em_account_editor_get_type(), NULL); + emae->pages = pages; + em_account_editor_construct(emae, account, type, id); + + return emae; +} + +/* ********************************************************************** */ + +static struct { + const gchar *label; + const gchar *value; +} ssl_options[] = { + /* Translators: This string is a "Use secure connection" option for + the Mailer. It will not use an encrypted connection. */ + { N_("No encryption"), "never" }, + /* Translators: This string is a "Use secure connection" option for + the Mailer. TLS (Transport Layer Security) is commonly known by + this abbreviation. */ + { N_("TLS encryption"), "when-possible" }, + /* Translators: This string is a "Use secure connection" option for + the Mailer. SSL (Secure Sockets Layer) is commonly known by this + abbreviation. */ + { N_("SSL encryption"), "always" } +}; + +#define num_ssl_options (sizeof (ssl_options) / sizeof (ssl_options[0])) + +static gboolean +is_email (const gchar *address) +{ + /* This is supposed to check if the address's domain could be + an FQDN but alas, it's not worth the pain and suffering. */ + const gchar *at; + + at = strchr (address, '@'); + /* make sure we have an '@' and that it's not the first or last gchar */ + if (!at || at == address || *(at + 1) == '\0') + return FALSE; + + return TRUE; +} + +static CamelURL * +emae_account_url(EMAccountEditor *emae, gint urlid) +{ + CamelURL *url = NULL; + const gchar *uri; + + uri = e_account_get_string(emae->account, urlid); + + if (uri && uri[0]) + url = camel_url_new(uri, NULL); + + if (url == NULL) { + url = camel_url_new("dummy:", NULL); + camel_url_set_protocol(url, NULL); + } + + return url; +} + +/* ********************************************************************** */ +static void +emae_license_state(GtkToggleButton *button, GtkDialog *dialog) +{ + gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT, + gtk_toggle_button_get_active(button)); +} + +static gboolean +emae_load_text(GtkTextView *view, const gchar *filename) +{ + FILE *fd; + gchar filebuf[1024]; + GtkTextIter iter; + GtkTextBuffer *buffer; + gint count; + + g_return_val_if_fail (filename != NULL , FALSE); + + fd = g_fopen (filename, "r"); + if (fd) { + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_get_start_iter (buffer, &iter); + while (!feof (fd) && !ferror (fd)) { + count = fread (filebuf, 1, sizeof (filebuf), fd); + gtk_text_buffer_insert (buffer, &iter, filebuf, count); + } + + gtk_text_view_set_buffer(GTK_TEXT_VIEW (view), GTK_TEXT_BUFFER(buffer)); + fclose (fd); + } + + return fd != NULL; +} + +static gboolean +emae_display_license(EMAccountEditor *emae, CamelProvider *prov) +{ + GladeXML *xml; + GtkWidget *w, *dialog; + gchar *tmp; + GtkResponseType response = GTK_RESPONSE_NONE; + gchar *gladefile; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-dialogs.glade", + NULL); + xml = glade_xml_new (gladefile, "license_dialog", NULL); + g_free (gladefile); + + dialog = glade_xml_get_widget(xml, "license_dialog"); + gtk_dialog_set_response_sensitive((GtkDialog *)dialog, GTK_RESPONSE_ACCEPT, FALSE); + tmp = g_strdup_printf(_("%s License Agreement"), prov->license); + gtk_window_set_title((GtkWindow *)dialog, tmp); + g_free(tmp); + + g_signal_connect(glade_xml_get_widget(xml, "license_checkbutton"), + "toggled", G_CALLBACK(emae_license_state), dialog); + + tmp = g_strdup_printf(_("\nPlease read carefully the license agreement\n" + "for %s displayed below\n" + "and tick the check box for accepting it\n"), prov->license); + gtk_label_set_text((GtkLabel *)glade_xml_get_widget(xml, "license_top_label"), tmp); + g_free(tmp); + + w = glade_xml_get_widget(xml, "license_textview"); + if (emae_load_text((GtkTextView *)w, prov->license_file)) { + gtk_text_view_set_editable((GtkTextView *)w, FALSE); + response = gtk_dialog_run((GtkDialog *)dialog); + } else { + e_error_run(emae->editor ? (GtkWindow *)gtk_widget_get_toplevel(emae->editor) : NULL, + "mail:no-load-license", prov->license_file, NULL); + } + + gtk_widget_destroy(dialog); + g_object_unref(xml); + + return (response == GTK_RESPONSE_ACCEPT); +} + +static gboolean +emae_check_license(EMAccountEditor *emae, CamelProvider *prov) +{ + gboolean accepted = TRUE; + + if (prov->flags & CAMEL_PROVIDER_HAS_LICENSE) { + GConfClient *gconf = mail_config_get_gconf_client(); + GSList *providers_list, *l; + + providers_list = gconf_client_get_list (gconf, "/apps/evolution/mail/licenses", GCONF_VALUE_STRING, NULL); + + for (l = providers_list, accepted = FALSE; l && !accepted; l = g_slist_next(l)) + accepted = (strcmp((gchar *)l->data, prov->protocol) == 0); + + if (!accepted + && (accepted = emae_display_license(emae, prov)) == TRUE) { + providers_list = g_slist_append(providers_list, g_strdup(prov->protocol)); + gconf_client_set_list(gconf, + "/apps/evolution/mail/licenses", + GCONF_VALUE_STRING, + providers_list, NULL); + } + + g_slist_foreach(providers_list, (GFunc)g_free, NULL); + g_slist_free(providers_list); + } + + return accepted; +} + +static void +default_folders_clicked (GtkButton *button, gpointer user_data) +{ + EMAccountEditor *emae = user_data; + const gchar *uri; + + uri = e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS); + em_folder_selection_button_set_selection((EMFolderSelectionButton *)emae->priv->drafts_folder_button, uri); + emae_account_folder_changed((EMFolderSelectionButton *)emae->priv->drafts_folder_button, emae); + + uri = e_mail_local_get_folder_uri (E_MAIL_FOLDER_SENT); + em_folder_selection_button_set_selection((EMFolderSelectionButton *)emae->priv->sent_folder_button, uri); + emae_account_folder_changed((EMFolderSelectionButton *)emae->priv->sent_folder_button, emae); +} + +/* custom widget factories */ +GtkWidget *em_account_editor_folder_selector_button_new (gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2); + +GtkWidget * +em_account_editor_folder_selector_button_new (gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2) +{ + return (GtkWidget *)em_folder_selection_button_new ( + string1 ? string1 : _("Select Folder"), NULL); +} + +GtkWidget *em_account_editor_dropdown_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2); + +GtkWidget * +em_account_editor_dropdown_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2) +{ + return gtk_combo_box_new (); +} + +GtkWidget *em_account_editor_ssl_selector_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2); + +GtkWidget * +em_account_editor_ssl_selector_new(gchar *widget_name, gchar *string1, gchar *string2, gint int1, gint int2) +{ + GtkComboBox *dropdown = (GtkComboBox *)gtk_combo_box_new(); + GtkCellRenderer *cell = gtk_cell_renderer_text_new(); + GtkListStore *store; + gint i; + GtkTreeIter iter; + + gtk_widget_show((GtkWidget *)dropdown); + + store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); + + for (i=0;i<num_ssl_options;i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, _(ssl_options[i].label), 1, ssl_options[i].value, -1); + } + + gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE); + gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL); + + gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store); + + return (GtkWidget *)dropdown; +} + +/* The camel provider auto-detect interface should be deprecated. + But it still needs to be replaced with something of similar functionality. + Just using the normal econfig plugin mechanism should be adequate. */ +static void +emae_auto_detect_free (gpointer key, gpointer value, gpointer user_data) +{ + g_free (key); + g_free (value); +} + +static void +emae_auto_detect(EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + EMAccountEditorService *service = &gui->source; + GHashTable *auto_detected; + GSList *l; + CamelProviderConfEntry *entries; + gchar *value; + gint i; + CamelURL *url; + + if (service->provider == NULL + || (entries = service->provider->extra_conf) == NULL) + return; + + d(printf("Running auto-detect\n")); + + url = emae_account_url(emae, E_ACCOUNT_SOURCE_URL); + camel_provider_auto_detect(service->provider, url, &auto_detected, NULL); + camel_url_free(url); + if (auto_detected == NULL) { + d(printf(" no values detected\n")); + return; + } + + for (i = 0; entries[i].type != CAMEL_PROVIDER_CONF_END; i++) { + struct _receive_options_item *item; + GtkWidget *w; + + if (entries[i].name == NULL + || (value = g_hash_table_lookup (auto_detected, entries[i].name)) == NULL) + continue; + + /* only 2 providers use this, and they only do it for 3 entries only */ + g_return_if_fail (entries[i].type == CAMEL_PROVIDER_CONF_ENTRY); + + w = NULL; + for (l = emae->priv->extra_items;l;l=g_slist_next(l)) { + item = l->data; + if (item->extra_table && (w = g_hash_table_lookup(item->extra_table, entries[i].name))) + break; + } + + gtk_entry_set_text((GtkEntry *)w, value?value:""); + } + + g_hash_table_foreach(auto_detected, emae_auto_detect_free, NULL); + g_hash_table_destroy(auto_detected); +} + +static gint +provider_compare (const CamelProvider *p1, const CamelProvider *p2) +{ + /* sort providers based on "location" (ie. local or remote) */ + if (p1->flags & CAMEL_PROVIDER_IS_REMOTE) { + if (p2->flags & CAMEL_PROVIDER_IS_REMOTE) + return 0; + return -1; + } else { + if (p2->flags & CAMEL_PROVIDER_IS_REMOTE) + return 1; + return 0; + } +} + +static void +emae_signature_added(ESignatureList *signatures, ESignature *sig, EMAccountEditor *emae) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_combo_box_get_model(emae->priv->signatures_dropdown); + + gtk_list_store_append((GtkListStore *)model, &iter); + gtk_list_store_set((GtkListStore *)model, &iter, 0, sig->autogen?_("Autogenerated"):sig->name, 1, sig->uid, -1); + + gtk_combo_box_set_active(emae->priv->signatures_dropdown, gtk_tree_model_iter_n_children(model, NULL)-1); +} + +static gint +emae_signature_get_iter(EMAccountEditor *emae, ESignature *sig, GtkTreeModel **modelp, GtkTreeIter *iter) +{ + GtkTreeModel *model; + gint found = 0; + + model = gtk_combo_box_get_model(emae->priv->signatures_dropdown); + *modelp = model; + if (!gtk_tree_model_get_iter_first(model, iter)) + return FALSE; + + do { + gchar *uid; + + gtk_tree_model_get(model, iter, 1, &uid, -1); + if (uid && !strcmp(uid, sig->uid)) + found = TRUE; + g_free(uid); + } while (!found && gtk_tree_model_iter_next(model, iter)); + + return found; +} + +static void +emae_signature_removed(ESignatureList *signatures, ESignature *sig, EMAccountEditor *emae) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + if (emae_signature_get_iter(emae, sig, &model, &iter)) + gtk_list_store_remove((GtkListStore *)model, &iter); +} + +static void +emae_signature_changed(ESignatureList *signatures, ESignature *sig, EMAccountEditor *emae) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + if (emae_signature_get_iter(emae, sig, &model, &iter)) + gtk_list_store_set((GtkListStore *)model, &iter, 0, sig->autogen?_("Autogenerated"):sig->name, -1); +} + +static void +emae_signaturetype_changed(GtkComboBox *dropdown, EMAccountEditor *emae) +{ + gint id = gtk_combo_box_get_active(dropdown); + GtkTreeModel *model; + GtkTreeIter iter; + gchar *uid = NULL; + + if (id != -1) { + model = gtk_combo_box_get_model(dropdown); + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) + gtk_tree_model_get(model, &iter, 1, &uid, -1); + } + + e_account_set_string(emae->account, E_ACCOUNT_ID_SIGNATURE, uid); + g_free(uid); +} + +static void +emae_signature_new(GtkWidget *w, EMAccountEditor *emae) +{ + EShell *shell; + EShellSettings *shell_settings; + GtkWidget *parent; + gboolean html_mode; + + shell = e_shell_get_default (); + shell_settings = e_shell_get_shell_settings (shell); + parent = gtk_widget_get_toplevel (w); + + html_mode = e_shell_settings_get_boolean ( + shell_settings, "composer-format-html"); + + em_composer_prefs_new_signature (GTK_WINDOW (parent), html_mode); +} + +static GtkWidget * +emae_setup_signatures(EMAccountEditor *emae, GladeXML *xml) +{ + EMAccountEditorPrivate *p = emae->priv; + GtkComboBox *dropdown = (GtkComboBox *)glade_xml_get_widget(xml, "signature_dropdown"); + GtkCellRenderer *cell = gtk_cell_renderer_text_new(); + GtkListStore *store; + gint i, active=0; + GtkTreeIter iter; + ESignatureList *signatures; + EIterator *it; + const gchar *current = e_account_get_string(emae->account, E_ACCOUNT_ID_SIGNATURE); + GtkWidget *button; + + emae->priv->signatures_dropdown = dropdown; + gtk_widget_show((GtkWidget *)dropdown); + + store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, _("None"), 1, NULL, -1); + + signatures = e_get_signature_list (); + + if (p->sig_added_id == 0) { + p->sig_added_id = g_signal_connect(signatures, "signature-added", G_CALLBACK(emae_signature_added), emae); + p->sig_removed_id = g_signal_connect(signatures, "signature-removed", G_CALLBACK(emae_signature_removed), emae); + p->sig_changed_id = g_signal_connect(signatures, "signature-changed", G_CALLBACK(emae_signature_changed), emae); + } + + /* we need to count the 'none' entry before using the index */ + i = 1; + it = e_list_get_iterator ((EList *) signatures); + while (e_iterator_is_valid (it)) { + ESignature *sig = (ESignature *)e_iterator_get(it); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, sig->autogen?_("Autogenerated"):sig->name, 1, sig->uid, -1); + + if (current && !strcmp(current, sig->uid)) + active = i; + + e_iterator_next(it); + i++; + } + g_object_unref (it); + + gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE); + gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL); + + gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store); + gtk_combo_box_set_active(dropdown, active); + + g_signal_connect(dropdown, "changed", G_CALLBACK(emae_signaturetype_changed), emae); + gtk_widget_set_sensitive((GtkWidget *)dropdown, e_account_writable(emae->account, E_ACCOUNT_ID_SIGNATURE)); + + button = glade_xml_get_widget(xml, "sigAddNew"); + g_signal_connect(button, "clicked", G_CALLBACK(emae_signature_new), emae); + gtk_widget_set_sensitive(button, + gconf_client_key_is_writable(mail_config_get_gconf_client(), + "/apps/evolution/mail/signatures", NULL)); + + return (GtkWidget *)dropdown; +} + +static void +emae_receipt_policy_changed(GtkComboBox *dropdown, EMAccountEditor *emae) +{ + gint id = gtk_combo_box_get_active(dropdown); + GtkTreeModel *model; + GtkTreeIter iter; + EAccountReceiptPolicy policy; + + if (id != -1) { + model = gtk_combo_box_get_model(dropdown); + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) { + gtk_tree_model_get(model, &iter, 1, &policy, -1); + e_account_set_int (emae->account, E_ACCOUNT_RECEIPT_POLICY, policy); + } + } + +} + +static GtkWidget * +emae_setup_receipt_policy (EMAccountEditor *emae, GladeXML *xml) +{ + GtkComboBox *dropdown = (GtkComboBox *)glade_xml_get_widget(xml, "receipt_policy_dropdown"); + GtkListStore *store; + gint i = 0, active = 0; + GtkTreeIter iter; + EAccountReceiptPolicy current = emae->account->receipt_policy; + static struct { + EAccountReceiptPolicy policy; + const gchar *label; + } receipt_policies[] = { + { E_ACCOUNT_RECEIPT_NEVER, N_("Never") }, + { E_ACCOUNT_RECEIPT_ALWAYS, N_("Always") }, + { E_ACCOUNT_RECEIPT_ASK, N_("Ask for each message") } + }; + + gtk_widget_show((GtkWidget *)dropdown); + + store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); + + for (i = 0; i < 3; ++i) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, _(receipt_policies[i].label), + 1, receipt_policies[i].policy, + -1); + if (current == receipt_policies[i].policy) + active = i; + } + + gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store); + gtk_combo_box_set_active(dropdown, active); + + g_signal_connect(dropdown, "changed", G_CALLBACK(emae_receipt_policy_changed), emae); + gtk_widget_set_sensitive((GtkWidget *)dropdown, e_account_writable(emae->account, E_ACCOUNT_RECEIPT_POLICY)); + + return (GtkWidget *)dropdown; +} + +static void +emae_account_entry_changed(GtkEntry *entry, EMAccountEditor *emae) +{ + gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)entry, "account-item")); + + e_account_set_string(emae->account, item, gtk_entry_get_text(entry)); +} + +static GtkEntry * +emae_account_entry(EMAccountEditor *emae, const gchar *name, gint item, GladeXML *xml) +{ + GtkEntry *entry; + const gchar *text; + + entry = (GtkEntry *)glade_xml_get_widget(xml, name); + text = e_account_get_string(emae->account, item); + if (text) + gtk_entry_set_text(entry, text); + g_object_set_data((GObject *)entry, "account-item", GINT_TO_POINTER(item)); + g_signal_connect(entry, "changed", G_CALLBACK(emae_account_entry_changed), emae); + gtk_widget_set_sensitive((GtkWidget *)entry, e_account_writable(emae->account, item)); + + return entry; +} + +static void +emae_account_toggle_changed(GtkToggleButton *toggle, EMAccountEditor *emae) +{ + gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)toggle, "account-item")); + + e_account_set_bool(emae->account, item, gtk_toggle_button_get_active(toggle)); +} + +static void +emae_account_toggle_widget(EMAccountEditor *emae, GtkToggleButton *toggle, gint item) +{ + gtk_toggle_button_set_active(toggle, e_account_get_bool(emae->account, item)); + g_object_set_data((GObject *)toggle, "account-item", GINT_TO_POINTER(item)); + g_signal_connect(toggle, "toggled", G_CALLBACK(emae_account_toggle_changed), emae); + gtk_widget_set_sensitive((GtkWidget *)toggle, e_account_writable(emae->account, item)); +} + +static GtkToggleButton * +emae_account_toggle(EMAccountEditor *emae, const gchar *name, gint item, GladeXML *xml) +{ + GtkToggleButton *toggle; + + toggle = (GtkToggleButton *)glade_xml_get_widget(xml, name); + emae_account_toggle_widget(emae, toggle, item); + + return toggle; +} + +static void +emae_account_spinint_changed(GtkSpinButton *spin, EMAccountEditor *emae) +{ + gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)spin, "account-item")); + + e_account_set_int(emae->account, item, gtk_spin_button_get_value(spin)); +} + +static void +emae_account_spinint_widget(EMAccountEditor *emae, GtkSpinButton *spin, gint item) +{ + gtk_spin_button_set_value(spin, e_account_get_int(emae->account, item)); + g_object_set_data((GObject *)spin, "account-item", GINT_TO_POINTER(item)); + g_signal_connect(spin, "value_changed", G_CALLBACK(emae_account_spinint_changed), emae); + gtk_widget_set_sensitive((GtkWidget *)spin, e_account_writable(emae->account, item)); +} + +#if 0 +static GtkSpinButton * +emae_account_spinint(EMAccountEditor *emae, const gchar *name, gint item) +{ + GtkSpinButton *spin; + + spin = (GtkSpinButton *)glade_xml_get_widget(emae->priv->xml, name); + emae_account_spinint_widget(emae, spin, item); + + return spin; +} +#endif + +static void +emae_account_folder_changed(EMFolderSelectionButton *folder, EMAccountEditor *emae) +{ + gint item = GPOINTER_TO_INT(g_object_get_data((GObject *)folder, "account-item")); + + e_account_set_string(emae->account, item, em_folder_selection_button_get_selection(folder)); +} + +static EMFolderSelectionButton * +emae_account_folder(EMAccountEditor *emae, const gchar *name, gint item, gint deffolder, GladeXML *xml) +{ + EMFolderSelectionButton *folder; + const gchar *uri; + + folder = (EMFolderSelectionButton *)glade_xml_get_widget(xml, name); + uri = e_account_get_string(emae->account, item); + if (uri) { + gchar *tmp = em_uri_to_camel(uri); + + em_folder_selection_button_set_selection(folder, tmp); + g_free(tmp); + } else { + const gchar *uri; + + uri = e_mail_local_get_folder_uri (deffolder); + em_folder_selection_button_set_selection(folder, uri); + } + + g_object_set_data((GObject *)folder, "account-item", GINT_TO_POINTER(item)); + g_object_set_data((GObject *)folder, "folder-default", GINT_TO_POINTER(deffolder)); + g_signal_connect(folder, "selected", G_CALLBACK(emae_account_folder_changed), emae); + gtk_widget_show((GtkWidget *)folder); + + gtk_widget_set_sensitive((GtkWidget *)folder, e_account_writable(emae->account, item)); + + return folder; +} + +#if defined (HAVE_NSS) +static void +smime_changed(EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + gint act; + const gchar *tmp; + + tmp = gtk_entry_get_text(gui->smime_sign_key); + act = tmp && tmp[0]; + gtk_widget_set_sensitive((GtkWidget *)gui->smime_sign_key_clear, act); + gtk_widget_set_sensitive((GtkWidget *)gui->smime_sign_default, act); + if (!act) + gtk_toggle_button_set_active(gui->smime_sign_default, FALSE); + + tmp = gtk_entry_get_text(gui->smime_encrypt_key); + act = tmp && tmp[0]; + gtk_widget_set_sensitive((GtkWidget *)gui->smime_encrypt_key_clear, act); + gtk_widget_set_sensitive((GtkWidget *)gui->smime_encrypt_default, act); + gtk_widget_set_sensitive((GtkWidget *)gui->smime_encrypt_to_self, act); + if (!act) { + gtk_toggle_button_set_active(gui->smime_encrypt_default, FALSE); + gtk_toggle_button_set_active(gui->smime_encrypt_to_self, FALSE); + } +} + +static void +smime_sign_key_selected(GtkWidget *dialog, const gchar *key, EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + + if (key != NULL) { + gtk_entry_set_text(gui->smime_sign_key, key); + smime_changed(emae); + } + + gtk_widget_destroy(dialog); +} + +static void +smime_sign_key_select(GtkWidget *button, EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + GtkWidget *w; + + w = e_cert_selector_new(E_CERT_SELECTOR_SIGNER, gtk_entry_get_text(gui->smime_sign_key)); + gtk_window_set_modal((GtkWindow *)w, TRUE); + gtk_window_set_transient_for((GtkWindow *)w, (GtkWindow *)gtk_widget_get_toplevel(button)); + g_signal_connect(w, "selected", G_CALLBACK(smime_sign_key_selected), emae); + gtk_widget_show(w); +} + +static void +smime_sign_key_clear(GtkWidget *w, EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + + gtk_entry_set_text(gui->smime_sign_key, ""); + smime_changed(emae); +} + +static void +smime_encrypt_key_selected(GtkWidget *dialog, const gchar *key, EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + + if (key != NULL) { + gtk_entry_set_text(gui->smime_encrypt_key, key); + smime_changed(emae); + } + + gtk_widget_destroy(dialog); +} + +static void +smime_encrypt_key_select(GtkWidget *button, EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + GtkWidget *w; + + w = e_cert_selector_new(E_CERT_SELECTOR_SIGNER, gtk_entry_get_text(gui->smime_encrypt_key)); + gtk_window_set_modal((GtkWindow *)w, TRUE); + gtk_window_set_transient_for((GtkWindow *)w, (GtkWindow *)gtk_widget_get_toplevel(button)); + g_signal_connect(w, "selected", G_CALLBACK(smime_encrypt_key_selected), emae); + gtk_widget_show(w); +} + +static void +smime_encrypt_key_clear(GtkWidget *w, EMAccountEditor *emae) +{ + EMAccountEditorPrivate *gui = emae->priv; + + gtk_entry_set_text(gui->smime_encrypt_key, ""); + smime_changed(emae); +} +#endif + +static void +emae_url_set_hostport(CamelURL *url, const gchar *txt) +{ + const gchar *port; + gchar *host; + + /* FIXME: what if this was a raw IPv6 address? */ + if (txt && (port = strchr(txt, ':'))) { + camel_url_set_port(url, atoi(port+1)); + host = g_strdup(txt); + host[port-txt] = 0; + } else { + /* "" is converted to NULL, but if we set NULL on the url, + camel_url_to_string strips lots of details */ + host = g_strdup((txt?txt:"")); + camel_url_set_port (url, 0); + } + + g_strstrip(host); + if (txt && *txt) + camel_url_set_host(url, host); + + g_free(host); +} + +/* This is used to map a funciton which will set on the url a string value. + if widgets[0] is set, it is the entry which will be called against setval() + We need our own function for host:port decoding, as above */ +struct _provider_host_info { + guint32 flag; + void (*setval)(CamelURL *, const gchar *); + glong widgets[3]; +}; + +static struct _provider_host_info emae_source_host_info[] = { + { CAMEL_URL_PART_HOST, emae_url_set_hostport, { G_STRUCT_OFFSET(EMAccountEditorService, hostname), G_STRUCT_OFFSET(EMAccountEditorService, hostlabel), }, }, + { CAMEL_URL_PART_USER, camel_url_set_user, { G_STRUCT_OFFSET(EMAccountEditorService, username), G_STRUCT_OFFSET(EMAccountEditorService, userlabel), } }, + { CAMEL_URL_PART_PATH, camel_url_set_path, { G_STRUCT_OFFSET(EMAccountEditorService, path), G_STRUCT_OFFSET(EMAccountEditorService, pathlabel), G_STRUCT_OFFSET(EMAccountEditorService, pathentry) }, }, + { CAMEL_URL_PART_AUTH, NULL, { 0, G_STRUCT_OFFSET(EMAccountEditorService, auth_frame), }, }, + { 0 }, +}; + +static struct _provider_host_info emae_transport_host_info[] = { + { CAMEL_URL_PART_HOST, emae_url_set_hostport, { G_STRUCT_OFFSET(EMAccountEditorService, hostname), G_STRUCT_OFFSET(EMAccountEditorService, hostlabel), }, }, + { CAMEL_URL_PART_USER, camel_url_set_user, { G_STRUCT_OFFSET(EMAccountEditorService, username), G_STRUCT_OFFSET(EMAccountEditorService, userlabel), } }, + { CAMEL_URL_PART_AUTH, NULL, { 0, G_STRUCT_OFFSET(EMAccountEditorService, auth_frame), }, }, + { 0 }, +}; + +/* This is used to map each of the two services in a typical account to the widgets that represent each service. + i.e. the receiving (source) service, and the sending (transport) service. + It is used throughout the following code to drive each page */ +static struct _service_info { + gint account_uri_key; + gint save_passwd_key; + + const gchar *frame; + const gchar *type_dropdown; + + const gchar *container; + const gchar *description; + const gchar *hostname; + const gchar *hostlabel; + const gchar *username; + const gchar *userlabel; + const gchar *path; + const gchar *pathlabel; + const gchar *pathentry; + + const gchar *security_frame; + const gchar *ssl_hbox; + const gchar *use_ssl; + const gchar *ssl_disabled; + + const gchar *needs_auth; + const gchar *auth_frame; + + const gchar *authtype; + const gchar *authtype_check; + + const gchar *remember_password; + + struct _provider_host_info *host_info; +} emae_service_info[CAMEL_NUM_PROVIDER_TYPES] = { + { E_ACCOUNT_SOURCE_URL, E_ACCOUNT_SOURCE_SAVE_PASSWD, + "source_frame", "source_type_dropdown", + "source_vbox", "source_description", "source_host", "source_host_label", "source_user", "source_user_label", "source_path", "source_path_label", "source_path_entry", + "source_security_frame", "source_ssl_hbox", "source_use_ssl", "source_ssl_disabled", + NULL, "source_auth_frame", + "source_auth_dropdown", "source_check_supported", + "source_remember_password", + emae_source_host_info, + }, + { E_ACCOUNT_TRANSPORT_URL, E_ACCOUNT_TRANSPORT_SAVE_PASSWD, + "transport_frame", "transport_type_dropdown", + "transport_vbox", "transport_description", "transport_host", "transport_host_label", "transport_user", "transport_user_label", NULL, NULL, NULL, + "transport_security_frame", "transport_ssl_hbox", "transport_use_ssl", "transport_ssl_disabled", + "transport_needs_auth", "transport_auth_frame", + "transport_auth_dropdown", "transport_check_supported", + "transport_remember_password", + emae_transport_host_info, + }, +}; + +static void +emae_uri_changed(EMAccountEditorService *service, CamelURL *url) +{ + gchar *uri; + + uri = camel_url_to_string(url, 0); + + e_account_set_string(service->emae->account, emae_service_info[service->type].account_uri_key, uri); + + /* small hack for providers which are store and transport - copy settings across */ + if (service->type == CAMEL_PROVIDER_STORE + && service->provider + && CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT(service->provider)) + e_account_set_string(service->emae->account, E_ACCOUNT_TRANSPORT_URL, uri); + + g_free(uri); +} + +static void +emae_service_url_changed(EMAccountEditorService *service, void (*setval)(CamelURL *, const gchar *), GtkEntry *entry) +{ + GtkComboBox *dropdown; + gint id; + GtkTreeModel *model; + GtkTreeIter iter; + CamelServiceAuthType *authtype; + + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + const gchar *text = gtk_entry_get_text(entry); + + setval(url, (text && text[0])?text:NULL); + + if (text && text[0] && setval == camel_url_set_user) { + dropdown = service->authtype; + if(dropdown) { + id = gtk_combo_box_get_active (dropdown); + if (id != -1) { + model = gtk_combo_box_get_model (dropdown); + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, id)) { + gtk_tree_model_get (model, &iter, 1, &authtype, -1); + if (authtype) + camel_url_set_authmech (url, authtype->authproto); + } + } + } + } + + emae_uri_changed(service, url); + camel_url_free(url); +} + +static void +emae_service_url_path_changed(EMAccountEditorService *service, void (*setval)(CamelURL *, const gchar *), GtkWidget *widget) +{ + GtkComboBox *dropdown; + gint id; + GtkTreeModel *model; + GtkTreeIter iter; + CamelServiceAuthType *authtype; + + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + const gchar *text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget)); + + setval(url, (text && text[0])?text:NULL); + + if (text && text[0] && setval == camel_url_set_user) { + dropdown = service->authtype; + if(dropdown) { + id = gtk_combo_box_get_active (dropdown); + if (id != -1) { + model = gtk_combo_box_get_model (dropdown); + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, id)) { + gtk_tree_model_get (model, &iter, 1, &authtype, -1); + if (authtype) + camel_url_set_authmech (url, authtype->authproto); + } + } + } + } + + emae_uri_changed(service, url); + camel_url_free(url); +} + +static void +emae_hostname_changed(GtkEntry *entry, EMAccountEditorService *service) +{ + emae_service_url_changed(service, emae_url_set_hostport, entry); +} + +static void +emae_username_changed(GtkEntry *entry, EMAccountEditorService *service) +{ + emae_service_url_changed(service, camel_url_set_user, entry); +} + +static void +emae_path_changed(GtkWidget *widget, EMAccountEditorService *service) +{ + emae_service_url_path_changed(service, camel_url_set_path, widget); +} + +static gint +emae_ssl_update(EMAccountEditorService *service, CamelURL *url) +{ + gint id = gtk_combo_box_get_active(service->use_ssl); + GtkTreeModel *model; + GtkTreeIter iter; + gchar *ssl; + + if (id == -1) + return 0; + + model = gtk_combo_box_get_model(service->use_ssl); + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) { + gtk_tree_model_get(model, &iter, 1, &ssl, -1); + if (!strcmp(ssl, "none")) + ssl = NULL; + camel_url_set_param(url, "use_ssl", ssl); + return 1; + } + + return 0; +} + +static void +emae_ssl_changed(GtkComboBox *dropdown, EMAccountEditorService *service) +{ + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + if (emae_ssl_update(service, url)) + emae_uri_changed(service, url); + camel_url_free(url); +} + +static void +emae_service_provider_changed(EMAccountEditorService *service) +{ + gint i, j; + void (*show)(GtkWidget *); + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + if (service->provider) { + gint enable; + GtkWidget *dwidget = NULL; + + camel_url_set_protocol(url, service->provider->protocol); + gtk_label_set_text(service->description, service->provider->description); + if (!emae_check_license(service->emae, service->provider)) + gtk_widget_hide(service->frame); + else + gtk_widget_show(service->frame); + + enable = e_account_writable_option(service->emae->account, service->provider->protocol, "auth"); + gtk_widget_set_sensitive((GtkWidget *)service->authtype, enable); + gtk_widget_set_sensitive((GtkWidget *)service->check_supported, enable); + + enable = e_account_writable_option(service->emae->account, service->provider->protocol, "use_ssl"); + gtk_widget_set_sensitive((GtkWidget *)service->use_ssl, enable); + + enable = e_account_writable(service->emae->account, emae_service_info[service->type].save_passwd_key); + gtk_widget_set_sensitive((GtkWidget *)service->remember, enable); + + for (i=0;emae_service_info[service->type].host_info[i].flag;i++) { + GtkWidget *w; + gint hide; + struct _provider_host_info *info = &emae_service_info[service->type].host_info[i]; + + enable = CAMEL_PROVIDER_ALLOWS(service->provider, info->flag); + hide = CAMEL_PROVIDER_HIDDEN(service->provider, info->flag); + show = (enable && !hide)?gtk_widget_show:gtk_widget_hide; + + for (j=0; j < sizeof(info->widgets)/sizeof(info->widgets[0]); j++) { + if (info->widgets[j] && (w = G_STRUCT_MEMBER(GtkWidget *, service, info->widgets[j]))) { + show(w); + if (j == 0) { + if (dwidget == NULL && enable) + dwidget = w; + + if (info->setval && !hide) + info->setval(url, enable?gtk_entry_get_text((GtkEntry *)w):NULL); + } + } + } + } + + if (dwidget) + gtk_widget_grab_focus(dwidget); + + if (CAMEL_PROVIDER_ALLOWS(service->provider, CAMEL_URL_PART_AUTH)) { + GList *ll; + + /* try to keep the authmech from the current url, or clear it */ + if (url->authmech) { + if (service->provider->authtypes) { + for (ll = service->provider->authtypes;ll;ll = g_list_next(ll)) + if (!strcmp(url->authmech, ((CamelServiceAuthType *)ll->data)->authproto)) + break; + if (ll == NULL) + camel_url_set_authmech(url, NULL); + } else { + camel_url_set_authmech(url, NULL); + } + } + + emae_refresh_authtype(service->emae, service); + if (service->needs_auth && !CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_AUTH)) + gtk_widget_show((GtkWidget *)service->needs_auth); + } else { + if (service->needs_auth) + gtk_widget_hide((GtkWidget *)service->needs_auth); + } +#ifdef HAVE_SSL + gtk_widget_hide(service->no_ssl); + if (service->provider->flags & CAMEL_PROVIDER_SUPPORTS_SSL) { + emae_ssl_update(service, url); + show = gtk_widget_show; + } else { + camel_url_set_param(url, "use_ssl", NULL); + show = gtk_widget_hide; + } + show(service->ssl_frame); + show(service->ssl_hbox); +#else + gtk_widget_hide(service->ssl_hbox); + gtk_widget_show(service->no_ssl); + camel_url_set_param(url, "use_ssl", NULL); +#endif + } else { + camel_url_set_protocol(url, NULL); + gtk_label_set_text(service->description, ""); + gtk_widget_hide(service->frame); + gtk_widget_hide(service->auth_frame); + gtk_widget_hide(service->ssl_frame); + } + + /* FIXME: linked services? */ + /* FIXME: permissions setup */ + + emae_uri_changed(service, url); + camel_url_free(url); +} + +static void +emae_provider_changed(GtkComboBox *dropdown, EMAccountEditorService *service) +{ + gint id = gtk_combo_box_get_active(dropdown); + GtkTreeModel *model; + GtkTreeIter iter; + + if (id == -1) + return; + + model = gtk_combo_box_get_model(dropdown); + if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) + return; + + gtk_tree_model_get(model, &iter, 1, &service->provider, -1); + + g_list_free(service->authtypes); + service->authtypes = NULL; + + emae_service_provider_changed(service); + + e_config_target_changed((EConfig *)service->emae->priv->config, E_CONFIG_TARGET_CHANGED_REBUILD); +} + +static void +emae_refresh_providers(EMAccountEditor *emae, EMAccountEditorService *service) +{ + EAccount *account = emae->account; + GtkListStore *store; + GtkTreeIter iter; + GList *l; + GtkCellRenderer *cell = gtk_cell_renderer_text_new(); + GtkComboBox *dropdown; + gint active = 0, i; + struct _service_info *info = &emae_service_info[service->type]; + const gchar *uri = e_account_get_string(account, info->account_uri_key); + gchar *current = NULL; + const gchar *tmp; + CamelURL *url; + + dropdown = service->providers; + gtk_widget_show((GtkWidget *)dropdown); + + if (uri) { + const gchar *colon = strchr(uri, ':'); + gint len; + + if (colon) { + len = colon-uri; + current = g_alloca(len+1); + memcpy(current, uri, len); + current[len] = 0; + } + } else { + current = (gchar *) "imap"; + } + + store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); + + i = 0; + + /* We just special case each type here, its just easier */ + if (service->type == CAMEL_PROVIDER_STORE) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, _("None"), 1, NULL, -1); + i++; + } + + for (l=emae->priv->providers; l; l=l->next) { + CamelProvider *provider = l->data; + + if (!((strcmp(provider->domain, "mail") == 0 + || strcmp (provider->domain, "news") == 0) + && provider->object_types[service->type] + && (service->type != CAMEL_PROVIDER_STORE || (provider->flags & CAMEL_PROVIDER_IS_SOURCE) != 0)) + /* hardcode not showing providers who's transport is done in the store */ + || (service->type == CAMEL_PROVIDER_TRANSPORT + && CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT (provider))) + continue; + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, provider->name, 1, provider, -1); + + /* find the displayed and set default */ + if (i == 0 || (current && strcmp(provider->protocol, current) == 0)) { + service->provider = provider; + active = i; + + /* we need to set this value on the uri too */ + if (current == NULL) { + CamelURL *url = emae_account_url(emae, info->account_uri_key); + + camel_url_set_protocol(url, provider->protocol); + emae_uri_changed(service, url); + camel_url_free(url); + } + } + i++; + } + + + gtk_cell_layout_clear((GtkCellLayout *)dropdown); + gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store); + gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE); + gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, NULL); + + g_signal_handlers_disconnect_by_func(dropdown, emae_provider_changed, service); + gtk_combo_box_set_active(dropdown, -1); /* needed for gtkcombo bug(?) */ + gtk_combo_box_set_active(dropdown, active); + g_signal_connect(dropdown, "changed", G_CALLBACK(emae_provider_changed), service); + + if (!uri || (url = camel_url_new(uri, NULL)) == NULL) { + return; + } + + tmp = camel_url_get_param(url, "use_ssl"); + if (tmp == NULL) + tmp = "never"; + for (i=0;i<num_ssl_options;i++) { + if (!strcmp(ssl_options[i].value, tmp)) { + gtk_combo_box_set_active(service->use_ssl, i); + break; + } + } +} + +static void +emae_authtype_changed(GtkComboBox *dropdown, EMAccountEditorService *service) +{ + gint id = gtk_combo_box_get_active(dropdown); + GtkTreeModel *model; + GtkTreeIter iter; + CamelServiceAuthType *authtype; + CamelURL *url; + + if (id == -1) + return; + + url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + model = gtk_combo_box_get_model(dropdown); + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) { + gtk_tree_model_get(model, &iter, 1, &authtype, -1); + if (authtype) + camel_url_set_authmech(url, authtype->authproto); + else + camel_url_set_authmech(url, NULL); + emae_uri_changed(service, url); + } + camel_url_free(url); + + gtk_widget_set_sensitive((GtkWidget *)service->remember, + authtype + ?(authtype->need_password && e_account_writable(service->emae->account, emae_service_info[service->type].save_passwd_key)) + :FALSE); +} + +static void +emae_needs_auth(GtkToggleButton *toggle, EMAccountEditorService *service) +{ + gint need = gtk_toggle_button_get_active(toggle); + + gtk_widget_set_sensitive(service->auth_frame, need); + + if (need) + emae_authtype_changed(service->authtype, service); + else { + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + camel_url_set_authmech(url, NULL); + emae_uri_changed(service, url); + camel_url_free(url); + } +} + +static void emae_check_authtype(GtkWidget *w, EMAccountEditorService *service); + +static void +emae_refresh_authtype (EMAccountEditor *emae, EMAccountEditorService *service) +{ + EAccount *account = emae->account; + GtkListStore *store; + GtkTreeIter iter; + GtkComboBox *dropdown; + gint active = 0; + gint i; + struct _service_info *info = &emae_service_info[service->type]; + const gchar *uri = e_account_get_string(account, info->account_uri_key); + GList *l, *ll; + CamelURL *url = NULL; + + dropdown = service->authtype; + gtk_widget_show((GtkWidget *)dropdown); + + store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN); + + if (uri) + url = camel_url_new(uri, NULL); + + if (service->provider) { + for (i=0, l=service->provider->authtypes; l; l=l->next, i++) { + CamelServiceAuthType *authtype = l->data; + gint avail; + + /* if we have some already shown */ + if (service->authtypes) { + for (ll = service->authtypes;ll;ll = g_list_next(ll)) + if (!strcmp(authtype->authproto, ((CamelServiceAuthType *)ll->data)->authproto)) + break; + avail = ll != NULL; + } else { + avail = TRUE; + } + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, authtype->name, 1, authtype, 2, !avail, -1); + + if (url && url->authmech && !strcmp(url->authmech, authtype->authproto)) + active = i; + } + } + + gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store); + gtk_combo_box_set_active(dropdown, -1); + + if (service->auth_changed_id == 0) { + GtkCellRenderer *cell = gtk_cell_renderer_text_new(); + + gtk_cell_layout_pack_start((GtkCellLayout *)dropdown, cell, TRUE); + gtk_cell_layout_set_attributes((GtkCellLayout *)dropdown, cell, "text", 0, "strikethrough", 2, NULL); + + service->auth_changed_id = g_signal_connect(dropdown, "changed", G_CALLBACK(emae_authtype_changed), service); + g_signal_connect(service->check_supported, "clicked", G_CALLBACK(emae_check_authtype), service); + } + + gtk_combo_box_set_active(dropdown, active); + + if (url) + camel_url_free(url); +} + +static void emae_check_authtype_done(const gchar *uri, CamelProviderType type, GList *types, gpointer data) +{ + EMAccountEditorService *service = data; + + if (service->check_dialog) { + if (service->authtypes) + g_list_free(service->authtypes); + + service->authtypes = g_list_copy(types); + emae_refresh_authtype(service->emae, service); + gtk_widget_destroy(service->check_dialog); + } + + if (service->emae->editor) + gtk_widget_set_sensitive(service->emae->editor, TRUE); + + service->check_id = -1; + g_object_unref(service->emae); +} + +static void emae_check_authtype_response(GtkWidget *d, gint button, EMAccountEditorService *service) +{ + mail_msg_cancel(service->check_id); + gtk_widget_destroy(service->check_dialog); + service->check_dialog = NULL; + + if (service->emae->editor) + gtk_widget_set_sensitive(service->emae->editor, TRUE); +} + +static void emae_check_authtype(GtkWidget *w, EMAccountEditorService *service) +{ + EMAccountEditor *emae = service->emae; + const gchar *uri; + + /* TODO: do we need to remove the auth mechanism from the uri? */ + uri = e_account_get_string(emae->account, emae_service_info[service->type].account_uri_key); + g_object_ref(emae); + + service->check_dialog = e_error_new(emae->editor ? (GtkWindow *)gtk_widget_get_toplevel(emae->editor) : NULL, + "mail:checking-service", NULL); + g_signal_connect(service->check_dialog, "response", G_CALLBACK(emae_check_authtype_response), service); + gtk_widget_show(service->check_dialog); + if (emae->editor) + gtk_widget_set_sensitive(emae->editor, FALSE); + service->check_id = mail_check_service(uri, service->type, emae_check_authtype_done, service); +} + +static void +emae_setup_service(EMAccountEditor *emae, EMAccountEditorService *service, GladeXML *xml) +{ + struct _service_info *info = &emae_service_info[service->type]; + CamelURL *url = emae_account_url(emae, info->account_uri_key); + const gchar *uri = e_account_get_string(emae->account, info->account_uri_key); + const gchar *tmp; + gint i; + + service->provider = uri?camel_provider_get(uri, NULL):NULL; + service->frame = glade_xml_get_widget(xml, info->frame); + service->container = glade_xml_get_widget(xml, info->container); + service->description = GTK_LABEL (glade_xml_get_widget (xml, info->description)); + service->hostname = GTK_ENTRY (glade_xml_get_widget (xml, info->hostname)); + service->hostlabel = (GtkLabel *)glade_xml_get_widget (xml, info->hostlabel); + service->username = GTK_ENTRY (glade_xml_get_widget (xml, info->username)); + service->userlabel = (GtkLabel *)glade_xml_get_widget (xml, info->userlabel); + if (info->pathentry) { + service->pathlabel = (GtkLabel *)glade_xml_get_widget(xml, info->pathlabel); + service->pathentry = glade_xml_get_widget(xml, info->pathentry); + } + + service->ssl_frame = glade_xml_get_widget (xml, info->security_frame); + gtk_widget_hide (service->ssl_frame); + service->ssl_hbox = glade_xml_get_widget (xml, info->ssl_hbox); + service->use_ssl = (GtkComboBox *)glade_xml_get_widget (xml, info->use_ssl); + service->no_ssl = glade_xml_get_widget (xml, info->ssl_disabled); + + /* configure ui for current settings */ + if (url->host) { + if (url->port) { + gchar *host = g_strdup_printf("%s:%d", url->host, url->port); + + gtk_entry_set_text(service->hostname, host); + g_free(host); + } else + gtk_entry_set_text(service->hostname, url->host); + } + if (url->user && *url->user) { + gtk_entry_set_text(service->username, url->user); + } + if (service->pathentry) { + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + + if (service->provider && (service->provider->url_flags & CAMEL_URL_NEED_PATH_DIR) == 0) + action = GTK_FILE_CHOOSER_ACTION_OPEN; + + if (action != gtk_file_chooser_get_action (GTK_FILE_CHOOSER (service->pathentry))) + gtk_file_chooser_set_action (GTK_FILE_CHOOSER (service->pathentry), action); + + if (url->path) + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (service->pathentry), url->path); + } + + tmp = camel_url_get_param(url, "use_ssl"); + if (tmp == NULL) + tmp = "never"; + + for (i=0;i<num_ssl_options;i++) { + if (!strcmp(ssl_options[i].value, tmp)) { + gtk_combo_box_set_active(service->use_ssl, i); + break; + } + } + + g_signal_connect (service->hostname, "changed", G_CALLBACK (emae_hostname_changed), service); + g_signal_connect (service->username, "changed", G_CALLBACK (emae_username_changed), service); + if (service->pathentry) + g_signal_connect (GTK_FILE_CHOOSER (service->pathentry), "selection-changed", G_CALLBACK (emae_path_changed), service); + + g_signal_connect(service->use_ssl, "changed", G_CALLBACK(emae_ssl_changed), service); + + service->auth_frame = glade_xml_get_widget(xml, info->auth_frame); + service->remember = emae_account_toggle(emae, info->remember_password, info->save_passwd_key, xml); + service->check_supported = (GtkButton *)glade_xml_get_widget(xml, info->authtype_check); + service->authtype = (GtkComboBox *)glade_xml_get_widget(xml, info->authtype); + /* old authtype will be destroyed when we exit */ + service->auth_changed_id = 0; + service->providers = (GtkComboBox *)glade_xml_get_widget(xml, info->type_dropdown); + emae_refresh_providers(emae, service); + emae_refresh_authtype(emae, service); + + if (info->needs_auth) { + service->needs_auth = (GtkToggleButton *)glade_xml_get_widget (xml, info->needs_auth); + gtk_toggle_button_set_active(service->needs_auth, url->authmech != NULL); + g_signal_connect(service->needs_auth, "toggled", G_CALLBACK(emae_needs_auth), service); + emae_needs_auth(service->needs_auth, service); + } else { + service->needs_auth = NULL; + } + + if (!e_account_writable (emae->account, info->account_uri_key)) + gtk_widget_set_sensitive(service->container, FALSE); + else + gtk_widget_set_sensitive(service->container, TRUE); + + emae_service_provider_changed(service); + + camel_url_free(url); +} + +/* do not re-order these, the order is used by various code to look up emae->priv->identity_entries[] */ +static struct { + const gchar *name; + gint item; +} emae_identity_entries[] = { + { "management_name", E_ACCOUNT_NAME }, + { "identity_full_name", E_ACCOUNT_ID_NAME }, + { "identity_address", E_ACCOUNT_ID_ADDRESS }, + { "identity_reply_to", E_ACCOUNT_ID_REPLY_TO }, + { "identity_organization", E_ACCOUNT_ID_ORGANIZATION }, +}; + +/* its a bit obtuse, but its simple */ +static void +emae_queue_widgets(EMAccountEditor *emae, GladeXML *xml, const gchar *first, ...) +{ + gint i = 0; + va_list ap; + + va_start(ap, first); + while (first) { + emae->priv->widgets_name[i] = first; + emae->priv->widgets[i++] = glade_xml_get_widget(xml, first); + first = va_arg(ap, const gchar *); + } + va_end(ap); + + g_return_if_fail(i < sizeof(emae->priv->widgets)/sizeof(emae->priv->widgets[0])); + + emae->priv->widgets[i] = NULL; + emae->priv->widgets_index = 0; +} + +static GtkWidget * +emae_identity_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + EMAccountEditorPrivate *gui = emae->priv; + EAccount *account = emae->account; + gint i; + GtkWidget *w; + GladeXML *xml; + gchar *gladefile; + + /*if (old) + return old;*/ + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + xml = glade_xml_new(gladefile, item->label, NULL); + g_free (gladefile); + + /* Management & Identity fields, in the druid the management frame is relocated to the last page later on */ + for (i=0;i<sizeof(emae_identity_entries)/sizeof(emae_identity_entries[0]);i++) + gui->identity_entries[i] = emae_account_entry(emae, emae_identity_entries[i].name, emae_identity_entries[i].item, xml); + + gui->management_frame = glade_xml_get_widget(xml, "management_frame"); + + gui->default_account = GTK_TOGGLE_BUTTON (glade_xml_get_widget (xml, "management_default")); + if (!e_get_default_account () + || (account == e_get_default_account ()) + || (GPOINTER_TO_INT(g_object_get_data (G_OBJECT (emae->account), "default_flagged"))) ) + gtk_toggle_button_set_active (gui->default_account, TRUE); + + if (emae->do_signature) { + emae_setup_signatures(emae, xml); + } else { + /* TODO: this could/should probably be neater */ + gtk_widget_hide(glade_xml_get_widget(xml, "sigLabel")); +#if 0 + gtk_widget_hide(glade_xml_get_widget(xml, "sigOption")); +#endif + gtk_widget_hide(glade_xml_get_widget(xml, "sigAddNew")); + } + + w = glade_xml_get_widget(xml, item->label); + if (emae->type == EMAE_PAGES) { + gtk_box_pack_start ((GtkBox *)emae->pages[0], w, TRUE, TRUE, 0); + } else if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) { + GladeXML *druidxml; + GtkWidget *page; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + druidxml = glade_xml_new(gladefile, "identity_page", NULL); + g_free (gladefile); + + page = glade_xml_get_widget(druidxml, "identity_page"); + + gtk_box_pack_start((GtkBox*)((GnomeDruidPageStandard *)page)->vbox, w, TRUE, TRUE, 0); + w = page; + g_object_unref(druidxml); + gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page); + } else { + gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Identity"))); + } + + emae_queue_widgets(emae, xml, "account_vbox", "identity_required_table", "identity_optional_table", NULL); + + g_object_unref(xml); + + return w; +} + +static GtkWidget * +emae_receive_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + EMAccountEditorPrivate *gui = emae->priv; + GtkWidget *w; + GladeXML *xml; + gchar *gladefile; + + /*if (old) + return old;*/ + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + xml = glade_xml_new(gladefile, item->label, NULL); + g_free (gladefile); + + gui->source.type = CAMEL_PROVIDER_STORE; + emae_setup_service(emae, &gui->source, xml); + + w = glade_xml_get_widget(xml, item->label); + if (emae->type == EMAE_PAGES) { + gtk_box_pack_start ((GtkBox *)emae->pages[1], w, TRUE, TRUE, 0); + } else if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) { + GladeXML *druidxml; + GtkWidget *page; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + druidxml = glade_xml_new(gladefile, "source_page", NULL); + g_free (gladefile); + + page = glade_xml_get_widget(druidxml, "source_page"); + + gtk_box_pack_start((GtkBox*)((GnomeDruidPageStandard *)page)->vbox, w, TRUE, TRUE, 0); + w = page; + g_object_unref(druidxml); + gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page); + } else { + gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Receiving Email"))); + } + + emae_queue_widgets(emae, xml, "source_type_table", "table4", "vbox181", "vbox179", NULL); + + g_object_unref(xml); + + return w; +} + +static void +emae_option_toggle_changed(GtkToggleButton *toggle, EMAccountEditorService *service) +{ + const gchar *name = g_object_get_data((GObject *)toggle, "option-name"); + GSList *depl = g_object_get_data((GObject *)toggle, "dependent-list"); + gint active = gtk_toggle_button_get_active(toggle); + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + for (;depl;depl = g_slist_next(depl)) + gtk_widget_set_sensitive((GtkWidget *)depl->data, active); + + camel_url_set_param(url, name, active?"":NULL); + emae_uri_changed(service, url); + camel_url_free(url); +} + +static GtkWidget * +emae_option_toggle(EMAccountEditorService *service, CamelURL *url, const gchar *text, const gchar *name, gint def) +{ + GtkWidget *w; + + /* FIXME: how do we get the default value ever? */ + w = gtk_check_button_new_with_mnemonic(text); + g_object_set_data((GObject *)w, "option-name", (gpointer)name); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), camel_url_get_param (url, name) != NULL); + g_signal_connect(w, "toggled", G_CALLBACK(emae_option_toggle_changed), service); + gtk_widget_show(w); + + return w; +} + +static void +emae_option_entry_changed(GtkEntry *entry, EMAccountEditorService *service) +{ + const gchar *name = g_object_get_data((GObject *)entry, "option-name"); + const gchar *text = gtk_entry_get_text(entry); + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + camel_url_set_param(url, name, text && text[0]?text:NULL); + emae_uri_changed(service, url); + camel_url_free(url); +} + +static GtkWidget * +emae_option_entry(EMAccountEditorService *service, CamelURL *url, const gchar *name, const gchar *def, GtkWidget *l) +{ + GtkWidget *w; + const gchar *val = camel_url_get_param(url, name); + + if (val == NULL) { + if (def) { + val = def; + camel_url_set_param(url, name, val); + emae_uri_changed(service, url); + } else + val = ""; + } + + w = g_object_new(gtk_entry_get_type(), + "text", val, + NULL); + gtk_label_set_mnemonic_widget ((GtkLabel*)l, w); + g_object_set_data((GObject *)w, "option-name", (gpointer)name); + g_signal_connect(w, "changed", G_CALLBACK(emae_option_entry_changed), service); + gtk_widget_show(w); + + return w; +} + +static void +emae_option_checkspin_changed(GtkSpinButton *spin, EMAccountEditorService *service) +{ + const gchar *name = g_object_get_data((GObject *)spin, "option-name"); + gchar value[16]; + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + sprintf(value, "%d", gtk_spin_button_get_value_as_int(spin)); + camel_url_set_param(url, name, value); + emae_uri_changed(service, url); + camel_url_free(url); +} + +static void +emae_option_checkspin_check_changed(GtkToggleButton *toggle, EMAccountEditorService *service) +{ + const gchar *name = g_object_get_data((GObject *)toggle, "option-name"); + GtkSpinButton *spin = g_object_get_data((GObject *)toggle, "option-target"); + + if (gtk_toggle_button_get_active(toggle)) { + gtk_widget_set_sensitive((GtkWidget *)spin, TRUE); + emae_option_checkspin_changed(spin, service); + } else { + CamelURL *url = emae_account_url(service->emae, emae_service_info[service->type].account_uri_key); + + camel_url_set_param(url, name, NULL); + gtk_widget_set_sensitive((GtkWidget *)spin, FALSE); + emae_uri_changed(service, url); + camel_url_free(url); + } +} + +/* this is a fugly api */ +static GtkWidget * +emae_option_checkspin(EMAccountEditorService *service, CamelURL *url, const gchar *name, const gchar *fmt, const gchar *info) +{ + GtkWidget *hbox, *check, *spin, *label = NULL; + double min, def, max; + gchar *pre, *post; + const gchar *val; + gchar on; + gint enable; + + pre = g_alloca(strlen(fmt)+1); + strcpy(pre, fmt); + post = strstr(pre, "%s"); + if (post) { + *post = 0; + post+=2; + } + + if (sscanf(info, "%c:%lf:%lf:%lf", &on, &min, &def, &max) != 4) { + min = 0.0; + def = 0.0; + max = 1.0; + } + + if ((enable = (val = camel_url_get_param(url, name)) != NULL) ) + def = strtod(val, NULL); + else + enable = (on == 'y'); + + hbox = gtk_hbox_new(FALSE, 0); + check = g_object_new(gtk_check_button_get_type(), "label", pre, "use_underline", TRUE, "active", enable, NULL); + + spin = gtk_spin_button_new((GtkAdjustment *)gtk_adjustment_new(def, min, max, 1, 1, 0), 1, 0); + if (post) + label = gtk_label_new_with_mnemonic(post); + gtk_box_pack_start((GtkBox *)hbox, check, FALSE, TRUE, 0); + gtk_box_pack_start((GtkBox *)hbox, spin, FALSE, TRUE, 0); + if (label) + gtk_box_pack_start((GtkBox *)hbox, label, FALSE, TRUE, 4); + + g_object_set_data((GObject *)spin, "option-name", (gpointer)name); + g_object_set_data((GObject *)check, "option-name", (gpointer)name); + g_object_set_data((GObject *)check, "option-target", (gpointer)spin); + + g_signal_connect(spin, "value_changed", G_CALLBACK(emae_option_checkspin_changed), service); + g_signal_connect(check, "toggled", G_CALLBACK(emae_option_checkspin_check_changed), service); + + gtk_widget_show_all(hbox); + + return hbox; +} + +static void +emae_option_options_changed (GtkComboBox *options, EMAccountEditorService *service) +{ + const gchar *name = g_object_get_data (G_OBJECT (options), "option-name"); + gchar *value = NULL; + CamelURL *url = emae_account_url (service->emae, emae_service_info[service->type].account_uri_key); + gint id = gtk_combo_box_get_active (options); + + if (id != -1) { + GtkTreeModel *model; + GtkTreeIter iter; + + model = gtk_combo_box_get_model (options); + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, id)) { + gtk_tree_model_get (model, &iter, 0, &value, -1); + } + } + + camel_url_set_param (url, name, value); + emae_uri_changed (service, url); + camel_url_free (url); + g_free (value); +} + +/* 'values' is in format "value0:caption0:value2:caption2:...valueN:captionN" */ +static GtkWidget * +emae_option_options (EMAccountEditorService *service, CamelURL *url, const gchar *name, const gchar *values, GtkWidget *l) +{ + GtkComboBox *w; + GtkListStore *store; + GtkTreeIter iter; + const gchar *p, *value, *caption; + GtkCellRenderer *cell; + gint active = 0; /* the first item entered is always a default item */ + gint i; + const gchar *val = camel_url_get_param (url, name); + + w = GTK_COMBO_BOX (gtk_combo_box_new ()); + + /* value and caption */ + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + + p = values; + for (p = values, i = 0; p; i++) { + gchar *vl, *cp; + + value = p; + caption = strchr (p, ':'); + if (caption) { + caption++; + } else { + g_warning (G_STRLOC ": expected ':' not found at '%s'", p); + break; + } + p = strchr (caption, ':'); + + vl = g_strndup (value, caption - value - 1); + if (p) { + p++; + cp = g_strndup (caption, p - caption - 1); + } else + cp = g_strdup (caption); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, vl, 1, dgettext (service->provider->translation_domain, cp), -1); + + if (val && g_ascii_strcasecmp (val, vl) == 0) + active = i; + + g_free (vl); + g_free (cp); + } + + gtk_combo_box_set_model (w, (GtkTreeModel *)store); + gtk_combo_box_set_active (w, i > 0 ? active : -1); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (w), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), cell, "text", 1, NULL); + + gtk_widget_show (GTK_WIDGET (w)); + + gtk_label_set_mnemonic_widget (GTK_LABEL (l), GTK_WIDGET (w)); + + g_object_set_data (G_OBJECT (w), "option-name", (gpointer)name); + g_signal_connect (w, "changed", G_CALLBACK (emae_option_options_changed), service); + + return GTK_WIDGET (w); +} + +static GtkWidget * +emae_receive_options_item(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + GtkWidget *w, *box, *spin; + gint row; + + if (emae->priv->source.provider == NULL + || emae->priv->source.provider->extra_conf == NULL) + return NULL; + + if (old) + return old; + + /* We have to add the automatic mail check item with the rest of the receive options */ + row = ((GtkTable *)parent)->nrows; + + box = gtk_hbox_new(FALSE, 4); + w = gtk_check_button_new_with_mnemonic (_("Check for _new messages every")); + emae_account_toggle_widget(emae, (GtkToggleButton *)w, E_ACCOUNT_SOURCE_AUTO_CHECK); + gtk_box_pack_start((GtkBox *)box, w, FALSE, FALSE, 0); + + spin = gtk_spin_button_new_with_range(1.0, 1440.0, 1.0); + emae_account_spinint_widget(emae, (GtkSpinButton *)spin, E_ACCOUNT_SOURCE_AUTO_CHECK_TIME); + gtk_box_pack_start((GtkBox *)box, spin, FALSE, TRUE, 0); + + w = gtk_label_new_with_mnemonic (_("minu_tes")); + gtk_label_set_mnemonic_widget (GTK_LABEL (w), spin); + gtk_box_pack_start((GtkBox *)box, w, FALSE, FALSE, 0); + + gtk_widget_show_all(box); + + gtk_table_attach((GtkTable *)parent, box, 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0); + + return box; +} + +static GtkWidget * +emae_receive_options_extra_item(EConfig *ec, EConfigItem *eitem, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + struct _receive_options_item *item = (struct _receive_options_item *)eitem; + GtkWidget *w, *l, *h; + CamelProviderConfEntry *entries; + GtkWidget *depw; + GSList *depl = NULL, *n; + EMAccountEditorService *service = &emae->priv->source; + gint row, i; + GHashTable *extra; + CamelURL *url; + + if (emae->priv->source.provider == NULL + || emae->priv->source.provider->extra_conf == NULL) + return NULL; + + entries = emae->priv->source.provider->extra_conf; + for (i=0;entries && entries[i].type != CAMEL_PROVIDER_CONF_END;i++) + if (entries[i].type == CAMEL_PROVIDER_CONF_SECTION_START + && entries[i].name + && strcmp(entries[i].name, eitem->user_data) == 0) + goto section; + + return NULL; +section: + d(printf("Building extra section '%s'\n", eitem->path)); + + url = emae_account_url(emae, emae_service_info[service->type].account_uri_key); + item->extra_table = g_hash_table_new(g_str_hash, g_str_equal); + extra = g_hash_table_new(g_str_hash, g_str_equal); + row = ((GtkTable *)parent)->nrows; + + for (;entries[i].type != CAMEL_PROVIDER_CONF_END && entries[i].type != CAMEL_PROVIDER_CONF_SECTION_END;i++) { + if (entries[i].depname) { + depw = g_hash_table_lookup(extra, entries[i].depname); + if (depw) + depl = g_object_steal_data((GObject *)depw, "dependent-list"); + } else + depw = NULL; + + switch (entries[i].type) { + case CAMEL_PROVIDER_CONF_SECTION_START: + case CAMEL_PROVIDER_CONF_SECTION_END: + break; + case CAMEL_PROVIDER_CONF_LABEL: + /* FIXME: This is a hack for exchange connector, labels should be removed from confentry */ + if (!strcmp(entries[i].name, "hostname")) + l = (GtkWidget *)emae->priv->source.hostlabel; + else if (!strcmp(entries[i].name, "username")) + l = (GtkWidget *)emae->priv->source.userlabel; + else + l = NULL; + + if (l) { + gtk_label_set_text_with_mnemonic((GtkLabel *)l, entries[i].text); + if (depw) + depl = g_slist_prepend(depl, l); + } + break; + case CAMEL_PROVIDER_CONF_CHECKBOX: + w = emae_option_toggle(service, url, entries[i].text, entries[i].name, atoi(entries[i].value)); + gtk_table_attach((GtkTable *)parent, w, 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0); + g_hash_table_insert(extra, (gpointer)entries[i].name, w); + if (depw) + depl = g_slist_prepend(depl, w); + row++; + /* HACK: keep_on_server is stored in the e-account, but is displayed as a properly on the uri, + make sure they track/match here */ + if (!strcmp(entries[i].name, "keep_on_server")) + emae_account_toggle_widget(emae, (GtkToggleButton *)w, E_ACCOUNT_SOURCE_KEEP_ON_SERVER); + break; + case CAMEL_PROVIDER_CONF_ENTRY: + l = g_object_new(gtk_label_get_type(), "label", entries[i].text, "xalign", 0.0, "use_underline", TRUE, NULL); + gtk_widget_show(l); + w = emae_option_entry(service, url, entries[i].name, entries[i].value, l); + gtk_table_attach((GtkTable *)parent, l, 0, 1, row, row+1, GTK_FILL, 0, 0, 0); + gtk_table_attach((GtkTable *)parent, w, 1, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0); + if (depw) { + depl = g_slist_prepend(depl, w); + depl = g_slist_prepend(depl, l); + } + row++; + /* FIXME: this is another hack for exchange/groupwise connector */ + g_hash_table_insert(item->extra_table, (gpointer)entries[i].name, w); + break; + case CAMEL_PROVIDER_CONF_CHECKSPIN: + w = emae_option_checkspin(service, url, entries[i].name, entries[i].text, entries[i].value); + gtk_table_attach((GtkTable *)parent, w, 0, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0, 0); + if (depw) + depl = g_slist_prepend(depl, w); + row++; + break; + case CAMEL_PROVIDER_CONF_OPTIONS: + h = gtk_hbox_new (FALSE, 4); + gtk_widget_show (h); + l = g_object_new (gtk_label_get_type (), "label", entries[i].text, "xalign", 0.0, "use_underline", TRUE, NULL); + gtk_widget_show (l); + w = emae_option_options (service, url, entries[i].name, entries[i].value, l); + gtk_box_pack_start (GTK_BOX (h), l, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (h), w, FALSE, FALSE, 0); + gtk_table_attach ((GtkTable *)parent, h, 0, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); + if (depw) { + depl = g_slist_prepend (depl, h); + } + row++; + break; + default: + break; + } + + if (depw && depl) { + gint act = gtk_toggle_button_get_active((GtkToggleButton *)depw); + + g_object_set_data_full((GObject *)depw, "dependent-list", depl, (GDestroyNotify)g_slist_free); + for (n=depl;n;n=g_slist_next(n)) + gtk_widget_set_sensitive((GtkWidget *)n->data, act); + } + } + + camel_url_free(url); + + /* Since EConfig destroys the factory widget when it changes, we + * need to destroy our own ones as well, and add a dummy item + * so it knows this section isn't empty */ + + w = gtk_label_new(""); + gtk_widget_hide(w); + gtk_table_attach((GtkTable *)parent, w, 0, 2, row, row+1, 0, 0, 0, 0); + + return w; +} + +static GtkWidget * +emae_send_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + EMAccountEditorPrivate *gui = emae->priv; + GtkWidget *w; + GladeXML *xml; + gchar *gladefile; + + /* no transport options page at all for these types of providers */ + if (gui->source.provider && CAMEL_PROVIDER_IS_STORE_AND_TRANSPORT(gui->source.provider)) { + memset(&gui->transport.frame, 0, ((gchar *)&gui->transport.check_dialog)-((gchar *)&gui->transport.frame)); + return NULL; + } + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + xml = glade_xml_new(gladefile, item->label, NULL); + g_free (gladefile); + + /* Transport */ + gui->transport.type = CAMEL_PROVIDER_TRANSPORT; + emae_setup_service(emae, &gui->transport, xml); + + w = glade_xml_get_widget(xml, item->label); + if (emae->type == EMAE_PAGES) { + gtk_box_pack_start ((GtkBox *)emae->pages[2], w, TRUE, TRUE, 0); + } else if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) { + GladeXML *druidxml; + GtkWidget *page; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + druidxml = glade_xml_new(gladefile, "transport_page", NULL); + g_free (gladefile); + + page = glade_xml_get_widget(druidxml, "transport_page"); + + gtk_box_pack_start((GtkBox*)((GnomeDruidPageStandard *)page)->vbox, w, TRUE, TRUE, 0); + w = page; + g_object_unref(druidxml); + gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page); + } else { + gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Sending Email"))); + } + + emae_queue_widgets(emae, xml, "transport_type_table", "vbox12", "vbox183", "vbox61", NULL); + + g_object_unref(xml); + + return w; +} + +static GtkWidget * +emae_defaults_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + EMAccountEditorPrivate *gui = emae->priv; + GtkWidget *w; + GladeXML *xml; + gchar *gladefile; + + /*if (old) + return old;*/ + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + xml = glade_xml_new(gladefile, item->label, NULL); + g_free (gladefile); + + /* Special folders */ + gui->drafts_folder_button = (GtkButton *)emae_account_folder(emae, "drafts_button", E_ACCOUNT_DRAFTS_FOLDER_URI, E_MAIL_FOLDER_DRAFTS, xml); + gui->sent_folder_button = (GtkButton *)emae_account_folder(emae, "sent_button", E_ACCOUNT_SENT_FOLDER_URI, E_MAIL_FOLDER_SENT, xml); + + /* Special Folders "Reset Defaults" button */ + gui->restore_folders_button = (GtkButton *)glade_xml_get_widget (xml, "default_folders_button"); + g_signal_connect (gui->restore_folders_button, "clicked", G_CALLBACK (default_folders_clicked), emae); + + /* Always Cc/Bcc */ + emae_account_toggle(emae, "always_cc", E_ACCOUNT_CC_ALWAYS, xml); + emae_account_entry(emae, "cc_addrs", E_ACCOUNT_CC_ADDRS, xml); + emae_account_toggle(emae, "always_bcc", E_ACCOUNT_BCC_ALWAYS, xml); + emae_account_entry(emae, "bcc_addrs", E_ACCOUNT_BCC_ADDRS, xml); + + gtk_widget_set_sensitive((GtkWidget *)gui->drafts_folder_button, e_account_writable(emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI)); + + gtk_widget_set_sensitive( (GtkWidget *)gui->sent_folder_button, + e_account_writable(emae->account, E_ACCOUNT_SENT_FOLDER_URI) + && + (emae->priv->source.provider ? !(emae->priv->source.provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER): TRUE) + ); + + gtk_widget_set_sensitive((GtkWidget *)gui->restore_folders_button, + (e_account_writable(emae->account, E_ACCOUNT_SENT_FOLDER_URI) + && ((emae->priv->source.provider && !( emae->priv->source.provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)) + || e_account_writable(emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI)))); + + /* Receipt policy */ + emae_setup_receipt_policy (emae, xml); + + w = glade_xml_get_widget(xml, item->label); + gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Defaults"))); + + emae_queue_widgets(emae, xml, "vbox184", "table8", NULL); + + g_object_unref(xml); + + return w; +} + +static GtkWidget * +emae_security_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; +#if defined (HAVE_NSS) + EMAccountEditorPrivate *gui = emae->priv; +#endif + GtkWidget *w; + GladeXML *xml; + gchar *gladefile; + + /*if (old) + return old;*/ + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + xml = glade_xml_new(gladefile, item->label, NULL); + g_free (gladefile); + + /* Security */ + emae_account_entry(emae, "pgp_key", E_ACCOUNT_PGP_KEY, xml); + emae_account_toggle(emae, "pgp_encrypt_to_self", E_ACCOUNT_PGP_ENCRYPT_TO_SELF, xml); + emae_account_toggle(emae, "pgp_always_sign", E_ACCOUNT_PGP_ALWAYS_SIGN, xml); + emae_account_toggle(emae, "pgp_no_imip_sign", E_ACCOUNT_PGP_NO_IMIP_SIGN, xml); + emae_account_toggle(emae, "pgp_always_trust", E_ACCOUNT_PGP_ALWAYS_TRUST, xml); + +#if defined (HAVE_NSS) + /* TODO: this should handle its entry separately? */ + gui->smime_sign_key = emae_account_entry(emae, "smime_sign_key", E_ACCOUNT_SMIME_SIGN_KEY, xml); + gui->smime_sign_key_select = (GtkButton *)glade_xml_get_widget (xml, "smime_sign_key_select"); + gui->smime_sign_key_clear = (GtkButton *)glade_xml_get_widget (xml, "smime_sign_key_clear"); + g_signal_connect(gui->smime_sign_key_select, "clicked", G_CALLBACK(smime_sign_key_select), emae); + g_signal_connect(gui->smime_sign_key_clear, "clicked", G_CALLBACK(smime_sign_key_clear), emae); + + gui->smime_sign_default = emae_account_toggle(emae, "smime_sign_default", E_ACCOUNT_SMIME_SIGN_DEFAULT, xml); + + gui->smime_encrypt_key = emae_account_entry(emae, "smime_encrypt_key", E_ACCOUNT_SMIME_ENCRYPT_KEY, xml); + gui->smime_encrypt_key_select = (GtkButton *)glade_xml_get_widget (xml, "smime_encrypt_key_select"); + gui->smime_encrypt_key_clear = (GtkButton *)glade_xml_get_widget (xml, "smime_encrypt_key_clear"); + g_signal_connect(gui->smime_encrypt_key_select, "clicked", G_CALLBACK(smime_encrypt_key_select), emae); + g_signal_connect(gui->smime_encrypt_key_clear, "clicked", G_CALLBACK(smime_encrypt_key_clear), emae); + + gui->smime_encrypt_default = emae_account_toggle(emae, "smime_encrypt_default", E_ACCOUNT_SMIME_ENCRYPT_DEFAULT, xml); + gui->smime_encrypt_to_self = emae_account_toggle(emae, "smime_encrypt_to_self", E_ACCOUNT_SMIME_ENCRYPT_TO_SELF, xml); + smime_changed(emae); +#else + { + /* Since we don't have NSS, hide the S/MIME config options */ + GtkWidget *frame; + + frame = glade_xml_get_widget(xml, "smime_vbox"); + gtk_widget_destroy(frame); + } +#endif /* HAVE_NSS */ + + w = glade_xml_get_widget(xml, item->label); + gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Security"))); + + g_object_unref(xml); + + return w; +} + +static GtkWidget * +emae_widget_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + gint i; + + for (i=0;emae->priv->widgets[i];i++) + if (!strcmp(emae->priv->widgets_name[i], item->label)) + return emae->priv->widgets[i]; + + g_warning("Mail account widget '%s' not found", item->label); + + return NULL; +} + +/* plugin meta-data for "org.gnome.evolution.mail.config.accountEditor" */ +static EMConfigItem emae_editor_items[] = { + { E_CONFIG_BOOK, (gchar *) "" }, + { E_CONFIG_PAGE, (gchar *) "00.identity", (gchar *) "vboxIdentityBorder", emae_identity_page }, + { E_CONFIG_SECTION, (gchar *) "00.identity/00.name", (gchar *) "account_vbox", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/10.required", (gchar *) "identity_required_table", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/20.info", (gchar *) "identity_optional_table", emae_widget_glade }, + + { E_CONFIG_PAGE, (gchar *) "10.receive", (gchar *) "vboxSourceBorder", emae_receive_page }, + { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/00.type", (gchar *) "source_type_table", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/10.config", (gchar *) "table4", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "10.receive/20.security", (gchar *) "vbox181", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "10.receive/30.auth", (gchar *) "vbox179", emae_widget_glade }, + + /* Most sections for this is auto-generated from the camel config */ + { E_CONFIG_PAGE, (gchar *) "20.receive_options", (gchar *) N_("Receiving Options"), }, + { E_CONFIG_SECTION_TABLE, (gchar *) "20.receive_options/10.mailcheck", (gchar *) N_("Checking for New Messages"), }, + { E_CONFIG_ITEM_TABLE, (gchar *) "20.receive_options/10.mailcheck/00.autocheck", NULL, emae_receive_options_item, }, + + { E_CONFIG_PAGE, (gchar *) "30.send", (gchar *) "vboxTransportBorder", emae_send_page }, + { E_CONFIG_SECTION_TABLE, (gchar *) "30.send/00.type", (gchar *) "transport_type_table", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "30.send/10.config", (gchar *) "vbox12", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "30.send/20.security", (gchar *) "vbox183", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "30.send/30.auth", (gchar *) "vbox61", emae_widget_glade }, + + { E_CONFIG_PAGE, (gchar *) "40.defaults", (gchar *) "vboxFoldersBorder", emae_defaults_page }, + { E_CONFIG_SECTION, (gchar *) "40.defaults/00.folders", (gchar *) "vbox184", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "40.defaults/10.composing", (gchar *) "table8", emae_widget_glade }, + + { E_CONFIG_PAGE, (gchar *) "50.security", (gchar *) "vboxSecurityBorder", emae_security_page }, + /* 1x1 table(!) not vbox: { E_CONFIG_SECTION, "50.security/00.gpg", "table19", emae_widget_glade }, */ + /* table not vbox: { E_CONFIG_SECTION, "50.security/10.smime", "smime_table", emae_widget_glade }, */ + { 0 }, +}; +static gboolean emae_editor_items_translated = FALSE; + +static GtkWidget * +emae_management_page(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMAccountEditor *emae = data; + EMAccountEditorPrivate *gui = emae->priv; + GtkWidget *w; + + w = gui->management_frame; + if (((EConfig *)gui->config)->type == E_CONFIG_DRUID) { + GladeXML *druidxml; + GtkWidget *page; + gchar *gladefile; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + druidxml = glade_xml_new(gladefile, "management_page", NULL); + g_free (gladefile); + + page = glade_xml_get_widget(druidxml, "management_page"); + + gtk_widget_reparent(w, ((GnomeDruidPageStandard *)page)->vbox); + w = page; + g_object_unref(druidxml); + gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)page); + } + + return w; +} + +static GtkWidget * +emae_widget_druid_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + GladeXML *druidxml; + GtkWidget *w; + gchar *gladefile; + EMAccountEditor *emae = (EMAccountEditor *)data; + + if (emae->type == EMAE_PAGES) + return NULL; + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + druidxml = glade_xml_new(gladefile, item->label, NULL); + g_free (gladefile); + + w = glade_xml_get_widget(druidxml, item->label); + /* i think the glade file has issues, we need to show all on at least the end page */ + gtk_widget_show_all(w); + g_object_unref(druidxml); + + gnome_druid_append_page((GnomeDruid *)parent, (GnomeDruidPage *)w); + + return w; +} + +/* plugin meta-data for "org.gnome.evolution.mail.config.accountDruid" */ +static EMConfigItem emae_druid_items[] = { + { E_CONFIG_DRUID, (gchar *) "" }, + { E_CONFIG_PAGE_START, (gchar *) "0.start", (gchar *) "start_page", emae_widget_druid_glade }, + + { E_CONFIG_PAGE, (gchar *) "00.identity", (gchar *) "vboxIdentityBorder", emae_identity_page }, + { E_CONFIG_SECTION, (gchar *) "00.identity/00.name", (gchar *) "account_vbox", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/10.required", (gchar *) "identity_required_table", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "00.identity/20.info", (gchar *) "identity_optional_table", emae_widget_glade }, + + { E_CONFIG_PAGE, (gchar *) "10.receive", (gchar *) "vboxSourceBorder", emae_receive_page }, + { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/00.type", (gchar *) "source_type_table", emae_widget_glade }, + { E_CONFIG_SECTION_TABLE, (gchar *) "10.receive/10.config", (gchar *) "table4", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "10.receive/20.security", (gchar *) "vbox181", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "10.receive/30.auth", (gchar *) "vbox179", emae_widget_glade }, + + /* Most sections for this is auto-generated fromt the camel config */ + { E_CONFIG_PAGE, (gchar *) "20.receive_options", (gchar *) N_("Receiving Options"), }, + { E_CONFIG_SECTION_TABLE, (gchar *) "20.receive_options/10.mailcheck", (gchar *) N_("Checking for New Messages"), }, + { E_CONFIG_ITEM_TABLE, (gchar *) "20.receive_options/10.mailcheck/00.autocheck", NULL, emae_receive_options_item, }, + + { E_CONFIG_PAGE, (gchar *) "30.send", (gchar *) "vboxTransportBorder", emae_send_page }, + { E_CONFIG_SECTION_TABLE, (gchar *) "30.send/00.type", (gchar *) "transport_type_table", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "30.send/10.config", (gchar *) "vbox12", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "30.send/20.security", (gchar *) "vbox183", emae_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "30.send/30.auth", (gchar *) "vbox61", emae_widget_glade }, + + { E_CONFIG_PAGE, (gchar *) "40.management", (gchar *) "management_frame", emae_management_page }, + + { E_CONFIG_PAGE_FINISH, (gchar *) "999.end", (gchar *) "finish_page", emae_widget_druid_glade }, + { 0 }, +}; +static gboolean emae_druid_items_translated = FALSE; + +static void +emae_free(EConfig *ec, GSList *items, gpointer data) +{ + g_slist_free(items); +} + +static void +emae_free_auto(EConfig *ec, GSList *items, gpointer data) +{ + GSList *l, *n; + + for (l=items;l;) { + struct _receive_options_item *item = l->data; + + n = g_slist_next(l); + g_free(item->item.path); + if (item->extra_table) + g_hash_table_destroy(item->extra_table); + g_free(item); + g_slist_free_1(l); + l = n; + } +} + +static gboolean +emae_service_complete(EMAccountEditor *emae, EMAccountEditorService *service) +{ + CamelURL *url; + gint ok = TRUE; + const gchar *uri; + + if (service->provider == NULL) + return TRUE; + + uri = e_account_get_string(emae->account, emae_service_info[service->type].account_uri_key); + if (uri == NULL || (url = camel_url_new(uri, NULL)) == NULL) + return FALSE; + + if (CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_HOST)) { + if (url->host == NULL || url->host[0] == 0) + ok = FALSE; + } + /* We only need the user if the service needs auth as well, i think */ + if (ok + && (service->needs_auth == NULL + || CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_AUTH) + || gtk_toggle_button_get_active(service->needs_auth)) + && CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_USER) + && (url->user == NULL || url->user[0] == 0)) + ok = FALSE; + + if (ok + && CAMEL_PROVIDER_NEEDS(service->provider, CAMEL_URL_PART_PATH) + && (url->path == NULL || url->path[0] == 0)) + ok = FALSE; + + camel_url_free(url); + + return ok; +} + +enum { + GMAIL = 0, + YAHOO, + AOL +}; +struct _server_prefill { + const gchar *key; + const gchar *recv; + const gchar *send; + const gchar *proto; + const gchar *ssl; +} mail_servers [] = { + {"gmail", "imap.gmail.com", "smtp.gmail.com", "imap", "always"}, + {"yahoo", "pop3.yahoo.com", "smtp.yahoo.com", "pop", "never"}, + {"aol", "imap.aol.com", "smtp.aol.com", "pop", "never"}, + {"msn", "pop3.email.msn.com", "smtp.email.msn.com", "pop", "never"} +}; + +static gint +check_servers (gchar *server) +{ + gint len = G_N_ELEMENTS(mail_servers), i; + + for (i=0; i<len; i++) { + if (strstr(server, mail_servers[i].key) != NULL) + return i; + } + + return -1; +} + +static gboolean +emae_check_complete(EConfig *ec, const gchar *pageid, gpointer data) +{ + EMAccountEditor *emae = data; + gint ok = TRUE; + const gchar *tmp; + EAccount *ea; + gboolean refresh = FALSE; + + /* We use the page-check of various pages to 'prepare' or + pre-load their values, only in the druid */ + if (pageid + && ((EConfig *)emae->priv->config)->type == E_CONFIG_DRUID) { + if (!strcmp(pageid, "00.identity")) { + if (!emae->priv->identity_set) { + gchar *uname; + + emae->priv->identity_set = 1; +#ifndef G_OS_WIN32 + uname = g_locale_to_utf8(g_get_real_name(), -1, NULL, NULL, NULL); +#else + uname = g_strdup(g_get_real_name()); +#endif + if (uname) { + gtk_entry_set_text(emae->priv->identity_entries[1], uname); + g_free(uname); + } + } + } else if (!strcmp(pageid, "10.receive")) { + if (!emae->priv->receive_set) { + gchar *user, *at; + gint index; + gchar *uri = g_strdup(e_account_get_string(emae->account, E_ACCOUNT_SOURCE_URL)); + CamelURL *url; + + emae->priv->receive_set = 1; + tmp = (gchar *)e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS); + at = strchr(tmp, '@'); + user = g_alloca(at-tmp+1); + memcpy(user, tmp, at-tmp); + user[at-tmp] = 0; + at++; + + index = check_servers(at); + gtk_entry_set_text(emae->priv->source.username, user); + gtk_entry_set_text(emae->priv->transport.username, user); + if (uri && (url = camel_url_new(uri, NULL)) != NULL) { + refresh = TRUE; + camel_url_set_protocol(url, mail_servers[index].proto); + camel_url_set_param(url, "use_ssl", mail_servers[index].ssl); + camel_url_set_host (url, mail_servers[index].recv); + camel_url_set_user (url, user); + gtk_entry_set_text(emae->priv->source.hostname, mail_servers[index].recv); + gtk_entry_set_text(emae->priv->transport.hostname, mail_servers[index].send); + uri = camel_url_to_string(url, 0); + e_account_set_string(emae->account, E_ACCOUNT_SOURCE_URL, uri); + + g_free(uri); + camel_url_free(url); + } + + } + } else if (!strcmp(pageid, "30.send")) { + CamelURL *url; + gchar *at, *user; + gint index; + gchar *uri = (gchar *)e_account_get_string(emae->account, E_ACCOUNT_TRANSPORT_URL); + + tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS); + at = strchr(tmp, '@'); + user = g_alloca(at-tmp+1); + memcpy(user, tmp, at-tmp); + user[at-tmp] = 0; + at++; + + index = check_servers(at); + if (uri && (url = camel_url_new(uri, NULL)) != NULL) { + refresh = TRUE; + camel_url_set_protocol (url, "smtp"); + camel_url_set_param(url, "use_ssl", mail_servers[index].ssl); + camel_url_set_host (url, mail_servers[index].send); + camel_url_set_user (url, user); + uri = camel_url_to_string(url, 0); + e_account_set_string(emae->account, E_ACCOUNT_TRANSPORT_URL, uri); + g_free(uri); + camel_url_free(url); + } else { + g_warning("buz2\n"); + } + + } else if (!strcmp(pageid, "20.receive_options")) { + if (emae->priv->source.provider + && emae->priv->extra_provider != emae->priv->source.provider) { + emae->priv->extra_provider = emae->priv->source.provider; + emae_auto_detect(emae); + } + } else if (!strcmp(pageid, "40.management")) { + if (!emae->priv->management_set) { + gchar *template; + guint i = 0, len; + + emae->priv->management_set = 1; + tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS); + len = strlen(tmp); + template = alloca(len + 14); + strcpy(template, tmp); + while (e_get_account_by_name (template)) + sprintf(template + len, " (%d)", i++); + + gtk_entry_set_text(emae->priv->identity_entries[0], template); + } + } + } + + /* + Setting a flag on the Account if it is marked as default. It is done in this way instead of + using a temporary variable so as to keep track of which account is marked as default in case of + editing multiple accounts at a time + */ + if (gtk_toggle_button_get_active(emae->priv->default_account)) + g_object_set_data (G_OBJECT (emae->account), "default_flagged", GINT_TO_POINTER(1)); + + if (pageid == NULL || !strcmp(pageid, "00.identity")) { + /* TODO: check the account name is set, and unique in the account list */ + ok = (tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_NAME)) + && tmp[0] + && (tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_ADDRESS)) + && is_email(tmp) + && ((tmp = e_account_get_string(emae->account, E_ACCOUNT_ID_REPLY_TO)) == NULL + || tmp[0] == 0 + || is_email(tmp)); + if (!ok) { + d(printf("identity incomplete\n")); + } + } + + if (ok && (pageid == NULL || !strcmp(pageid, "10.receive"))) { + if (emae->type == EMAE_PAGES && refresh) { + emae_refresh_providers(emae, &emae->priv->source); + } + ok = emae_service_complete(emae, &emae->priv->source); + if (!ok) { + d(printf("receive page incomplete\n")); + } + } + + if (ok && (pageid == NULL || !strcmp(pageid, "30.send"))) { + if (emae->type == EMAE_PAGES && refresh) { + emae_refresh_providers(emae, &emae->priv->transport); + } + ok = emae_service_complete(emae, &emae->priv->transport); + if (!ok) { + d(printf("send page incomplete\n")); + } + } + + if (ok && (pageid == NULL || !strcmp(pageid, "40.management"))) { + ok = (tmp = e_account_get_string(emae->account, E_ACCOUNT_NAME)) + && tmp[0] + && ((ea = e_get_account_by_name (tmp)) == NULL + || ea == emae->original); + if (!ok) { + d(printf("management page incomplete\n")); + } + } + + return ok; +} + +void +em_account_editor_check (EMAccountEditor *emae, const gchar *page) +{ + emae_check_complete((EConfig *)emae->config, page, emae); +} + +/* HACK: FIXME: the component should listen to the account object directly */ +static void +add_new_store (gchar *uri, CamelStore *store, gpointer user_data) +{ +#if 0 /* KILL-BONOBO: Try to actually fix this? */ + MailComponent *component = mail_component_peek (); + EAccount *account = user_data; + + if (store == NULL) + return; + + mail_component_add_store (component, store, account->name); +#endif +} + +static void +emae_commit(EConfig *ec, GSList *items, gpointer data) +{ + EMAccountEditor *emae = data; + EAccountList *accounts = e_get_account_list (); + EAccount *account; + + /* the mail-config*acconts* api needs a lot of work */ + + if (emae->original) { + d(printf("Committing account '%s'\n", e_account_get_string(emae->account, E_ACCOUNT_NAME))); + e_account_import(emae->original, emae->account); + account = emae->original; + e_account_list_change(accounts, account); + } else { + d(printf("Adding new account '%s'\n", e_account_get_string(emae->account, E_ACCOUNT_NAME))); + e_account_list_add(accounts, emae->account); + account = emae->account; + + /* HACK: this will add the account to the folder tree. + We should just be listening to the account list directly for changed events */ + if (account->enabled + && emae->priv->source.provider + && (emae->priv->source.provider->flags & CAMEL_PROVIDER_IS_STORAGE)) + mail_get_store(e_account_get_string(emae->account, E_ACCOUNT_SOURCE_URL), NULL, add_new_store, account); + } + + if (gtk_toggle_button_get_active(emae->priv->default_account)) + e_account_list_set_default(accounts, account); + + e_account_list_save(accounts); +} + +void +em_account_editor_commit (EMAccountEditor *emae) +{ + emae_commit ((EConfig *)emae->config, NULL, emae); +} + +static void +emae_editor_destroyed(GtkWidget *dialog, EMAccountEditor *emae) +{ + emae->editor = NULL; + g_object_unref(emae); +} + +static void +em_account_editor_construct(EMAccountEditor *emae, EAccount *account, em_account_editor_t type, const gchar *id) +{ + EMAccountEditorPrivate *gui = emae->priv; + gint i, index; + GSList *l; + GList *prov; + EMConfig *ec; + EMConfigTargetAccount *target; + GHashTable *have; + EConfigItem *items; + + emae->type = type; + emae->original = account; + if (emae->original) { + gchar *xml; + + g_object_ref(emae->original); + xml = e_account_to_xml(emae->original); + emae->account = e_account_new_from_xml(xml); + g_free(xml); + + emae->do_signature = TRUE; + } else { + /* TODO: have a get_default_account thing?? */ + emae->account = e_account_new(); + emae->account->enabled = TRUE; + + e_account_set_string ( + emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI, + e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS)); + + e_account_set_string ( + emae->account, E_ACCOUNT_SENT_FOLDER_URI, + e_mail_local_get_folder_uri (E_MAIL_FOLDER_SENT)); + } + + /* sort the providers, remote first */ + gui->providers = g_list_sort(camel_provider_list(TRUE), (GCompareFunc)provider_compare); + + if (type == EMAE_NOTEBOOK) { + ec = em_config_new(E_CONFIG_BOOK, id); + items = emae_editor_items; + if (!emae_editor_items_translated) { + for (i=0;items[i].path;i++) { + if (items[i].label) + items[i].label = gettext(items[i].label); + } + emae_editor_items_translated = TRUE; + } + } else { + ec = em_config_new(E_CONFIG_DRUID, id); + items = emae_druid_items; + if (!emae_druid_items_translated) { + for (i=0;items[i].path;i++) { + if (items[i].label) + items[i].label = _(items[i].label); + } + emae_druid_items_translated = TRUE; + } + } + + emae->config = gui->config = ec; + l = NULL; + for (i=0;items[i].path;i++) + l = g_slist_prepend(l, &items[i]); + e_config_add_items((EConfig *)ec, l, emae_commit, NULL, emae_free, emae); + + /* This is kinda yuck, we're dynamically mapping from the 'old style' extensibility api to the new one */ + l = NULL; + have = g_hash_table_new(g_str_hash, g_str_equal); + index = 20; + for (prov=gui->providers;prov;prov=g_list_next(prov)) { + CamelProviderConfEntry *entries = ((CamelProvider *)prov->data)->extra_conf; + + for (i=0;entries && entries[i].type != CAMEL_PROVIDER_CONF_END;i++) { + struct _receive_options_item *item; + const gchar *name = entries[i].name; + gint myindex = index; + + if (entries[i].type != CAMEL_PROVIDER_CONF_SECTION_START + || name == NULL + || g_hash_table_lookup(have, name)) + continue; + + /* override mailcheck since we also insert our own mailcheck item at this index */ + if (name && !strcmp(name, "mailcheck")) + myindex = 10; + + item = g_malloc0(sizeof(*item)); + item->item.type = E_CONFIG_SECTION_TABLE; + item->item.path = g_strdup_printf("20.receive_options/%02d.%s", myindex, name?name:"unnamed"); + item->item.label = g_strdup (entries[i].text); + + l = g_slist_prepend(l, item); + + item = g_malloc0(sizeof(*item)); + item->item.type = E_CONFIG_ITEM_TABLE; + item->item.path = g_strdup_printf("20.receive_options/%02d.%s/80.camelitem", myindex, name?name:"unnamed"); + item->item.factory = emae_receive_options_extra_item; + item->item.user_data = g_strdup (entries[i].name); + + l = g_slist_prepend(l, item); + + index += 10; + g_hash_table_insert(have, (gpointer)entries[i].name, have); + } + } + g_hash_table_destroy(have); + e_config_add_items((EConfig *)ec, l, NULL, NULL, emae_free_auto, emae); + gui->extra_items = l; + + e_config_add_page_check((EConfig *)ec, NULL, emae_check_complete, emae); + + target = em_config_target_new_account(ec, emae->account); + e_config_set_target((EConfig *)ec, (EConfigTarget *)target); + + if (type != EMAE_PAGES) { + emae->editor = e_config_create_window((EConfig *)ec, NULL, type==EMAE_NOTEBOOK?_("Account Editor"):_("Evolution Account Assistant")); + g_signal_connect(emae->editor, "destroy", G_CALLBACK(emae_editor_destroyed), emae); + } else { + e_config_create_widget((EConfig *)ec); + } +} diff --git a/modules/mail/em-account-editor.h b/modules/mail/em-account-editor.h new file mode 100644 index 0000000000..d8d96f58f0 --- /dev/null +++ b/modules/mail/em-account-editor.h @@ -0,0 +1,86 @@ +/* + * 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/> + * + * + * Authors: + * Jeffrey Stedfast <fejj@ximian.com> + * Dan Winship <danw@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef EM_ACCOUNT_EDITOR_H +#define EM_ACCOUNT_EDITOR_H + +#include <gtk/gtk.h> + +#include <mail/em-config.h> + +G_BEGIN_DECLS + +typedef struct _EMAccountEditor EMAccountEditor; +typedef struct _EMAccountEditorClass EMAccountEditorClass; +typedef struct _EMAccountEditorPrivate EMAccountEditorPrivate; + +typedef enum { + EMAE_NOTEBOOK, + EMAE_DRUID, + EMAE_PAGES +} em_account_editor_t; + +struct _EMAccountEditor { + GObject gobject; + + EMAccountEditorPrivate *priv; + + em_account_editor_t type; + GtkWidget *editor; /* gtknotebook or druid, depending on type */ + + EMConfig *config; /* driver object */ + + EAccount *account; /* working account, must instant apply to this */ + EAccount *original; /* original account, not changed unless commit is invoked */ + + GtkWidget **pages; /* Pages for Anjal's page type editor */ + + guint do_signature:1; /* allow editing signature */ +}; + +struct _EMAccountEditorClass { + GObjectClass gobject_class; +}; + +GType em_account_editor_get_type(void); + +EMAccountEditor *em_account_editor_new(EAccount *account, em_account_editor_t type, const gchar *id); +EMAccountEditor *em_account_editor_new_for_pages(EAccount *account, em_account_editor_t type, gchar *id, GtkWidget **pages); +void em_account_editor_commit (EMAccountEditor *emae); +void em_account_editor_check (EMAccountEditor *emae, const gchar *page); + +gboolean em_account_editor_save (EMAccountEditor *gui); +void em_account_editor_destroy (EMAccountEditor *gui); + +gboolean em_account_editor_identity_complete (EMAccountEditor *gui, GtkWidget **incomplete); +gboolean em_account_editor_source_complete (EMAccountEditor *gui, GtkWidget **incomplete); +gboolean em_account_editor_transport_complete (EMAccountEditor *gui, GtkWidget **incomplete); +gboolean em_account_editor_management_complete (EMAccountEditor *gui, GtkWidget **incomplete); + +void em_account_editor_build_extra_conf (EMAccountEditor *gui, const gchar *url); + +void em_account_editor_auto_detect_extra_conf (EMAccountEditor *gui); + +G_END_DECLS + +#endif /* EM_ACCOUNT_EDITOR_H */ diff --git a/modules/mail/em-account-prefs.c b/modules/mail/em-account-prefs.c new file mode 100644 index 0000000000..f2c646d5ed --- /dev/null +++ b/modules/mail/em-account-prefs.c @@ -0,0 +1,323 @@ +/* + * em-account-prefs.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) + * + */ + +/* XXX EAccountManager handles all the user interface stuff. This subclass + * applies policies using mailer resources that EAccountManager does not + * have access to. The desire is to someday move account management + * completely out of the mailer, perhaps to evolution-data-server. */ + +#include "em-account-prefs.h" + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <camel/camel-url.h> + +#include <glib/gi18n.h> + +#include "e-util/e-error.h" + +#include "e-mail-store.h" +#include "em-config.h" +#include "em-account-editor.h" + +#define EM_ACCOUNT_PREFS_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefsPrivate)) + +struct _EMAccountPrefsPrivate { + gpointer druid; /* weak pointer */ + gpointer editor; /* weak pointer */ +}; + +static gpointer parent_class; + +static void +account_prefs_enable_account_cb (EAccountTreeView *tree_view) +{ + EAccount *account; + + account = e_account_tree_view_get_selected (tree_view); + g_return_if_fail (account != NULL); + + e_mail_store_add_by_uri (account->source->url, account->name); +} + +static void +account_prefs_disable_account_cb (EAccountTreeView *tree_view) +{ + EAccountList *account_list; + EAccount *account; + gpointer parent; + gint response; + + account = e_account_tree_view_get_selected (tree_view); + g_return_if_fail (account != NULL); + + account_list = e_account_tree_view_get_account_list (tree_view); + g_return_if_fail (account_list != NULL); + + if (!e_account_list_account_has_proxies (account_list, account)) + return; + + parent = gtk_widget_get_toplevel (GTK_WIDGET (tree_view)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + response = e_error_run ( + parent, "mail:ask-delete-proxy-accounts", NULL); + + if (response != GTK_RESPONSE_YES) { + g_signal_stop_emission_by_name (tree_view, "disable-account"); + return; + } + + e_account_list_remove_account_proxies (account_list, account); + + e_mail_store_remove_by_uri (account->source->url); +} + +static void +account_prefs_add_account (EAccountManager *manager) +{ + EMAccountPrefsPrivate *priv; + EMAccountEditor *emae; + gpointer parent; + + priv = EM_ACCOUNT_PREFS_GET_PRIVATE (manager); + + if (priv->druid != NULL) { + gtk_window_present (GTK_WINDOW (priv->druid)); + return; + } + + parent = gtk_widget_get_toplevel (GTK_WIDGET (manager)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + /** @HookPoint-EMConfig: New Mail Account Druid + * @Id: org.gnome.evolution.mail.config.accountDruid + * @Type: E_CONFIG_DRUID + * @Class: org.gnome.evolution.mail.config:1.0 + * @Target: EMConfigTargetAccount + * + * The new mail account druid. + */ + emae = em_account_editor_new ( + NULL, EMAE_DRUID, + "org.gnome.evolution.mail.config.accountDruid"); + priv->druid = emae->editor; + + g_object_add_weak_pointer (G_OBJECT (priv->druid), &priv->druid); + gtk_window_set_transient_for (GTK_WINDOW (priv->druid), parent); + gtk_widget_show (priv->druid); +} + +static void +account_prefs_edit_account (EAccountManager *manager) +{ + EMAccountPrefsPrivate *priv; + EMAccountEditor *emae; + EAccountTreeView *tree_view; + EAccountList *account_list; + EAccount *account; + gpointer parent; + + priv = EM_ACCOUNT_PREFS_GET_PRIVATE (manager); + + if (priv->editor != NULL) { + gtk_window_present (GTK_WINDOW (priv->editor)); + return; + } + + account_list = e_account_manager_get_account_list (manager); + tree_view = e_account_manager_get_tree_view (manager); + account = e_account_tree_view_get_selected (tree_view); + g_return_if_fail (account != NULL); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (manager)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + /** @HookPoint-EMConfig: Mail Account Editor + * @Id: org.gnome.evolution.mail.config.accountEditor + * @Type: E_CONFIG_BOOK + * @Class: org.gnome.evolution.mail.config:1.0 + * @Target: EMConfigTargetAccount + * + * The account editor window. + */ + emae = em_account_editor_new ( + account, EMAE_NOTEBOOK, + "org.gnome.evolution.mail.config.accountEditor"); + priv->editor = emae->editor; + + g_object_add_weak_pointer (G_OBJECT (priv->editor), &priv->editor); + gtk_window_set_transient_for (GTK_WINDOW (priv->editor), parent); + gtk_widget_show (priv->editor); +} + +static void +account_prefs_delete_account (EAccountManager *manager) +{ + EMAccountPrefsPrivate *priv; + EAccountTreeView *tree_view; + EAccountList *account_list; + EAccount *account; + gboolean has_proxies; + gpointer parent; + gint response; + + priv = EM_ACCOUNT_PREFS_GET_PRIVATE (manager); + + account_list = e_account_manager_get_account_list (manager); + tree_view = e_account_manager_get_tree_view (manager); + account = e_account_tree_view_get_selected (tree_view); + g_return_if_fail (account != NULL); + + /* Make sure we aren't editing anything... */ + if (priv->editor != NULL) + return; + + parent = gtk_widget_get_toplevel (GTK_WIDGET (manager)); + parent = GTK_WIDGET_TOPLEVEL (parent) ? parent : NULL; + + has_proxies = + e_account_list_account_has_proxies (account_list, account); + + response = e_error_run ( + parent, has_proxies ? + "mail:ask-delete-account-with-proxies" : + "mail:ask-delete-account", NULL); + + if (response != GTK_RESPONSE_YES) { + g_signal_stop_emission_by_name (manager, "delete-account"); + return; + } + + /* Remove the account from the folder tree. */ + if (account->enabled && account->source && account->source->url) + e_mail_store_remove_by_uri (account->source->url); + + /* Remove all the proxies the account has created. */ + if (has_proxies) + e_account_list_remove_account_proxies (account_list, account); + + /* Remove it from the config file. */ + e_account_list_remove (account_list, account); + + e_account_list_save (account_list); +} + +static void +account_prefs_dispose (GObject *object) +{ + EMAccountPrefsPrivate *priv; + + priv = EM_ACCOUNT_PREFS_GET_PRIVATE (object); + + if (priv->druid != NULL) { + g_object_remove_weak_pointer ( + G_OBJECT (priv->druid), &priv->druid); + priv->druid = NULL; + } + + if (priv->editor != NULL) { + g_object_remove_weak_pointer ( + G_OBJECT (priv->editor), &priv->editor); + priv->editor = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +account_prefs_class_init (EMAccountPrefsClass *class) +{ + GObjectClass *object_class; + EAccountManagerClass *account_manager_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMAccountPrefsPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = account_prefs_dispose; + + account_manager_class = E_ACCOUNT_MANAGER_CLASS (class); + account_manager_class->add_account = account_prefs_add_account; + account_manager_class->edit_account = account_prefs_edit_account; + account_manager_class->delete_account = account_prefs_delete_account; +} + +static void +account_prefs_init (EMAccountPrefs *prefs) +{ + EAccountManager *manager; + EAccountTreeView *tree_view; + + prefs->priv = EM_ACCOUNT_PREFS_GET_PRIVATE (prefs); + + manager = E_ACCOUNT_MANAGER (prefs); + tree_view = e_account_manager_get_tree_view (manager); + + g_signal_connect ( + tree_view, "enable-account", + G_CALLBACK (account_prefs_enable_account_cb), NULL); + + g_signal_connect ( + tree_view, "disable-account", + G_CALLBACK (account_prefs_disable_account_cb), NULL); +} + +GType +em_account_prefs_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMAccountPrefsClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) account_prefs_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMAccountPrefs), + 0, /* n_preallocs */ + (GInstanceInitFunc) account_prefs_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + E_TYPE_ACCOUNT_MANAGER, "EMAccountPrefs", + &type_info, 0); + } + + return type; +} + +GtkWidget * +em_account_prefs_new (EAccountList *account_list) +{ + g_return_val_if_fail (E_IS_ACCOUNT_LIST (account_list), NULL); + + return g_object_new ( + EM_TYPE_ACCOUNT_PREFS, "account-list", account_list, NULL); +} diff --git a/modules/mail/em-account-prefs.h b/modules/mail/em-account-prefs.h new file mode 100644 index 0000000000..82df8fa941 --- /dev/null +++ b/modules/mail/em-account-prefs.h @@ -0,0 +1,69 @@ +/* + * em-account-prefs.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 EM_ACCOUNT_PREFS_H +#define EM_ACCOUNT_PREFS_H + +#include <gtk/gtk.h> +#include <table/e-table.h> +#include <libedataserver/e-account-list.h> +#include <misc/e-account-manager.h> + +/* Standard GObject macros */ +#define EM_TYPE_ACCOUNT_PREFS \ + (em_account_prefs_get_type ()) +#define EM_ACCOUNT_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefs)) +#define EM_ACCOUNT_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefsClass)) +#define EM_IS_ACCOUNT_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), EM_TYPE_ACCOUNT_PREFS)) +#define EM_IS_ACCOUNT_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), EM_TYPE_ACCOUNT_PREFS)) +#define EM_ACCOUNT_PREFS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), EM_TYPE_ACCOUNT_PREFS, EMAccountPrefsClass)) + +G_BEGIN_DECLS + +typedef struct _EMAccountPrefs EMAccountPrefs; +typedef struct _EMAccountPrefsClass EMAccountPrefsClass; +typedef struct _EMAccountPrefsPrivate EMAccountPrefsPrivate; + +struct _EMAccountPrefs { + EAccountManager parent; + EMAccountPrefsPrivate *priv; +}; + +struct _EMAccountPrefsClass { + EAccountManagerClass parent_class; +}; + +GType em_account_prefs_get_type (void); +GtkWidget * em_account_prefs_new (EAccountList *account_list); + +G_END_DECLS + +#endif /* EM_ACCOUNT_PREFS_H */ diff --git a/modules/mail/em-composer-prefs.c b/modules/mail/em-composer-prefs.c new file mode 100644 index 0000000000..18dae567ca --- /dev/null +++ b/modules/mail/em-composer-prefs.c @@ -0,0 +1,621 @@ +/* + * 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/> + * + * + * Authors: + * Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#include "e-util/e-binding.h" +#include "e-util/e-signature-utils.h" +#include "e-util/gconf-bridge.h" + +#include "em-composer-prefs.h" +#include "composer/e-msg-composer.h" + +#include <camel/camel-iconv.h> + +#include <misc/e-gui-utils.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <gtkhtml/gtkhtml.h> +#include <editor/gtkhtml-spell-language.h> + +#include "misc/e-charset-picker.h" +#include "misc/e-signature-editor.h" +#include "misc/e-signature-manager.h" +#include "misc/e-signature-preview.h" +#include "e-util/e-error.h" +#include "e-util/e-util-private.h" + +#include "mail-config.h" +#include "em-config.h" + +static gpointer parent_class; + +static gboolean +transform_color_to_string (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + const GdkColor *color; + gchar *string; + + color = g_value_get_boxed (src_value); + string = gdk_color_to_string (color); + g_value_set_string (dst_value, string); + g_free (string); + + return TRUE; +} + +static gboolean +transform_string_to_color (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + GdkColor color; + const gchar *string; + gboolean success = FALSE; + + string = g_value_get_string (src_value); + if (gdk_color_parse (string, &color)) { + g_value_set_boxed (dst_value, &color); + success = TRUE; + } + + return success; +} + +static gboolean +transform_old_to_new_reply_style (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gboolean success = TRUE; + + /* XXX This is the kind of legacy crap we wind up + * with when we don't migrate things properly. */ + + switch (g_value_get_int (src_value)) { + case 0: /* Quoted: 0 -> 2 */ + g_value_set_int (dst_value, 2); + break; + + case 1: /* Do Not Quote: 1 -> 3 */ + g_value_set_int (dst_value, 3); + break; + + case 2: /* Attach: 2 -> 0 */ + g_value_set_int (dst_value, 0); + break; + + case 3: /* Outlook: 3 -> 1 */ + g_value_set_int (dst_value, 1); + break; + + default: + success = FALSE; + break; + } + + return success; +} + +static gboolean +transform_new_to_old_reply_style (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gboolean success = TRUE; + + /* XXX This is the kind of legacy crap we wind up + * with when we don't migrate things properly. */ + + switch (g_value_get_int (src_value)) { + case 0: /* Attach: 0 -> 2 */ + g_value_set_int (dst_value, 2); + break; + + case 1: /* Outlook: 1 -> 3 */ + g_value_set_int (dst_value, 3); + break; + + case 2: /* Quoted: 2 -> 0 */ + g_value_set_int (dst_value, 0); + break; + + case 3: /* Do Not Quote: 3 -> 1 */ + g_value_set_int (dst_value, 1); + break; + + default: + success = FALSE; + break; + } + + return success; +} + +static void +composer_prefs_finalize (GObject *object) +{ + EMComposerPrefs *prefs = (EMComposerPrefs *) object; + + g_object_unref (prefs->gui); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +composer_prefs_class_init (EMComposerPrefsClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = composer_prefs_finalize; +} + +static void +composer_prefs_init (EMComposerPrefs *prefs) +{ +} + +GType +em_composer_prefs_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMComposerPrefsClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) composer_prefs_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMComposerPrefs), + 0, /* n_allocs */ + (GInstanceInitFunc) composer_prefs_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + GTK_TYPE_VBOX, "EMComposerPrefs", &type_info, 0); + } + + return type; +} + +void +em_composer_prefs_new_signature (GtkWindow *parent, + gboolean html_mode) +{ + GtkWidget *editor; + + editor = e_signature_editor_new (); + gtkhtml_editor_set_html_mode (GTKHTML_EDITOR (editor), html_mode); + gtk_window_set_transient_for (GTK_WINDOW (editor), parent); + gtk_widget_show (editor); +} + +static void +spell_language_toggled_cb (GtkCellRendererToggle *renderer, + const gchar *path_string, + EMComposerPrefs *prefs) +{ + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + gboolean active; + gboolean valid; + + model = prefs->language_model; + + /* Convert the path string to a tree iterator. */ + path = gtk_tree_path_new_from_string (path_string); + valid = gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + g_return_if_fail (valid); + + /* Toggle the active state. */ + gtk_tree_model_get (model, &iter, 0, &active, -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, !active, -1); +} + +static void +spell_language_save (EMComposerPrefs *prefs) +{ + GList *spell_languages = NULL; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + + model = prefs->language_model; + + /* Build a list of active spell languages. */ + valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) { + const GtkhtmlSpellLanguage *language; + gboolean active; + + gtk_tree_model_get ( + model, &iter, 0, &active, 2, &language, -1); + + if (active) + spell_languages = g_list_prepend ( + spell_languages, (gpointer) language); + + valid = gtk_tree_model_iter_next (model, &iter); + } + spell_languages = g_list_reverse (spell_languages); + + /* Update the GConf value. */ + e_save_spell_languages (spell_languages); + + g_list_free (spell_languages); +} + +static void +spell_setup (EMComposerPrefs *prefs) +{ + const GList *available_languages; + GList *active_languages; + GtkListStore *store; + + store = GTK_LIST_STORE (prefs->language_model); + available_languages = gtkhtml_spell_language_get_available (); + + active_languages = e_load_spell_languages (); + + /* Populate the GtkListStore. */ + while (available_languages != NULL) { + const GtkhtmlSpellLanguage *language; + GtkTreeIter tree_iter; + const gchar *name; + gboolean active; + + language = available_languages->data; + name = gtkhtml_spell_language_get_name (language); + active = (g_list_find (active_languages, language) != NULL); + + gtk_list_store_append (store, &tree_iter); + + gtk_list_store_set ( + store, &tree_iter, + 0, active, 1, name, 2, language, -1); + + available_languages = available_languages->next; + } + + g_list_free (active_languages); +} + +static void +charset_activate (GtkWidget *item, + EMComposerPrefs *prefs) +{ + GConfClient *client; + GtkWidget *menu; + gchar *string; + + client = mail_config_get_gconf_client (); + menu = gtk_option_menu_get_menu (prefs->charset); + string = e_charset_picker_get_charset (menu); + + if (string == NULL) + string = g_strdup (camel_iconv_locale_charset ()); + + gconf_client_set_string ( + client, "/apps/evolution/mail/composer/charset", + string, NULL); + + g_free (string); +} + +static void +option_menu_connect (EMComposerPrefs *prefs, + GtkOptionMenu *omenu, + GCallback callback, + const gchar *key) +{ + GConfClient *client; + GtkWidget *menu; + GList *list; + + client = mail_config_get_gconf_client (); + menu = gtk_option_menu_get_menu (omenu); + list = GTK_MENU_SHELL (menu)->children; + + while (list != NULL) { + GtkWidget *widget = list->data; + + g_object_set_data (G_OBJECT (widget), "key", (gpointer) key); + g_signal_connect (widget, "activate", callback, prefs); + list = list->next; + } + + if (!gconf_client_key_is_writable (client, key, NULL)) + gtk_widget_set_sensitive (GTK_WIDGET (omenu), FALSE); +} + +static GtkWidget * +emcp_widget_glade (EConfig *ec, + EConfigItem *item, + GtkWidget *parent, + GtkWidget *old, + gpointer data) +{ + EMComposerPrefs *prefs = data; + + return glade_xml_get_widget (prefs->gui, item->label); +} + +/* plugin meta-data */ +static EMConfigItem emcp_items[] = { + { E_CONFIG_BOOK, (gchar *) "", (gchar *) "composer_toplevel", emcp_widget_glade }, + { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "vboxGeneral", emcp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/00.behavior", (gchar *) "vboxBehavior", emcp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/10.alerts", (gchar *) "vboxAlerts", emcp_widget_glade }, + { E_CONFIG_PAGE, (gchar *) "10.signatures", (gchar *) "vboxSignatures", emcp_widget_glade }, + /* signature/signatures and signature/preview parts not usable */ + + { E_CONFIG_PAGE, (gchar *) "20.spellcheck", (gchar *) "vboxSpellChecking", emcp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "20.spellcheck/00.languages", (gchar *) "vbox178", emcp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "20.spellcheck/00.options", (gchar *) "vboxOptions", emcp_widget_glade }, +}; + +static void +emcp_free (EConfig *ec, GSList *items, gpointer data) +{ + /* the prefs data is freed automagically */ + g_slist_free (items); +} + +static void +em_composer_prefs_construct (EMComposerPrefs *prefs, + EShell *shell) +{ + GtkWidget *toplevel, *widget, *menu, *info_pixmap; + GtkWidget *container; + EShellSettings *shell_settings; + ESignatureList *signature_list; + ESignatureTreeView *signature_tree_view; + GladeXML *gui; + GtkTreeView *view; + GtkListStore *store; + GtkTreeSelection *selection; + GtkCellRenderer *renderer; + GConfBridge *bridge; + GConfClient *client; + gchar *buf; + EMConfig *ec; + EMConfigTargetPrefs *target; + GSList *l; + gint i; + gchar *gladefile; + + bridge = gconf_bridge_get (); + client = mail_config_get_gconf_client (); + shell_settings = e_shell_get_shell_settings (shell); + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + gui = glade_xml_new (gladefile, "composer_toplevel", NULL); + prefs->gui = gui; + g_free (gladefile); + + /** @HookPoint-EMConfig: Mail Composer Preferences + * @Id: org.gnome.evolution.mail.composerPrefs + * @Type: E_CONFIG_BOOK + * @Class: org.gnome.evolution.mail.config:1.0 + * @Target: EMConfigTargetPrefs + * + * The mail composer preferences settings page. + */ + ec = em_config_new(E_CONFIG_BOOK, "org.gnome.evolution.mail.composerPrefs"); + l = NULL; + for (i = 0; i < G_N_ELEMENTS (emcp_items); i++) + l = g_slist_prepend(l, &emcp_items[i]); + e_config_add_items((EConfig *)ec, l, NULL, NULL, emcp_free, prefs); + + /* General tab */ + + /* Default Behavior */ + widget = glade_xml_get_widget (gui, "chkSendHTML"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-format-html", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkPromptEmptySubject"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-prompt-empty-subject", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkPromptBccOnly"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-prompt-only-bcc", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkAutoSmileys"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-magic-smileys", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkRequestReceipt"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-request-receipt", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkReplyStartBottom"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-reply-start-bottom", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkOutlookFilenames"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-outlook-filenames", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkTopSignature"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-top-signature", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkEnableSpellChecking"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "composer-inline-spelling", + G_OBJECT (widget), "active"); + + prefs->charset = GTK_OPTION_MENU ( + glade_xml_get_widget (gui, "omenuCharset1")); + buf = gconf_client_get_string ( + client, "/apps/evolution/mail/composer/charset", NULL); + menu = e_charset_picker_new ( + buf && *buf ? buf : camel_iconv_locale_charset ()); + gtk_option_menu_set_menu (prefs->charset, GTK_WIDGET (menu)); + option_menu_connect ( + prefs, prefs->charset, + G_CALLBACK (charset_activate), + "/apps/evolution/mail/composer/charset"); + g_free (buf); + + /* Spell Checking */ + widget = glade_xml_get_widget (gui, "listSpellCheckLanguage"); + view = GTK_TREE_VIEW (widget); + store = gtk_list_store_new ( + 3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER); + g_signal_connect_swapped ( + store, "row-changed", + G_CALLBACK (spell_language_save), prefs); + prefs->language_model = GTK_TREE_MODEL (store); + gtk_tree_view_set_model (view, prefs->language_model); + renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect ( + renderer, "toggled", + G_CALLBACK (spell_language_toggled_cb), prefs); + gtk_tree_view_insert_column_with_attributes ( + view, -1, _("Enabled"), + renderer, "active", 0, NULL); + + gtk_tree_view_insert_column_with_attributes ( + view, -1, _("Language(s)"), + gtk_cell_renderer_text_new (), + "text", 1, NULL); + selection = gtk_tree_view_get_selection (view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE); + info_pixmap = glade_xml_get_widget (gui, "pixmapSpellInfo"); + gtk_image_set_from_stock ( + GTK_IMAGE (info_pixmap), + GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON); + + widget = glade_xml_get_widget (gui, "colorButtonSpellCheckColor"); + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "composer-spell-color", + G_OBJECT (widget), "color", + transform_string_to_color, + transform_color_to_string, + NULL, NULL); + + spell_setup (prefs); + + /* Forwards and Replies */ + widget = glade_xml_get_widget (gui, "comboboxForwardStyle"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-forward-style", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "comboboxReplyStyle"); + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "mail-reply-style", + G_OBJECT (widget), "active", + transform_old_to_new_reply_style, + transform_new_to_old_reply_style, + NULL, NULL); + + /* Signatures */ + signature_list = e_get_signature_list (); + container = glade_xml_get_widget (gui, "alignSignatures"); + widget = e_signature_manager_new (signature_list); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + /* The mail shell backend responds to the "window-created" signal + * that this triggers and configures it with composer preferences. */ + g_signal_connect_swapped ( + widget, "editor-created", + G_CALLBACK (e_shell_watch_window), shell); + + e_binding_new ( + G_OBJECT (shell_settings), "composer-format-html", + G_OBJECT (widget), "prefer-html"); + + e_binding_new_with_negation ( + G_OBJECT (shell_settings), "disable-command-line", + G_OBJECT (widget), "allow-scripts"); + + signature_tree_view = e_signature_manager_get_tree_view ( + E_SIGNATURE_MANAGER (widget)); + + container = glade_xml_get_widget (gui, "scrolled-sig"); + widget = e_signature_preview_new (); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + e_binding_new_with_negation ( + G_OBJECT (shell_settings), "disable-command-line", + G_OBJECT (widget), "allow-scripts"); + + e_binding_new ( + G_OBJECT (signature_tree_view), "selected", + G_OBJECT (widget), "signature"); + + /* get our toplevel widget */ + target = em_config_target_new_prefs (ec, client); + e_config_set_target ((EConfig *)ec, (EConfigTarget *)target); + toplevel = e_config_create_widget ((EConfig *)ec); + gtk_container_add (GTK_CONTAINER (prefs), toplevel); +} + +GtkWidget * +em_composer_prefs_new (EShell *shell) +{ + EMComposerPrefs *prefs; + + g_return_val_if_fail (E_IS_SHELL (shell), NULL); + + prefs = g_object_new (EM_TYPE_COMPOSER_PREFS, NULL); + em_composer_prefs_construct (prefs, shell); + + return GTK_WIDGET (prefs); +} diff --git a/modules/mail/em-composer-prefs.h b/modules/mail/em-composer-prefs.h new file mode 100644 index 0000000000..3b848f1a2f --- /dev/null +++ b/modules/mail/em-composer-prefs.h @@ -0,0 +1,93 @@ +/* + * 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/> + * + * + * Authors: + * Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef EM_COMPOSER_PREFS_H +#define EM_COMPOSER_PREFS_H + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <gtkhtml/gtkhtml.h> + +#include <shell/e-shell.h> + +/* Standard GObject macros */ +#define EM_TYPE_COMPOSER_PREFS \ + (em_composer_prefs_get_type ()) +#define EM_COMPOSER_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), EM_TYPE_COMPOSER_PREFS, EMComposerPrefs)) +#define EM_COMPOSER_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), EM_TYPE_COMPOSER_PREFS, EMComposerPrefsClass)) +#define EM_IS_COMPOSER_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), EM_TYPE_COMPOSER_PREFS)) +#define EM_IS_COMPOSER_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), EM_TYPE_COMPOSER_PREFS)) +#define EM_COMPOSER_PREFS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), EM_TYPE_COMPOSER_PREFS, EMComposerPrefsClass)) + +G_BEGIN_DECLS + +typedef struct _EMComposerPrefs EMComposerPrefs; +typedef struct _EMComposerPrefsClass EMComposerPrefsClass; + +struct _ESignature; + +struct _EMComposerPrefs { + GtkVBox parent; + + GladeXML *gui; + + /* General tab */ + + /* Default Behavior */ + GtkOptionMenu *charset; + + GtkTreeModel *language_model; + + /* Forwards and Replies */ + GtkComboBox *forward_style; + GtkComboBox *reply_style; + + /* Signatures */ + GtkHTML *sig_preview; +}; + +struct _EMComposerPrefsClass { + GtkVBoxClass parent_class; +}; + +GType em_composer_prefs_get_type (void); +GtkWidget * em_composer_prefs_new (EShell *shell); +void em_composer_prefs_new_signature (GtkWindow *parent, + gboolean html_mode); + +/* needed by global config */ +#define EM_COMPOSER_PREFS_CONTROL_ID \ + "OAFIID:GNOME_Evolution_Mail_ComposerPrefs_ConfigControl:" BASE_VERSION + +G_END_DECLS + +#endif /* EM_COMPOSER_PREFS_H */ diff --git a/modules/mail/em-mailer-prefs.c b/modules/mail/em-mailer-prefs.c new file mode 100644 index 0000000000..fa450dc2ee --- /dev/null +++ b/modules/mail/em-mailer-prefs.c @@ -0,0 +1,1332 @@ +/* + * 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/> + * + * + * Authors: + * Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib/gi18n-lib.h> + +#include "em-mailer-prefs.h" +#include "em-format/em-format.h" + +#include <camel/camel-iconv.h> +#include <gtkhtml/gtkhtml-properties.h> +#include <libxml/tree.h> +#include "misc/e-charset-picker.h" + +#include <glade/glade.h> + +#include <gconf/gconf-client.h> + +#include "libedataserverui/e-cell-renderer-color.h" + +#include "e-util/e-binding.h" +#include "e-util/e-util-private.h" + +#include "e-mail-label-manager.h" +#include "mail-config.h" +#include "em-junk-hook.h" +#include "em-config.h" +#include "mail-session.h" + +static void em_mailer_prefs_class_init (EMMailerPrefsClass *class); +static void em_mailer_prefs_init (EMMailerPrefs *dialog); +static void em_mailer_prefs_finalize (GObject *object); + +static GtkVBoxClass *parent_class = NULL; + +enum { + HEADER_LIST_NAME_COLUMN, /* displayable name of the header (may be a translation) */ + HEADER_LIST_ENABLED_COLUMN, /* is the header enabled? */ + HEADER_LIST_IS_DEFAULT_COLUMN, /* is this header a default header, eg From: */ + HEADER_LIST_HEADER_COLUMN, /* the real name of this header */ + HEADER_LIST_N_COLUMNS +}; + +static GType col_types[] = { + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_STRING +}; + +/* temporarily copied from em-format.c */ +static const gchar *default_headers[] = { + N_("From"), + N_("Reply-To"), + N_("To"), + N_("Cc"), + N_("Bcc"), + N_("Subject"), + N_("Date"), + N_("Newsgroups"), + N_("Face"), + "x-evolution-mailer", /* DO NOT translate */ +}; + +#define EM_FORMAT_HEADER_XMAILER "x-evolution-mailer" + +/* for empty trash on exit frequency */ +static const struct { + const gchar *label; + gint days; +} empty_trash_frequency[] = { + { N_("Every time"), 0 }, + { N_("Once per day"), 1 }, + { N_("Once per week"), 7 }, + { N_("Once per month"), 30 }, +}; + +GType +em_mailer_prefs_get_type (void) +{ + static GType type = 0; + + if (!type) { + GTypeInfo type_info = { + sizeof (EMMailerPrefsClass), + NULL, NULL, + (GClassInitFunc) em_mailer_prefs_class_init, + NULL, NULL, + sizeof (EMMailerPrefs), + 0, + (GInstanceInitFunc) em_mailer_prefs_init, + }; + + type = g_type_register_static (gtk_vbox_get_type (), "EMMailerPrefs", &type_info, 0); + } + + return type; +} + +static void +em_mailer_prefs_class_init (EMMailerPrefsClass *klass) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *) klass; + parent_class = g_type_class_ref (gtk_vbox_get_type ()); + + object_class->finalize = em_mailer_prefs_finalize; +} + +static void +em_mailer_prefs_init (EMMailerPrefs *preferences) +{ + preferences->gconf = mail_config_get_gconf_client (); +} + +static void +em_mailer_prefs_finalize (GObject *obj) +{ + EMMailerPrefs *prefs = (EMMailerPrefs *) obj; + + g_object_unref (prefs->gui); + + if (prefs->labels_change_notify_id) { + gconf_client_notify_remove (prefs->gconf, prefs->labels_change_notify_id); + + prefs->labels_change_notify_id = 0; + } + + ((GObjectClass *)(parent_class))->finalize (obj); +} + +static gboolean +mark_seen_timeout_transform (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gdouble v_double; + + /* Shell Settings (gint) -> Spin Button (double) */ + v_double = (gdouble) g_value_get_int (src_value); + g_value_set_double (dst_value, v_double / 1000.0); + + return TRUE; +} + +static gboolean +mark_seen_timeout_reverse_transform (const GValue *src_value, + GValue *dst_value, + gpointer user_data) +{ + gdouble v_double; + + /* Spin Button (double) -> Shell Settings (gint) */ + v_double = g_value_get_double (src_value); + g_value_set_int (dst_value, v_double * 1000); + + return TRUE; +} + +enum { + JH_LIST_COLUMN_NAME, + JH_LIST_COLUMN_VALUE +}; + +static void +jh_tree_refill (EMMailerPrefs *prefs) +{ + GtkListStore *store = prefs->junk_header_list_store; + GSList *l, *cjh = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, NULL); + + gtk_list_store_clear (store); + + for (l = cjh; l; l = l->next) { + GtkTreeIter iter; + gchar **tokens = g_strsplit (l->data, "=", 2); + + gtk_list_store_append (store, &iter); + gtk_list_store_set ( + store, &iter, + JH_LIST_COLUMN_NAME , tokens[0] ? tokens[0] : "", + JH_LIST_COLUMN_VALUE, tokens[1] ? tokens[1] : "" , + -1); + g_strfreev (tokens); + } + + g_slist_foreach (cjh, (GFunc) g_free, NULL); + g_slist_free (cjh); +} + +static void +jh_add_cb (GtkWidget *widget, gpointer user_data) +{ + EMMailerPrefs *prefs = (EMMailerPrefs *) user_data; + GtkWidget *dialog, *l1, *l2, *entry1, *entry2, *vbox, *hbox; + gint response; + dialog = gtk_dialog_new_with_buttons (_("Add Custom Junk Header"), (GtkWindow *)gtk_widget_get_toplevel (widget), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); + + vbox = gtk_vbox_new (FALSE, 6); + hbox = gtk_hbox_new (FALSE, 0); + l1 = gtk_label_new_with_mnemonic (_("Header Name:")); + l2 = gtk_label_new_with_mnemonic (_("Header Value Contains:")); + entry1 = gtk_entry_new (); + entry2 = gtk_entry_new (); + gtk_box_pack_start ((GtkBox *) hbox, l1, FALSE, FALSE, 6); + gtk_box_pack_start ((GtkBox *)hbox, entry1, FALSE, FALSE, 6); + gtk_box_pack_start ((GtkBox *)vbox, hbox, FALSE, FALSE, 6); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start ((GtkBox *)hbox, l2, FALSE, FALSE, 6); + gtk_box_pack_start ((GtkBox *)hbox, entry2, FALSE, FALSE, 6); + gtk_box_pack_start ((GtkBox *)vbox, hbox, FALSE, FALSE, 6); + + gtk_widget_show_all (vbox); + gtk_container_add ((GtkContainer *)((GtkDialog *)dialog)->vbox, vbox); + response = gtk_dialog_run ((GtkDialog *)dialog); + if (response == GTK_RESPONSE_ACCEPT) { + const gchar *name = gtk_entry_get_text ((GtkEntry *)entry1); + const gchar *value = gtk_entry_get_text ((GtkEntry *)entry2); + gchar *tok; + GSList *list = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, NULL); + + /* FIXME: Validate the values */ + + tok = g_strdup_printf ("%s=%s", name, value); + list = g_slist_append (list, tok); + gconf_client_set_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, list, NULL); + g_slist_foreach (list, (GFunc)g_free, NULL); + + g_slist_free (list); + } + gtk_widget_destroy (dialog); + jh_tree_refill (prefs); +} + +static void +jh_remove_cb (GtkWidget *widget, gpointer user_data) +{ + EMMailerPrefs *prefs = user_data; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + + g_return_if_fail (prefs != NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->junk_header_tree)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gchar *name=NULL, *value=NULL; + GSList *prev = NULL, *node, *list = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, NULL); + gtk_tree_model_get (model, &iter, JH_LIST_COLUMN_NAME, &name, JH_LIST_COLUMN_VALUE, &value, -1); + node = list; + while (node) { + gchar *test; + gint len = strlen (name); + test = strncmp (node->data, name, len) == 0 ? (gchar *) node->data+len:NULL; + + if (test) { + test++; + if (strcmp (test, value) == 0) + break; + } + + prev = node; + node = node->next; + } + + if (prev && !node) { + /* Not found. So what? */ + } else if (prev && node) { + prev->next = node->next; + g_free (node->data); + } else if (!prev && node) { + list = list->next; + g_free (node->data); + } + + gconf_client_set_list (prefs->gconf, "/apps/evolution/mail/junk/custom_header", GCONF_VALUE_STRING, list, NULL); + + g_slist_foreach (list, (GFunc)g_free, NULL); + g_slist_free (list); + g_free (name); + g_free (value); + + jh_tree_refill (prefs); + } +} + + +static GtkListStore * +init_junk_tree (GtkWidget *label_tree, EMMailerPrefs *prefs) +{ + GtkListStore *store; + GtkCellRenderer *renderer; + gint col; + + g_return_val_if_fail (label_tree != NULL, NULL); + g_return_val_if_fail (prefs != NULL, NULL); + + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (label_tree), GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_text_new (); + col = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (label_tree), -1, _("Header"), renderer, "text", JH_LIST_COLUMN_NAME, NULL); + g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (label_tree), -1, _("Contains Value"), renderer, "text", JH_LIST_COLUMN_VALUE, NULL); + g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL); + + return store; +} + +static void +emmp_header_remove_sensitivity (EMMailerPrefs *prefs) +{ + GtkTreeIter iter; + GtkTreeSelection *selection = gtk_tree_view_get_selection (prefs->header_list); + gboolean is_default; + + /* remove button should be sensitive if the currenlty selected entry in the list view + is not a default header. if there are no entries, or none is selected, it should be + disabled + */ + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (prefs->header_list_store), &iter, + HEADER_LIST_IS_DEFAULT_COLUMN, &is_default, + -1); + if (is_default) + gtk_widget_set_sensitive (GTK_WIDGET (prefs->remove_header), FALSE); + else + gtk_widget_set_sensitive (GTK_WIDGET (prefs->remove_header), TRUE); + } else { + gtk_widget_set_sensitive (GTK_WIDGET (prefs->remove_header), FALSE); + } +} + +static gboolean +emmp_header_is_valid (const gchar *header) +{ + gint len = g_utf8_strlen (header, -1); + + if (header[0] == 0 + || g_utf8_strchr (header, len, ':') != NULL + || g_utf8_strchr (header, len, ' ') != NULL) + return FALSE; + + return TRUE; +} + +static void +emmp_header_add_sensitivity (EMMailerPrefs *prefs) +{ + const gchar *entry_contents; + GtkTreeIter iter; + gboolean valid; + + /* the add header button should be sensitive if the text box contains + a valid header string, that is not a duplicate with something already + in the list view + */ + entry_contents = gtk_entry_get_text (GTK_ENTRY (prefs->entry_header)); + if (!emmp_header_is_valid (entry_contents)) { + gtk_widget_set_sensitive (GTK_WIDGET (prefs->add_header), FALSE); + return; + } + + /* check if this is a duplicate */ + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (prefs->header_list_store), &iter); + while (valid) { + gchar *header_name; + + gtk_tree_model_get (GTK_TREE_MODEL (prefs->header_list_store), &iter, + HEADER_LIST_HEADER_COLUMN, &header_name, + -1); + if (g_ascii_strcasecmp (header_name, entry_contents) == 0) { + gtk_widget_set_sensitive (GTK_WIDGET (prefs->add_header), FALSE); + return; + } + + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (prefs->header_list_store), &iter); + } + + gtk_widget_set_sensitive (GTK_WIDGET (prefs->add_header), TRUE); +} + +static void +emmp_save_headers (EMMailerPrefs *prefs) +{ + GSList *header_list; + GtkTreeIter iter; + gboolean valid; + + /* Headers */ + header_list = NULL; + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (prefs->header_list_store), &iter); + while (valid) { + struct _EMMailerPrefsHeader h; + gboolean enabled; + gchar *xml; + + gtk_tree_model_get (GTK_TREE_MODEL (prefs->header_list_store), &iter, + HEADER_LIST_HEADER_COLUMN, &h.name, + HEADER_LIST_ENABLED_COLUMN, &enabled, + -1); + h.enabled = enabled; + + if ((xml = em_mailer_prefs_header_to_xml (&h))) + header_list = g_slist_append (header_list, xml); + + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (prefs->header_list_store), &iter); + } + + gconf_client_set_list (prefs->gconf, "/apps/evolution/mail/display/headers", GCONF_VALUE_STRING, header_list, NULL); + g_slist_foreach (header_list, (GFunc) g_free, NULL); + g_slist_free (header_list); +} + +static void +emmp_header_list_enabled_toggled (GtkCellRendererToggle *cell, const gchar *path_string, EMMailerPrefs *prefs) +{ + GtkTreeModel *model = GTK_TREE_MODEL (prefs->header_list_store); + GtkTreePath *path = gtk_tree_path_new_from_string (path_string); + GtkTreeIter iter; + gint enabled; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, HEADER_LIST_ENABLED_COLUMN, &enabled, -1); + enabled = !enabled; + gtk_list_store_set (GTK_LIST_STORE (model), &iter, HEADER_LIST_ENABLED_COLUMN, + enabled, -1); + gtk_tree_path_free (path); + + emmp_save_headers (prefs); +} + +static void +emmp_header_add_header (GtkWidget *widget, EMMailerPrefs *prefs) +{ + GtkTreeModel *model = GTK_TREE_MODEL (prefs->header_list_store); + GtkTreeIter iter; + const gchar *text = gtk_entry_get_text (prefs->entry_header); + + g_strstrip ((gchar *)text); + + if (text && (strlen (text)>0)) { + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, + HEADER_LIST_NAME_COLUMN, text, + HEADER_LIST_ENABLED_COLUMN, TRUE, + HEADER_LIST_HEADER_COLUMN, text, + HEADER_LIST_IS_DEFAULT_COLUMN, FALSE, + -1); + gtk_entry_set_text (prefs->entry_header, ""); + emmp_header_remove_sensitivity (prefs); + emmp_header_add_sensitivity (prefs); + + emmp_save_headers (prefs); + } +} + +static void +emmp_header_remove_header (GtkWidget *button, gpointer user_data) +{ + EMMailerPrefs *prefs = (EMMailerPrefs *) user_data; + GtkTreeModel *model = GTK_TREE_MODEL (prefs->header_list_store); + GtkTreeSelection *selection = gtk_tree_view_get_selection (prefs->header_list); + GtkTreeIter iter; + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + emmp_header_remove_sensitivity (prefs); + + emmp_save_headers (prefs); +} + +static void +emmp_header_list_row_selected (GtkTreeSelection *selection, gpointer user_data) +{ + EMMailerPrefs *prefs = (EMMailerPrefs *) user_data; + + emmp_header_remove_sensitivity (prefs); +} + +static void +emmp_header_entry_changed (GtkWidget *entry, gpointer user_data) +{ + EMMailerPrefs *prefs = (EMMailerPrefs *) user_data; + + emmp_header_add_sensitivity (prefs); +} + +static void +toggle_button_toggled (GtkToggleButton *toggle, EMMailerPrefs *prefs) +{ + const gchar *key; + + key = g_object_get_data ((GObject *) toggle, "key"); + gconf_client_set_bool (prefs->gconf, key, gtk_toggle_button_get_active (toggle), NULL); +} + +static void +junk_book_lookup_button_toggled (GtkToggleButton *toggle, EMMailerPrefs *prefs) +{ + toggle_button_toggled (toggle, prefs); + gtk_widget_set_sensitive (GTK_WIDGET (prefs->junk_lookup_local_only), gtk_toggle_button_get_active (toggle)); +} + +static void +custom_junk_button_toggled (GtkToggleButton *toggle, EMMailerPrefs *prefs) +{ + toggle_button_toggled (toggle, prefs); + if (gtk_toggle_button_get_active (toggle)) { + gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_remove, TRUE); + gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_add, TRUE); + gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_tree, TRUE); + } else { + gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_tree, FALSE); + gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_add, FALSE); + gtk_widget_set_sensitive ((GtkWidget *) prefs->junk_header_remove, FALSE); + } + + +} + +static void +toggle_button_init (EMMailerPrefs *prefs, GtkToggleButton *toggle, gint not, const gchar *key, GCallback toggled) +{ + gboolean bool; + + bool = gconf_client_get_bool (prefs->gconf, key, NULL); + gtk_toggle_button_set_active (toggle, not ? !bool : bool); + + if (toggled) { + g_object_set_data ((GObject *) toggle, "key", (gpointer) key); + g_signal_connect (toggle, "toggled", toggled, prefs); + } + + if (!gconf_client_key_is_writable (prefs->gconf, key, NULL)) + gtk_widget_set_sensitive ((GtkWidget *) toggle, FALSE); +} + +static void +charset_activate (GtkWidget *item, EMMailerPrefs *prefs) +{ + GtkWidget *menu; + gchar *string; + + menu = gtk_option_menu_get_menu (prefs->charset); + if (!(string = e_charset_picker_get_charset (menu))) + string = g_strdup (camel_iconv_locale_charset ()); + + gconf_client_set_string (prefs->gconf, "/apps/evolution/mail/display/charset", string, NULL); + g_free (string); +} + +static void +charset_menu_init (EMMailerPrefs *prefs) +{ + GtkWidget *menu, *item; + GList *items; + gchar *buf; + + buf = gconf_client_get_string (prefs->gconf, "/apps/evolution/mail/display/charset", NULL); + menu = e_charset_picker_new (buf && *buf ? buf : camel_iconv_locale_charset ()); + gtk_option_menu_set_menu (prefs->charset, GTK_WIDGET (menu)); + g_free (buf); + + items = GTK_MENU_SHELL (menu)->children; + while (items) { + item = items->data; + g_signal_connect (item, "activate", G_CALLBACK (charset_activate), prefs); + items = items->next; + } + + if (!gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/display/charset", NULL)) + gtk_widget_set_sensitive ((GtkWidget *) prefs->charset, FALSE); +} + +static void +trash_days_activate (GtkWidget *item, EMMailerPrefs *prefs) +{ + gint days; + + days = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "days")); + gconf_client_set_int (prefs->gconf, "/apps/evolution/mail/trash/empty_on_exit_days", days, NULL); +} + +static void +emmp_empty_trash_init (EMMailerPrefs *prefs) +{ + gint locked, days, hist = 0, i; + GtkWidget *menu, *item; + + days = gconf_client_get_int(prefs->gconf, "/apps/evolution/mail/trash/empty_on_exit_days", NULL); + menu = gtk_menu_new(); + for (i = 0; i < G_N_ELEMENTS (empty_trash_frequency); i++) { + if (days >= empty_trash_frequency[i].days) + hist = i; + + item = gtk_menu_item_new_with_label (_(empty_trash_frequency[i].label)); + g_object_set_data ((GObject *) item, "days", GINT_TO_POINTER (empty_trash_frequency[i].days)); + g_signal_connect (item, "activate", G_CALLBACK (trash_days_activate), prefs); + + gtk_widget_show (item); + gtk_menu_shell_append((GtkMenuShell *)menu, item); + } + + gtk_widget_show(menu); + gtk_option_menu_set_menu((GtkOptionMenu *)prefs->empty_trash_days, menu); + gtk_option_menu_set_history((GtkOptionMenu *)prefs->empty_trash_days, hist); + + locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/trash/empty_on_exit_days", NULL); + gtk_widget_set_sensitive ((GtkWidget *) prefs->empty_trash_days, !locked); +} + +static void +junk_days_activate (GtkWidget *item, EMMailerPrefs *prefs) +{ + gint days; + + days = GPOINTER_TO_INT (g_object_get_data ((GObject *) item, "days")); + gconf_client_set_int (prefs->gconf, "/apps/evolution/mail/junk/empty_on_exit_days", days, NULL); +} + +static void +emmp_empty_junk_init (EMMailerPrefs *prefs) +{ + gint locked, days, hist = 0, i; + GtkWidget *menu, *item; + + toggle_button_init (prefs, prefs->empty_junk, FALSE, + "/apps/evolution/mail/junk/empty_on_exit", + G_CALLBACK (toggle_button_toggled)); + + days = gconf_client_get_int(prefs->gconf, "/apps/evolution/mail/junk/empty_on_exit_days", NULL); + menu = gtk_menu_new(); + for (i = 0; i < G_N_ELEMENTS (empty_trash_frequency); i++) { + if (days >= empty_trash_frequency[i].days) + hist = i; + + item = gtk_menu_item_new_with_label (_(empty_trash_frequency[i].label)); + g_object_set_data ((GObject *) item, "days", GINT_TO_POINTER (empty_trash_frequency[i].days)); + g_signal_connect (item, "activate", G_CALLBACK (junk_days_activate), prefs); + + gtk_widget_show (item); + gtk_menu_shell_append((GtkMenuShell *)menu, item); + } + + gtk_widget_show(menu); + gtk_option_menu_set_menu((GtkOptionMenu *)prefs->empty_junk_days, menu); + gtk_option_menu_set_history((GtkOptionMenu *)prefs->empty_junk_days, hist); + + locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/junk/empty_on_exit_days", NULL); + gtk_widget_set_sensitive ((GtkWidget *) prefs->empty_junk_days, !locked); +} + +static void +http_images_changed (GtkWidget *widget, EMMailerPrefs *prefs) +{ + gint when; + + if (gtk_toggle_button_get_active (prefs->images_always)) + when = MAIL_CONFIG_HTTP_ALWAYS; + else if (gtk_toggle_button_get_active (prefs->images_sometimes)) + when = MAIL_CONFIG_HTTP_SOMETIMES; + else + when = MAIL_CONFIG_HTTP_NEVER; + + gconf_client_set_int (prefs->gconf, "/apps/evolution/mail/display/load_http_images", when, NULL); +} + + +static GtkWidget * +emmp_widget_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMMailerPrefs *prefs = data; + + return glade_xml_get_widget(prefs->gui, item->label); +} + +/* plugin meta-data */ +static EMConfigItem emmp_items[] = { + { E_CONFIG_BOOK, (gchar *) "", (gchar *) "preferences_toplevel", emmp_widget_glade }, + { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "vboxGeneral", emmp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/00.fonts", (gchar *) "vboxMessageFonts", emmp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/10.display", (gchar *) "vboxMessageDisplay", emmp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/20.delete", (gchar *) "vboxDeletingMail", emmp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/30.newmail", (gchar *) "vboxNewMailNotify", emmp_widget_glade }, + { E_CONFIG_PAGE, (gchar *) "10.html", (gchar *) "vboxHtmlMail", emmp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "10.html/00.general", (gchar *) "vbox173", emmp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "10.html/10.images", (gchar *) "vbox190", emmp_widget_glade }, + { E_CONFIG_PAGE, (gchar *) "20.labels", (gchar *) "frameColours", emmp_widget_glade }, + /* this is a table, so we can't use it { E_CONFIG_SECTION, "20.labels/00.labels", "tableColours", emmp_widget_glade }, */ + { E_CONFIG_PAGE, (gchar *) "30.headers", (gchar *) "vboxHeaderTab", emmp_widget_glade }, + /* no subvbox for section { E_CONFIG_PAGE, "30.headers/00.headers", "vbox199", emmp_widget_glade }, */ + { E_CONFIG_PAGE, (gchar *) "40.junk", (gchar *) "vbox161", emmp_widget_glade }, + /* no subvbox for section { E_CONFIG_SECTION, "40.junk/00.general", xxx, emmp_widget_glade } */ + { E_CONFIG_SECTION, (gchar *) "40.junk/10.options", (gchar *) "vbox204", emmp_widget_glade }, +}; + +static void +emmp_free(EConfig *ec, GSList *items, gpointer data) +{ + /* the prefs data is freed automagically */ + + g_slist_free(items); +} + +static void +junk_plugin_changed (GtkWidget *combo, EMMailerPrefs *prefs) +{ + gchar *def_plugin = gtk_combo_box_get_active_text(GTK_COMBO_BOX (combo)); + const GList *plugins = mail_session_get_junk_plugins(); + + gconf_client_set_string (prefs->gconf, "/apps/evolution/mail/junk/default_plugin", def_plugin, NULL); + while (plugins) { + struct _EMJunkHookItem *item = plugins->data;; + + if (item->plugin_name && def_plugin && !strcmp (item->plugin_name, def_plugin)) { + gboolean status; + + session->junk_plugin = CAMEL_JUNK_PLUGIN (&(item->csp)); + status = e_plugin_invoke (item->hook->hook.plugin, item->validate_binary, NULL) != NULL; + if ((gboolean)status == TRUE) { + gchar *text, *html; + gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-info", GTK_ICON_SIZE_MENU); + text = g_strdup_printf (_("%s plugin is available and the binary is installed."), item->plugin_name); + html = g_strdup_printf ("<i>%s</i>", text); + gtk_label_set_markup (prefs->plugin_status, html); + g_free (html); + g_free (text); + } else { + gchar *text, *html; + gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-warning", GTK_ICON_SIZE_MENU); + text = g_strdup_printf (_("%s plugin is not available. Please check whether the package is installed."), item->plugin_name); + html = g_strdup_printf ("<i>%s</i>", text); + gtk_label_set_markup (prefs->plugin_status, html); + g_free (html); + g_free (text); + } + break; + } + plugins = plugins->next; + } +} + +static void +junk_plugin_setup (GtkWidget *combo, EMMailerPrefs *prefs) +{ + gint index = 0; + gboolean def_set = FALSE; + const GList *plugins = mail_session_get_junk_plugins(); + gchar *pdefault = gconf_client_get_string (prefs->gconf, "/apps/evolution/mail/junk/default_plugin", NULL); + + if (!plugins || !g_list_length ((GList *)plugins)) { + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _("No Junk plugin available")); + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); + gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE); + gtk_widget_hide (GTK_WIDGET (prefs->plugin_image)); + gtk_widget_hide (GTK_WIDGET (prefs->plugin_status)); + gtk_image_set_from_stock (prefs->plugin_image, NULL, 0); + g_free (pdefault); + + return; + } + + while (plugins) { + struct _EMJunkHookItem *item = plugins->data;; + + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), item->plugin_name); + if (!def_set && pdefault && item->plugin_name && !strcmp(pdefault, item->plugin_name)) { + gboolean status; + + def_set = TRUE; + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), index); + status = e_plugin_invoke (item->hook->hook.plugin, item->validate_binary, NULL) != NULL; + if (status) { + gchar *text, *html; + gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-info", GTK_ICON_SIZE_MENU); + /* May be a better text */ + text = g_strdup_printf (_("%s plugin is available and the binary is installed."), item->plugin_name); + html = g_strdup_printf ("<i>%s</i>", text); + gtk_label_set_markup (prefs->plugin_status, html); + g_free (html); + g_free (text); + } else { + gchar *text, *html; + gtk_image_set_from_stock (prefs->plugin_image, "gtk-dialog-warning", GTK_ICON_SIZE_MENU); + /* May be a better text */ + text = g_strdup_printf (_("%s plugin is not available. Please check whether the package is installed."), item->plugin_name); + html = g_strdup_printf ("<i>%s</i>", text); + gtk_label_set_markup (prefs->plugin_status, html); + g_free (html); + g_free (text); + } + } + plugins = plugins->next; + index++; + } + + g_signal_connect (combo, "changed", G_CALLBACK(junk_plugin_changed), prefs); + g_free (pdefault); +} + +GtkWidget * +create_combo_text_widget (void) { + return gtk_combo_box_new_text (); +} + +static void +em_mailer_prefs_construct (EMMailerPrefs *prefs, + EShell *shell) +{ + GSList *header_config_list, *header_add_list, *p; + EShellSettings *shell_settings; + GHashTable *default_header_hash; + GtkWidget *toplevel; + GtkWidget *container; + GtkWidget *widget; + GtkTreeSelection *selection; + GtkCellRenderer *renderer; + GtkTreeIter iter; + GladeXML *gui; + gboolean locked; + gint val, i; + EMConfig *ec; + EMConfigTargetPrefs *target; + GSList *l; + gchar *gladefile; + + shell_settings = e_shell_get_shell_settings (shell); + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + gui = glade_xml_new (gladefile, "preferences_toplevel", NULL); + g_free (gladefile); + + prefs->gui = gui; + + /** @HookPoint-EMConfig: Mail Preferences Page + * @Id: org.gnome.evolution.mail.prefs + * @Type: E_CONFIG_BOOK + * @Class: org.gnome.evolution.mail.config:1.0 + * @Target: EMConfigTargetPrefs + * + * The main mail preferences page. + */ + ec = em_config_new(E_CONFIG_BOOK, "org.gnome.evolution.mail.prefs"); + l = NULL; + for (i=0;i<sizeof(emmp_items)/sizeof(emmp_items[0]);i++) + l = g_slist_prepend(l, &emmp_items[i]); + e_config_add_items((EConfig *)ec, l, NULL, NULL, emmp_free, prefs); + + /* General tab */ + + /* Message Display */ + widget = glade_xml_get_widget (gui, "chkMarkTimeout"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-mark-seen", + G_OBJECT (widget), "active"); + + /* The "mark seen" timeout requires special transform functions + * because we display the timeout value to the user in seconds + * but store the settings value in milliseconds. */ + widget = glade_xml_get_widget (gui, "spinMarkTimeout"); + prefs->timeout = GTK_SPIN_BUTTON (widget); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-mark-seen", + G_OBJECT (widget), "sensitive"); + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "mail-mark-seen-timeout", + G_OBJECT (widget), "value", + mark_seen_timeout_transform, + mark_seen_timeout_reverse_transform, + NULL, NULL); + + widget = glade_xml_get_widget (gui, "mlimit_checkbutton"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-force-message-limit", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "mlimit_spin"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-force-message-limit", + G_OBJECT (widget), "sensitive"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-message-text-part-limit", + G_OBJECT (widget), "value"); + + widget = glade_xml_get_widget (gui, "address_checkbox"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-address-compress", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "address_spin"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-address-compress", + G_OBJECT (widget), "sensitive"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-address-count", + G_OBJECT (widget), "value"); + + widget = glade_xml_get_widget (gui, "magic_spacebar_checkbox"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-magic-spacebar", + G_OBJECT (widget), "active"); + + prefs->charset = GTK_OPTION_MENU (glade_xml_get_widget (gui, "omenuCharset")); + charset_menu_init (prefs); + + widget = glade_xml_get_widget (gui, "chkHighlightCitations"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-mark-citations", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "colorButtonHighlightCitations"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-mark-citations", + G_OBJECT (widget), "sensitive"); + e_mutual_binding_new_full ( + G_OBJECT (shell_settings), "mail-citation-color", + G_OBJECT (widget), "color", + e_binding_transform_string_to_color, + e_binding_transform_color_to_string, + NULL, NULL); + + widget = glade_xml_get_widget (gui, "chkEnableSearchFolders"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-enable-search-folders", + G_OBJECT (widget), "active"); + + /* Deleting Mail */ + widget = glade_xml_get_widget (gui, "chkEmptyTrashOnExit"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-empty-trash-on-exit", + G_OBJECT (widget), "active"); + + prefs->empty_trash_days = GTK_OPTION_MENU (glade_xml_get_widget (gui, "omenuEmptyTrashDays")); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-empty-trash-on-exit", + G_OBJECT (prefs->empty_trash_days), "sensitive"); + emmp_empty_trash_init (prefs); + + widget = glade_xml_get_widget (gui, "chkConfirmExpunge"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-confirm-expunge", + G_OBJECT (widget), "active"); + + /* Mail Fonts */ + widget = glade_xml_get_widget (gui, "radFontUseSame"); + e_mutual_binding_new_with_negation ( + G_OBJECT (shell_settings), "mail-use-custom-fonts", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "FontFixed"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-font-monospace", + G_OBJECT (widget), "font-name"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-use-custom-fonts", + G_OBJECT (widget), "sensitive"); + + widget = glade_xml_get_widget (gui, "FontVariable"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-font-variable", + G_OBJECT (widget), "font-name"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-use-custom-fonts", + G_OBJECT (widget), "sensitive"); + + /* HTML Mail tab */ + + /* Loading Images */ + locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/display/load_http_images", NULL); + + val = gconf_client_get_int (prefs->gconf, "/apps/evolution/mail/display/load_http_images", NULL); + prefs->images_never = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "radImagesNever")); + gtk_toggle_button_set_active (prefs->images_never, val == MAIL_CONFIG_HTTP_NEVER); + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->images_never, FALSE); + + prefs->images_sometimes = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "radImagesSometimes")); + gtk_toggle_button_set_active (prefs->images_sometimes, val == MAIL_CONFIG_HTTP_SOMETIMES); + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->images_sometimes, FALSE); + + prefs->images_always = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "radImagesAlways")); + gtk_toggle_button_set_active (prefs->images_always, val == MAIL_CONFIG_HTTP_ALWAYS); + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->images_always, FALSE); + + g_signal_connect (prefs->images_never, "toggled", G_CALLBACK (http_images_changed), prefs); + g_signal_connect (prefs->images_sometimes, "toggled", G_CALLBACK (http_images_changed), prefs); + g_signal_connect (prefs->images_always, "toggled", G_CALLBACK (http_images_changed), prefs); + + widget = glade_xml_get_widget (gui, "chkShowAnimatedImages"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-show-animated-images", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "chkPromptWantHTML"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-confirm-unwanted-html", + G_OBJECT (widget), "active"); + + container = glade_xml_get_widget (gui, "labels-alignment"); + widget = e_mail_label_manager_new (); + gtk_container_add (GTK_CONTAINER (container), widget); + gtk_widget_show (widget); + + e_binding_new ( + G_OBJECT (shell_settings), "mail-label-list-store", + G_OBJECT (widget), "list-store"); + + /* headers */ + locked = !gconf_client_key_is_writable (prefs->gconf, "/apps/evolution/mail/display/headers", NULL); + + widget = glade_xml_get_widget (gui, "photo_show"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-show-sender-photo", + G_OBJECT (widget), "active"); + + widget = glade_xml_get_widget (gui, "photo_local"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-show-sender-photo", + G_OBJECT (widget), "sensitive"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-only-local-photos", + G_OBJECT (widget), "active"); + + /* always de-sensitised until the user types something in the entry */ + prefs->add_header = GTK_BUTTON (glade_xml_get_widget (gui, "cmdHeadersAdd")); + gtk_widget_set_sensitive ((GtkWidget *) prefs->add_header, FALSE); + + /* always de-sensitised until the user selects a header in the list */ + prefs->remove_header = GTK_BUTTON (glade_xml_get_widget (gui, "cmdHeadersRemove")); + gtk_widget_set_sensitive ((GtkWidget *) prefs->remove_header, FALSE); + + prefs->entry_header = GTK_ENTRY (glade_xml_get_widget (gui, "txtHeaders")); + gtk_widget_set_sensitive ((GtkWidget *) prefs->entry_header, !locked); + + prefs->header_list = GTK_TREE_VIEW (glade_xml_get_widget (gui, "treeHeaders")); + gtk_widget_set_sensitive ((GtkWidget *) prefs->header_list, !locked); + + selection = gtk_tree_view_get_selection (prefs->header_list); + g_signal_connect (selection, "changed", G_CALLBACK (emmp_header_list_row_selected), prefs); + g_signal_connect (prefs->entry_header, "changed", G_CALLBACK (emmp_header_entry_changed), prefs); + g_signal_connect (prefs->entry_header, "activate", G_CALLBACK (emmp_header_add_header), prefs); + /* initialise the tree with appropriate headings */ + prefs->header_list_store = gtk_list_store_newv (HEADER_LIST_N_COLUMNS, col_types); + g_signal_connect (prefs->add_header, "clicked", G_CALLBACK (emmp_header_add_header), prefs); + g_signal_connect (prefs->remove_header, "clicked", G_CALLBACK (emmp_header_remove_header), prefs); + gtk_tree_view_set_model (prefs->header_list, GTK_TREE_MODEL (prefs->header_list_store)); + + renderer = gtk_cell_renderer_toggle_new (); + g_object_set (renderer, "activatable", TRUE, NULL); + g_signal_connect (renderer, "toggled", G_CALLBACK (emmp_header_list_enabled_toggled), prefs); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (prefs->header_list), -1, + "Enabled", renderer, + "active", HEADER_LIST_ENABLED_COLUMN, + NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (prefs->header_list), -1, + "Name", renderer, + "text", HEADER_LIST_NAME_COLUMN, + NULL); + + /* populated the listview with entries; firstly we add all the default headers, and then + we add read header configuration out of gconf. If a header in gconf is a default header, + we update the enabled flag accordingly + */ + header_add_list = NULL; + default_header_hash = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < G_N_ELEMENTS (default_headers); i++) { + struct _EMMailerPrefsHeader *h; + + h = g_malloc (sizeof (struct _EMMailerPrefsHeader)); + h->is_default = TRUE; + h->name = g_strdup (default_headers[i]); + h->enabled = strcmp ((gchar *)default_headers[i], "x-evolution-mailer") != 0; + g_hash_table_insert (default_header_hash, (gpointer) default_headers[i], h); + header_add_list = g_slist_append (header_add_list, h); + } + + /* read stored headers from gconf */ + header_config_list = gconf_client_get_list (prefs->gconf, "/apps/evolution/mail/display/headers", GCONF_VALUE_STRING, NULL); + p = header_config_list; + while (p) { + struct _EMMailerPrefsHeader *h, *def; + gchar *xml = (gchar *) p->data; + + h = em_mailer_prefs_header_from_xml (xml); + if (h) { + def = g_hash_table_lookup (default_header_hash, h->name); + if (def) { + def->enabled = h->enabled; + em_mailer_prefs_header_free (h); + } else { + h->is_default = FALSE; + header_add_list = g_slist_append (header_add_list, h); + } + } + + p = p->next; + } + + g_hash_table_destroy (default_header_hash); + g_slist_foreach (header_config_list, (GFunc) g_free, NULL); + g_slist_free (header_config_list); + + p = header_add_list; + while (p) { + struct _EMMailerPrefsHeader *h = (struct _EMMailerPrefsHeader *) p->data; + const gchar *name; + + if (g_ascii_strcasecmp (h->name, EM_FORMAT_HEADER_XMAILER) == 0) + name = _("Mailer"); + else + name = _(h->name); + + gtk_list_store_append (prefs->header_list_store, &iter); + gtk_list_store_set (prefs->header_list_store, &iter, + HEADER_LIST_NAME_COLUMN, name, + HEADER_LIST_ENABLED_COLUMN, h->enabled, + HEADER_LIST_IS_DEFAULT_COLUMN, h->is_default, + HEADER_LIST_HEADER_COLUMN, h->name, + -1); + + em_mailer_prefs_header_free (h); + p = p->next; + } + + g_slist_free (header_add_list); + + /* Junk prefs */ + widget = glade_xml_get_widget (gui, "chkCheckIncomingMail"); + e_mutual_binding_new ( + G_OBJECT (shell_settings), "mail-check-for-junk", + G_OBJECT (widget), "active"); + + prefs->empty_junk = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "junk_empty_check")); + prefs->empty_junk_days = GTK_OPTION_MENU (glade_xml_get_widget (gui, "junk_empty_combo")); + emmp_empty_junk_init (prefs); + + prefs->default_junk_plugin = GTK_COMBO_BOX (glade_xml_get_widget (gui, "default_junk_plugin")); + prefs->plugin_status = GTK_LABEL (glade_xml_get_widget (gui, "plugin_status")); + prefs->plugin_image = GTK_IMAGE (glade_xml_get_widget (gui, "plugin_image")); + junk_plugin_setup (GTK_WIDGET (prefs->default_junk_plugin), prefs); + + prefs->junk_header_check = (GtkToggleButton *)glade_xml_get_widget (gui, "junk_header_check"); + prefs->junk_header_tree = (GtkTreeView *)glade_xml_get_widget (gui, "junk_header_tree"); + prefs->junk_header_add = (GtkButton *)glade_xml_get_widget (gui, "junk_header_add"); + prefs->junk_header_remove = (GtkButton *)glade_xml_get_widget (gui, "junk_header_remove"); + prefs->junk_book_lookup = (GtkToggleButton *)glade_xml_get_widget (gui, "lookup_book"); + prefs->junk_lookup_local_only = (GtkToggleButton *)glade_xml_get_widget (gui, "junk_lookup_local_only"); + toggle_button_init (prefs, prefs->junk_book_lookup, FALSE, + "/apps/evolution/mail/junk/lookup_addressbook", + G_CALLBACK (junk_book_lookup_button_toggled)); + + toggle_button_init (prefs, prefs->junk_lookup_local_only, FALSE, + "/apps/evolution/mail/junk/lookup_addressbook_local_only", + G_CALLBACK (toggle_button_toggled)); + + junk_book_lookup_button_toggled (prefs->junk_book_lookup, prefs); + + prefs->junk_header_list_store = init_junk_tree ((GtkWidget *)prefs->junk_header_tree, prefs); + toggle_button_init (prefs, prefs->junk_header_check, FALSE, + "/apps/evolution/mail/junk/check_custom_header", + G_CALLBACK (custom_junk_button_toggled)); + + custom_junk_button_toggled (prefs->junk_header_check, prefs); + jh_tree_refill (prefs); + g_signal_connect (G_OBJECT (prefs->junk_header_add), "clicked", G_CALLBACK (jh_add_cb), prefs); + g_signal_connect (G_OBJECT (prefs->junk_header_remove), "clicked", G_CALLBACK (jh_remove_cb), prefs); + + /* get our toplevel widget */ + target = em_config_target_new_prefs(ec, prefs->gconf); + e_config_set_target((EConfig *)ec, (EConfigTarget *)target); + toplevel = e_config_create_widget((EConfig *)ec); + gtk_container_add (GTK_CONTAINER (prefs), toplevel); +} + +GtkWidget * +em_mailer_prefs_new (EShell *shell) +{ + EMMailerPrefs *new; + + g_return_val_if_fail (E_IS_SHELL (shell), NULL); + + new = g_object_new (EM_TYPE_MAILER_PREFS, NULL); + + /* FIXME Kill this function. */ + em_mailer_prefs_construct (new, shell); + + return GTK_WIDGET (new); +} + + +static struct _EMMailerPrefsHeader * +emmp_header_from_xmldoc (xmlDocPtr doc) +{ + struct _EMMailerPrefsHeader *h; + xmlNodePtr root; + xmlChar *name; + + if (doc == NULL) + return NULL; + + root = doc->children; + if (strcmp ((gchar *)root->name, "header") != 0) + return NULL; + + name = xmlGetProp (root, (const guchar *)"name"); + if (name == NULL) + return NULL; + + h = g_malloc0 (sizeof (struct _EMMailerPrefsHeader)); + h->name = g_strdup ((gchar *)name); + xmlFree (name); + + if (xmlHasProp (root, (const guchar *)"enabled")) + h->enabled = 1; + else + h->enabled = 0; + + return h; +} + +/** + * em_mailer_prefs_header_from_xml + * @xml: XML configuration data + * + * Parses passed XML data, which should be of + * the format <header name="foo" enabled />, and + * returns a EMMailerPrefs structure, or NULL if there + * is an error. + **/ +struct _EMMailerPrefsHeader * +em_mailer_prefs_header_from_xml (const gchar *xml) +{ + struct _EMMailerPrefsHeader *header; + xmlDocPtr doc; + + if (!(doc = xmlParseDoc ((guchar *) xml))) + return NULL; + + header = emmp_header_from_xmldoc (doc); + xmlFreeDoc (doc); + + return header; +} + +/** + * em_mailer_prefs_header_free + * @header: header to free + * + * Frees the memory associated with the passed header + * structure. + */ +void +em_mailer_prefs_header_free (struct _EMMailerPrefsHeader *header) +{ + if (header == NULL) + return; + + g_free (header->name); + g_free (header); +} + +/** + * em_mailer_prefs_header_to_xml + * @header: header from which to generate XML + * + * Returns the passed header as a XML structure, + * or NULL on error + */ +gchar * +em_mailer_prefs_header_to_xml (struct _EMMailerPrefsHeader *header) +{ + xmlDocPtr doc; + xmlNodePtr root; + xmlChar *xml; + gchar *out; + gint size; + + g_return_val_if_fail (header != NULL, NULL); + g_return_val_if_fail (header->name != NULL, NULL); + + doc = xmlNewDoc ((const guchar *)"1.0"); + + root = xmlNewDocNode (doc, NULL, (const guchar *)"header", NULL); + xmlSetProp (root, (const guchar *)"name", (guchar *)header->name); + if (header->enabled) + xmlSetProp (root, (const guchar *)"enabled", NULL); + + xmlDocSetRootElement (doc, root); + xmlDocDumpMemory (doc, &xml, &size); + xmlFreeDoc (doc); + + out = g_malloc (size + 1); + memcpy (out, xml, size); + out[size] = '\0'; + xmlFree (xml); + + return out; +} diff --git a/modules/mail/em-mailer-prefs.h b/modules/mail/em-mailer-prefs.h new file mode 100644 index 0000000000..b33e620392 --- /dev/null +++ b/modules/mail/em-mailer-prefs.h @@ -0,0 +1,141 @@ +/* + * 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/> + * + * + * Authors: + * Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef EM_MAILER_PREFS_H +#define EM_MAILER_PREFS_H + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <gconf/gconf-client.h> +#include <shell/e-shell.h> + +/* Standard GObject macros */ +#define EM_TYPE_MAILER_PREFS \ + (em_mailer_prefs_get_type ()) +#define EM_MAILER_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), EM_TYPE_MAILER_PREFS, EMMailerPrefs)) +#define EM_MAILER_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), EM_TYPE_MAILER_PREFS, EMMailerPrefsClass)) +#define EM_IS_MAILER_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), EM_TYPE_MAILER_PREFS)) +#define EM_IS_MAILER_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), EM_TYPE_MAILER_PREFS)) +#define EM_MAILER_PREFS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), EM_TYPE_MAILER_PREFS)) + +G_BEGIN_DECLS + +typedef struct _EMMailerPrefs EMMailerPrefs; +typedef struct _EMMailerPrefsClass EMMailerPrefsClass; +typedef struct _EMMailerPrefsHeader EMMailerPrefsHeader; + +struct _EMMailerPrefsHeader { + gchar *name; + guint enabled:1; + guint is_default:1; +}; + +struct _EMMailerPrefs { + GtkVBox parent_object; + + GladeXML *gui; + GConfClient *gconf; + + /* General tab */ + + /* Message Display */ + GtkSpinButton *timeout; + GtkOptionMenu *charset; + + /* Deleting Mail */ + GtkToggleButton *empty_trash; + GtkComboBox *empty_trash_days; + GtkToggleButton *confirm_expunge; + + /* HTML Mail tab */ + GtkFontButton *font_variable; + GtkFontButton *font_fixed; + GtkToggleButton *font_share; + + /* Loading Images */ + GtkToggleButton *images_always; + GtkToggleButton *images_sometimes; + GtkToggleButton *images_never; + + GtkToggleButton *autodetect_links; + + /* Labels and Colours tab */ + GtkWidget *label_add; + GtkWidget *label_edit; + GtkWidget *label_remove; + GtkWidget *label_tree; + GtkListStore *label_list_store; + guint labels_change_notify_id; /* mail_config's notify id */ + + /* Headers tab */ + GtkButton *add_header; + GtkButton *remove_header; + GtkEntry *entry_header; + GtkTreeView *header_list; + GtkListStore *header_list_store; + + /* Junk prefs */ + GtkToggleButton *empty_junk; + GtkComboBox *empty_junk_days; + + GtkToggleButton *sa_local_tests_only; + GtkToggleButton *sa_use_daemon; + GtkComboBox *default_junk_plugin; + GtkLabel *plugin_status; + GtkImage *plugin_image; + + GtkToggleButton *junk_header_check; + GtkTreeView *junk_header_tree; + GtkListStore *junk_header_list_store; + GtkButton *junk_header_add; + GtkButton *junk_header_remove; + GtkToggleButton *junk_book_lookup; + GtkToggleButton *junk_lookup_local_only; +}; + +struct _EMMailerPrefsClass { + GtkVBoxClass parent_class; +}; + +GType em_mailer_prefs_get_type (void); +GtkWidget * create_combo_text_widget (void); + +GtkWidget * em_mailer_prefs_new (EShell *shell); + +EMMailerPrefsHeader * + em_mailer_prefs_header_from_xml (const gchar *xml); +gchar * em_mailer_prefs_header_to_xml (EMMailerPrefsHeader *header); +void em_mailer_prefs_header_free (EMMailerPrefsHeader *header); + +G_END_DECLS + +#endif /* EM_MAILER_PREFS_H */ diff --git a/modules/mail/em-network-prefs.c b/modules/mail/em-network-prefs.c new file mode 100644 index 0000000000..e530d4d076 --- /dev/null +++ b/modules/mail/em-network-prefs.c @@ -0,0 +1,499 @@ +/* + * 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/> + * + * + * Authors: + * Veerapuram Varadhan <vvaradhan@novell.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#include "em-network-prefs.h" + +#include <gdk/gdkkeysyms.h> +#include <gconf/gconf-client.h> +#include <glade/glade.h> + +#include <glib/gstdio.h> + +#include "e-util/e-error.h" +#include "e-util/e-util-private.h" + +#include "mail-config.h" +#include "em-config.h" + +#define d(x) + +#define GCONF_E_SHELL_NETWORK_CONFIG_PATH "/apps/evolution/shell/network_config/" +#define GCONF_E_HTTP_HOST_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "http_host" +#define GCONF_E_HTTP_PORT_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "http_port" +#define GCONF_E_HTTPS_HOST_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "secure_host" +#define GCONF_E_HTTPS_PORT_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "secure_port" +#define GCONF_E_SOCKS_HOST_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "socks_host" +#define GCONF_E_SOCKS_PORT_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "socks_port" +#define GCONF_E_IGNORE_HOSTS_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "ignore_hosts" +#define GCONF_E_USE_AUTH_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "use_authentication" +#define GCONF_E_PROXY_TYPE_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "proxy_type" +#define GCONF_E_AUTH_USER_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "authentication_user" +#define GCONF_E_AUTH_PWD_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "authentication_password" +#define GCONF_E_USE_PROXY_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "use_http_proxy" +#define GCONF_E_AUTOCONFIG_URL_KEY GCONF_E_SHELL_NETWORK_CONFIG_PATH "autoconfig_url" + +static void em_network_prefs_class_init (EMNetworkPrefsClass *class); +static void em_network_prefs_init (EMNetworkPrefs *dialog); +static void em_network_prefs_destroy (GtkObject *obj); +static void em_network_prefs_finalise (GObject *obj); + + +static GtkVBoxClass *parent_class = NULL; + + +GType +em_network_prefs_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (EMNetworkPrefsClass), + NULL, NULL, + (GClassInitFunc) em_network_prefs_class_init, + NULL, NULL, + sizeof (EMNetworkPrefs), + 0, + (GInstanceInitFunc) em_network_prefs_init, + }; + + type = g_type_register_static (gtk_vbox_get_type (), "EMNetworkPrefs", &info, 0); + } + + return type; +} + +static void +em_network_prefs_class_init (EMNetworkPrefsClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (gtk_vbox_get_type ()); + + object_class->destroy = em_network_prefs_destroy; + gobject_class->finalize = em_network_prefs_finalise; +} + +static void +em_network_prefs_init (EMNetworkPrefs *prefs) +{ + /* do something here */ +} + +static void +em_network_prefs_finalise (GObject *obj) +{ + d(g_print ("Network preferences finalize is called\n")); + + /* do something here */ + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +em_network_prefs_destroy (GtkObject *obj) +{ + d(g_print ("Network preferences destroy is called\n")); + + GTK_OBJECT_CLASS (parent_class)->destroy (obj); +} + +static void +toggle_button_toggled (GtkToggleButton *toggle, EMNetworkPrefs *prefs) +{ + const gchar *key; + + key = g_object_get_data ((GObject *) toggle, "key"); + gconf_client_set_bool (prefs->gconf, key, gtk_toggle_button_get_active (toggle), NULL); + if (toggle == prefs->use_auth) { + gboolean sensitivity = gtk_toggle_button_get_active (prefs->use_auth); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_user, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_pwd, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_user, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_pwd, sensitivity); + } +} + +static void +toggle_button_init (EMNetworkPrefs *prefs, GtkToggleButton *toggle, const gchar *key) +{ + gboolean bool; + + bool = gconf_client_get_bool (prefs->gconf, key, NULL); + gtk_toggle_button_set_active (toggle, bool); + + g_object_set_data ((GObject *) toggle, "key", (gpointer) key); + g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_button_toggled), prefs); + + if (!gconf_client_key_is_writable (prefs->gconf, key, NULL)) + gtk_widget_set_sensitive ((GtkWidget *) toggle, FALSE); +} + +static GtkWidget * +emnp_widget_glade(EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gpointer data) +{ + EMNetworkPrefs *prefs = data; + + return glade_xml_get_widget(prefs->gui, item->label); +} + +static void +emnp_set_sensitiveness (EMNetworkPrefs *prefs, NetworkConfigProxyType type, gboolean sensitivity) +{ +#if 0 + if (type == NETWORK_PROXY_AUTOCONFIG) { + gtk_widget_set_sensitive ((GtkWidget *) prefs->auto_proxy_url, sensitivity); + d(g_print ("Setting sensitivity of autoconfig to: %d\n", sensitivity)); + } else +#endif + if (type == NETWORK_PROXY_MANUAL) { + gboolean state; + + gtk_widget_set_sensitive ((GtkWidget *) prefs->http_host, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->https_host, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->ignore_hosts, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->use_auth, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->http_port, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->https_port, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_ignore_hosts, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_http_host, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_http_port, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_https_host, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_https_port, sensitivity); +#if 0 + gtk_widget_set_sensitive ((GtkWidget *) prefs->socks_host, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->socks_port, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_socks_host, sensitivity); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_socks_port, sensitivity); +#endif + state = sensitivity && gtk_toggle_button_get_active (prefs->use_auth); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_user, state); + gtk_widget_set_sensitive ((GtkWidget *) prefs->lbl_auth_pwd, state); + gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_user, state); + gtk_widget_set_sensitive ((GtkWidget *) prefs->auth_pwd, state); + + d(g_print ("Setting sensitivity of manual proxy to: %d\n", sensitivity)); + } +} + +static void +notify_proxy_type_changed (GtkWidget *widget, EMNetworkPrefs *prefs) +{ + gint type; + + if (gtk_toggle_button_get_active (prefs->sys_proxy)) + type = NETWORK_PROXY_SYS_SETTINGS; + else if (gtk_toggle_button_get_active (prefs->no_proxy)) + type = NETWORK_PROXY_DIRECT_CONNECTION; + else if (gtk_toggle_button_get_active (prefs->manual_proxy)) + type = NETWORK_PROXY_MANUAL; + else +#if 0 + type = NETWORK_PROXY_AUTOCONFIG; +#else + type = NETWORK_PROXY_SYS_SETTINGS; +#endif + + gconf_client_set_int (prefs->gconf, "/apps/evolution/shell/network_config/proxy_type", type, NULL); + + if (type == NETWORK_PROXY_DIRECT_CONNECTION || + type == NETWORK_PROXY_SYS_SETTINGS) { + emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE); + emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE); + } else if (type == NETWORK_PROXY_AUTOCONFIG) { + emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE); + emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, TRUE); + } else if (type == NETWORK_PROXY_MANUAL) { + emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE); + emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, TRUE); + } + + if (type != NETWORK_PROXY_DIRECT_CONNECTION) + gconf_client_set_bool (prefs->gconf, GCONF_E_USE_PROXY_KEY, TRUE, NULL); + else if (type != NETWORK_PROXY_SYS_SETTINGS) + gconf_client_set_bool (prefs->gconf, GCONF_E_USE_PROXY_KEY, FALSE, NULL); + +} + +static void +widget_entry_changed_cb (GtkWidget *widget, gpointer data) +{ + const gchar *value; + gint port = -1; + GConfClient *gconf = mail_config_get_gconf_client (); + + /* + Do not change the order of comparison - + GtkSpinButton is an extended form of GtkEntry + */ + if (GTK_IS_SPIN_BUTTON (widget)) { + port = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget)); + gconf_client_set_int (gconf, (const gchar *)data, port, NULL); + d(g_print ("%s:%s: %s is SpinButton: value = [%d]\n", G_STRLOC, G_STRFUNC, (const gchar *)data, port)); + } else if (GTK_IS_ENTRY (widget)) { + value = gtk_entry_get_text (GTK_ENTRY (widget)); + gconf_client_set_string (gconf, (const gchar *)data, value, NULL); + d(g_print ("%s:%s: %s is Entry: value = [%s]\n", G_STRLOC, G_STRFUNC, (const gchar *)data, value)); + } + +} + +/* plugin meta-data */ +static EMConfigItem emnp_items[] = { + { E_CONFIG_BOOK, (gchar *) "", (gchar *) "network_preferences_toplevel", emnp_widget_glade }, + { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) "vboxGeneral", emnp_widget_glade }, + { E_CONFIG_SECTION, (gchar *) "00.general/00.proxy", (gchar *) "frameProxy", emnp_widget_glade }, +}; + +static void +emnp_free(EConfig *ec, GSList *items, gpointer data) +{ + /* the prefs data is freed automagically */ + + g_slist_free(items); +} + +static void +emnp_set_markups (EMNetworkPrefs *prefs) +{ + gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->sys_proxy)->child), TRUE); + gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->no_proxy)->child), TRUE); + gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->manual_proxy)->child), TRUE); +#if 0 + gtk_label_set_use_markup (GTK_LABEL (GTK_BIN(prefs->auto_proxy)->child), TRUE); +#endif +} + +static void +em_network_prefs_construct (EMNetworkPrefs *prefs) +{ + GtkWidget *toplevel; + GladeXML *gui; + GSList* l; + gchar *buf; + EMConfig *ec; + EMConfigTargetPrefs *target; + gboolean locked; + gint i, val, port; + gchar *gladefile; + + prefs->gconf = mail_config_get_gconf_client (); + + gladefile = g_build_filename (EVOLUTION_GLADEDIR, + "mail-config.glade", + NULL); + gui = glade_xml_new (gladefile, "network_preferences_toplevel", NULL); + prefs->gui = gui; + g_free (gladefile); + + /** @HookPoint-EMConfig: Network Preferences + * @Id: org.gnome.evolution.mail.networkPrefs + * @Type: E_CONFIG_BOOK + * @Class: org.gnome.evolution.mail.config:1.0 + * @Target: EMConfigTargetPrefs + * + * The network preferences settings page. + */ + ec = em_config_new(E_CONFIG_BOOK, "org.gnome.evolution.mail.networkPrefs"); + l = NULL; + for (i=0;i<sizeof(emnp_items)/sizeof(emnp_items[0]);i++) + l = g_slist_prepend(l, &emnp_items[i]); + e_config_add_items((EConfig *)ec, l, NULL, NULL, emnp_free, prefs); + + /* Proxy tab */ + + /* Default Behavior */ + locked = !gconf_client_key_is_writable (prefs->gconf, GCONF_E_PROXY_TYPE_KEY, NULL); + + val = gconf_client_get_int (prefs->gconf, GCONF_E_PROXY_TYPE_KEY, NULL); + + /* no auto-proxy at the moment */ + if (val == NETWORK_PROXY_AUTOCONFIG) + val = NETWORK_PROXY_SYS_SETTINGS; + + prefs->sys_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoSysSettings")); + gtk_toggle_button_set_active (prefs->sys_proxy, val == NETWORK_PROXY_SYS_SETTINGS); + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->sys_proxy, FALSE); + + d(g_print ("Sys settings ----!!! \n")); + + prefs->no_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoNoProxy")); + gtk_toggle_button_set_active (prefs->no_proxy, val == NETWORK_PROXY_DIRECT_CONNECTION); + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->no_proxy, FALSE); + + d(g_print ("No proxy settings ----!!! \n")); + + /* no auto-proxy at the moment */ +#if 0 + prefs->auto_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoAutoConfig")); + prefs->auto_proxy_url = GTK_ENTRY (glade_xml_get_widget (gui, "txtAutoConfigUrl")); + + gtk_toggle_button_set_active (prefs->auto_proxy, val == NETWORK_PROXY_AUTOCONFIG); + + g_signal_connect(prefs->auto_proxy_url, "changed", G_CALLBACK(widget_entry_changed_cb), GCONF_E_AUTOCONFIG_URL_KEY); + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->auto_proxy, FALSE); +#endif + + d(g_print ("Auto config settings ----!!! \n")); + + prefs->manual_proxy = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "rdoManualProxy")); + prefs->http_host = GTK_ENTRY (glade_xml_get_widget (gui, "txtHttpHost")); + prefs->https_host = GTK_ENTRY (glade_xml_get_widget (gui, "txtHttpsHost")); + prefs->ignore_hosts = GTK_ENTRY (glade_xml_get_widget (gui, "txtIgnoreHosts")); + prefs->http_port = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "spnHttpPort")); + prefs->https_port = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "spnHttpsPort")); + prefs->lbl_http_host = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpHost")); + prefs->lbl_http_port = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpPort")); + prefs->lbl_https_host = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpsHost")); + prefs->lbl_https_port = GTK_LABEL (glade_xml_get_widget (gui, "lblHttpsPort")); + prefs->lbl_ignore_hosts = GTK_LABEL (glade_xml_get_widget (gui, "lblIgnoreHosts")); + prefs->use_auth = GTK_TOGGLE_BUTTON (glade_xml_get_widget (gui, "chkUseAuth")); + toggle_button_init (prefs, prefs->use_auth, GCONF_E_USE_AUTH_KEY); + prefs->lbl_auth_user = GTK_LABEL (glade_xml_get_widget (gui, "lblAuthUser")); + prefs->lbl_auth_pwd = GTK_LABEL (glade_xml_get_widget (gui, "lblAuthPwd")); + prefs->auth_user = GTK_ENTRY (glade_xml_get_widget (gui, "txtAuthUser")); + prefs->auth_pwd = GTK_ENTRY (glade_xml_get_widget (gui, "txtAuthPwd")); + +#if 0 + prefs->socks_host = GTK_ENTRY (glade_xml_get_widget (gui, "txtSocksHost")); + prefs->socks_port = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "spnSocksPort")); + prefs->lbl_socks_host = GTK_LABEL (glade_xml_get_widget (gui, "lblSocksHost")); + prefs->lbl_socks_port = GTK_LABEL (glade_xml_get_widget (gui, "lblSocksPort")); + g_signal_connect (prefs->socks_host, "changed", + G_CALLBACK(widget_entry_changed_cb), GCONF_E_SOCKS_HOST_KEY); + g_signal_connect (prefs->socks_port, "value_changed", + G_CALLBACK(widget_entry_changed_cb), GCONF_E_SOCKS_PORT_KEY); +#endif + + /* Manual proxy options */ + g_signal_connect (prefs->http_host, "changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_HTTP_HOST_KEY); + g_signal_connect (prefs->https_host, "changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_HTTPS_HOST_KEY); + g_signal_connect (prefs->ignore_hosts, "changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_IGNORE_HOSTS_KEY); + g_signal_connect (prefs->http_port, "value_changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_HTTP_PORT_KEY); + g_signal_connect (prefs->https_port, "value_changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_HTTPS_PORT_KEY); + g_signal_connect (prefs->auth_user, "changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_AUTH_USER_KEY); + g_signal_connect (prefs->auth_pwd, "changed", + G_CALLBACK(widget_entry_changed_cb), + (gpointer) GCONF_E_AUTH_PWD_KEY); + + gtk_toggle_button_set_active (prefs->manual_proxy, val == NETWORK_PROXY_MANUAL); + g_signal_connect (prefs->sys_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs); + g_signal_connect (prefs->no_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs); +#if 0 + g_signal_connect (prefs->auto_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs); +#endif + g_signal_connect (prefs->manual_proxy, "toggled", G_CALLBACK (notify_proxy_type_changed), prefs); + + if (locked) + gtk_widget_set_sensitive ((GtkWidget *) prefs->manual_proxy, FALSE); + d(g_print ("Manual settings ----!!! \n")); + + buf = gconf_client_get_string (prefs->gconf, GCONF_E_HTTP_HOST_KEY, NULL); + gtk_entry_set_text (prefs->http_host, buf ? buf : ""); + g_free (buf); + + buf = gconf_client_get_string (prefs->gconf, GCONF_E_HTTPS_HOST_KEY, NULL); + gtk_entry_set_text (prefs->https_host, buf ? buf : ""); + g_free (buf); + + buf = gconf_client_get_string (prefs->gconf, GCONF_E_IGNORE_HOSTS_KEY, NULL); + gtk_entry_set_text (prefs->ignore_hosts, buf ? buf : ""); + g_free (buf); + + buf = gconf_client_get_string (prefs->gconf, GCONF_E_AUTH_USER_KEY, NULL); + gtk_entry_set_text (prefs->auth_user, buf ? buf : ""); + g_free (buf); + + buf = gconf_client_get_string (prefs->gconf, GCONF_E_AUTH_PWD_KEY, NULL); + gtk_entry_set_text (prefs->auth_pwd, buf ? buf : ""); + g_free (buf); + + port = gconf_client_get_int (prefs->gconf, GCONF_E_HTTP_PORT_KEY, NULL); + gtk_spin_button_set_value (prefs->http_port, (gdouble)port); + + port = gconf_client_get_int (prefs->gconf, GCONF_E_HTTPS_PORT_KEY, NULL); + gtk_spin_button_set_value (prefs->https_port, (gdouble)port); + +#if 0 + buf = gconf_client_get_string (prefs->gconf, GCONF_E_SOCKS_HOST_KEY, NULL); + gtk_entry_set_text (prefs->socks_host, buf ? buf : ""); + g_free (buf); + + port = gconf_client_get_int (prefs->gconf, GCONF_E_SOCKS_PORT_KEY, NULL); + gtk_spin_button_set_value (prefs->socks_port, (gdouble)port); +#endif + emnp_set_markups (prefs); + + if (val == NETWORK_PROXY_DIRECT_CONNECTION || + val == NETWORK_PROXY_SYS_SETTINGS) { + emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE); + emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE); + } else if (val == NETWORK_PROXY_AUTOCONFIG) { + emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, FALSE); + emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, TRUE); + } else if (val == NETWORK_PROXY_MANUAL) { + emnp_set_sensitiveness (prefs, NETWORK_PROXY_AUTOCONFIG, FALSE); + emnp_set_sensitiveness (prefs, NETWORK_PROXY_MANUAL, TRUE); + } + + /* get our toplevel widget */ + target = em_config_target_new_prefs(ec, prefs->gconf); + e_config_set_target((EConfig *)ec, (EConfigTarget *)target); + toplevel = e_config_create_widget((EConfig *)ec); + gtk_container_add (GTK_CONTAINER (prefs), toplevel); +} + +GtkWidget * +em_network_prefs_new (void) +{ + EMNetworkPrefs *new; + + new = (EMNetworkPrefs *) g_object_new (em_network_prefs_get_type (), NULL); + em_network_prefs_construct (new); + + return (GtkWidget *) new; +} diff --git a/modules/mail/em-network-prefs.h b/modules/mail/em-network-prefs.h new file mode 100644 index 0000000000..057247b750 --- /dev/null +++ b/modules/mail/em-network-prefs.h @@ -0,0 +1,102 @@ +/* + * 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/> + * + * + * Authors: + * Veerapuram Varadhan <vvaradhan@novell.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef EM_NETWORK_PREFS_H +#define EM_NETWORK_PREFS_H + +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <gconf/gconf-client.h> + +/* Standard GObject macros */ +#define EM_TYPE_NETWORK_PREFS \ + (em_network_prefs_get_type ()) +#define EM_NETWORK_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), EM_TYPE_NETWORK_PREFS, EMNetworkPrefs)) +#define EM_NETWORK_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), EM_TYPE_NETWORK_PREFS, EMNetworkPrefsClass)) +#define EM_IS_NETWORK_PREFS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), EM_TYPE_NETWORK_PREFS)) +#define EM_IS_NETWORK_PREFS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), EM_TYPE_NETWORK_PREFS)) +#define EM_NETWORK_PREFS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), EM_TYPE_NETWORK_PREFS, EMNetworkPrefsClass)) + +G_BEGIN_DECLS + +typedef struct _EMNetworkPrefs EMNetworkPrefs; +typedef struct _EMNetworkPrefsClass EMNetworkPrefsClass; + +typedef enum { + NETWORK_PROXY_SYS_SETTINGS, + NETWORK_PROXY_DIRECT_CONNECTION, + NETWORK_PROXY_MANUAL, + NETWORK_PROXY_AUTOCONFIG +} NetworkConfigProxyType; + +struct _EMNetworkPrefs { + GtkVBox parent_object; + + GConfClient *gconf; + + GladeXML *gui; + + /* Default Behavior */ + GtkToggleButton *sys_proxy; + GtkToggleButton *no_proxy; + GtkToggleButton *manual_proxy; + GtkToggleButton *use_auth; + + GtkEntry *http_host; + GtkEntry *https_host; + GtkEntry *socks_host; + GtkEntry *ignore_hosts; + GtkEntry *auth_user; + GtkEntry *auth_pwd; + + GtkLabel *lbl_http_host; + GtkLabel *lbl_http_port; + GtkLabel *lbl_https_host; + GtkLabel *lbl_https_port; + GtkLabel *lbl_ignore_hosts; + GtkLabel *lbl_auth_user; + GtkLabel *lbl_auth_pwd; + + GtkSpinButton *http_port; + GtkSpinButton *https_port; +}; + +struct _EMNetworkPrefsClass { + GtkVBoxClass parent_class; +}; + +GType em_network_prefs_get_type (void); +GtkWidget * em_network_prefs_new (void); + +G_END_DECLS + +#endif /* EM_NETWORK_PREFS_H */ diff --git a/modules/mail/evolution-module-mail.c b/modules/mail/evolution-module-mail.c new file mode 100644 index 0000000000..97bc953c41 --- /dev/null +++ b/modules/mail/evolution-module-mail.c @@ -0,0 +1,59 @@ +/* + * evolution-module-mail.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-mail-shell-backend.h" +#include "e-mail-shell-content.h" +#include "e-mail-shell-sidebar.h" +#include "e-mail-shell-view.h" + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); +const gchar * g_module_check_init (GModule *module); + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + /* Register dynamically loaded types. */ + + e_mail_shell_backend_register_type (type_module); + e_mail_shell_content_register_type (type_module); + e_mail_shell_sidebar_register_type (type_module); + e_mail_shell_view_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} + +G_MODULE_EXPORT const gchar * +g_module_check_init (GModule *module) +{ + /* FIXME Until mail is split into a module library and a + * reusable shared library, prevent the module from + * being unloaded. Unloading the module resets all + * static variables, which screws up foo_get_type() + * functions among other things. */ + g_module_make_resident (module); + + return NULL; +} |