aboutsummaryrefslogtreecommitdiffstats
path: root/mail/e-mail-account-store.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/e-mail-account-store.c')
-rw-r--r--mail/e-mail-account-store.c1439
1 files changed, 1439 insertions, 0 deletions
diff --git a/mail/e-mail-account-store.c b/mail/e-mail-account-store.c
new file mode 100644
index 0000000000..ccfbe3b879
--- /dev/null
+++ b/mail/e-mail-account-store.c
@@ -0,0 +1,1439 @@
+/*
+ * e-mail-account-store.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/>
+ *
+ */
+
+#include "e-mail-account-store.h"
+
+#include <config.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+
+#include <libebackend/e-extensible.h>
+
+#include <e-util/e-marshal.h>
+#include <e-util/e-account-utils.h>
+#include <e-util/e-alert-dialog.h>
+#include <mail/mail-ops.h>
+#include <mail/mail-vfolder.h>
+
+#define E_MAIL_ACCOUNT_STORE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_ACCOUNT_STORE, EMailAccountStorePrivate))
+
+typedef struct _IndexItem IndexItem;
+
+struct _EMailAccountStorePrivate {
+ CamelService *default_service;
+ GHashTable *service_index;
+ gchar *sort_order_filename;
+ gboolean express_mode;
+ gpointer session; /* weak pointer */
+ guint busy_count;
+};
+
+struct _IndexItem {
+ CamelService *service;
+ GtkTreeRowReference *reference;
+ gulong notify_handler_id;
+};
+
+enum {
+ PROP_0,
+ PROP_BUSY,
+ PROP_DEFAULT_SERVICE,
+ PROP_EXPRESS_MODE,
+ PROP_SESSION
+};
+
+enum {
+ SERVICE_ADDED,
+ SERVICE_REMOVED,
+ SERVICE_ENABLED,
+ SERVICE_DISABLED,
+ SERVICES_REORDERED,
+ REMOVE_REQUESTED,
+ ENABLE_REQUESTED,
+ DISABLE_REQUESTED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void e_mail_account_store_interface_init
+ (GtkTreeModelIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EMailAccountStore,
+ e_mail_account_store,
+ GTK_TYPE_LIST_STORE,
+ G_IMPLEMENT_INTERFACE (
+ GTK_TYPE_TREE_MODEL,
+ e_mail_account_store_interface_init)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_EXTENSIBLE, NULL))
+
+static void
+index_item_free (IndexItem *item)
+{
+ g_signal_handler_disconnect (
+ item->service, item->notify_handler_id);
+
+ g_object_unref (item->service);
+ gtk_tree_row_reference_free (item->reference);
+
+ g_slice_free (IndexItem, item);
+}
+
+static void
+mail_account_store_save_default (EMailAccountStore *store)
+{
+ EAccountList *account_list;
+ EAccount *account;
+ CamelService *service;
+ const gchar *uid;
+
+ service = e_mail_account_store_get_default_service (store);
+
+ account_list = e_get_account_list ();
+ uid = camel_service_get_uid (service);
+ account = e_get_account_by_uid (uid);
+ g_return_if_fail (account != NULL);
+
+ e_account_list_set_default (account_list, account);
+}
+
+static gboolean
+mail_account_store_get_iter (EMailAccountStore *store,
+ CamelService *service,
+ GtkTreeIter *iter)
+{
+ IndexItem *item;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ gboolean iter_set;
+
+ g_return_val_if_fail (service != NULL, FALSE);
+
+ item = g_hash_table_lookup (store->priv->service_index, service);
+
+ if (item == NULL)
+ return FALSE;
+
+ if (!gtk_tree_row_reference_valid (item->reference))
+ return FALSE;
+
+ model = gtk_tree_row_reference_get_model (item->reference);
+ path = gtk_tree_row_reference_get_path (item->reference);
+ iter_set = gtk_tree_model_get_iter (model, iter, path);
+ gtk_tree_path_free (path);
+
+ return iter_set;
+}
+
+static gint
+mail_account_store_default_compare (CamelService *service_a,
+ CamelService *service_b,
+ EMailAccountStore *store)
+{
+ const gchar *display_name_a;
+ const gchar *display_name_b;
+ const gchar *uid_a;
+ const gchar *uid_b;
+
+ uid_a = camel_service_get_uid (service_a);
+ uid_b = camel_service_get_uid (service_b);
+
+ /* Check for special cases first. */
+
+ if (e_mail_account_store_get_express_mode (store)) {
+ if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID) &&
+ g_str_equal (uid_b, E_MAIL_SESSION_VFOLDER_UID))
+ return -1;
+ else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID) &&
+ g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
+ return 1;
+ else if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID))
+ return 1;
+ else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID))
+ return -1;
+ else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
+ return 1;
+ else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
+ return -1;
+ } else {
+ if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID))
+ return -1;
+ else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID))
+ return 1;
+ else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
+ return 1;
+ else if (g_str_equal (uid_b, E_MAIL_SESSION_VFOLDER_UID))
+ return -1;
+ }
+
+ /* Otherwise sort them alphabetically. */
+
+ display_name_a = camel_service_get_display_name (service_a);
+ display_name_b = camel_service_get_display_name (service_b);
+
+ if (display_name_a == NULL)
+ display_name_a = "";
+
+ if (display_name_b == NULL)
+ display_name_b = "";
+
+ return g_utf8_collate (display_name_a, display_name_b);
+}
+
+static void
+mail_account_store_update_row (EMailAccountStore *store,
+ CamelService *service,
+ GtkTreeIter *iter)
+{
+ CamelProvider *provider;
+ gboolean is_default;
+ const gchar *backend_name;
+ const gchar *display_name;
+
+ is_default = (service == store->priv->default_service);
+ display_name = camel_service_get_display_name (service);
+
+ provider = camel_service_get_provider (service);
+ backend_name = (provider != NULL) ? provider->protocol : NULL;
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_DEFAULT, is_default,
+ E_MAIL_ACCOUNT_STORE_COLUMN_BACKEND_NAME, backend_name,
+ E_MAIL_ACCOUNT_STORE_COLUMN_DISPLAY_NAME, display_name,
+ -1);
+}
+
+static void
+mail_account_store_service_notify_cb (CamelService *service,
+ GParamSpec *pspec,
+ EMailAccountStore *store)
+{
+ GtkTreeIter iter;
+
+ if (mail_account_store_get_iter (store, service, &iter))
+ mail_account_store_update_row (store, service, &iter);
+}
+
+static void
+mail_account_store_clean_index (EMailAccountStore *store)
+{
+ GQueue trash = G_QUEUE_INIT;
+ GHashTable *hash_table;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ hash_table = store->priv->service_index;
+ g_hash_table_iter_init (&iter, hash_table);
+
+ /* Remove index items with invalid GtkTreeRowReferences. */
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ IndexItem *item = value;
+
+ if (!gtk_tree_row_reference_valid (item->reference))
+ g_queue_push_tail (&trash, key);
+ }
+
+ while ((key = g_queue_pop_head (&trash)) != NULL)
+ g_hash_table_remove (hash_table, key);
+}
+
+static void
+mail_account_store_update_index (EMailAccountStore *store,
+ GtkTreePath *path,
+ GtkTreeIter *iter)
+{
+ CamelService *service = NULL;
+ GHashTable *hash_table;
+ GtkTreeModel *model;
+ IndexItem *item;
+
+ model = GTK_TREE_MODEL (store);
+ hash_table = store->priv->service_index;
+
+ gtk_tree_model_get (
+ model, iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, &service, -1);
+
+ if (service == NULL)
+ return;
+
+ item = g_hash_table_lookup (hash_table, service);
+
+ if (item == NULL) {
+ item = g_slice_new0 (IndexItem);
+ item->service = g_object_ref (service);
+
+ item->notify_handler_id = g_signal_connect (
+ service, "notify", G_CALLBACK (
+ mail_account_store_service_notify_cb), store);
+
+ g_hash_table_insert (hash_table, item->service, item);
+ }
+
+ /* Update the row reference so the IndexItem will survive
+ * drag-and-drop (new row is inserted, old row is deleted). */
+ gtk_tree_row_reference_free (item->reference);
+ item->reference = gtk_tree_row_reference_new (model, path);
+
+ g_object_unref (service);
+}
+
+static void
+mail_account_store_set_session (EMailAccountStore *store,
+ EMailSession *session)
+{
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (store->priv->session == NULL);
+
+ store->priv->session = session;
+
+ g_object_add_weak_pointer (
+ G_OBJECT (store->priv->session),
+ &store->priv->session);
+}
+
+static void
+mail_account_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_DEFAULT_SERVICE:
+ e_mail_account_store_set_default_service (
+ E_MAIL_ACCOUNT_STORE (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_EXPRESS_MODE:
+ e_mail_account_store_set_express_mode (
+ E_MAIL_ACCOUNT_STORE (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_SESSION:
+ mail_account_store_set_session (
+ E_MAIL_ACCOUNT_STORE (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_account_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_BUSY:
+ g_value_set_boolean (
+ value,
+ e_mail_account_store_get_busy (
+ E_MAIL_ACCOUNT_STORE (object)));
+ return;
+
+ case PROP_DEFAULT_SERVICE:
+ g_value_set_object (
+ value,
+ e_mail_account_store_get_default_service (
+ E_MAIL_ACCOUNT_STORE (object)));
+ return;
+
+ case PROP_EXPRESS_MODE:
+ g_value_set_boolean (
+ value,
+ e_mail_account_store_get_express_mode (
+ E_MAIL_ACCOUNT_STORE (object)));
+ return;
+
+ case PROP_SESSION:
+ g_value_set_object (
+ value,
+ e_mail_account_store_get_session (
+ E_MAIL_ACCOUNT_STORE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_account_store_dispose (GObject *object)
+{
+ EMailAccountStorePrivate *priv;
+
+ priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (object);
+
+ if (priv->session != NULL) {
+ g_object_remove_weak_pointer (
+ G_OBJECT (priv->session), &priv->session);
+ priv->session = NULL;
+ }
+
+ if (priv->default_service != NULL) {
+ g_object_unref (priv->default_service);
+ priv->default_service = NULL;
+ }
+
+ g_hash_table_remove_all (priv->service_index);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_mail_account_store_parent_class)->dispose (object);
+}
+
+static void
+mail_account_store_finalize (GObject *object)
+{
+ EMailAccountStorePrivate *priv;
+
+ priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (object);
+
+ g_warn_if_fail (priv->busy_count == 0);
+ g_hash_table_destroy (priv->service_index);
+ g_free (priv->sort_order_filename);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_mail_account_store_parent_class)->finalize (object);
+}
+
+static void
+mail_account_store_constructed (GObject *object)
+{
+ EMailAccountStore *store;
+ const gchar *config_dir;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_mail_account_store_parent_class)->constructed (object);
+
+ store = E_MAIL_ACCOUNT_STORE (object);
+ config_dir = mail_session_get_config_dir ();
+
+ /* XXX Should we take the filename as a constructor property? */
+ store->priv->sort_order_filename = g_build_filename (
+ config_dir, "sortorder.ini", NULL);
+
+ /* XXX This is kinda lame, but should work until EAccount dies. */
+ g_signal_connect (
+ object, "notify::default-service",
+ G_CALLBACK (mail_account_store_save_default), NULL);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static void
+mail_account_store_service_added (EMailAccountStore *store,
+ CamelService *service)
+{
+ /* Placeholder so subclasses can safely chain up. */
+}
+
+static void
+mail_account_store_service_removed (EMailAccountStore *store,
+ CamelService *service)
+{
+ /* XXX On the account-mgmt branch this operation is asynchronous.
+ * The 'busy_count' is bumped until changes are written back
+ * to the D-Bus service. For now I guess we'll just block. */
+
+ EAccountList *account_list;
+ EAccount *account;
+ const gchar *uid;
+
+ account_list = e_get_account_list ();
+ uid = camel_service_get_uid (service);
+ account = e_get_account_by_uid (uid);
+ g_return_if_fail (account != NULL);
+
+ if (account->enabled) {
+ CamelProvider *provider;
+
+ provider = camel_service_get_provider (service);
+ g_return_if_fail (provider != NULL);
+
+ if (provider->flags & CAMEL_PROVIDER_IS_STORAGE)
+ mail_disconnect_store (CAMEL_STORE (service));
+ }
+
+ /* Remove all the proxies the account has created.
+ * FIXME This proxy stuff belongs in evolution-groupwise. */
+ e_account_list_remove_account_proxies (account_list, account);
+
+ e_account_list_remove (account_list, account);
+
+ e_account_list_save (account_list);
+}
+
+static void
+mail_account_store_service_enabled (EMailAccountStore *store,
+ CamelService *service)
+{
+ /* XXX On the account-mgmt branch this operation is asynchronous.
+ * The 'busy_count' is bumped until changes are written back
+ * to the D-Bus service. For now I guess we'll just block. */
+
+ GSettings *settings;
+ const gchar *uid;
+
+ uid = camel_service_get_uid (service);
+
+ /* Handle built-in services that don't have an EAccount. */
+
+ if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ g_settings_set_boolean (settings, "enable-local", TRUE);
+ g_object_unref (settings);
+
+ } else if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0) {
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ g_settings_set_boolean (settings, "enable-vfolders", TRUE);
+ g_object_unref (settings);
+
+ } else {
+ EAccountList *account_list;
+ EAccount *account;
+
+ account_list = e_get_account_list ();
+ account = e_get_account_by_uid (uid);
+ g_return_if_fail (account != NULL);
+
+ account->enabled = TRUE;
+
+ e_account_list_change (account_list, account);
+ e_account_list_save (account_list);
+ }
+}
+
+static void
+mail_account_store_service_disabled (EMailAccountStore *store,
+ CamelService *service)
+{
+ /* XXX On the account-mgmt branch this operation is asynchronous.
+ * The 'busy_count' is bumped until changes are written back
+ * to the D-Bus service. For now I guess we'll just block. */
+
+ GSettings *settings;
+ const gchar *uid;
+
+ uid = camel_service_get_uid (service);
+
+ /* Handle built-in services that don't have an EAccount. */
+
+ if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ g_settings_set_boolean (settings, "enable-local", FALSE);
+ g_object_unref (settings);
+
+ } else if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0) {
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ g_settings_set_boolean (settings, "enable-vfolders", FALSE);
+ g_object_unref (settings);
+
+ } else {
+ EAccountList *account_list;
+ EAccount *account;
+ CamelProvider *provider;
+
+ account_list = e_get_account_list ();
+ account = e_get_account_by_uid (uid);
+ g_return_if_fail (account != NULL);
+
+ account->enabled = FALSE;
+
+ provider = camel_service_get_provider (service);
+ g_return_if_fail (provider != NULL);
+
+ if (provider->flags & CAMEL_PROVIDER_IS_STORAGE)
+ mail_disconnect_store (CAMEL_STORE (service));
+
+ /* FIXME This proxy stuff belongs in evolution-groupwise. */
+ e_account_list_remove_account_proxies (account_list, account);
+
+ if (account->parent_uid != NULL)
+ e_account_list_remove (account_list, account);
+
+ e_account_list_change (account_list, account);
+ e_account_list_save (account_list);
+ }
+}
+
+static void
+mail_account_store_services_reordered (EMailAccountStore *store,
+ gboolean default_restored)
+{
+ /* XXX Should this be made asynchronous? */
+
+ GError *error = NULL;
+
+ if (default_restored) {
+ const gchar *filename;
+
+ filename = store->priv->sort_order_filename;
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ g_unlink (filename);
+
+ return;
+ }
+
+ if (!e_mail_account_store_save_sort_order (store, &error)) {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+mail_account_store_remove_requested (EMailAccountStore *store,
+ GtkWindow *parent_window,
+ CamelService *service)
+{
+ EAccountList *account_list;
+ EAccount *account;
+ const gchar *alert;
+ const gchar *uid;
+ gint response;
+
+ account_list = e_get_account_list ();
+ uid = camel_service_get_uid (service);
+ account = e_get_account_by_uid (uid);
+
+ g_return_val_if_fail (account != NULL, FALSE);
+
+ /* FIXME This proxy stuff belongs in evolution-groupwise. */
+ if (e_account_list_account_has_proxies (account_list, account))
+ alert = "mail:ask-delete-account-with-proxies";
+ else
+ alert = "mail:ask-delete-account";
+
+ response = e_alert_run_dialog_for_args (parent_window, alert, NULL);
+
+ return (response == GTK_RESPONSE_YES);
+}
+
+static gboolean
+mail_account_store_enable_requested (EMailAccountStore *store,
+ GtkWindow *parent_window,
+ CamelService *service)
+{
+ return TRUE;
+}
+
+static gboolean
+mail_account_store_disable_requested (EMailAccountStore *store,
+ GtkWindow *parent_window,
+ CamelService *service)
+{
+ EAccountList *account_list;
+ EAccount *account;
+ const gchar *uid;
+ gint response;
+
+ account_list = e_get_account_list ();
+ uid = camel_service_get_uid (service);
+ account = e_get_account_by_uid (uid);
+
+ /* "On This Computer" and "Search Folders" do not have
+ * EAccounts, so just silently return TRUE if we failed
+ * to find a matching EAccount for the CamelService. */
+
+ /* Silently return TRUE if we failed to find a matching
+ * EAccount since "On This Computer" and "Search Folders"
+ * do not have EAccounts. */
+ if (account == NULL)
+ return TRUE;
+
+ /* FIXME This proxy stuff belongs in evolution-groupwise. */
+ if (e_account_list_account_has_proxies (account_list, account))
+ response = e_alert_run_dialog_for_args (
+ parent_window,
+ "mail:ask-delete-proxy-accounts", NULL);
+ else
+ response = GTK_RESPONSE_YES;
+
+ return (response == GTK_RESPONSE_YES);
+}
+
+static void
+mail_account_store_row_changed (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter)
+{
+ EMailAccountStore *store;
+
+ /* Neither GtkTreeModel nor GtkListStore implements
+ * this method, so there is nothing to chain up to. */
+
+ store = E_MAIL_ACCOUNT_STORE (tree_model);
+ mail_account_store_update_index (store, path, iter);
+}
+
+static void
+mail_account_store_row_inserted (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter)
+{
+ EMailAccountStore *store;
+
+ /* Neither GtkTreeModel nor GtkListStore implements
+ * this method, so there is nothing to chain up to. */
+
+ store = E_MAIL_ACCOUNT_STORE (tree_model);
+ mail_account_store_update_index (store, path, iter);
+}
+
+static gboolean
+mail_account_store_true_proceed (GSignalInvocationHint *ihint,
+ GValue *return_accumulator,
+ const GValue *handler_return,
+ gpointer not_used)
+{
+ gboolean proceed;
+
+ proceed = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accumulator, proceed);
+
+ return proceed;
+}
+
+static void
+e_mail_account_store_class_init (EMailAccountStoreClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EMailAccountStorePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = mail_account_store_set_property;
+ object_class->get_property = mail_account_store_get_property;
+ object_class->dispose = mail_account_store_dispose;
+ object_class->finalize = mail_account_store_finalize;
+ object_class->constructed = mail_account_store_constructed;
+
+ class->service_added = mail_account_store_service_added;
+ class->service_removed = mail_account_store_service_removed;
+ class->service_enabled = mail_account_store_service_enabled;
+ class->service_disabled = mail_account_store_service_disabled;
+ class->services_reordered = mail_account_store_services_reordered;
+ class->remove_requested = mail_account_store_remove_requested;
+ class->enable_requested = mail_account_store_enable_requested;
+ class->disable_requested = mail_account_store_disable_requested;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_BUSY,
+ g_param_spec_boolean (
+ "busy",
+ "Busy",
+ "Whether async operations are in progress",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_DEFAULT_SERVICE,
+ g_param_spec_object (
+ "default-service",
+ "Default Service",
+ "Default mail store",
+ CAMEL_TYPE_SERVICE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EXPRESS_MODE,
+ g_param_spec_boolean (
+ "express-mode",
+ "Express Mode",
+ "Whether express mode is enabled",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SESSION,
+ g_param_spec_object (
+ "session",
+ "Session",
+ "Mail session",
+ E_TYPE_MAIL_SESSION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ signals[SERVICE_ADDED] = g_signal_new (
+ "service-added",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, service_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ CAMEL_TYPE_SERVICE);
+
+ signals[SERVICE_REMOVED] = g_signal_new (
+ "service-removed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, service_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ CAMEL_TYPE_SERVICE);
+
+ signals[SERVICE_ENABLED] = g_signal_new (
+ "service-enabled",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, service_enabled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ CAMEL_TYPE_SERVICE);
+
+ signals[SERVICE_DISABLED] = g_signal_new (
+ "service-disabled",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, service_disabled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ CAMEL_TYPE_SERVICE);
+
+ signals[SERVICES_REORDERED] = g_signal_new (
+ "services-reordered",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, services_reordered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1,
+ G_TYPE_BOOLEAN);
+
+ signals[REMOVE_REQUESTED] = g_signal_new (
+ "remove-requested",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, remove_requested),
+ mail_account_store_true_proceed, NULL,
+ e_marshal_BOOLEAN__OBJECT_OBJECT,
+ G_TYPE_BOOLEAN, 2,
+ GTK_TYPE_WINDOW,
+ CAMEL_TYPE_SERVICE);
+
+ signals[ENABLE_REQUESTED] = g_signal_new (
+ "enable-requested",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, enable_requested),
+ mail_account_store_true_proceed, NULL,
+ e_marshal_BOOLEAN__OBJECT_OBJECT,
+ G_TYPE_BOOLEAN, 2,
+ GTK_TYPE_WINDOW,
+ CAMEL_TYPE_SERVICE);
+
+ signals[DISABLE_REQUESTED] = g_signal_new (
+ "disable-requested",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMailAccountStoreClass, disable_requested),
+ mail_account_store_true_proceed, NULL,
+ e_marshal_BOOLEAN__OBJECT_OBJECT,
+ G_TYPE_BOOLEAN, 2,
+ GTK_TYPE_WINDOW,
+ CAMEL_TYPE_SERVICE);
+}
+
+static void
+e_mail_account_store_interface_init (GtkTreeModelIface *interface)
+{
+ interface->row_changed = mail_account_store_row_changed;
+ interface->row_inserted = mail_account_store_row_inserted;
+}
+
+static void
+e_mail_account_store_init (EMailAccountStore *store)
+{
+ GType types[E_MAIL_ACCOUNT_STORE_NUM_COLUMNS];
+ GHashTable *service_index;
+ gint ii = 0;
+
+ service_index = g_hash_table_new_full (
+ (GHashFunc) g_direct_hash,
+ (GEqualFunc) g_direct_equal,
+ (GDestroyNotify) NULL,
+ (GDestroyNotify) index_item_free);
+
+ store->priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (store);
+ store->priv->service_index = service_index;
+
+ types[ii++] = CAMEL_TYPE_SERVICE; /* COLUMN_SERVICE */
+ types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_BUILTIN */
+ types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_ENABLED */
+ types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_DEFAULT */
+ types[ii++] = G_TYPE_STRING; /* COLUMN_BACKEND_NAME */
+ types[ii++] = G_TYPE_STRING; /* COLUMN_DISPLAY_NAME */
+
+ g_assert (ii == E_MAIL_ACCOUNT_STORE_NUM_COLUMNS);
+
+ gtk_list_store_set_column_types (
+ GTK_LIST_STORE (store),
+ G_N_ELEMENTS (types), types);
+}
+
+EMailAccountStore *
+e_mail_account_store_new (EMailSession *session)
+{
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
+
+ return g_object_new (
+ E_TYPE_MAIL_ACCOUNT_STORE,
+ "session", session, NULL);
+}
+
+void
+e_mail_account_store_clear (EMailAccountStore *store)
+{
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+
+ gtk_list_store_clear (GTK_LIST_STORE (store));
+ g_hash_table_remove_all (store->priv->service_index);
+}
+
+gboolean
+e_mail_account_store_get_busy (EMailAccountStore *store)
+{
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
+
+ return (store->priv->busy_count > 0);
+}
+
+EMailSession *
+e_mail_account_store_get_session (EMailAccountStore *store)
+{
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), NULL);
+
+ return E_MAIL_SESSION (store->priv->session);
+}
+
+CamelService *
+e_mail_account_store_get_default_service (EMailAccountStore *store)
+{
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), NULL);
+
+ return store->priv->default_service;
+}
+
+void
+e_mail_account_store_set_default_service (EMailAccountStore *store,
+ CamelService *service)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_set;
+
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+
+ if (service == store->priv->default_service)
+ return;
+
+ if (service != NULL) {
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+ g_object_ref (service);
+ }
+
+ if (store->priv->default_service != NULL)
+ g_object_unref (store->priv->default_service);
+
+ store->priv->default_service = service;
+
+ model = GTK_TREE_MODEL (store);
+ iter_set = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (iter_set) {
+ CamelService *candidate;
+
+ gtk_tree_model_get (
+ model, &iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE,
+ &candidate, -1);
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (model), &iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_DEFAULT,
+ service == candidate, -1);
+
+ g_object_unref (candidate);
+
+ iter_set = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ g_object_notify (G_OBJECT (store), "default-service");
+}
+
+gboolean
+e_mail_account_store_get_express_mode (EMailAccountStore *store)
+{
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
+
+ return store->priv->express_mode;
+}
+
+void
+e_mail_account_store_set_express_mode (EMailAccountStore *store,
+ gboolean express_mode)
+{
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+
+ store->priv->express_mode = express_mode;
+
+ g_object_notify (G_OBJECT (store), "express-mode");
+}
+
+void
+e_mail_account_store_add_service (EMailAccountStore *store,
+ CamelService *service)
+{
+ GSettings *settings;
+ GtkTreeIter iter;
+ const gchar *filename;
+ const gchar *uid;
+ gboolean builtin;
+ gboolean enabled;
+
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ /* Avoid duplicate services in the account store. */
+ if (mail_account_store_get_iter (store, service, &iter))
+ g_return_if_reached ();
+
+ uid = camel_service_get_uid (service);
+
+ /* Handle built-in services that don't have an EAccount. */
+
+ if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
+ builtin = TRUE;
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ enabled = g_settings_get_boolean (settings, "enable-local");
+ g_object_unref (settings);
+
+ } else if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0) {
+ builtin = TRUE;
+
+ settings = g_settings_new ("org.gnome.evolution.mail");
+ enabled = g_settings_get_boolean (settings, "enable-vfolders");
+ g_object_unref (settings);
+
+ } else {
+ EAccount *account;
+
+ account = e_get_account_by_uid (uid);
+ g_return_if_fail (account != NULL);
+
+ builtin = FALSE;
+ enabled = account->enabled;
+ }
+
+ /* Where do we insert new services now that accounts can be
+ * reordered? This is just a simple policy I came up with.
+ * It's certainly subject to debate and tweaking.
+ *
+ * Always insert new services in row 0 initially. Then test
+ * for the presence of the sort order file. If present, the
+ * user has messed around with the ordering so leave the new
+ * service at row 0. If not present, services are sorted in
+ * their default order. So re-apply the default order using
+ * e_mail_account_store_reorder_services(store, NULL) so the
+ * new service moves to its proper default position. */
+
+ gtk_list_store_prepend (GTK_LIST_STORE (store), &iter);
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, service,
+ E_MAIL_ACCOUNT_STORE_COLUMN_BUILTIN, builtin,
+ E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, enabled,
+ -1);
+
+ /* This populates the rest of the columns. */
+ mail_account_store_update_row (store, service, &iter);
+
+ g_signal_emit (store, signals[SERVICE_ADDED], 0, service);
+
+ if (enabled)
+ g_signal_emit (store, signals[SERVICE_ENABLED], 0, service);
+ else
+ g_signal_emit (store, signals[SERVICE_DISABLED], 0, service);
+
+ filename = store->priv->sort_order_filename;
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ e_mail_account_store_reorder_services (store, NULL);
+}
+
+void
+e_mail_account_store_remove_service (EMailAccountStore *store,
+ GtkWindow *parent_window,
+ CamelService *service)
+{
+ GtkTreeIter iter;
+ gboolean proceed;
+
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ if (!mail_account_store_get_iter (store, service, &iter))
+ g_return_if_reached ();
+
+ /* Possibly request user confirmation. */
+ g_signal_emit (
+ store, signals[REMOVE_REQUESTED], 0,
+ parent_window, service, &proceed);
+
+ if (proceed) {
+ g_object_ref (service);
+
+ gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
+
+ mail_account_store_clean_index (store);
+
+ g_signal_emit (store, signals[SERVICE_REMOVED], 0, service);
+
+ g_object_unref (service);
+ }
+}
+
+void
+e_mail_account_store_enable_service (EMailAccountStore *store,
+ GtkWindow *parent_window,
+ CamelService *service)
+{
+ GtkTreeIter iter;
+ gboolean proceed;
+
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ if (!mail_account_store_get_iter (store, service, &iter))
+ g_return_if_reached ();
+
+ /* Possibly request user confirmation. */
+ g_signal_emit (
+ store, signals[ENABLE_REQUESTED], 0,
+ parent_window, service, &proceed);
+
+ if (proceed) {
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, TRUE, -1);
+
+ g_signal_emit (store, signals[SERVICE_ENABLED], 0, service);
+ }
+}
+
+void
+e_mail_account_store_disable_service (EMailAccountStore *store,
+ GtkWindow *parent_window,
+ CamelService *service)
+{
+ GtkTreeIter iter;
+ gboolean proceed;
+
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+ g_return_if_fail (CAMEL_IS_SERVICE (service));
+
+ if (!mail_account_store_get_iter (store, service, &iter))
+ g_return_if_reached ();
+
+ /* Possibly request user confirmation. */
+ g_signal_emit (
+ store, signals[DISABLE_REQUESTED], 0,
+ parent_window, service, &proceed);
+
+ if (proceed) {
+ gtk_list_store_set (
+ GTK_LIST_STORE (store), &iter,
+ E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, FALSE, -1);
+
+ g_signal_emit (store, signals[SERVICE_DISABLED], 0, service);
+ }
+}
+
+void
+e_mail_account_store_reorder_services (EMailAccountStore *store,
+ GQueue *ordered_services)
+{
+ GQueue *current_order = NULL;
+ GQueue *default_order = NULL;
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+ gboolean use_default_order;
+ gboolean iter_set;
+ GList *head, *link;
+ gint *new_order;
+ gint n_children;
+ gint new_pos = 0;
+ guint length;
+
+ g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
+
+ tree_model = GTK_TREE_MODEL (store);
+ n_children = gtk_tree_model_iter_n_children (tree_model, NULL);
+
+ /* Treat NULL queues and empty queues the same. */
+ if (ordered_services != NULL && g_queue_is_empty (ordered_services))
+ ordered_services = NULL;
+
+ use_default_order = (ordered_services == NULL);
+
+ if (ordered_services != NULL) {
+ length = g_queue_get_length (ordered_services);
+ g_return_if_fail (length == n_children);
+ }
+
+ current_order = g_queue_new ();
+ iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+ /* Build a queue of CamelServices in the order they appear in
+ * the list store. We'll use this to construct the mapping to
+ * pass to gtk_list_store_reorder(). */
+ while (iter_set) {
+ GValue value = G_VALUE_INIT;
+ const gint column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
+
+ gtk_tree_model_get_value (tree_model, &iter, column, &value);
+ g_queue_push_tail (current_order, g_value_get_object (&value));
+ g_value_unset (&value);
+
+ iter_set = gtk_tree_model_iter_next (tree_model, &iter);
+ }
+
+ /* If a custom ordering was not given, revert to default. */
+ if (use_default_order) {
+ default_order = g_queue_copy (current_order);
+
+ g_queue_sort (
+ default_order, (GCompareDataFunc)
+ mail_account_store_default_compare, store);
+
+ ordered_services = default_order;
+ }
+
+ new_order = g_new0 (gint, n_children);
+ head = g_queue_peek_head_link (ordered_services);
+
+ for (link = head; link != NULL; link = g_list_next (link)) {
+ GList *matching_link;
+ gint old_pos;
+
+ matching_link = g_queue_find (current_order, link->data);
+
+ if (matching_link == NULL || matching_link->data == NULL)
+ break;
+
+ old_pos = g_queue_link_index (current_order, matching_link);
+
+ matching_link->data = NULL;
+ new_order[new_pos++] = old_pos;
+ }
+
+ if (new_pos == n_children) {
+ gtk_list_store_reorder (GTK_LIST_STORE (store), new_order);
+ g_signal_emit (
+ store, signals[SERVICES_REORDERED], 0,
+ use_default_order);
+ }
+
+ g_free (new_order);
+
+ if (current_order != NULL)
+ g_queue_free (current_order);
+
+ if (default_order != NULL)
+ g_queue_free (default_order);
+}
+
+gint
+e_mail_account_store_compare_services (EMailAccountStore *store,
+ CamelService *service_a,
+ CamelService *service_b)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path_a;
+ GtkTreePath *path_b;
+ GtkTreeIter iter_a;
+ GtkTreeIter iter_b;
+ gboolean iter_a_set;
+ gboolean iter_b_set;
+ gint result;
+
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), -1);
+ g_return_val_if_fail (CAMEL_IS_SERVICE (service_a), -1);
+ g_return_val_if_fail (CAMEL_IS_SERVICE (service_b), -1);
+
+ /* XXX This is horribly inefficient but should be
+ * over a small enough set to not be noticable. */
+
+ iter_a_set = mail_account_store_get_iter (store, service_a, &iter_a);
+ iter_b_set = mail_account_store_get_iter (store, service_b, &iter_b);
+
+ if (!iter_a_set && !iter_b_set)
+ return 0;
+
+ if (!iter_a_set)
+ return -1;
+
+ if (!iter_b_set)
+ return 1;
+
+ model = GTK_TREE_MODEL (store);
+
+ path_a = gtk_tree_model_get_path (model, &iter_a);
+ path_b = gtk_tree_model_get_path (model, &iter_b);
+
+ result = gtk_tree_path_compare (path_a, path_b);
+
+ gtk_tree_path_free (path_a);
+ gtk_tree_path_free (path_b);
+
+ return result;
+}
+
+gboolean
+e_mail_account_store_load_sort_order (EMailAccountStore *store,
+ GError **error)
+{
+ GQueue service_queue = G_QUEUE_INIT;
+ EMailSession *session;
+ GKeyFile *key_file;
+ const gchar *filename;
+ gchar **service_uids;
+ gboolean success = TRUE;
+ gsize ii, length;
+
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
+
+ session = e_mail_account_store_get_session (store);
+
+ key_file = g_key_file_new ();
+ filename = store->priv->sort_order_filename;
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS))
+ success = g_key_file_load_from_file (
+ key_file, filename, G_KEY_FILE_NONE, error);
+
+ if (!success) {
+ g_key_file_free (key_file);
+ return FALSE;
+ }
+
+ /* If the key is not present, length is set to zero. */
+ service_uids = g_key_file_get_string_list (
+ key_file, "Accounts", "SortOrder", &length, NULL);
+
+ for (ii = 0; ii < length; ii++) {
+ CamelService *service;
+
+ service = camel_session_get_service (
+ CAMEL_SESSION (session), service_uids[ii]);
+ if (service != NULL)
+ g_queue_push_tail (&service_queue, service);
+ }
+
+ e_mail_account_store_reorder_services (store, &service_queue);
+
+ g_queue_clear (&service_queue);
+ g_strfreev (service_uids);
+
+ g_key_file_free (key_file);
+
+ return TRUE;
+}
+
+gboolean
+e_mail_account_store_save_sort_order (EMailAccountStore *store,
+ GError **error)
+{
+ GKeyFile *key_file;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ const gchar **service_uids;
+ const gchar *filename;
+ gchar *contents;
+ gboolean iter_set;
+ gboolean success;
+ gsize length;
+ gsize ii = 0;
+
+ g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
+
+ model = GTK_TREE_MODEL (store);
+ length = gtk_tree_model_iter_n_children (model, NULL);
+
+ /* Empty store, nothing to save. */
+ if (length == 0)
+ return TRUE;
+
+ service_uids = g_new0 (const gchar *, length);
+
+ iter_set = gtk_tree_model_get_iter_first (model, &iter);
+
+ while (iter_set) {
+ GValue value = G_VALUE_INIT;
+ const gint column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
+ CamelService *service;
+
+ gtk_tree_model_get_value (model, &iter, column, &value);
+ service = g_value_get_object (&value);
+ service_uids[ii++] = camel_service_get_uid (service);
+ g_value_unset (&value);
+
+ iter_set = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ key_file = g_key_file_new ();
+ filename = store->priv->sort_order_filename;
+
+ g_key_file_set_string_list (
+ key_file, "Accounts", "SortOrder", service_uids, length);
+
+ contents = g_key_file_to_data (key_file, &length, NULL);
+ success = g_file_set_contents (filename, contents, length, error);
+ g_free (contents);
+
+ g_key_file_free (key_file);
+
+ g_free (service_uids);
+
+ return success;
+}
+