From e0548d25707ad8c4713e2e74c622a92bf4988545 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 26 Sep 2011 11:45:59 +0200 Subject: Bug #351025 - Make the order of the mail accounts configurable --- mail/e-mail-backend.c | 35 +- mail/e-mail-backend.h | 7 + mail/e-mail-reader.c | 6 +- mail/e-mail-sidebar.c | 2 +- mail/e-mail-store.c | 36 +- mail/em-composer-utils.c | 2 +- mail/em-folder-selection-button.c | 4 +- mail/em-folder-tree-model.c | 367 ++++++++++++---- mail/em-folder-tree-model.h | 11 +- mail/em-folder-tree.c | 2 +- mail/em-folder-utils.c | 19 +- mail/em-subscription-editor.c | 2 +- mail/em-utils.c | 168 ++++++++ mail/em-utils.h | 4 + mail/em-vfolder-rule.c | 2 +- mail/evolution-mail.schemas.in | 35 +- mail/message-list.c | 2 +- modules/mail/e-mail-shell-settings.c | 8 + modules/mail/e-mail-shell-view.c | 2 +- modules/mail/em-account-prefs.c | 95 ++++- plugins/mark-all-read/mark-all-read.c | 6 +- widgets/misc/e-account-manager.c | 103 +++++ widgets/misc/e-account-tree-view.c | 772 +++++++++++++++++++++++++++++++++- widgets/misc/e-account-tree-view.h | 46 ++ 24 files changed, 1628 insertions(+), 108 deletions(-) diff --git a/mail/e-mail-backend.c b/mail/e-mail-backend.c index c9ee133bb0..ff37b7a93a 100644 --- a/mail/e-mail-backend.c +++ b/mail/e-mail-backend.c @@ -71,9 +71,16 @@ enum { PROP_SESSION }; +enum { + ACCOUNT_SORT_ORDER_CHANGED, + LAST_SIGNAL +}; + /* FIXME Kill this thing. It's a horrible hack. */ extern gint camel_application_is_exiting; +static guint signals[LAST_SIGNAL]; + G_DEFINE_ABSTRACT_TYPE ( EMailBackend, e_mail_backend, @@ -538,7 +545,7 @@ mail_backend_folder_changed_cb (MailFolderCache *folder_cache, const gchar *msg_uid, const gchar *msg_sender, const gchar *msg_subject, - EShell *shell) + EMailBackend *mail_backend) { CamelFolder *folder = NULL; EMEvent *event = em_event_peek (); @@ -565,12 +572,12 @@ mail_backend_folder_changed_cb (MailFolderCache *folder_cache, folder_type = (flags & CAMEL_FOLDER_TYPE_MASK); target->is_inbox = (folder_type == CAMEL_FOLDER_TYPE_INBOX); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (mail_backend); target->display_name = em_folder_tree_model_get_folder_name ( model, store, folder_name); if (target->new > 0) - e_shell_event (shell, "mail-icon", (gpointer) "mail-unread"); + e_shell_event (e_shell_backend_get_shell (E_SHELL_BACKEND (mail_backend)), "mail-icon", (gpointer) "mail-unread"); /** @Event: folder.changed * @Title: Folder changed @@ -766,7 +773,7 @@ mail_backend_constructed (GObject *object) e_account_combo_box_set_session (CAMEL_SESSION (priv->session)); /* FIXME EMailBackend should own the default EMFolderTreeModel. */ - folder_tree_model = em_folder_tree_model_get_default (); + folder_tree_model = em_folder_tree_model_get_default (E_MAIL_BACKEND (shell_backend)); em_folder_tree_model_set_session (folder_tree_model, priv->session); g_signal_connect ( @@ -801,7 +808,7 @@ mail_backend_constructed (GObject *object) g_signal_connect ( folder_cache, "folder-changed", - G_CALLBACK (mail_backend_folder_changed_cb), shell); + G_CALLBACK (mail_backend_folder_changed_cb), shell_backend); mail_config_init (priv->session); mail_msg_init (); @@ -838,6 +845,15 @@ e_mail_backend_class_init (EMailBackendClass *class) NULL, E_TYPE_MAIL_SESSION, G_PARAM_READABLE)); + + signals[ACCOUNT_SORT_ORDER_CHANGED] = g_signal_new ( + "account-sort-order-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EMailBackendClass, account_sort_order_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -934,3 +950,12 @@ e_mail_backend_submit_alert (EMailBackend *backend, e_alert_submit_valist (E_ALERT_SINK (shell_content), tag, va); va_end (va); } + +void +e_mail_backend_account_sort_order_changed (EMailBackend *backend) +{ + g_return_if_fail (backend != NULL); + g_return_if_fail (E_IS_MAIL_BACKEND (backend)); + + g_signal_emit (backend, signals[ACCOUNT_SORT_ORDER_CHANGED], 0); +} diff --git a/mail/e-mail-backend.h b/mail/e-mail-backend.h index 6d425197f3..4d3cc10799 100644 --- a/mail/e-mail-backend.h +++ b/mail/e-mail-backend.h @@ -67,6 +67,10 @@ struct _EMailBackendClass { (EMailBackend *backend); gboolean (*empty_trash_policy_decision) (EMailBackend *backend); + + /* Signals */ + void (*account_sort_order_changed) + (EMailBackend *backend); }; GType e_mail_backend_get_type (void); @@ -79,6 +83,9 @@ void e_mail_backend_submit_alert (EMailBackend *backend, const gchar *tag, ...) G_GNUC_NULL_TERMINATED; +void e_mail_backend_account_sort_order_changed + (EMailBackend *backend); + G_END_DECLS #endif /* E_MAIL_BACKEND_H */ diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c index 430f71dc4f..a8e3899ace 100644 --- a/mail/e-mail-reader.c +++ b/mail/e-mail-reader.c @@ -300,7 +300,7 @@ action_mail_copy_cb (GtkAction *action, window = e_mail_reader_get_window (reader); uids = e_mail_reader_get_selected_uids (reader); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); dialog = em_folder_selector_new ( window, backend, model, @@ -732,7 +732,7 @@ action_mail_mark_unread_cb (GtkAction *action, /* Notify the tree model that the user has marked messages as * unread so it doesn't mistake the event as new mail arriving. */ - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (e_mail_reader_get_backend (reader)); folder = e_mail_reader_get_folder (reader); em_folder_tree_model_user_marked_unread (model, folder, n_marked); } @@ -806,7 +806,7 @@ action_mail_move_cb (GtkAction *action, uids = e_mail_reader_get_selected_uids (reader); window = e_mail_reader_get_window (reader); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); dialog = em_folder_selector_new ( window, backend, model, diff --git a/mail/e-mail-sidebar.c b/mail/e-mail-sidebar.c index 35048f8293..6a971ea51f 100644 --- a/mail/e-mail-sidebar.c +++ b/mail/e-mail-sidebar.c @@ -471,7 +471,7 @@ e_mail_sidebar_new (EMailBackend *backend, g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL); g_return_val_if_fail (E_IS_ALERT_SINK (alert_sink), NULL); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); return g_object_new ( E_TYPE_MAIL_SIDEBAR, diff --git a/mail/e-mail-store.c b/mail/e-mail-store.c index ea44e4238b..7b7774399d 100644 --- a/mail/e-mail-store.c +++ b/mail/e-mail-store.c @@ -39,6 +39,9 @@ #include "mail/mail-mt.h" #include "mail/mail-ops.h" +#include "shell/e-shell.h" +#include "shell/e-shell-settings.h" + typedef struct _StoreInfo StoreInfo; typedef void (*AddStoreCallback) (MailFolderCache *folder_cache, @@ -152,6 +155,32 @@ mail_store_note_store_cb (MailFolderCache *folder_cache, return TRUE; } +static gboolean +special_mail_store_is_enabled (CamelStore *store) +{ + CamelService *service; + EShell *shell; + EShellSettings *shell_settings; + const gchar *uid, *prop = NULL; + + service = CAMEL_SERVICE (store); + g_return_val_if_fail (service, FALSE); + + uid = camel_service_get_uid (service); + if (g_strcmp0 (uid, "local") == 0) + prop = "mail-enable-local-folders"; + else if (g_strcmp0 (uid, "vfolder") == 0) + prop = "mail-enable-search-folders"; + + if (!prop) + return TRUE; + + shell = e_shell_get_default (); + shell_settings = e_shell_get_shell_settings (shell); + + return e_shell_settings_get_boolean (shell_settings, prop); +} + static void mail_store_add (EMailSession *session, CamelStore *store, @@ -165,7 +194,7 @@ mail_store_add (EMailSession *session, g_return_if_fail (store != NULL); g_return_if_fail (CAMEL_IS_STORE (store)); - default_model = em_folder_tree_model_get_default (); + default_model = em_folder_tree_model_get_default (NULL); folder_cache = e_mail_session_get_folder_cache (session); store_info = store_info_new (store); @@ -173,7 +202,8 @@ mail_store_add (EMailSession *session, g_hash_table_insert (store_table, store, store_info); - em_folder_tree_model_add_store (default_model, store); + if (special_mail_store_is_enabled (store)) + em_folder_tree_model_add_store (default_model, store); mail_folder_cache_note_store ( folder_cache, CAMEL_SESSION (session), store, NULL, @@ -387,7 +417,7 @@ e_mail_store_remove (EMailSession *session, folder_cache = e_mail_session_get_folder_cache (session); mail_folder_cache_note_store_remove (folder_cache, store); - default_model = em_folder_tree_model_get_default (); + default_model = em_folder_tree_model_get_default (NULL); em_folder_tree_model_remove_store (default_model, store); mail_disconnect_store (store); diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index 8c98040ada..e42559ab3b 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -2989,7 +2989,7 @@ post_header_clicked_cb (EComposerPostHeader *header, shell_backend = e_shell_get_backend_by_name (shell, "mail"); /* FIXME Limit the folder tree to the NNTP account? */ - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (E_MAIL_BACKEND (shell_backend)); dialog = em_folder_selector_new ( GTK_WINDOW (composer), diff --git a/mail/em-folder-selection-button.c b/mail/em-folder-selection-button.c index 3f05147078..26a3c9c2b0 100644 --- a/mail/em-folder-selection-button.c +++ b/mail/em-folder-selection-button.c @@ -272,13 +272,13 @@ folder_selection_button_clicked (GtkButton *button) session = e_mail_backend_get_session (priv->backend); - model = em_folder_tree_model_new (); + model = em_folder_tree_model_new (priv->backend); em_folder_tree_model_set_session (model, session); em_folder_tree_model_add_store (model, priv->store); } if (model == NULL) - model = g_object_ref (em_folder_tree_model_get_default ()); + model = g_object_ref (em_folder_tree_model_get_default (priv->backend)); dialog = em_folder_selector_new ( parent, priv->backend, model, diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c index eb26ad5fff..2116faf854 100644 --- a/mail/em-folder-tree-model.c +++ b/mail/em-folder-tree-model.c @@ -69,6 +69,7 @@ struct _EMFolderTreeModelPrivate { EAccountList *accounts; EMailSession *session; + EMailBackend *backend; /* CamelStore -> EMFolderTreeStoreInfo */ GHashTable *store_index; @@ -84,7 +85,8 @@ struct _EMFolderTreeModelPrivate { enum { PROP_0, PROP_SELECTION, - PROP_SESSION + PROP_SESSION, + PROP_BACKEND }; enum { @@ -124,55 +126,69 @@ folder_tree_model_sort (GtkTreeModel *model, GtkTreeIter *b, gpointer user_data) { - EShell *shell; + EShell *shell = user_data; gchar *aname, *bname; CamelStore *store; gboolean is_store; guint32 aflags, bflags; + guint asortorder, bsortorder; gint rv = -2; - /* XXX Pass the EShell in as user_data. */ - shell = e_shell_get_default (); - gtk_tree_model_get ( model, a, COL_BOOL_IS_STORE, &is_store, COL_POINTER_CAMEL_STORE, &store, COL_STRING_DISPLAY_NAME, &aname, - COL_UINT_FLAGS, &aflags, -1); + COL_UINT_FLAGS, &aflags, + COL_UINT_SORTORDER, &asortorder, + -1); gtk_tree_model_get ( model, b, COL_STRING_DISPLAY_NAME, &bname, - COL_UINT_FLAGS, &bflags, -1); + COL_UINT_FLAGS, &bflags, + COL_UINT_SORTORDER, &bsortorder, + -1); if (is_store) { - /* On This Computer is always first, and Search Folders - * is always last. */ - if (e_shell_get_express_mode (shell)) { - if (!strcmp (aname, _("On This Computer")) && - !strcmp (bname, _("Search Folders"))) - rv = -1; - else if (!strcmp (bname, _("On This Computer")) && - !strcmp (aname, _("Search Folders"))) - rv = 1; - else if (!strcmp (aname, _("On This Computer"))) - rv = 1; - else if (!strcmp (bname, _("On This Computer"))) - rv = -1; - else if (!strcmp (aname, _("Search Folders"))) - rv = 1; - else if (!strcmp (bname, _("Search Folders"))) - rv = -1; - } else { - if (!strcmp (aname, _("On This Computer"))) + if (e_shell_settings_get_boolean (e_shell_get_shell_settings (shell), "mail-sort-accounts-alpha")) { + const gchar *on_this_computer = _("On This Computer"); + const gchar *search_folders = _("Search Folders"); + + /* On This Computer is always first, and Search Folders + * is always last. */ + if (e_shell_get_express_mode (shell)) { + if (g_str_equal (aname, on_this_computer) && + g_str_equal (bname, search_folders)) + rv = -1; + else if (g_str_equal (bname, on_this_computer) && + g_str_equal (aname, search_folders)) + rv = 1; + else if (g_str_equal (aname, on_this_computer)) + rv = 1; + else if (g_str_equal (bname, on_this_computer)) + rv = -1; + else if (g_str_equal (aname, search_folders)) + rv = 1; + else if (g_str_equal (bname, search_folders)) + rv = -1; + } else { + if (g_str_equal (aname, on_this_computer)) + rv = -1; + else if (g_str_equal (bname, on_this_computer)) + rv = 1; + else if (g_str_equal (aname, search_folders)) + rv = 1; + else if (g_str_equal (bname, search_folders)) + rv = -1; + } + } else if (asortorder || bsortorder) { + if (asortorder < bsortorder) rv = -1; - else if (!strcmp (bname, _("On This Computer"))) - rv = 1; - else if (!strcmp (aname, _("Search Folders"))) + else if (asortorder > bsortorder) rv = 1; - else if (!strcmp (bname, _("Search Folders"))) - rv = -1; + else + rv = 0; } } else if (store == vfolder_store) { /* UNMATCHED is always last. */ @@ -191,6 +207,8 @@ folder_tree_model_sort (GtkTreeModel *model, if (aname == NULL) { if (bname == NULL) rv = 0; + else + rv = -1; } else if (bname == NULL) rv = 1; @@ -259,6 +277,106 @@ account_added_cb (EAccountList *accounts, e_mail_store_add_by_account (session, account); } +static void +folder_tree_model_sort_changed (EMFolderTreeModel *tree_model) +{ + GtkTreeModel *model; + EShellBackend *shell_backend; + + g_return_if_fail (tree_model != NULL); + g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (tree_model)); + + model = GTK_TREE_MODEL (tree_model); + if (!model) + return; + + shell_backend = E_SHELL_BACKEND (em_folder_tree_model_get_backend (tree_model)); + + /* this invokes also sort on a GtkTreeStore */ + gtk_tree_sortable_set_default_sort_func ( + GTK_TREE_SORTABLE (model), + folder_tree_model_sort, e_shell_backend_get_shell (shell_backend), NULL); +} + +static void +account_sort_order_changed_cb (EMFolderTreeModel *folder_tree_model) +{ + EMailBackend *mail_backend; + GtkTreeModel *model; + GtkTreeStore *tree_store; + GtkTreeIter iter; + + g_return_if_fail (folder_tree_model != NULL); + + model = GTK_TREE_MODEL (folder_tree_model); + g_return_if_fail (model != NULL); + + tree_store = GTK_TREE_STORE (folder_tree_model); + g_return_if_fail (tree_store != NULL); + + if (!gtk_tree_model_get_iter_first (model, &iter)) + return; + + mail_backend = em_folder_tree_model_get_backend (folder_tree_model); + + do { + CamelStore *store = NULL; + + gtk_tree_model_get (model, &iter, COL_POINTER_CAMEL_STORE, &store, -1); + + if (store) { + const gchar *account_uid; + guint sortorder; + + account_uid = camel_service_get_uid (CAMEL_SERVICE (store)); + sortorder = em_utils_get_account_sort_order (mail_backend, account_uid); + + gtk_tree_store_set (tree_store, &iter, COL_UINT_SORTORDER, sortorder, -1); + } + } while (gtk_tree_model_iter_next (model, &iter)); + + folder_tree_model_sort_changed (folder_tree_model); +} + +static void +add_remove_special_folder (EMFolderTreeModel *model, const gchar *account_uid, gboolean add) +{ + EMailSession *session; + CamelService *service; + + session = em_folder_tree_model_get_session (model); + + service = camel_session_get_service (CAMEL_SESSION (session), account_uid); + + if (!CAMEL_IS_STORE (service)) + return; + + if (add) + em_folder_tree_model_add_store (model, CAMEL_STORE (service)); + else + em_folder_tree_model_remove_store (model, CAMEL_STORE (service)); +} + +static void +enable_local_folders_changed_cb (EMFolderTreeModel *model, GParamSpec *spec, EShellSettings *shell_settings) +{ + g_return_if_fail (model != NULL); + g_return_if_fail (shell_settings != NULL); + + add_remove_special_folder (model, "local", + e_shell_settings_get_boolean (shell_settings, "mail-enable-local-folders")); +} + +static void +enable_search_folders_changed_cb (EMFolderTreeModel *model, GParamSpec *spec, EShellSettings *shell_settings) +{ + g_return_if_fail (model != NULL); + g_return_if_fail (shell_settings != NULL); + + add_remove_special_folder (model, "vfolder", + e_shell_settings_get_boolean (shell_settings, "mail-enable-search-folders")); +} + static void folder_tree_model_selection_finalized_cb (EMFolderTreeModel *model) { @@ -285,6 +403,11 @@ folder_tree_model_set_property (GObject *object, EM_FOLDER_TREE_MODEL (object), g_value_get_object (value)); return; + case PROP_BACKEND: + em_folder_tree_model_set_backend ( + EM_FOLDER_TREE_MODEL (object), + g_value_get_object (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -310,11 +433,74 @@ folder_tree_model_get_property (GObject *object, em_folder_tree_model_get_session ( EM_FOLDER_TREE_MODEL (object))); return; + case PROP_BACKEND: + g_value_set_object ( + value, + em_folder_tree_model_get_backend ( + EM_FOLDER_TREE_MODEL (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } + +static void +folder_tree_model_constructed (GObject *object) +{ + EShell *shell; + EShellSettings *shell_settings; + EMFolderTreeModel *model; + + GType col_types[] = { + G_TYPE_STRING, /* display name */ + G_TYPE_POINTER, /* store object */ + G_TYPE_STRING, /* full name */ + G_TYPE_STRING, /* icon name */ + G_TYPE_STRING, /* uri */ + G_TYPE_UINT, /* unread count */ + G_TYPE_UINT, /* flags */ + G_TYPE_BOOLEAN, /* is a store node */ + G_TYPE_BOOLEAN, /* is a folder node */ + G_TYPE_BOOLEAN, /* has not-yet-loaded subfolders */ + G_TYPE_UINT, /* last known unread count */ + G_TYPE_BOOLEAN, /* folder is a draft folder */ + G_TYPE_UINT /* user's sortorder */ + }; + + model = EM_FOLDER_TREE_MODEL (object); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (model->priv->backend)); + shell_settings = e_shell_get_shell_settings (shell); + + gtk_tree_store_set_column_types ( + GTK_TREE_STORE (model), NUM_COLUMNS, col_types); + gtk_tree_sortable_set_default_sort_func ( + GTK_TREE_SORTABLE (model), + folder_tree_model_sort, shell, NULL); + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (model), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + model->priv->accounts = e_get_account_list (); + model->priv->account_changed_id = g_signal_connect ( + model->priv->accounts, "account-changed", + G_CALLBACK (account_changed_cb), model); + model->priv->account_removed_id = g_signal_connect ( + model->priv->accounts, "account-removed", + G_CALLBACK (account_removed_cb), model); + model->priv->account_added_id = g_signal_connect ( + model->priv->accounts, "account-added", + G_CALLBACK (account_added_cb), model); + + g_signal_connect_swapped (model->priv->backend, "account-sort-order-changed", G_CALLBACK (account_sort_order_changed_cb), model); + g_signal_connect_swapped (shell_settings, "notify::mail-sort-accounts-alpha", G_CALLBACK (account_sort_order_changed_cb), model); + g_signal_connect_swapped (shell_settings, "notify::mail-enable-local-folders", G_CALLBACK (enable_local_folders_changed_cb), model); + g_signal_connect_swapped (shell_settings, "notify::mail-enable-search-folders", G_CALLBACK (enable_search_folders_changed_cb), model); + + G_OBJECT_CLASS (parent_class)->constructed (object); +} + static void folder_tree_model_dispose (GObject *object) { @@ -334,6 +520,24 @@ folder_tree_model_dispose (GObject *object) priv->session = NULL; } + if (priv->backend) { + EShell *shell; + EShellSettings *shell_settings; + EMFolderTreeModel *model; + + model = EM_FOLDER_TREE_MODEL (object); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (priv->backend)); + shell_settings = e_shell_get_shell_settings (shell); + + g_signal_handlers_disconnect_by_func (priv->backend, G_CALLBACK (account_sort_order_changed_cb), model); + g_signal_handlers_disconnect_by_func (shell_settings, G_CALLBACK (account_sort_order_changed_cb), model); + g_signal_handlers_disconnect_by_func (shell_settings, G_CALLBACK (enable_local_folders_changed_cb), model); + g_signal_handlers_disconnect_by_func (shell_settings, G_CALLBACK (enable_search_folders_changed_cb), model); + + g_object_unref (priv->backend); + priv->backend = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -370,6 +574,7 @@ em_folder_tree_model_class_init (EMFolderTreeModelClass *class) object_class = G_OBJECT_CLASS (class); object_class->set_property = folder_tree_model_set_property; object_class->get_property = folder_tree_model_get_property; + object_class->constructed = folder_tree_model_constructed; object_class->dispose = folder_tree_model_dispose; object_class->finalize = folder_tree_model_finalize; @@ -393,6 +598,16 @@ em_folder_tree_model_class_init (EMFolderTreeModelClass *class) E_TYPE_MAIL_SESSION, G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, + PROP_BACKEND, + g_param_spec_object ( + "backend", + NULL, + NULL, + E_TYPE_MAIL_BACKEND, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + signals[LOADING_ROW] = g_signal_new ( "loading-row", G_OBJECT_CLASS_TYPE (object_class), @@ -477,21 +692,6 @@ em_folder_tree_model_init (EMFolderTreeModel *model) GHashTable *store_index; GHashTable *uri_index; - GType col_types[] = { - G_TYPE_STRING, /* display name */ - G_TYPE_POINTER, /* store object */ - G_TYPE_STRING, /* full name */ - G_TYPE_STRING, /* icon name */ - G_TYPE_STRING, /* uri */ - G_TYPE_UINT, /* unread count */ - G_TYPE_UINT, /* flags */ - G_TYPE_BOOLEAN, /* is a store node */ - G_TYPE_BOOLEAN, /* is a folder node */ - G_TYPE_BOOLEAN, /* has not-yet-loaded subfolders */ - G_TYPE_UINT, /* last known unread count */ - G_TYPE_BOOLEAN /* folder is a draft folder */ - }; - store_index = g_hash_table_new_full ( g_direct_hash, g_direct_equal, (GDestroyNotify) NULL, @@ -505,42 +705,29 @@ em_folder_tree_model_init (EMFolderTreeModel *model) model->priv = EM_FOLDER_TREE_MODEL_GET_PRIVATE (model); model->priv->store_index = store_index; model->priv->uri_index = uri_index; - - gtk_tree_store_set_column_types ( - GTK_TREE_STORE (model), NUM_COLUMNS, col_types); - gtk_tree_sortable_set_default_sort_func ( - GTK_TREE_SORTABLE (model), - folder_tree_model_sort, NULL, NULL); - gtk_tree_sortable_set_sort_column_id ( - GTK_TREE_SORTABLE (model), - GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, - GTK_SORT_ASCENDING); - - model->priv->accounts = e_get_account_list (); - model->priv->account_changed_id = g_signal_connect ( - model->priv->accounts, "account-changed", - G_CALLBACK (account_changed_cb), model); - model->priv->account_removed_id = g_signal_connect ( - model->priv->accounts, "account-removed", - G_CALLBACK (account_removed_cb), model); - model->priv->account_added_id = g_signal_connect ( - model->priv->accounts, "account-added", - G_CALLBACK (account_added_cb), model); } EMFolderTreeModel * -em_folder_tree_model_new (void) +em_folder_tree_model_new (EMailBackend *mail_backend) { - return g_object_new (EM_TYPE_FOLDER_TREE_MODEL, NULL); + return g_object_new (EM_TYPE_FOLDER_TREE_MODEL, "backend", mail_backend, NULL); } EMFolderTreeModel * -em_folder_tree_model_get_default (void) +em_folder_tree_model_get_default (EMailBackend *mail_backend) { static EMFolderTreeModel *default_folder_tree_model; - if (G_UNLIKELY (default_folder_tree_model == NULL)) - default_folder_tree_model = em_folder_tree_model_new (); + if (G_UNLIKELY (default_folder_tree_model == NULL)) { + if (!mail_backend) { + EShell *shell; + + shell = e_shell_get_default (); + mail_backend = E_MAIL_BACKEND (e_shell_get_backend_by_name (shell, "mail")); + } + + default_folder_tree_model = em_folder_tree_model_new (mail_backend); + } return default_folder_tree_model; } @@ -620,6 +807,33 @@ em_folder_tree_model_set_session (EMFolderTreeModel *model, g_object_notify (G_OBJECT (model), "session"); } +EMailBackend * +em_folder_tree_model_get_backend (EMFolderTreeModel *model) +{ + g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL); + + return model->priv->backend; +} + +void +em_folder_tree_model_set_backend (EMFolderTreeModel *model, + EMailBackend *backend) +{ + g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); + + if (backend != NULL) { + g_return_if_fail (E_IS_MAIL_BACKEND (backend)); + g_object_ref (backend); + } + + if (model->priv->backend != NULL) + g_object_unref (model->priv->backend); + + model->priv->backend = backend; + + g_object_notify (G_OBJECT (model), "backend"); +} + void em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *iter, @@ -1002,6 +1216,7 @@ void em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store) { + EMailBackend *mail_backend; EMFolderTreeModelStoreInfo *si; GtkTreeRowReference *reference; GtkTreeStore *tree_store; @@ -1011,6 +1226,7 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelProvider *provider; CamelURL *service_url; const gchar *display_name; + const gchar *account_uid; gchar *uri; g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); @@ -1022,6 +1238,7 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, provider = camel_service_get_provider (service); service_url = camel_service_get_camel_url (service); display_name = camel_service_get_display_name (service); + account_uid = camel_service_get_uid (service); /* Ignore stores that should not be added to the tree model. */ @@ -1040,6 +1257,8 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, uri = camel_url_to_string (service_url, CAMEL_URL_HIDE_ALL); + mail_backend = em_folder_tree_model_get_backend (model); + /* Add the store to the tree. */ gtk_tree_store_append (tree_store, &iter, NULL); gtk_tree_store_set ( @@ -1049,7 +1268,9 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, COL_STRING_FULL_NAME, NULL, COL_BOOL_LOAD_SUBDIRS, TRUE, COL_BOOL_IS_STORE, TRUE, - COL_STRING_URI, uri, -1); + COL_STRING_URI, uri, + COL_UINT_SORTORDER, em_utils_get_account_sort_order (mail_backend, account_uid), + -1); path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path); diff --git a/mail/em-folder-tree-model.h b/mail/em-folder-tree-model.h index 1bf5483367..3765cfea11 100644 --- a/mail/em-folder-tree-model.h +++ b/mail/em-folder-tree-model.h @@ -26,6 +26,7 @@ #include #include +#include #include /* Standard GObject macros */ @@ -72,6 +73,7 @@ enum { * been added to the tree */ COL_UINT_UNREAD_LAST_SEL, /* last known unread count */ COL_BOOL_IS_DRAFT, /* %TRUE for a draft folder */ + COL_UINT_SORTORDER, /* user sort-order for the node */ NUM_COLUMNS }; @@ -109,9 +111,9 @@ struct _EMFolderTreeModelClass { GType em_folder_tree_model_get_type (void); EMFolderTreeModel * - em_folder_tree_model_new (void); + em_folder_tree_model_new (EMailBackend *mail_backend); EMFolderTreeModel * - em_folder_tree_model_get_default (void); + em_folder_tree_model_get_default (EMailBackend *mail_backend); GtkTreeSelection * em_folder_tree_model_get_selection (EMFolderTreeModel *model); @@ -123,6 +125,11 @@ EMailSession * em_folder_tree_model_get_session void em_folder_tree_model_set_session (EMFolderTreeModel *model, EMailSession *session); +EMailBackend * em_folder_tree_model_get_backend + (EMFolderTreeModel *model); +void em_folder_tree_model_set_backend + (EMFolderTreeModel *model, + EMailBackend *backend); void em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *iter, diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c index 061c3540f7..6212f8a8ca 100644 --- a/mail/em-folder-tree.c +++ b/mail/em-folder-tree.c @@ -1770,7 +1770,7 @@ em_folder_tree_new (EMailBackend *backend, g_return_val_if_fail (E_IS_MAIL_BACKEND (backend), NULL); g_return_val_if_fail (E_IS_ALERT_SINK (alert_sink), NULL); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); return em_folder_tree_new_with_model (backend, alert_sink, model); } diff --git a/mail/em-folder-utils.c b/mail/em-folder-utils.c index 775dfbb663..54586ad763 100644 --- a/mail/em-folder-utils.c +++ b/mail/em-folder-utils.c @@ -487,7 +487,7 @@ em_folder_utils_copy_folder (GtkWindow *parent, label = delete ? _("_Move") : _("C_opy"); title = delete ? _("Move Folder To") : _("Copy Folder To"); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); dialog = em_folder_selector_new ( parent, backend, model, @@ -547,6 +547,8 @@ em_folder_utils_create_folder (GtkWindow *parent, EMFolderTree *emft, const gchar *initial_uri) { + EShell *shell; + EShellSettings *shell_settings; EMailSession *session; EMFolderSelector *selector; EMFolderTree *folder_tree; @@ -560,7 +562,10 @@ em_folder_utils_create_folder (GtkWindow *parent, g_return_if_fail (GTK_IS_WINDOW (parent)); g_return_if_fail (E_IS_MAIL_BACKEND (backend)); - model = em_folder_tree_model_new (); + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); + shell_settings = e_shell_get_shell_settings (shell); + + model = em_folder_tree_model_new (backend); session = e_mail_backend_get_session (backend); em_folder_tree_model_set_session (model, session); @@ -569,6 +574,7 @@ em_folder_utils_create_folder (GtkWindow *parent, for (link = list; link != NULL; link = g_list_next (link)) { CamelService *service; CamelStore *store; + const gchar *uid, *prop = NULL; service = CAMEL_SERVICE (link->data); @@ -580,6 +586,15 @@ em_folder_utils_create_folder (GtkWindow *parent, if ((store->flags & CAMEL_STORE_CAN_EDIT_FOLDERS) == 0) continue; + uid = camel_service_get_uid (service); + if (g_strcmp0 (uid, "local") == 0) + prop = "mail-enable-local-folders"; + else if (g_strcmp0 (uid, "vfolder") == 0) + prop = "mail-enable-search-folders"; + + if (prop && !e_shell_settings_get_boolean (shell_settings, prop)) + continue; + em_folder_tree_model_add_store (model, store); } diff --git a/mail/em-subscription-editor.c b/mail/em-subscription-editor.c index e2452d70bf..2dca93b51a 100644 --- a/mail/em-subscription-editor.c +++ b/mail/em-subscription-editor.c @@ -1077,7 +1077,7 @@ subscription_editor_realize (GtkWidget *widget) /* Find stores to display, and watch for the initial store. */ - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (NULL); list = em_folder_tree_model_list_stores (model); for (link = list; link != NULL; link = g_list_next (link)) { diff --git a/mail/em-utils.c b/mail/em-utils.c index 968165385a..5ff93887a2 100644 --- a/mail/em-utils.c +++ b/mail/em-utils.c @@ -84,6 +84,8 @@ extern const gchar *shell_builtin_backend; #define d(x) +static void free_account_sort_order_cache (void); + gboolean em_utils_ask_open_many (GtkWindow *parent, gint how_many) @@ -2014,6 +2016,8 @@ emu_free_mail_cache (void) photos_cache = NULL; G_UNLOCK (photos_cache); + + free_account_sort_order_cache (); } void @@ -2369,3 +2373,167 @@ em_utils_disconnect_service_sync (CamelService *service, return res; } + +G_LOCK_DEFINE_STATIC (accounts_sort_order_cache); +static GHashTable *accounts_sort_order_cache = NULL; /* account_uid string to sort order uint */ + +static void +free_account_sort_order_cache (void) +{ + G_LOCK (accounts_sort_order_cache); + + if (accounts_sort_order_cache) { + g_hash_table_destroy (accounts_sort_order_cache); + accounts_sort_order_cache = NULL; + } + + G_UNLOCK (accounts_sort_order_cache); +} + +static void +fill_accounts_sort_order_cache (EMailBackend *backend, gboolean force_reload) +{ + GSList *account_uids; + + G_LOCK (accounts_sort_order_cache); + + if (!force_reload && accounts_sort_order_cache) { + G_UNLOCK (accounts_sort_order_cache); + return; + } + + if (!accounts_sort_order_cache) + accounts_sort_order_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + else + g_hash_table_remove_all (accounts_sort_order_cache); + + account_uids = em_utils_load_accounts_sort_order (backend); + if (account_uids) { + GSList *iter; + guint index; + + for (index = 1, iter = account_uids; iter; index++, iter = iter->next) { + if (iter->data) + g_hash_table_insert (accounts_sort_order_cache, iter->data, GUINT_TO_POINTER (index)); + } + + /* items are stolen into the cache */ + /* g_slist_foreach (account_uids, (GFunc) g_free, NULL); */ + g_slist_free (account_uids); + } + + G_UNLOCK (accounts_sort_order_cache); +} + +static gchar * +emu_get_sort_order_filename (EMailBackend *backend) +{ + g_return_val_if_fail (backend != NULL, NULL); + + return g_build_filename ( + e_shell_backend_get_config_dir (E_SHELL_BACKEND (backend)), + "sortorder.ini", NULL); +} + +static GKeyFile * +emu_get_sort_order_key_file (EMailBackend *backend) +{ + gchar *filename; + GKeyFile *key_file; + + g_return_val_if_fail (backend != NULL, NULL); + + filename = emu_get_sort_order_filename (backend); + g_return_val_if_fail (filename != NULL, NULL); + + key_file = g_key_file_new (); + g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL); + + g_free (filename); + + return key_file; +} + +void +em_utils_save_accounts_sort_order (EMailBackend *backend, const GSList *account_uids) +{ + gchar *filename; + GKeyFile *key_file; + gint ii; + gchar key[32]; + gchar *content; + gsize length = 0; + + key_file = emu_get_sort_order_key_file (backend); + g_return_if_fail (key_file != NULL); + + filename = emu_get_sort_order_filename (backend); + g_return_if_fail (filename != NULL); + + g_key_file_remove_group (key_file, "accounts", NULL); + + for (ii = 0; account_uids; ii++, account_uids = account_uids->next) { + sprintf (key, "%d", ii); + + g_key_file_set_string (key_file, "accounts", key, account_uids->data); + } + + content = g_key_file_to_data (key_file, &length, NULL); + if (content) + g_file_set_contents (filename, content, length, NULL); + + g_free (content); + g_free (filename); + g_key_file_free (key_file); + + fill_accounts_sort_order_cache (backend, TRUE); + e_mail_backend_account_sort_order_changed (backend); +} + +GSList * +em_utils_load_accounts_sort_order (EMailBackend *backend) +{ + GKeyFile *key_file; + GSList *account_uids = NULL; + gchar key[32]; + gchar *value; + gint ii; + + key_file = emu_get_sort_order_key_file (backend); + g_return_val_if_fail (key_file != NULL, NULL); + + ii = 0; + do { + sprintf (key, "%d", ii); + ii++; + + value = g_key_file_get_string (key_file, "accounts", key, NULL); + if (!value) + break; + + account_uids = g_slist_prepend (account_uids, value); + } while (*key); + + g_key_file_free (key_file); + + return g_slist_reverse (account_uids); +} + +guint +em_utils_get_account_sort_order (EMailBackend *backend, const gchar *account_uid) +{ + guint res; + + g_return_val_if_fail (backend != NULL, 0); + g_return_val_if_fail (account_uid != NULL, 0); + + fill_accounts_sort_order_cache (backend, FALSE); + + G_LOCK (accounts_sort_order_cache); + + res = GPOINTER_TO_UINT (g_hash_table_lookup (accounts_sort_order_cache, account_uid)); + + G_UNLOCK (accounts_sort_order_cache); + + return res; +} diff --git a/mail/em-utils.h b/mail/em-utils.h index e8ffd190d6..e6ebe8ca4b 100644 --- a/mail/em-utils.h +++ b/mail/em-utils.h @@ -99,6 +99,10 @@ gboolean em_utils_is_local_delivery_mbox_file (CamelURL *url); gboolean em_utils_connect_service_sync (CamelService *service, GCancellable *cancellable, GError **error); gboolean em_utils_disconnect_service_sync (CamelService *service, gboolean clean, GCancellable *cancellable, GError **error); +void em_utils_save_accounts_sort_order (EMailBackend *backend, const GSList *account_uids); +GSList *em_utils_load_accounts_sort_order (EMailBackend *backend); +guint em_utils_get_account_sort_order (EMailBackend *backend, const gchar *account_uid); + G_END_DECLS #endif /* __EM_UTILS_H__ */ diff --git a/mail/em-vfolder-rule.c b/mail/em-vfolder-rule.c index be18f2c248..fbb711738f 100644 --- a/mail/em-vfolder-rule.c +++ b/mail/em-vfolder-rule.c @@ -610,7 +610,7 @@ source_add (GtkWidget *widget, backend = em_vfolder_rule_get_backend (data->vr); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); dialog = em_folder_selector_new ( parent, backend, model, diff --git a/mail/evolution-mail.schemas.in b/mail/evolution-mail.schemas.in index f1dcd73ea6..82be1319c8 100644 --- a/mail/evolution-mail.schemas.in +++ b/mail/evolution-mail.schemas.in @@ -677,6 +677,20 @@ + + /schemas/apps/evolution/mail/display/enable_local + /apps/evolution/mail/display/enable_local + evolution-mail + bool + true + + Enable local folders + + Whether to show local folders (On This Computer) in a folder tree. + + + + /schemas/apps/evolution/mail/display/safe_list /apps/evolution/mail/display/safe_list @@ -885,7 +899,7 @@ - + /schemas/apps/evolution/mail/display/paned_view_headers_state /apps/evolution/mail/display/paned_view_headers_state @@ -901,7 +915,24 @@ - + + + /schemas/apps/evolution/mail/display/sort_accounts_alpha + /apps/evolution/mail/display/sort_accounts_alpha + evolution-mail + bool + true + + Sort accounts alphabetically in a folder tree + + Tells how to sort accounts in a folder tree used in a Mail view. + When set to true accounts are sorted alphabetically, with an exception + of On This Computer and Search folders, otherwise accounts are + sorted based on an order given by a user. + + + + diff --git a/mail/message-list.c b/mail/message-list.c index d98f860f8c..cd44b9506a 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -4191,7 +4191,7 @@ on_click (ETree *tree, if (col == COL_MESSAGE_STATUS && (flags & CAMEL_MESSAGE_SEEN)) { EMFolderTreeModel *model; - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (list->priv->backend); em_folder_tree_model_user_marked_unread ( model, list->folder, 1); } diff --git a/modules/mail/e-mail-shell-settings.c b/modules/mail/e-mail-shell-settings.c index 99402d5f29..b0d5f1eed8 100644 --- a/modules/mail/e-mail-shell-settings.c +++ b/modules/mail/e-mail-shell-settings.c @@ -141,6 +141,10 @@ e_mail_shell_settings_init (EShellBackend *shell_backend) "mail-enable-search-folders", "/apps/evolution/mail/display/enable_vfolders"); + e_shell_settings_install_property_for_key ( + "mail-enable-local-folders", + "/apps/evolution/mail/display/enable_local"); + e_shell_settings_install_property_for_key ( "mail-font-monospace", "/apps/evolution/mail/display/fonts/monospace"); @@ -200,6 +204,10 @@ e_mail_shell_settings_init (EShellBackend *shell_backend) "mail-show-real-date", "/apps/evolution/mail/display/show_real_date"); + e_shell_settings_install_property_for_key ( + "mail-sort-accounts-alpha", + "/apps/evolution/mail/display/sort_accounts_alpha"); + e_shell_settings_install_property_for_key ( "mail-prompt-delete-in-vfolder", "/apps/evolution/mail/prompts/delete_in_vfolder"); diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c index 5759b5830f..3a92ce284b 100644 --- a/modules/mail/e-mail-shell-view.c +++ b/modules/mail/e-mail-shell-view.c @@ -868,7 +868,7 @@ mail_shell_view_update_actions (EShellView *shell_view) shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); state = e_shell_sidebar_check_state (shell_sidebar); - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (e_mail_reader_get_backend (reader)); folder_allows_children = (state & E_MAIL_SIDEBAR_FOLDER_ALLOWS_CHILDREN); diff --git a/modules/mail/em-account-prefs.c b/modules/mail/em-account-prefs.c index b43f1fbe35..1b11b41b68 100644 --- a/modules/mail/em-account-prefs.c +++ b/modules/mail/em-account-prefs.c @@ -40,6 +40,8 @@ #include "e-mail-store.h" #include "em-config.h" #include "em-account-editor.h" +#include "em-utils.h" +#include "mail-vfolder.h" #include "shell/e-shell.h" #include "capplet/settings/mail-capplet-shell.h" @@ -59,6 +61,36 @@ G_DEFINE_TYPE ( em_account_prefs, E_TYPE_ACCOUNT_MANAGER) +static gboolean +account_prefs_toggle_enable_special (EMAccountPrefs *prefs, EAccountTreeViewSelectedType type, gboolean enabled) +{ + const gchar *prop = NULL; + EShell *shell; + EShellSettings *shell_settings; + + g_return_val_if_fail (prefs != NULL, FALSE); + g_return_val_if_fail (prefs->priv != NULL, FALSE); + + if (type == E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL) + prop = "mail-enable-local-folders"; + else if (type == E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER) + prop = "mail-enable-search-folders"; + + if (!prop) + return FALSE; + + shell = e_shell_backend_get_shell (E_SHELL_BACKEND (prefs->priv->backend)); + shell_settings = e_shell_get_shell_settings (shell); + + e_shell_settings_set_boolean (shell_settings, prop, enabled); + + /* make sure "Search Folders" are loaded when enabled */ + if (enabled && type == E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER) + vfolder_load_storage (prefs->priv->backend); + + return TRUE; +} + static void account_prefs_enable_account_cb (EAccountTreeView *tree_view, EMAccountPrefs *prefs) @@ -67,6 +99,11 @@ account_prefs_enable_account_cb (EAccountTreeView *tree_view, EMailSession *session; account = e_account_tree_view_get_selected (tree_view); + if (!account) { + if (account_prefs_toggle_enable_special (prefs, e_account_tree_view_get_selected_type (tree_view), TRUE)) + return; + } + g_return_if_fail (account != NULL); session = e_mail_backend_get_session (prefs->priv->backend); @@ -84,6 +121,11 @@ account_prefs_disable_account_cb (EAccountTreeView *tree_view, gint response; account = e_account_tree_view_get_selected (tree_view); + if (!account) { + if (account_prefs_toggle_enable_special (prefs, e_account_tree_view_get_selected_type (tree_view), FALSE)) + return; + } + g_return_if_fail (account != NULL); account_list = e_account_tree_view_get_account_list (tree_view); @@ -380,14 +422,35 @@ em_account_prefs_init (EMAccountPrefs *prefs) G_CALLBACK (account_prefs_disable_account_cb), prefs); } +static void +account_tree_view_sort_order_changed_cb (EAccountTreeView *tree_view, EMailBackend *backend) +{ + GSList *account_uids; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (backend != NULL); + + account_uids = e_account_tree_view_get_sort_order (tree_view); + if (!account_uids) + return; + + em_utils_save_accounts_sort_order (backend, account_uids); + + g_slist_foreach (account_uids, (GFunc) g_free, NULL); + g_slist_free (account_uids); +} + GtkWidget * em_account_prefs_new (EPreferencesWindow *window) { EShell *shell; EShellBackend *shell_backend; EAccountList *account_list; + EAccountTreeView *tree_view; EMailSession *session; const gchar *data_dir; + GtkWidget *res; + GSList *account_uids; account_list = e_get_account_list (); g_return_val_if_fail (E_IS_ACCOUNT_LIST (account_list), NULL); @@ -402,10 +465,40 @@ em_account_prefs_new (EPreferencesWindow *window) /* Make sure the e-mail-local is initialized. */ e_mail_local_init (session, data_dir); - return g_object_new ( + res = g_object_new ( EM_TYPE_ACCOUNT_PREFS, "account-list", account_list, "backend", shell_backend, NULL); + + tree_view = e_account_manager_get_tree_view (E_ACCOUNT_MANAGER (res)); + e_account_tree_view_set_express_mode (tree_view, e_shell_get_express_mode (shell)); + + g_object_bind_property ( + e_shell_get_shell_settings (shell), "mail-sort-accounts-alpha", + tree_view, "sort-alpha", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + g_object_bind_property ( + e_shell_get_shell_settings (shell), "mail-enable-local-folders", + tree_view, "enable-local-folders", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + g_object_bind_property ( + e_shell_get_shell_settings (shell), "mail-enable-search-folders", + tree_view, "enable-search-folders", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + account_uids = em_utils_load_accounts_sort_order (E_MAIL_BACKEND (shell_backend)); + if (account_uids) { + e_account_tree_view_set_sort_order (tree_view, account_uids); + g_slist_foreach (account_uids, (GFunc) g_free, NULL); + g_slist_free (account_uids); + } + + g_signal_connect (tree_view, "sort-order-changed", + G_CALLBACK (account_tree_view_sort_order_changed_cb), shell_backend); + + return res; } EMailBackend * diff --git a/plugins/mark-all-read/mark-all-read.c b/plugins/mark-all-read/mark-all-read.c index 4fe19cf71b..ab8549495b 100644 --- a/plugins/mark-all-read/mark-all-read.c +++ b/plugins/mark-all-read/mark-all-read.c @@ -318,7 +318,7 @@ scan_folder_tree_for_unread_helper (GtkTreeModel *model, } static gint -scan_folder_tree_for_unread (const gchar *folder_uri) +scan_folder_tree_for_unread (EMailBackend *backend, const gchar *folder_uri) { GtkTreeRowReference *reference; EMFolderTreeModel *model; @@ -338,7 +338,7 @@ scan_folder_tree_for_unread (const gchar *folder_uri) if (folder_uri == NULL) return 0; - model = em_folder_tree_model_get_default (); + model = em_folder_tree_model_get_default (backend); reference = em_folder_tree_model_lookup_uri (model, folder_uri); if (gtk_tree_row_reference_valid (reference)) { @@ -602,7 +602,7 @@ update_actions_cb (EShellView *shell_view, g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL); folder_uri = em_folder_tree_get_selected_uri (folder_tree); - visible = (scan_folder_tree_for_unread (folder_uri) > 0); + visible = (scan_folder_tree_for_unread (E_MAIL_BACKEND (e_shell_view_get_shell_backend (shell_view)), folder_uri) > 0); gtk_action_set_visible (action, visible); g_object_unref (folder_tree); diff --git a/widgets/misc/e-account-manager.c b/widgets/misc/e-account-manager.c index e4ee68736a..b2c0583b8b 100644 --- a/widgets/misc/e-account-manager.c +++ b/widgets/misc/e-account-manager.c @@ -37,6 +37,9 @@ struct _EAccountManagerPrivate { GtkWidget *edit_button; GtkWidget *delete_button; GtkWidget *default_button; + GtkWidget *sort_toggle; + GtkWidget *sort_up_button; + GtkWidget *sort_down_button; }; enum { @@ -96,16 +99,22 @@ account_manager_selection_changed_cb (EAccountManager *manager, EAccountList *account_list; EAccount *default_account; EAccount *account; + GtkTreeModel *model = NULL; + GtkTreeIter iter1, iter2; GtkWidget *add_button; GtkWidget *edit_button; GtkWidget *delete_button; GtkWidget *default_button; + GtkWidget *sort_up_button; + GtkWidget *sort_down_button; gboolean sensitive; add_button = manager->priv->add_button; edit_button = manager->priv->edit_button; delete_button = manager->priv->delete_button; default_button = manager->priv->default_button; + sort_up_button = manager->priv->sort_up_button; + sort_down_button = manager->priv->sort_down_button; tree_view = e_account_manager_get_tree_view (manager); account = e_account_tree_view_get_selected (tree_view); @@ -126,6 +135,50 @@ account_manager_selection_changed_cb (EAccountManager *manager, sensitive = (account != NULL && account != default_account); gtk_widget_set_sensitive (default_button, sensitive); + + sensitive = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (manager->priv->sort_toggle)) && + gtk_tree_selection_get_selected (selection, &model, &iter1); + iter2 = iter1; + gtk_widget_set_sensitive (sort_up_button, sensitive && gtk_tree_model_iter_previous (model, &iter1)); + gtk_widget_set_sensitive (sort_down_button, sensitive && gtk_tree_model_iter_next (model, &iter2)); +} + +static void +account_manager_sort_toggled_cb (EAccountManager *manager) +{ + GtkTreeView *tree_view; + GtkTreeSelection *selection; + + tree_view = GTK_TREE_VIEW (e_account_manager_get_tree_view (manager)); + selection = gtk_tree_view_get_selection (tree_view); + + account_manager_selection_changed_cb (manager, selection); +} + +static void +account_manager_sort_up_cb (EAccountManager *manager) +{ + GtkTreeView *tree_view; + GtkTreeSelection *selection; + + tree_view = GTK_TREE_VIEW (e_account_manager_get_tree_view (manager)); + selection = gtk_tree_view_get_selection (tree_view); + + e_account_tree_view_move_up (e_account_manager_get_tree_view (manager)); + account_manager_selection_changed_cb (manager, selection); +} + +static void +account_manager_sort_down_cb (EAccountManager *manager) +{ + GtkTreeView *tree_view; + GtkTreeSelection *selection; + + tree_view = GTK_TREE_VIEW (e_account_manager_get_tree_view (manager)); + selection = gtk_tree_view_get_selection (tree_view); + + e_account_tree_view_move_down (e_account_manager_get_tree_view (manager)); + account_manager_selection_changed_cb (manager, selection); } static void @@ -195,6 +248,21 @@ account_manager_dispose (GObject *object) priv->delete_button = NULL; } + if (priv->sort_toggle != NULL) { + g_object_unref (priv->sort_toggle); + priv->sort_toggle = NULL; + } + + if (priv->sort_up_button != NULL) { + g_object_unref (priv->sort_up_button); + priv->sort_up_button = NULL; + } + + if (priv->sort_down_button != NULL) { + g_object_unref (priv->sort_down_button); + priv->sort_down_button = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_account_manager_parent_class)->dispose (object); } @@ -312,6 +380,23 @@ e_account_manager_init (EAccountManager *manager) container = GTK_WIDGET (manager); + widget = gtk_check_button_new_with_mnemonic (_("Use default Evolution _sort order for accounts")); + manager->priv->sort_toggle = g_object_ref (widget); + gtk_widget_show (widget); + gtk_table_attach ( + GTK_TABLE (container), widget, 0, 1, 1, 2, + GTK_EXPAND | GTK_FILL, 0, 4, 0); + + g_object_bind_property ( + manager->priv->tree_view, "sort-alpha", + widget, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + + g_signal_connect_swapped ( + widget, "toggled", + G_CALLBACK (account_manager_sort_toggled_cb), manager); + widget = gtk_vbutton_box_new (); gtk_button_box_set_layout ( GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START); @@ -361,6 +446,24 @@ e_account_manager_init (EAccountManager *manager) g_signal_connect_swapped ( widget, "clicked", G_CALLBACK (account_manager_default_clicked_cb), manager); + + widget = gtk_button_new_from_stock (GTK_STOCK_GO_UP); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + manager->priv->sort_up_button = g_object_ref (widget); + gtk_widget_show (widget); + + g_signal_connect_swapped ( + widget, "clicked", + G_CALLBACK (account_manager_sort_up_cb), manager); + + widget = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + manager->priv->sort_down_button = g_object_ref (widget); + gtk_widget_show (widget); + + g_signal_connect_swapped ( + widget, "clicked", + G_CALLBACK (account_manager_sort_down_cb), manager); } GtkWidget * diff --git a/widgets/misc/e-account-tree-view.c b/widgets/misc/e-account-tree-view.c index 18c39dccec..a2136433d4 100644 --- a/widgets/misc/e-account-tree-view.c +++ b/widgets/misc/e-account-tree-view.c @@ -33,25 +33,35 @@ enum { COLUMN_DEFAULT, COLUMN_ENABLED, COLUMN_NAME, - COLUMN_PROTOCOL + COLUMN_PROTOCOL, + COLUMN_SORTORDER }; enum { PROP_0, PROP_ACCOUNT_LIST, - PROP_SELECTED + PROP_SELECTED, + PROP_SORT_ALPHA, + PROP_EXPRESS_MODE, + PROP_ENABLE_LOCAL_FOLDERS, + PROP_ENABLE_SEARCH_FOLDERS }; enum { ENABLE_ACCOUNT, DISABLE_ACCOUNT, REFRESHED, + SORT_ORDER_CHANGED, LAST_SIGNAL }; struct _EAccountTreeViewPrivate { EAccountList *account_list; GHashTable *index; + gboolean sort_alpha; + gboolean express_mode; + gboolean enable_local_folders; + gboolean enable_search_folders; }; static guint signals[LAST_SIGNAL]; @@ -61,10 +71,124 @@ G_DEFINE_TYPE ( e_account_tree_view, GTK_TYPE_TREE_VIEW) +static gint +account_tree_view_sort (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gint rv = -2; + gchar *aname = NULL, *bname = NULL; + EAccount *aaccount = NULL, *baccount = NULL; + guint asortorder = 0, bsortorder = 0; + + gtk_tree_model_get (model, a, + COLUMN_ACCOUNT, &aaccount, + COLUMN_NAME, &aname, + COLUMN_SORTORDER, &asortorder, + -1); + + gtk_tree_model_get (model, b, + COLUMN_ACCOUNT, &baccount, + COLUMN_NAME, &bname, + COLUMN_SORTORDER, &bsortorder, + -1); + + if ((!aaccount || !baccount || !e_account_tree_view_get_sort_alpha (user_data)) && aname && bname) { + if (e_account_tree_view_get_sort_alpha (user_data)) { + const gchar *on_this_computer = _("On This Computer"); + const gchar *search_folders = _("Search Folders"); + + if (e_account_tree_view_get_express_mode (user_data)) { + if (g_str_equal (aname, on_this_computer) && + g_str_equal (bname, search_folders)) + rv = -1; + else if (g_str_equal (bname, on_this_computer) && + g_str_equal (aname, search_folders)) + rv = 1; + else if (g_str_equal (aname, on_this_computer)) + rv = 1; + else if (g_str_equal (bname, on_this_computer)) + rv = -1; + else if (g_str_equal (aname, search_folders)) + rv = 1; + else if (g_str_equal (bname, search_folders)) + rv = -1; + } else { + if (g_str_equal (aname, on_this_computer)) + rv = -1; + else if (g_str_equal (bname, on_this_computer)) + rv = 1; + else if (g_str_equal (aname, search_folders)) + rv = 1; + else if (g_str_equal (bname, search_folders)) + rv = -1; + } + } else { + if (asortorder < bsortorder) + rv = -1; + else if (asortorder > bsortorder) + rv = 1; + else + rv = 0; + } + } + + if (rv == -2) { + if (aname == NULL) { + if (bname == NULL) + rv = 0; + else + rv = -1; + } else if (bname == NULL) + rv = 1; + + if (rv == -2) + rv = g_utf8_collate (aname, bname); + } + + g_free (aname); + g_free (bname); + + if (aaccount) + g_object_unref (aaccount); + if (baccount) + g_object_unref (baccount); + + return rv; +} + +static void +account_tree_view_normalize_sortorder_column (EAccountTreeView *tree_view) +{ + GtkListStore *list_store; + GtkTreeModel *model; + GtkTreeIter iter; + guint index; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + if (!model || !gtk_tree_model_get_iter_first (model, &iter)) + return; + + list_store = GTK_LIST_STORE (model); + g_return_if_fail (list_store != NULL); + + index = 1; + do { + gtk_list_store_set (list_store, &iter, COLUMN_SORTORDER, index, -1); + + index++; + } while (gtk_tree_model_iter_next (model, &iter)); +} + static gboolean account_tree_view_refresh_timeout_cb (gpointer ptree_view) { EAccountTreeView *tree_view; + EAccountTreeViewSelectedType selected; EAccountList *account_list; EAccount *account; GtkListStore *store; @@ -73,18 +197,28 @@ account_tree_view_refresh_timeout_cb (gpointer ptree_view) EIterator *account_iter; EAccount *default_account; GHashTable *index; + GSList *sort_order; GList *list = NULL; GList *iter; tree_view = ptree_view; account_list = e_account_tree_view_get_account_list (tree_view); + sort_order = e_account_tree_view_get_sort_order (tree_view); store = gtk_list_store_new ( - 5, E_TYPE_ACCOUNT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_STRING, G_TYPE_STRING); + 6, E_TYPE_ACCOUNT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT); model = GTK_TREE_MODEL (store); index = tree_view->priv->index; + gtk_tree_sortable_set_default_sort_func ( + GTK_TREE_SORTABLE (model), + account_tree_view_sort, tree_view, NULL); + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (model), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + g_hash_table_remove_all (index); if (account_list == NULL) @@ -148,8 +282,28 @@ account_tree_view_refresh_timeout_cb (gpointer ptree_view) camel_url_free (url); } -skip: + gtk_list_store_append (store, &tree_iter); + gtk_list_store_set ( + store, &tree_iter, + COLUMN_ACCOUNT, NULL, + COLUMN_DEFAULT, FALSE, + COLUMN_ENABLED, tree_view->priv->enable_local_folders, + COLUMN_NAME, _("On This Computer"), + COLUMN_PROTOCOL, NULL, + -1); + + gtk_list_store_append (store, &tree_iter); + gtk_list_store_set ( + store, &tree_iter, + COLUMN_ACCOUNT, NULL, + COLUMN_DEFAULT, FALSE, + COLUMN_ENABLED, tree_view->priv->enable_search_folders, + COLUMN_NAME, _("Search Folders"), + COLUMN_PROTOCOL, NULL, + -1); + skip: /* Restore the previously selected account. */ + selected = e_account_tree_view_get_selected_type (tree_view); account = e_account_tree_view_get_selected (tree_view); if (account != NULL) g_object_ref (account); @@ -157,6 +311,13 @@ skip: e_account_tree_view_set_selected (tree_view, account); if (account != NULL) g_object_unref (account); + else if (selected == E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL || + selected == E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER) + e_account_tree_view_set_selected_type (tree_view, selected); + + e_account_tree_view_set_sort_order (tree_view, sort_order); + g_slist_foreach (sort_order, (GFunc) g_free, NULL); + g_slist_free (sort_order); g_signal_emit (tree_view, signals[REFRESHED], 0); @@ -302,6 +463,26 @@ account_tree_view_set_property (GObject *object, E_ACCOUNT_TREE_VIEW (object), g_value_get_object (value)); return; + case PROP_SORT_ALPHA: + e_account_tree_view_set_sort_alpha ( + E_ACCOUNT_TREE_VIEW (object), + g_value_get_boolean (value)); + return; + case PROP_EXPRESS_MODE: + e_account_tree_view_set_express_mode ( + E_ACCOUNT_TREE_VIEW (object), + g_value_get_boolean (value)); + return; + case PROP_ENABLE_LOCAL_FOLDERS: + e_account_tree_view_set_enable_local_folders ( + E_ACCOUNT_TREE_VIEW (object), + g_value_get_boolean (value)); + return; + case PROP_ENABLE_SEARCH_FOLDERS: + e_account_tree_view_set_enable_search_folders ( + E_ACCOUNT_TREE_VIEW (object), + g_value_get_boolean (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -327,6 +508,30 @@ account_tree_view_get_property (GObject *object, e_account_tree_view_get_selected ( E_ACCOUNT_TREE_VIEW (object))); return; + case PROP_SORT_ALPHA: + g_value_set_boolean ( + value, + e_account_tree_view_get_sort_alpha ( + E_ACCOUNT_TREE_VIEW (object))); + return; + case PROP_EXPRESS_MODE: + g_value_set_boolean ( + value, + e_account_tree_view_get_express_mode ( + E_ACCOUNT_TREE_VIEW (object))); + return; + case PROP_ENABLE_LOCAL_FOLDERS: + g_value_set_boolean ( + value, + e_account_tree_view_get_enable_local_folders ( + E_ACCOUNT_TREE_VIEW (object))); + return; + case PROP_ENABLE_SEARCH_FOLDERS: + g_value_set_boolean ( + value, + e_account_tree_view_get_enable_search_folders ( + E_ACCOUNT_TREE_VIEW (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -442,6 +647,46 @@ e_account_tree_view_class_init (EAccountTreeViewClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property ( + object_class, + PROP_SORT_ALPHA, + g_param_spec_boolean ( + "sort-alpha", + "Sort alphabetically", + NULL, + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_EXPRESS_MODE, + g_param_spec_boolean ( + "express-mode", + "Express Mode sorting", + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_ENABLE_LOCAL_FOLDERS, + g_param_spec_boolean ( + "enable-local-folders", + "Enable Local Folders", + NULL, + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_ENABLE_SEARCH_FOLDERS, + g_param_spec_boolean ( + "enable-search-folders", + "Enable Search Folders", + NULL, + TRUE, + G_PARAM_READWRITE)); + signals[ENABLE_ACCOUNT] = g_signal_new ( "enable-account", G_TYPE_FROM_CLASS (class), @@ -468,6 +713,15 @@ e_account_tree_view_class_init (EAccountTreeViewClass *class) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + signals[SORT_ORDER_CHANGED] = g_signal_new ( + "sort-order-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAccountTreeViewClass, sort_order_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void @@ -485,6 +739,10 @@ e_account_tree_view_init (EAccountTreeView *tree_view) tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE ( tree_view, E_TYPE_ACCOUNT_TREE_VIEW, EAccountTreeViewPrivate); tree_view->priv->index = index; + tree_view->priv->sort_alpha = TRUE; + tree_view->priv->express_mode = FALSE; + tree_view->priv->enable_local_folders = TRUE; + tree_view->priv->enable_search_folders = TRUE; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); @@ -622,3 +880,507 @@ e_account_tree_view_set_selected (EAccountTreeView *tree_view, return TRUE; } + +/** + * e_account_tree_view_get_selected_type: + * @tree_view: an #EAccountTreeView + * + * Returns: What node type is selected. This is useful for virtual + * nodes "On This Computer" and "Search Folders", which doesn't have + * their #EAccount representations. if the function returns + * #E_ACCOUNT_TREE_VIEW_SELECTED_ACCOUNT, then the selected account + * can be obtained with e_account_tree_view_get_selected(). + * + * Since: 3.4 + **/ +EAccountTreeViewSelectedType +e_account_tree_view_get_selected_type (EAccountTreeView *tree_view) +{ + EAccountTreeViewSelectedType res = E_ACCOUNT_TREE_VIEW_SELECTED_NONE; + EAccount *account = NULL; + gchar *name = NULL; + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + + g_return_val_if_fail (tree_view != NULL, E_ACCOUNT_TREE_VIEW_SELECTED_NONE); + g_return_val_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view), E_ACCOUNT_TREE_VIEW_SELECTED_NONE); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + return E_ACCOUNT_TREE_VIEW_SELECTED_NONE; + + gtk_tree_model_get (model, &iter, + COLUMN_ACCOUNT, &account, + COLUMN_NAME, &name, + -1); + + if (account) { + res = E_ACCOUNT_TREE_VIEW_SELECTED_ACCOUNT; + g_object_unref (account); + } else if (name) { + if (g_str_equal (name, _("On This Computer"))) + res = E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL; + else if (g_str_equal (name, _("Search Folders"))) + res = E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER; + } + + g_free (name); + + return res; +} + +/** + * e_account_tree_view_set_selected_type: + * @tree_view: an #EAccountTreeView + * @select: what to select; see below what can be used here + * + * Selects special nodes in a view. Can be only either #E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL + * or #E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER. + * + * Since: 3.4 + **/ +void +e_account_tree_view_set_selected_type (EAccountTreeView *tree_view, EAccountTreeViewSelectedType select) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean found; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + + if (!model || !gtk_tree_model_get_iter_first (model, &iter)) + return; + + if (select != E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL && + select != E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER) + return; + + found = FALSE; + do { + gchar *name = NULL; + EAccount *account = NULL; + + gtk_tree_model_get (model, &iter, + COLUMN_ACCOUNT, &account, + COLUMN_NAME, &name, + -1); + + if (account) { + g_object_unref (account); + } else { + switch (select) { + case E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL: + found = g_strcmp0 (name, _("On This Computer")) == 0; + break; + case E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER: + found = g_strcmp0 (name, _("Search Folders")) == 0; + break; + default: + break; + } + } + + g_free (name); + } while (!found && gtk_tree_model_iter_next (model, &iter)); + + if (found) + gtk_tree_selection_select_iter (selection, &iter); +} + +static guint +account_tree_view_get_slist_index (const GSList *account_uids, const gchar *uid) +{ + guint res = 0; + + while (account_uids) { + if (g_strcmp0 (uid, account_uids->data) == 0) + return res; + + account_uids = account_uids->next; + res++; + } + + return -1; +} + +/** + * e_account_tree_view_set_sort_order: + * @tree_view: an #EAccountTreeView + * @account_uids: a #GSList of account uids as string + * + * Sets user sort order for accounts based on the order + * in @account_uids. This is used only when sort + * alphabetically is set to #FALSE. + * + * Since: 3.4 + **/ +void +e_account_tree_view_set_sort_order (EAccountTreeView *tree_view, const GSList *account_uids) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + + if (!model || !gtk_tree_model_get_iter_first (model, &iter)) + return; + + do { + gchar *name = NULL; + EAccount *account = NULL; + guint sort_order = 0; + + gtk_tree_model_get (model, &iter, + COLUMN_ACCOUNT, &account, + COLUMN_NAME, &name, + -1); + + if (account) { + sort_order = account_tree_view_get_slist_index (account_uids, account->uid) + 1; + g_object_unref (account); + } else if (g_strcmp0 (name, _("On This Computer")) == 0) { + sort_order = account_tree_view_get_slist_index (account_uids, "local") + 1; + } else if (g_strcmp0 (name, _("Search Folders")) == 0) { + sort_order = account_tree_view_get_slist_index (account_uids, "vfolder") + 1; + } else { + g_warn_if_reached (); + } + + gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_SORTORDER, sort_order, -1); + g_free (name); + } while (gtk_tree_model_iter_next (model, &iter)); + + account_tree_view_normalize_sortorder_column (tree_view); + e_account_tree_view_sort_changed (tree_view); +} + +static gint +eval_order_by_sort_hash_cb (gconstpointer a, gconstpointer b, gpointer user_data) +{ + guint asortorder = GPOINTER_TO_UINT (g_hash_table_lookup (user_data, a)); + guint bsortorder = GPOINTER_TO_UINT (g_hash_table_lookup (user_data, b)); + + if (asortorder < bsortorder) + return -1; + if (asortorder > bsortorder) + return 1; + + return 0; +} + +/** + * e_account_tree_view_get_sort_order: + * @tree_view: an #EAccountTreeView + * + * Returns: Newly allocated #GSList of newly allocated strings + * containing account UIDs in order as user wish to see them. + * Each item of the returned list should be freed with g_free() + * and the list itself should be freed with g_slist_free(), when + * no longer needed. + * + * Since: 3.4 + **/ +GSList * +e_account_tree_view_get_sort_order (EAccountTreeView *tree_view) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GHashTable *hash; + GSList *res = NULL; + + g_return_val_if_fail (tree_view != NULL, NULL); + g_return_val_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view), NULL); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + + if (!model || !gtk_tree_model_get_iter_first (model, &iter)) + return NULL; + + hash = g_hash_table_new (g_direct_hash, g_direct_equal); + + do { + gchar *toadd = NULL; + gchar *name = NULL; + EAccount *account = NULL; + guint sort_order = 0; + + gtk_tree_model_get (model, &iter, + COLUMN_ACCOUNT, &account, + COLUMN_NAME, &name, + COLUMN_SORTORDER, &sort_order, + -1); + + if (account) { + toadd = g_strdup (account->uid); + g_object_unref (account); + } else if (g_strcmp0 (name, _("On This Computer")) == 0) { + toadd = g_strdup ("local"); + } else if (g_strcmp0 (name, _("Search Folders")) == 0) { + toadd = g_strdup ("vfolder"); + } else { + g_warn_if_reached (); + } + + if (toadd) { + g_hash_table_insert (hash, toadd, GUINT_TO_POINTER (sort_order)); + res = g_slist_prepend (res, toadd); + } + + g_free (name); + } while (gtk_tree_model_iter_next (model, &iter)); + + res = g_slist_sort_with_data (res, eval_order_by_sort_hash_cb, hash); + + g_hash_table_destroy (hash); + + return res; +} + +/** + * e_account_tree_view_sort_changed: + * @tree_view: an #EAccountTreeView + * + * Notifies @tree_view about sort order change, thus it resorts + * items in a view. + * + * Since: 3.4 + **/ +void +e_account_tree_view_sort_changed (EAccountTreeView *tree_view) +{ + GtkTreeModel *model; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + if (!model) + return; + + /* this invokes also sort on a GtkListStore */ + gtk_tree_sortable_set_default_sort_func ( + GTK_TREE_SORTABLE (model), + account_tree_view_sort, tree_view, NULL); +} + +static void +account_tree_view_swap_sort_order (EAccountTreeView *tree_view, gint direction) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter1, iter2; + guint sortorder1, sortorder2; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + g_return_if_fail (direction != 0); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + if (!gtk_tree_selection_get_selected (selection, &model, &iter1)) + return; + + iter2 = iter1; + if ((direction < 0 && !gtk_tree_model_iter_previous (model, &iter2)) || + (direction > 0 && !gtk_tree_model_iter_next (model, &iter2))) + return; + + gtk_tree_model_get (model, &iter1, COLUMN_SORTORDER, &sortorder1, -1); + gtk_tree_model_get (model, &iter2, COLUMN_SORTORDER, &sortorder2, -1); + + gtk_list_store_set (GTK_LIST_STORE (model), &iter1, COLUMN_SORTORDER, sortorder2, -1); + gtk_list_store_set (GTK_LIST_STORE (model), &iter2, COLUMN_SORTORDER, sortorder1, -1); + + e_account_tree_view_sort_changed (tree_view); + + g_signal_emit (tree_view, signals[SORT_ORDER_CHANGED], 0); +} + +/** + * e_account_tree_view_move_up: + * @tree_view: an #EAccountTreeView + * + * Moves currently selected node up within user's sort order. + * + * Since: 3.4 + **/ +void +e_account_tree_view_move_up (EAccountTreeView *tree_view) +{ + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + + account_tree_view_swap_sort_order (tree_view, -1); +} + +/** + * e_account_tree_view_move_down: + * @tree_view: an #EAccountTreeView + * + * Moves currently selected node down within user's sort order. + * + * Since: 3.4 + **/ +void +e_account_tree_view_move_down (EAccountTreeView *tree_view) +{ + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + + account_tree_view_swap_sort_order (tree_view, +1); +} + +void +e_account_tree_view_set_sort_alpha (EAccountTreeView *tree_view, gboolean sort_alpha) +{ + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv != NULL); + + if ((tree_view->priv->sort_alpha ? 1 : 0) == (sort_alpha ? 1 : 0)) + return; + + tree_view->priv->sort_alpha = sort_alpha; + + g_object_notify (G_OBJECT (tree_view), "sort-alpha"); + e_account_tree_view_sort_changed (tree_view); +} + +gboolean +e_account_tree_view_get_sort_alpha (EAccountTreeView *tree_view) +{ + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (tree_view->priv != NULL, FALSE); + + return tree_view->priv->sort_alpha; +} + +void +e_account_tree_view_set_express_mode (EAccountTreeView *tree_view, gboolean express_mode) +{ + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv != NULL); + + if ((tree_view->priv->express_mode ? 1 : 0) == (express_mode ? 1 : 0)) + return; + + tree_view->priv->express_mode = express_mode; + + g_object_notify (G_OBJECT (tree_view), "express-mode"); + e_account_tree_view_sort_changed (tree_view); +} + +gboolean +e_account_tree_view_get_express_mode (EAccountTreeView *tree_view) +{ + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (tree_view->priv != NULL, FALSE); + + return tree_view->priv->express_mode; +} + +static void +update_special_enable_state (EAccountTreeView *tree_view, const gchar *display_name, gboolean enabled) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkListStore *list_store; + + g_return_if_fail (tree_view != NULL); + g_return_if_fail (display_name != NULL); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); + if (!model) + return; + + list_store = GTK_LIST_STORE (model); + g_return_if_fail (list_store != NULL); + + if (!gtk_tree_model_get_iter_first (model, &iter)) + return; + + do { + gchar *name = NULL; + EAccount *account = NULL; + + gtk_tree_model_get (model, &iter, + COLUMN_ACCOUNT, &account, + COLUMN_NAME, &name, + -1); + + if (account) { + g_object_unref (account); + } else if (g_strcmp0 (name, display_name) == 0) { + gtk_list_store_set (list_store, &iter, COLUMN_ENABLED, enabled, -1); + g_free (name); + break; + } + + g_free (name); + } while (gtk_tree_model_iter_next (model, &iter)); +} + +void +e_account_tree_view_set_enable_local_folders (EAccountTreeView *tree_view, gboolean enabled) +{ + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv != NULL); + + if ((tree_view->priv->enable_local_folders ? 1 : 0) == (enabled ? 1 : 0)) + return; + + tree_view->priv->enable_local_folders = enabled; + + g_object_notify (G_OBJECT (tree_view), "enable-local-folders"); + + update_special_enable_state (tree_view, _("On This Computer"), enabled); +} + +gboolean +e_account_tree_view_get_enable_local_folders (EAccountTreeView *tree_view) +{ + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (tree_view->priv != NULL, FALSE); + + return tree_view->priv->enable_local_folders; +} + +void +e_account_tree_view_set_enable_search_folders (EAccountTreeView *tree_view, gboolean enabled) +{ + g_return_if_fail (tree_view != NULL); + g_return_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv != NULL); + + if ((tree_view->priv->enable_search_folders ? 1 : 0) == (enabled ? 1 : 0)) + return; + + tree_view->priv->enable_search_folders = enabled; + + g_object_notify (G_OBJECT (tree_view), "enable-search-folders"); + + update_special_enable_state (tree_view, _("Search Folders"), enabled); +} + +gboolean +e_account_tree_view_get_enable_search_folders (EAccountTreeView *tree_view) +{ + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (E_IS_ACCOUNT_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (tree_view->priv != NULL, FALSE); + + return tree_view->priv->enable_search_folders; +} diff --git a/widgets/misc/e-account-tree-view.h b/widgets/misc/e-account-tree-view.h index e9b2f7f8d3..d62d040bbd 100644 --- a/widgets/misc/e-account-tree-view.h +++ b/widgets/misc/e-account-tree-view.h @@ -47,6 +47,14 @@ G_BEGIN_DECLS +typedef enum +{ + E_ACCOUNT_TREE_VIEW_SELECTED_NONE, + E_ACCOUNT_TREE_VIEW_SELECTED_ACCOUNT, + E_ACCOUNT_TREE_VIEW_SELECTED_LOCAL, + E_ACCOUNT_TREE_VIEW_SELECTED_VFOLDER +} EAccountTreeViewSelectedType; + typedef struct _EAccountTreeView EAccountTreeView; typedef struct _EAccountTreeViewClass EAccountTreeViewClass; typedef struct _EAccountTreeViewPrivate EAccountTreeViewPrivate; @@ -62,6 +70,7 @@ struct _EAccountTreeViewClass { void (*enable_account) (EAccountTreeView *tree_view); void (*disable_account) (EAccountTreeView *tree_view); void (*refreshed) (EAccountTreeView *tree_view); + void (*sort_order_changed) (EAccountTreeView *tree_view); }; GType e_account_tree_view_get_type (void); @@ -80,6 +89,43 @@ EAccount * e_account_tree_view_get_selected gboolean e_account_tree_view_set_selected (EAccountTreeView *tree_view, EAccount *account); +EAccountTreeViewSelectedType + e_account_tree_view_get_selected_type + (EAccountTreeView *tree_view); +void e_account_tree_view_set_selected_type + (EAccountTreeView *tree_view, + EAccountTreeViewSelectedType select); + +void e_account_tree_view_set_sort_order + (EAccountTreeView *tree_view, + const GSList *account_uids); + +GSList * e_account_tree_view_get_sort_order + (EAccountTreeView *tree_view); + +void e_account_tree_view_sort_changed + (EAccountTreeView *tree_view); +void e_account_tree_view_move_up (EAccountTreeView *tree_view); +void e_account_tree_view_move_down (EAccountTreeView *tree_view); + +void e_account_tree_view_set_sort_alpha + (EAccountTreeView *tree_view, + gboolean sort_alpha); +gboolean e_account_tree_view_get_sort_alpha + (EAccountTreeView *tree_view); +void e_account_tree_view_set_express_mode + (EAccountTreeView *tree_view, + gboolean express_mode); +gboolean e_account_tree_view_get_express_mode + (EAccountTreeView *tree_view); +void e_account_tree_view_set_enable_local_folders + (EAccountTreeView *tree_view, gboolean enabled); +gboolean e_account_tree_view_get_enable_local_folders + (EAccountTreeView *tree_view); +void e_account_tree_view_set_enable_search_folders + (EAccountTreeView *tree_view, gboolean enabled); +gboolean e_account_tree_view_get_enable_search_folders + (EAccountTreeView *tree_view); G_END_DECLS -- cgit