diff options
Diffstat (limited to 'mail/em-folder-tree-model.c')
-rw-r--r-- | mail/em-folder-tree-model.c | 1371 |
1 files changed, 591 insertions, 780 deletions
diff --git a/mail/em-folder-tree-model.c b/mail/em-folder-tree-model.c index 0a2ce6204e..9154e13c48 100644 --- a/mail/em-folder-tree-model.c +++ b/mail/em-folder-tree-model.c @@ -20,9 +20,7 @@ * */ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif +#include "em-folder-tree-model.h" #include <stdio.h> #include <string.h> @@ -32,13 +30,8 @@ #include <errno.h> #include <sys/stat.h> -#include <libxml/parser.h> - -#include <libedataserver/e-xml-utils.h> -#include <libedataserver/e-data-server-util.h> - -#include <e-util/e-util.h> -#include <e-util/e-mktemp.h> +#include "e-util/e-util.h" +#include "e-util/e-account-utils.h" #include <glib/gi18n.h> @@ -50,7 +43,6 @@ #include "mail-mt.h" /* sigh, these 2 only needed for outbox total count checking - a mess */ -#include "mail-component.h" #include "mail-folder-cache.h" #include "em-utils.h" @@ -60,22 +52,39 @@ #include <camel/camel-folder.h> #include <camel/camel-vee-store.h> -#include "em-folder-tree-model.h" +#include "e-mail-local.h" -#define u(x) /* unread count debug */ #define d(x) -/* GObject virtual method overrides */ -static void em_folder_tree_model_class_init (EMFolderTreeModelClass *klass); -static void em_folder_tree_model_init (EMFolderTreeModel *model); -static void em_folder_tree_model_finalize (GObject *obj); +#define EM_FOLDER_TREE_MODEL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), EM_TYPE_FOLDER_TREE_MODEL, EMFolderTreeModelPrivate)) + +struct _EMFolderTreeModelPrivate { + /* This is set by EMailShellSidebar. It allows new EMFolderTree + * instances to initialize their selection and expanded states to + * mimic the sidebar. */ + GtkTreeSelection *selection; /* weak reference */ + + EAccountList *accounts; -/* interface init methods */ -static void tree_model_iface_init (GtkTreeModelIface *iface); -static void tree_sortable_iface_init (GtkTreeSortableIface *iface); + /* EAccount -> EMFolderTreeStoreInfo */ + GHashTable *account_index; -static void account_changed (EAccountList *accounts, EAccount *account, gpointer user_data); -static void account_removed (EAccountList *accounts, EAccount *account, gpointer user_data); + /* CamelStore -> EMFolderTreeStoreInfo */ + GHashTable *store_index; + + /* URI -> GtkTreeRowReference */ + GHashTable *uri_index; + + gulong account_changed_id; + gulong account_removed_id; +}; + +enum { + PROP_0, + PROP_SELECTION +}; enum { LOADING_ROW, @@ -86,94 +95,30 @@ enum { extern CamelStore *vfolder_store; -static guint signals[LAST_SIGNAL] = { 0, }; -static GtkTreeStoreClass *parent_class = NULL; - -GType -em_folder_tree_model_get_type (void) -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo info = { - sizeof (EMFolderTreeModelClass), - NULL, /* base_class_init */ - NULL, /* base_class_finalize */ - (GClassInitFunc) em_folder_tree_model_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (EMFolderTreeModel), - 0, /* n_preallocs */ - (GInstanceInitFunc) em_folder_tree_model_init, - }; - static const GInterfaceInfo tree_model_info = { - (GInterfaceInitFunc) tree_model_iface_init, - NULL, - NULL - }; - static const GInterfaceInfo sortable_info = { - (GInterfaceInitFunc) tree_sortable_iface_init, - NULL, - NULL - }; - - type = g_type_register_static (GTK_TYPE_TREE_STORE, "EMFolderTreeModel", &info, 0); - - g_type_add_interface_static (type, GTK_TYPE_TREE_MODEL, - &tree_model_info); - g_type_add_interface_static (type, GTK_TYPE_TREE_SORTABLE, - &sortable_info); - } - - return type; -} +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; static void -em_folder_tree_model_class_init (EMFolderTreeModelClass *klass) +store_info_free (EMFolderTreeModelStoreInfo *si) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_ref (GTK_TYPE_TREE_STORE); - - object_class->finalize = em_folder_tree_model_finalize; - - /* signals */ - signals[LOADING_ROW] = - g_signal_new ("loading-row", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EMFolderTreeModelClass, loading_row), - NULL, NULL, - e_marshal_VOID__POINTER_POINTER, - G_TYPE_NONE, 2, - G_TYPE_POINTER, - G_TYPE_POINTER); - - signals[LOADED_ROW] = - g_signal_new ("loaded-row", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EMFolderTreeModelClass, loaded_row), - NULL, NULL, - e_marshal_VOID__POINTER_POINTER, - G_TYPE_NONE, 2, - G_TYPE_POINTER, - G_TYPE_POINTER); - - signals[FOLDER_ADDED] = - g_signal_new ("folder-added", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (EMFolderTreeModelClass, folder_added), - NULL, NULL, - e_marshal_VOID__STRING_STRING, - G_TYPE_NONE, 2, - G_TYPE_STRING, - G_TYPE_STRING); + camel_object_remove_event (si->store, si->created_id); + camel_object_remove_event (si->store, si->deleted_id); + camel_object_remove_event (si->store, si->renamed_id); + camel_object_remove_event (si->store, si->subscribed_id); + camel_object_remove_event (si->store, si->unsubscribed_id); + + g_free (si->display_name); + camel_object_unref (si->store); + gtk_tree_row_reference_free (si->row); + g_hash_table_destroy (si->full_hash); + g_free (si); } static gint -sort_cb (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) +folder_tree_model_sort (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) { gchar *aname, *bname; CamelStore *store; @@ -181,13 +126,21 @@ sort_cb (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data guint32 aflags, bflags; gint rv = -2; - 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); - gtk_tree_model_get (model, b, COL_STRING_DISPLAY_NAME, &bname, COL_UINT_FLAGS, &bflags, -1); + 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); + + gtk_tree_model_get ( + model, b, + COL_STRING_DISPLAY_NAME, &bname, + COL_UINT_FLAGS, &bflags, -1); if (is_store) { - /* On This Computer is always first and Search Folders is always last */ + /* On This Computer is always first, and Search Folders + * is always last. */ if (!strcmp (aname, _("On This Computer"))) rv = -1; else if (!strcmp (bname, _("On This Computer"))) @@ -197,13 +150,13 @@ sort_cb (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data else if (!strcmp (bname, _("Search Folders"))) rv = -1; } else if (store == vfolder_store) { - /* UNMATCHED is always last */ + /* UNMATCHED is always last. */ if (aname && !strcmp (aname, _("UNMATCHED"))) rv = 1; else if (bname && !strcmp (bname, _("UNMATCHED"))) rv = -1; } else { - /* Inbox is always first */ + /* Inbox is always first. */ if ((aflags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX) rv = -1; else if ((bflags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX) @@ -226,136 +179,203 @@ sort_cb (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data } static void -store_info_free (struct _EMFolderTreeModelStoreInfo *si) +account_changed_cb (EAccountList *accounts, + EAccount *account, + EMFolderTreeModel *model) { - camel_object_remove_event (si->store, si->created_id); - camel_object_remove_event (si->store, si->deleted_id); - camel_object_remove_event (si->store, si->renamed_id); - camel_object_remove_event (si->store, si->subscribed_id); - camel_object_remove_event (si->store, si->unsubscribed_id); + EMFolderTreeModelStoreInfo *si; + CamelProvider *provider; + CamelStore *store; + CamelException ex; + gchar *uri; - g_free (si->display_name); - camel_object_unref (si->store); - gtk_tree_row_reference_free (si->row); - g_hash_table_destroy (si->full_hash); - g_free (si); -} + si = g_hash_table_lookup (model->priv->account_index, account); + if (si == NULL) + return; -static void -emft_model_unread_count_changed (GtkTreeModel *model, GtkTreeIter *iter) -{ - GtkTreeIter parent_iter; - GtkTreeIter child_iter = *iter; + em_folder_tree_model_remove_store (model, si->store); - g_signal_handlers_block_by_func ( - model, emft_model_unread_count_changed, NULL); + /* check if store needs to be added at all*/ + if (!account->enabled ||!(uri = account->source->url)) + return; - /* Folders are displayed with a bold weight to indicate that - they contain unread messages. We signal that parent rows - have changed here to update them. */ + camel_exception_init (&ex); + if (!(provider = camel_provider_get(uri, &ex))) { + camel_exception_clear (&ex); + return; + } - while (gtk_tree_model_iter_parent (model, &parent_iter, &child_iter)) { - GtkTreePath *parent_path; + /* make sure the new store belongs in the tree */ + if (!(provider->flags & CAMEL_PROVIDER_IS_STORAGE)) + return; - parent_path = gtk_tree_model_get_path (model, &parent_iter); - gtk_tree_model_row_changed (model, parent_path, &parent_iter); - gtk_tree_path_free (parent_path); - child_iter = parent_iter; + if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex))) { + camel_exception_clear (&ex); + return; } - g_signal_handlers_unblock_by_func ( - model, emft_model_unread_count_changed, NULL); + em_folder_tree_model_add_store (model, store, account->name); + camel_object_unref (store); } static void -em_folder_tree_model_init (EMFolderTreeModel *model) +account_removed_cb (EAccountList *accounts, + EAccount *account, + EMFolderTreeModel *model) { - model->store_hash = g_hash_table_new_full ( - g_direct_hash, g_direct_equal, - (GDestroyNotify) NULL, - (GDestroyNotify) store_info_free); + EMFolderTreeModelStoreInfo *si; - model->uri_hash = g_hash_table_new_full ( - g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) gtk_tree_row_reference_free); - - gtk_tree_sortable_set_default_sort_func ((GtkTreeSortable *) model, sort_cb, NULL, NULL); + si = g_hash_table_lookup (model->priv->account_index, account); + if (si == NULL) + return; - model->accounts = mail_config_get_accounts (); - model->account_hash = g_hash_table_new (g_direct_hash, g_direct_equal); - model->account_changed_id = g_signal_connect (model->accounts, "account-changed", G_CALLBACK (account_changed), model); - model->account_removed_id = g_signal_connect (model->accounts, "account-removed", G_CALLBACK (account_removed), model); - /* g_signal_connect (model, "row-changed", G_CALLBACK (emft_model_unread_count_changed), NULL); */ + em_folder_tree_model_remove_store (model, si->store); } static void -em_folder_tree_model_finalize (GObject *obj) +folder_tree_model_selection_finalized_cb (EMFolderTreeModel *model) { - EMFolderTreeModel *model = (EMFolderTreeModel *) obj; + model->priv->selection = NULL; - g_free (model->filename); - if (model->state) - xmlFreeDoc (model->state); - - g_hash_table_destroy (model->store_hash); - g_hash_table_destroy (model->uri_hash); + g_object_notify (G_OBJECT (model), "selection"); +} - g_hash_table_destroy (model->account_hash); - g_signal_handler_disconnect (model->accounts, model->account_changed_id); - g_signal_handler_disconnect (model->accounts, model->account_removed_id); +static void +folder_tree_model_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SELECTION: + em_folder_tree_model_set_selection ( + EM_FOLDER_TREE_MODEL (object), + g_value_get_object (value)); + return; + } - G_OBJECT_CLASS (parent_class)->finalize (obj); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -tree_model_iface_init (GtkTreeModelIface *iface) +folder_tree_model_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - ; + switch (property_id) { + case PROP_SELECTION: + g_value_set_object ( + value, + em_folder_tree_model_get_selection ( + EM_FOLDER_TREE_MODEL (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -tree_sortable_iface_init (GtkTreeSortableIface *iface) +folder_tree_model_dispose (GObject *object) { - ; + EMFolderTreeModelPrivate *priv; + + priv = EM_FOLDER_TREE_MODEL_GET_PRIVATE (object); + + if (priv->selection != NULL) { + g_object_weak_unref ( + G_OBJECT (priv->selection), (GWeakNotify) + folder_tree_model_selection_finalized_cb, object); + priv->selection = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); } static void -em_folder_tree_model_load_state (EMFolderTreeModel *model, const gchar *filename) +folder_tree_model_finalize (GObject *object) { - xmlNodePtr root, node; + EMFolderTreeModelPrivate *priv; - if (model->state) - xmlFreeDoc (model->state); + priv = EM_FOLDER_TREE_MODEL_GET_PRIVATE (object); - if ((model->state = e_xml_parse_file (filename)) != NULL) { - node = xmlDocGetRootElement (model->state); - if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) { - /* it is not expected XML file, thus free it and use the default */ - xmlFreeDoc (model->state); - } else - return; - } + g_hash_table_destroy (priv->account_index); + g_hash_table_destroy (priv->store_index); + g_hash_table_destroy (priv->uri_index); - /* setup some defaults - expand "Local Folders" and "Search Folders" */ - model->state = xmlNewDoc ((const guchar *)"1.0"); - root = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL); - xmlDocSetRootElement (model->state, root); + g_signal_handler_disconnect ( + priv->accounts, priv->account_changed_id); + g_signal_handler_disconnect ( + priv->accounts, priv->account_removed_id); - node = xmlNewChild (root, NULL, (const guchar *)"node", NULL); - xmlSetProp (node, (const guchar *)"name", (const guchar *)"local"); - xmlSetProp (node, (const guchar *)"expand", (const guchar *)"true"); + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} - node = xmlNewChild (root, NULL, (const guchar *)"node", NULL); - xmlSetProp (node, (const guchar *)"name", (const guchar *)"vfolder"); - xmlSetProp (node, (const guchar *)"expand", (const guchar *)"true"); +static void +folder_tree_model_class_init (EMFolderTreeModelClass *class) +{ + GObjectClass *object_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EMFolderTreeModelPrivate)); + + 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->dispose = folder_tree_model_dispose; + object_class->finalize = folder_tree_model_finalize; + + g_object_class_install_property ( + object_class, + PROP_SELECTION, + g_param_spec_object ( + "selection", + "Selection", + NULL, + GTK_TYPE_TREE_SELECTION, + G_PARAM_READWRITE)); + + signals[LOADING_ROW] = g_signal_new ( + "loading-row", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMFolderTreeModelClass, loading_row), + NULL, NULL, + e_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, + G_TYPE_POINTER, + G_TYPE_POINTER); + + signals[LOADED_ROW] = g_signal_new ( + "loaded-row", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMFolderTreeModelClass, loaded_row), + NULL, NULL, + e_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, + G_TYPE_POINTER, + G_TYPE_POINTER); + + signals[FOLDER_ADDED] = g_signal_new ( + "folder-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMFolderTreeModelClass, folder_added), + NULL, NULL, + e_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_STRING); } -EMFolderTreeModel * -em_folder_tree_model_new (const gchar *evolution_dir) +static void +folder_tree_model_init (EMFolderTreeModel *model) { - EMFolderTreeModel *model; - gchar *filename; + GHashTable *store_index; + GHashTable *uri_index; GType col_types[] = { G_TYPE_STRING, /* display name */ @@ -371,80 +391,134 @@ em_folder_tree_model_new (const gchar *evolution_dir) G_TYPE_UINT /* last known unread count */ }; - model = g_object_new (EM_TYPE_FOLDER_TREE_MODEL, NULL); - gtk_tree_store_set_column_types ((GtkTreeStore *) model, NUM_COLUMNS, col_types); - gtk_tree_sortable_set_sort_column_id ((GtkTreeSortable *) model, - GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, - GTK_SORT_ASCENDING); + store_index = g_hash_table_new_full ( + g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) store_info_free); - filename = g_build_filename (evolution_dir, "mail", "config", "folder-tree-expand-state.xml", NULL); - em_folder_tree_model_load_state (model, filename); - model->filename = filename; + uri_index = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gtk_tree_row_reference_free); - return 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_index = + g_hash_table_new (g_direct_hash, g_direct_equal); + 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); } -static void -account_changed (EAccountList *accounts, EAccount *account, gpointer user_data) +GType +em_folder_tree_model_get_type (void) { - EMFolderTreeModel *model = user_data; - struct _EMFolderTreeModelStoreInfo *si; - CamelProvider *provider; - CamelStore *store; - CamelException ex; - gchar *uri; + static GType type = 0; - if (!(si = g_hash_table_lookup (model->account_hash, account))) - return; + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EMFolderTreeModelClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) folder_tree_model_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EMFolderTreeModel), + 0, /* n_preallocs */ + (GInstanceInitFunc) folder_tree_model_init, + NULL /* value_table */ + }; - em_folder_tree_model_remove_store (model, si->store); + type = g_type_register_static ( + GTK_TYPE_TREE_STORE, "EMFolderTreeModel", + &type_info, 0); + } - /* check if store needs to be added at all*/ - if (!account->enabled ||!(uri = account->source->url)) - return; + return type; +} - camel_exception_init (&ex); - if (!(provider = camel_provider_get(uri, &ex))) { - camel_exception_clear (&ex); - return; - } +EMFolderTreeModel * +em_folder_tree_model_new (void) +{ + return g_object_new (EM_TYPE_FOLDER_TREE_MODEL, NULL); +} - /* make sure the new store belongs in the tree */ - if (!(provider->flags & CAMEL_PROVIDER_IS_STORAGE)) - return; +EMFolderTreeModel * +em_folder_tree_model_get_default (void) +{ + static EMFolderTreeModel *default_folder_tree_model; - if (!(store = (CamelStore *) camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex))) { - camel_exception_clear (&ex); - return; - } + if (G_UNLIKELY (default_folder_tree_model == NULL)) + default_folder_tree_model = em_folder_tree_model_new (); - em_folder_tree_model_add_store (model, store, account->name); - camel_object_unref (store); + return default_folder_tree_model; } -static void -account_removed (EAccountList *accounts, EAccount *account, gpointer user_data) +GtkTreeSelection * +em_folder_tree_model_get_selection (EMFolderTreeModel *model) +{ + g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL); + + return GTK_TREE_SELECTION (model->priv->selection); +} + +void +em_folder_tree_model_set_selection (EMFolderTreeModel *model, + GtkTreeSelection *selection) { - EMFolderTreeModel *model = user_data; - struct _EMFolderTreeModelStoreInfo *si; + g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); - if (!(si = g_hash_table_lookup (model->account_hash, account))) - return; + if (selection != NULL) + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - em_folder_tree_model_remove_store (model, si->store); + if (model->priv->selection != NULL) { + g_object_weak_unref ( + G_OBJECT (model->priv->selection), (GWeakNotify) + folder_tree_model_selection_finalized_cb, model); + model->priv->selection = NULL; + } + + model->priv->selection = selection; + + if (model->priv->selection != NULL) + g_object_weak_ref ( + G_OBJECT (model->priv->selection), (GWeakNotify) + folder_tree_model_selection_finalized_cb, model); + + g_object_notify (G_OBJECT (model), "selection"); } void -em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *iter, - struct _EMFolderTreeModelStoreInfo *si, - CamelFolderInfo *fi, gint fully_loaded) +em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, + GtkTreeIter *iter, + EMFolderTreeModelStoreInfo *si, + CamelFolderInfo *fi, + gint fully_loaded) { GtkTreeRowReference *uri_row, *path_row; GtkTreeStore *tree_store; guint unread; GtkTreePath *path; GtkTreeIter sub; - gboolean load = FALSE, is_drafts = FALSE, is_templates = FALSE; + gboolean load = FALSE; + gboolean is_drafts = FALSE; + gboolean is_templates = FALSE; CamelFolder *folder; gboolean emitted = FALSE; const gchar *name; @@ -461,20 +535,28 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *ite if (!fully_loaded) load = fi->child == NULL && !(fi->flags & (CAMEL_FOLDER_NOCHILDREN | CAMEL_FOLDER_NOINFERIORS)); - path = gtk_tree_model_get_path ((GtkTreeModel *) model, iter); - uri_row = gtk_tree_row_reference_new ((GtkTreeModel *) model, path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); + uri_row = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path); path_row = gtk_tree_row_reference_copy (uri_row); gtk_tree_path_free (path); - g_hash_table_insert (model->uri_hash, g_strdup (fi->uri), uri_row); - g_hash_table_insert (si->full_hash, g_strdup (fi->full_name), path_row); + g_hash_table_insert ( + model->priv->uri_index, g_strdup (fi->uri), uri_row); + g_hash_table_insert ( + si->full_hash, g_strdup (fi->full_name), path_row); /* HACK: if we have the folder, and its the outbox folder, we need the total count, not unread */ /* HACK2: We do the same to the draft folder */ /* This is duplicated in mail-folder-cache too, should perhaps be functionised */ unread = fi->unread; if (mail_note_get_folder_from_uri(fi->uri, &folder) && folder) { - if (folder == mail_component_get_folder(NULL, MAIL_COMPONENT_FOLDER_OUTBOX)) { + CamelFolder *local_drafts; + CamelFolder *local_outbox; + + local_drafts = e_mail_local_get_folder (E_MAIL_FOLDER_DRAFTS); + local_outbox = e_mail_local_get_folder (E_MAIL_FOLDER_OUTBOX); + + if (folder == local_outbox) { gint total; if ((total = camel_folder_get_message_count (folder)) > 0) { @@ -486,7 +568,7 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *ite unread = total > 0 ? total : 0; } - if (folder == mail_component_get_folder(NULL, MAIL_COMPONENT_FOLDER_DRAFTS)) { + if (folder == local_drafts) { gint total; if ((total = camel_folder_get_message_count (folder)) > 0) { @@ -505,7 +587,7 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *ite /* TODO: maybe this should be handled by mail_get_folderinfo (except em-folder-tree doesn't use it, duh) */ flags = fi->flags; name = fi->name; - if (si->store == mail_component_peek_local_store(NULL)) { + if (si->store == e_mail_local_get_store ()) { if (!strcmp(fi->full_name, "Drafts")) { name = _("Drafts"); is_drafts = TRUE; @@ -589,7 +671,7 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *ite COL_UINT_UNREAD_LAST_SEL, 0, -1); - path = gtk_tree_model_get_path ((GtkTreeModel *) model, iter); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); g_signal_emit (model, signals[LOADING_ROW], 0, path, iter); gtk_tree_path_free (path); return; @@ -602,7 +684,7 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *ite gtk_tree_store_append (tree_store, &sub, iter); if (!emitted) { - path = gtk_tree_model_get_path ((GtkTreeModel *) model, iter); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); g_signal_emit (model, signals[LOADED_ROW], 0, path, iter); gtk_tree_path_free (path); emitted = TRUE; @@ -614,23 +696,26 @@ em_folder_tree_model_set_folder_info (EMFolderTreeModel *model, GtkTreeIter *ite } if (!emitted) { - path = gtk_tree_model_get_path ((GtkTreeModel *) model, iter); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); g_signal_emit (model, signals[LOADED_ROW], 0, path, iter); gtk_tree_path_free (path); } } static void -folder_subscribed (CamelStore *store, CamelFolderInfo *fi, EMFolderTreeModel *model) +folder_subscribed (CamelStore *store, + CamelFolderInfo *fi, + EMFolderTreeModel *model) { - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; GtkTreeIter parent, iter; GtkTreePath *path; gboolean load; gchar *dirname, *p; - if (!(si = g_hash_table_lookup (model->store_hash, store))) + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) goto done; /* make sure we don't already know about it? */ @@ -643,140 +728,159 @@ folder_subscribed (CamelStore *store, CamelFolderInfo *fi, EMFolderTreeModel *mo p = strrchr(dirname, '/'); if (p == NULL) { /* user subscribed to a toplevel folder */ - row = si->row; + reference = si->row; } else { *p = 0; - row = g_hash_table_lookup (si->full_hash, dirname); - - /* if row is NULL, don't bother adding to the tree, - * when the user expands enough nodes - it will be - * added auto-magically */ - if (row == NULL) - goto done; + reference = g_hash_table_lookup (si->full_hash, dirname); } - path = gtk_tree_row_reference_get_path (row); - if (!(gtk_tree_model_get_iter ((GtkTreeModel *) model, &parent, path))) { - gtk_tree_path_free (path); + if (!gtk_tree_row_reference_valid (reference)) goto done; - } + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &parent, path); gtk_tree_path_free (path); /* make sure parent's subfolders have already been loaded */ - gtk_tree_model_get ((GtkTreeModel *) model, &parent, COL_BOOL_LOAD_SUBDIRS, &load, -1); + gtk_tree_model_get ( + GTK_TREE_MODEL (model), &parent, + COL_BOOL_LOAD_SUBDIRS, &load, -1); if (load) goto done; /* append a new node */ - gtk_tree_store_append ((GtkTreeStore *) model, &iter, &parent); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent); em_folder_tree_model_set_folder_info (model, &iter, si, fi, TRUE); g_signal_emit (model, signals[FOLDER_ADDED], 0, fi->full_name, fi->uri); - done: - +done: camel_object_unref (store); camel_folder_info_free (fi); } static void -folder_subscribed_cb (CamelStore *store, gpointer event_data, EMFolderTreeModel *model) +folder_subscribed_cb (CamelStore *store, + gpointer event_data, + EMFolderTreeModel *model) { CamelFolderInfo *fi; camel_object_ref (store); fi = camel_folder_info_clone (event_data); - mail_async_event_emit (mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) folder_subscribed, store, fi, model); + + mail_async_event_emit ( + mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) + folder_subscribed, store, fi, model); } static void -folder_unsubscribed (CamelStore *store, CamelFolderInfo *fi, EMFolderTreeModel *model) +folder_unsubscribed (CamelStore *store, + CamelFolderInfo *fi, + EMFolderTreeModel *model) { - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; GtkTreePath *path; GtkTreeIter iter; - if (!(si = g_hash_table_lookup (model->store_hash, store))) + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) goto done; - if (!(row = g_hash_table_lookup (si->full_hash, fi->full_name))) + reference = g_hash_table_lookup (si->full_hash, fi->full_name); + if (!gtk_tree_row_reference_valid (reference)) goto done; - path = gtk_tree_row_reference_get_path (row); - if (!(gtk_tree_model_get_iter ((GtkTreeModel *) model, &iter, path))) { - gtk_tree_path_free (path); - goto done; - } + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); + gtk_tree_path_free (path); em_folder_tree_model_remove_folders (model, si, &iter); - done: - +done: camel_object_unref (store); camel_folder_info_free (fi); } static void -folder_unsubscribed_cb (CamelStore *store, gpointer event_data, EMFolderTreeModel *model) +folder_unsubscribed_cb (CamelStore *store, + gpointer event_data, + EMFolderTreeModel *model) { CamelFolderInfo *fi; camel_object_ref (store); fi = camel_folder_info_clone (event_data); - mail_async_event_emit (mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) folder_unsubscribed, store, fi, model); + + mail_async_event_emit ( + mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) + folder_unsubscribed, store, fi, model); } static void -folder_created_cb (CamelStore *store, gpointer event_data, EMFolderTreeModel *model) +folder_created_cb (CamelStore *store, + gpointer event_data, + EMFolderTreeModel *model) { CamelFolderInfo *fi; - /* we only want created events to do more work if we don't support subscriptions */ + /* We only want created events to do more + * work if we don't support subscriptions. */ if (camel_store_supports_subscriptions (store)) return; camel_object_ref (store); fi = camel_folder_info_clone (event_data); - mail_async_event_emit (mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) folder_subscribed, store, fi, model); + + mail_async_event_emit ( + mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) + folder_subscribed, store, fi, model); } static void -folder_deleted_cb (CamelStore *store, gpointer event_data, EMFolderTreeModel *model) +folder_deleted_cb (CamelStore *store, + gpointer event_data, + EMFolderTreeModel *model) { CamelFolderInfo *fi; - /* we only want deleted events to do more work if we don't support subscriptions */ + /* We only want deleted events to do more + * work if we don't support subscriptions. */ if (camel_store_supports_subscriptions (store)) return; camel_object_ref (store); fi = camel_folder_info_clone (event_data); - mail_async_event_emit (mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) folder_unsubscribed_cb, store, fi, model); + + mail_async_event_emit ( + mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) + folder_unsubscribed_cb, store, fi, model); } static void -folder_renamed (CamelStore *store, CamelRenameInfo *info, EMFolderTreeModel *model) +folder_renamed (CamelStore *store, + CamelRenameInfo *info, + EMFolderTreeModel *model) { - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; GtkTreeIter root, iter; GtkTreePath *path; gchar *parent, *p; - if (!(si = g_hash_table_lookup (model->store_hash, store))) + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) goto done; - if (!(row = g_hash_table_lookup (si->full_hash, info->old_base))) + reference = g_hash_table_lookup (si->full_hash, info->old_base); + if (!gtk_tree_row_reference_valid (reference)) goto done; - path = gtk_tree_row_reference_get_path (row); - if (!(gtk_tree_model_get_iter ((GtkTreeModel *) model, &iter, path))) { - gtk_tree_path_free (path); - goto done; - } + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); + gtk_tree_path_free (path); em_folder_tree_model_remove_folders (model, si, &iter); @@ -784,36 +888,25 @@ folder_renamed (CamelStore *store, CamelRenameInfo *info, EMFolderTreeModel *mod p = strrchr(parent, '/'); if (p) *p = 0; - if (p == NULL || parent == p) { + if (p == NULL || parent == p) /* renamed to a toplevel folder on the store */ - path = gtk_tree_row_reference_get_path (si->row); - } else { - if (!(row = g_hash_table_lookup (si->full_hash, parent))) { - /* NOTE: this should never happen, but I - * suppose if it does in reality, we can add - * code here to add the missing nodes to the - * tree */ - g_warning ("This shouldn't be reached\n"); - g_free (parent); - goto done; - } - - path = gtk_tree_row_reference_get_path (row); - } + reference = si->row; + else + reference = g_hash_table_lookup (si->full_hash, parent); g_free (parent); - if (!gtk_tree_model_get_iter ((GtkTreeModel *) model, &root, path)) { - gtk_tree_path_free (path); - g_warning ("This shouldn't be reached\n"); + if (!gtk_tree_row_reference_valid (reference)) goto done; - } - gtk_tree_store_append ((GtkTreeStore *) model, &iter, &root); - em_folder_tree_model_set_folder_info (model, &iter, si, info->new, TRUE); + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &root, path); + gtk_tree_path_free (path); - done: + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &root); + em_folder_tree_model_set_folder_info (model, &iter, si, info->new, TRUE); +done: camel_object_unref (store); g_free (info->old_base); @@ -822,7 +915,9 @@ folder_renamed (CamelStore *store, CamelRenameInfo *info, EMFolderTreeModel *mod } static void -folder_renamed_cb (CamelStore *store, gpointer event_data, EMFolderTreeModel *model) +folder_renamed_cb (CamelStore *store, + gpointer event_data, + EMFolderTreeModel *model) { CamelRenameInfo *rinfo, *info = event_data; @@ -832,14 +927,19 @@ folder_renamed_cb (CamelStore *store, gpointer event_data, EMFolderTreeModel *mo rinfo->old_base = g_strdup (info->old_base); rinfo->new = camel_folder_info_clone (info->new); - mail_async_event_emit (mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) folder_renamed, store, rinfo, model); + mail_async_event_emit ( + mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) + folder_renamed, store, rinfo, model); } void -em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, const gchar *display_name) +em_folder_tree_model_add_store (EMFolderTreeModel *model, + CamelStore *store, + const gchar *display_name) { - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; + GtkTreeStore *tree_store; GtkTreeIter root, iter; GtkTreePath *path; EAccount *account; @@ -849,62 +949,77 @@ em_folder_tree_model_add_store (EMFolderTreeModel *model, CamelStore *store, con g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (display_name != NULL); - if ((si = g_hash_table_lookup (model->store_hash, store))) + tree_store = GTK_TREE_STORE (model); + + si = em_folder_tree_model_lookup_store_info (model, store); + if (si != NULL) em_folder_tree_model_remove_store (model, store); - uri = camel_url_to_string (((CamelService *) store)->url, CAMEL_URL_HIDE_ALL); + uri = camel_url_to_string ( + ((CamelService *) store)->url, CAMEL_URL_HIDE_ALL); account = mail_config_get_account_by_source_url (uri); /* add the store to the tree */ - gtk_tree_store_append ((GtkTreeStore *) model, &iter, NULL); - gtk_tree_store_set ((GtkTreeStore *) model, &iter, - COL_STRING_DISPLAY_NAME, display_name, - COL_POINTER_CAMEL_STORE, store, - COL_STRING_FULL_NAME, NULL, - COL_BOOL_LOAD_SUBDIRS, TRUE, - COL_BOOL_IS_STORE, TRUE, - COL_STRING_URI, uri, -1); - - path = gtk_tree_model_get_path ((GtkTreeModel *) model, &iter); - row = gtk_tree_row_reference_new ((GtkTreeModel *) model, path); - - si = g_new (struct _EMFolderTreeModelStoreInfo, 1); + gtk_tree_store_append (tree_store, &iter, NULL); + gtk_tree_store_set ( + tree_store, &iter, + COL_STRING_DISPLAY_NAME, display_name, + COL_POINTER_CAMEL_STORE, store, + COL_STRING_FULL_NAME, NULL, + COL_BOOL_LOAD_SUBDIRS, TRUE, + COL_BOOL_IS_STORE, TRUE, + COL_STRING_URI, uri, -1); + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path); + + si = g_new (EMFolderTreeModelStoreInfo, 1); si->display_name = g_strdup (display_name); camel_object_ref (store); si->store = store; si->account = account; - si->row = row; + si->row = gtk_tree_row_reference_copy (reference); si->full_hash = g_hash_table_new_full ( g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) gtk_tree_row_reference_free); - g_hash_table_insert (model->store_hash, store, si); - g_hash_table_insert (model->account_hash, account, si); + g_hash_table_insert (model->priv->store_index, store, si); + g_hash_table_insert (model->priv->account_index, account, si); + + /* Transfer ownership of the URI and GtkTreeRowReference. */ + g_hash_table_insert (model->priv->uri_index, uri, reference); /* each store has folders... but we don't load them until the user demands them */ root = iter; - gtk_tree_store_append ((GtkTreeStore *) model, &iter, &root); - gtk_tree_store_set ((GtkTreeStore *) model, &iter, - COL_STRING_DISPLAY_NAME, _("Loading..."), - COL_POINTER_CAMEL_STORE, NULL, - COL_STRING_FULL_NAME, NULL, - COL_BOOL_LOAD_SUBDIRS, FALSE, - COL_BOOL_IS_STORE, FALSE, - COL_STRING_URI, NULL, - COL_UINT_UNREAD, 0, - COL_UINT_UNREAD_LAST_SEL, 0, - -1); - - g_free (uri); + gtk_tree_store_append (tree_store, &iter, &root); + gtk_tree_store_set ( + tree_store, &iter, + COL_STRING_DISPLAY_NAME, _("Loading..."), + COL_POINTER_CAMEL_STORE, NULL, + COL_STRING_FULL_NAME, NULL, + COL_BOOL_LOAD_SUBDIRS, FALSE, + COL_BOOL_IS_STORE, FALSE, + COL_STRING_URI, NULL, + COL_UINT_UNREAD, 0, + COL_UINT_UNREAD_LAST_SEL, 0, -1); /* listen to store events */ -#define CAMEL_CALLBACK(func) ((CamelObjectEventHookFunc) func) - si->created_id = camel_object_hook_event (store, "folder_created", CAMEL_CALLBACK (folder_created_cb), model); - si->deleted_id = camel_object_hook_event (store, "folder_deleted", CAMEL_CALLBACK (folder_deleted_cb), model); - si->renamed_id = camel_object_hook_event (store, "folder_renamed", CAMEL_CALLBACK (folder_renamed_cb), model); - si->subscribed_id = camel_object_hook_event (store, "folder_subscribed", CAMEL_CALLBACK (folder_subscribed_cb), model); - si->unsubscribed_id = camel_object_hook_event (store, "folder_unsubscribed", CAMEL_CALLBACK (folder_unsubscribed_cb), model); + si->created_id = camel_object_hook_event ( + store, "folder_created", + (CamelObjectEventHookFunc) folder_created_cb, model); + si->deleted_id = camel_object_hook_event ( + store, "folder_deleted", + (CamelObjectEventHookFunc) folder_deleted_cb, model); + si->renamed_id = camel_object_hook_event ( + store, "folder_renamed", + (CamelObjectEventHookFunc) folder_renamed_cb, model); + si->subscribed_id = camel_object_hook_event ( + store, "folder_subscribed", + (CamelObjectEventHookFunc) folder_subscribed_cb, model); + si->unsubscribed_id = camel_object_hook_event ( + store, "folder_unsubscribed", + (CamelObjectEventHookFunc) folder_unsubscribed_cb, model); g_signal_emit (model, signals[LOADED_ROW], 0, path, &root); gtk_tree_path_free (path); @@ -916,50 +1031,55 @@ em_folder_tree_model_remove_uri (EMFolderTreeModel *model, const gchar *uri) g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); g_return_if_fail (uri != NULL); - g_hash_table_remove (model->uri_hash, uri); + g_hash_table_remove (model->priv->uri_index, uri); } static void -em_folder_tree_model_remove_store_info (EMFolderTreeModel *model, CamelStore *store) +em_folder_tree_model_remove_store_info (EMFolderTreeModel *model, + CamelStore *store) { - struct _EMFolderTreeModelStoreInfo *si; + EMFolderTreeModelStoreInfo *si; g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); g_return_if_fail (CAMEL_IS_STORE (store)); - if (!(si = g_hash_table_lookup (model->store_hash, store))) + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) return; - g_hash_table_remove (model->account_hash, si->account); - /* store_hash owns and frees the si structure, thus free it after done with it */ - g_hash_table_remove (model->store_hash, si->store); + g_hash_table_remove (model->priv->account_index, si->account); + g_hash_table_remove (model->priv->store_index, si->store); } void -em_folder_tree_model_remove_folders (EMFolderTreeModel *model, struct _EMFolderTreeModelStoreInfo *si, GtkTreeIter *toplevel) +em_folder_tree_model_remove_folders (EMFolderTreeModel *model, + EMFolderTreeModelStoreInfo *si, + GtkTreeIter *toplevel) { gchar *uri, *full_name; gboolean is_store, go; GtkTreeIter iter; - if (gtk_tree_model_iter_children ((GtkTreeModel *) model, &iter, toplevel)) { + if (gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, toplevel)) { do { GtkTreeIter next = iter; - go = gtk_tree_model_iter_next ((GtkTreeModel *) model, &next); + go = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &next); em_folder_tree_model_remove_folders (model, si, &iter); iter = next; } while (go); } - gtk_tree_model_get ((GtkTreeModel *) model, toplevel, COL_STRING_URI, &uri, - COL_STRING_FULL_NAME, &full_name, - COL_BOOL_IS_STORE, &is_store, -1); + gtk_tree_model_get ( + GTK_TREE_MODEL (model), toplevel, + COL_STRING_URI, &uri, + COL_STRING_FULL_NAME, &full_name, + COL_BOOL_IS_STORE, &is_store, -1); - if (full_name) + if (full_name != NULL) g_hash_table_remove (si->full_hash, full_name); - if (uri) + if (uri != NULL) em_folder_tree_model_remove_uri (model, uri); gtk_tree_store_remove ((GtkTreeStore *) model, toplevel); @@ -972,298 +1092,36 @@ em_folder_tree_model_remove_folders (EMFolderTreeModel *model, struct _EMFolderT } void -em_folder_tree_model_remove_store (EMFolderTreeModel *model, CamelStore *store) +em_folder_tree_model_remove_store (EMFolderTreeModel *model, + CamelStore *store) { - struct _EMFolderTreeModelStoreInfo *si; + EMFolderTreeModelStoreInfo *si; GtkTreePath *path; GtkTreeIter iter; g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); g_return_if_fail (CAMEL_IS_STORE (store)); - if (!(si = g_hash_table_lookup (model->store_hash, store))) + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) return; path = gtk_tree_row_reference_get_path (si->row); - gtk_tree_model_get_iter ((GtkTreeModel *) model, &iter, path); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); gtk_tree_path_free (path); /* recursively remove subfolders and finally the toplevel store */ em_folder_tree_model_remove_folders (model, si, &iter); } -static xmlNodePtr -find_xml_node (xmlNodePtr root, const gchar *name) -{ - xmlNodePtr node; - gchar *nname; - - node = root->children; - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "node")) { - nname = (gchar *)xmlGetProp (node, (const guchar *)"name"); - if (nname && !strcmp (nname, name)) { - xmlFree (nname); - return node; - } - - xmlFree (nname); - } - - node = node->next; - } - - return node; -} - -gboolean -em_folder_tree_model_get_expanded (EMFolderTreeModel *model, const gchar *key) -{ - xmlNodePtr node; - const gchar *name; - gchar *buf, *p; - - /* This code needs to be rewritten. - First it doesn't belong on the model - Second, it shouldn't use an xml tree to store a bit table in memory! */ - - node = model->state ? model->state->children : NULL; - if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) - return FALSE; - - name = buf = g_alloca (strlen (key) + 1); - p = g_stpcpy (buf, key); - if (p[-1] == '/') - p[-1] = '\0'; - p = NULL; - - do { - if ((p = strchr (name, '/'))) - *p = '\0'; - - if ((node = find_xml_node (node, name))) { - gboolean expanded; - - buf = (gchar *)xmlGetProp (node, (const guchar *)"expand"); - expanded = buf && !strcmp ((gchar *)buf, "true"); - xmlFree (buf); - - if (!expanded || p == NULL) - return expanded; - } - - name = p ? p + 1 : NULL; - } while (name && node); - - return FALSE; -} - -void -em_folder_tree_model_set_expanded (EMFolderTreeModel *model, const gchar *key, gboolean expanded) -{ - xmlNodePtr node, parent; - const gchar *name; - gchar *buf, *p; - - if (model->state == NULL) - model->state = xmlNewDoc ((const guchar *)"1.0"); - - if (!model->state->children) { - node = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL); - xmlDocSetRootElement (model->state, node); - } else { - node = model->state->children; - } - - name = buf = g_alloca (strlen (key) + 1); - p = g_stpcpy (buf, key); - if (p[-1] == '/') - p[-1] = '\0'; - p = NULL; - - do { - parent = node; - if ((p = strchr (name, '/'))) - *p = '\0'; - - if (!(node = find_xml_node (node, name))) { - if (!expanded) { - /* node doesn't exist, so we don't need to set expanded to FALSE */ - return; - } - - /* node (or parent node) doesn't exist, need to add it */ - node = xmlNewChild (parent, NULL, (const guchar *)"node", NULL); - xmlSetProp (node, (const guchar *)"name", (guchar *)name); - } - - xmlSetProp (node, (const guchar *)"expand", (const guchar *)(expanded || p ? "true" : "false")); - - name = p ? p + 1 : NULL; - } while (name); -} - -/** - * emftm_uri_to_key - * Converts uri to key used in functions like em_folder_tree_model_[s/g]et_expanded. - * @param uri Uri to be converted. - * @return Key of the uri or NULL, if failed. Returned value should be clear by g_free. - **/ -static gchar * -emftm_uri_to_key (const gchar *uri) -{ - CamelException ex = { 0 }; - CamelStore *store; - CamelURL *url; - gchar *key; - - if (!uri) - return NULL; - - store = (CamelStore *)camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex); - camel_exception_clear(&ex); - - url = camel_url_new (uri, NULL); - - if (store == NULL || url == NULL) { - key = NULL; - } else { - const gchar *path; - EAccount *account; - - if (((CamelService *)store)->provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH) - path = url->fragment; - else - path = url->path && url->path[0]=='/' ? url->path+1:url->path; - - if (path == NULL) - path = ""; - - if ( (account = mail_config_get_account_by_source_url (uri)) ) - key = g_strdup_printf ("%s/%s", account->uid, path); - else if (CAMEL_IS_VEE_STORE (store)) - key = g_strdup_printf ("vfolder/%s", path); - else - key = g_strdup_printf ("local/%s", path); - } - - if (url) - camel_url_free (url); - - if (store) - camel_object_unref (store); - - return key; -} - -/** - * em_folder_tree_model_get_expanded_uri - * Same as @ref em_folder_tree_model_get_expanded, but here we use uri, not key for node. - **/ gboolean -em_folder_tree_model_get_expanded_uri (EMFolderTreeModel *model, const gchar *uri) +em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, + CamelStore *store, + const gchar *full) { - gchar *key; - gboolean expanded; - - g_return_val_if_fail (model != NULL, FALSE); - g_return_val_if_fail (uri != NULL, FALSE); - - key = emftm_uri_to_key (uri); - expanded = key && em_folder_tree_model_get_expanded (model, key); - - g_free (key); - - return expanded; -} - -/** - * em_folder_tree_model_set_expanded_uri - * Same as @ref em_folder_tree_model_set_expanded, but here we use uri, not key for node. - **/ -void -em_folder_tree_model_set_expanded_uri (EMFolderTreeModel *model, const gchar *uri, gboolean expanded) -{ - gchar *key; - - g_return_if_fail (model != NULL); - g_return_if_fail (uri != NULL); - - key = emftm_uri_to_key (uri); - if (key) - em_folder_tree_model_set_expanded (model, key, expanded); - - g_free (key); -} - -void -em_folder_tree_model_save_state (EMFolderTreeModel *model) -{ - gchar *dirname; - - if (model->state == NULL) - return; - - dirname = g_path_get_dirname (model->filename); - if (g_mkdir_with_parents (dirname, 0777) == -1 && errno != EEXIST) { - g_free (dirname); - return; - } - - g_free (dirname); - - e_xml_save_file (model->filename, model->state); -} - -static void -expand_foreach_r (EMFolderTreeModel *model, xmlNodePtr parent, const gchar *dirname, EMFTModelExpandFunc func, gpointer user_data) -{ - xmlNodePtr node = parent->children; - gchar *path, *name, *expand; - - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "node")) { - name = (gchar *)xmlGetProp (node, (const guchar *)"name"); - expand = (gchar *)xmlGetProp (node, (const guchar *)"expand"); - - if (expand && name && !strcmp ((gchar *)expand, "true")) { - if (dirname) - path = g_strdup_printf ("%s/%s", dirname, name); - else - path = g_strdup (name); - - func (model, path, user_data); - if (node->children) - expand_foreach_r (model, node, path, func, user_data); - g_free (path); - } - - xmlFree (expand); - xmlFree (name); - } - - node = node->next; - } -} - -void -em_folder_tree_model_expand_foreach (EMFolderTreeModel *model, EMFTModelExpandFunc func, gpointer user_data) -{ - xmlNodePtr root; - - root = model->state ? model->state->children : NULL; - if (!root || !root->children || strcmp ((gchar *)root->name, "tree-state") != 0) - return; - - expand_foreach_r (model, root, NULL, func, user_data); -} - -gboolean -em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, CamelStore *store, const gchar *full) -{ - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; - GtkTreePath *tree_path; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; + GtkTreePath *path; GtkTreeIter iter; guint32 flags; @@ -1271,40 +1129,33 @@ em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model, CamelStore *store, g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE); g_return_val_if_fail (full != NULL, FALSE); - u(printf("Checking if the folder is an INBOX type %p '%s' %d\n", store, full, unread)); - - if (!(si = g_hash_table_lookup (model->store_hash, store))) { - u(printf(" can't find store\n")); + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) return FALSE; - } - if (!(row = g_hash_table_lookup (si->full_hash, full))) { - u(printf(" can't find row\n")); + reference = g_hash_table_lookup (si->full_hash, full); + if (!gtk_tree_row_reference_valid (reference)) return FALSE; - } - tree_path = gtk_tree_row_reference_get_path (row); - if (!gtk_tree_model_get_iter ((GtkTreeModel *) model, &iter, tree_path)) { - gtk_tree_path_free (tree_path); - return FALSE; - } - - gtk_tree_path_free (tree_path); - - gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, COL_UINT_FLAGS, &flags, -1); + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); + gtk_tree_path_free (path); - if ((flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX) - return TRUE; + gtk_tree_model_get ( + GTK_TREE_MODEL (model), &iter, + COL_UINT_FLAGS, &flags, -1); - return FALSE; + return ((flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX); } gchar * -em_folder_tree_model_get_folder_name (EMFolderTreeModel *model, CamelStore *store, const gchar *full) +em_folder_tree_model_get_folder_name (EMFolderTreeModel *model, + CamelStore *store, + const gchar *full) { - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; - GtkTreePath *tree_path; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; + GtkTreePath *path; GtkTreeIter iter; gchar *name = NULL; @@ -1312,140 +1163,100 @@ em_folder_tree_model_get_folder_name (EMFolderTreeModel *model, CamelStore *stor g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); g_return_val_if_fail (full != NULL, NULL); - if (!(si = g_hash_table_lookup (model->store_hash, store))) { - u(printf(" can't find store\n")); + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) return NULL; - } - if (!(row = g_hash_table_lookup (si->full_hash, full))) { - u(printf(" can't find row\n")); + reference = g_hash_table_lookup (si->full_hash, full); + if (!gtk_tree_row_reference_valid (reference)) return NULL; - } - tree_path = gtk_tree_row_reference_get_path (row); - if (!gtk_tree_model_get_iter ((GtkTreeModel *) model, &iter, tree_path)) { - gtk_tree_path_free (tree_path); - return NULL; - } - - gtk_tree_path_free (tree_path); + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); + gtk_tree_path_free (path); - gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, COL_STRING_DISPLAY_NAME, &name, -1); + gtk_tree_model_get ( + GTK_TREE_MODEL (model), &iter, + COL_STRING_DISPLAY_NAME, &name, -1); return name; } void -em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, CamelStore *store, const gchar *full, gint unread) +em_folder_tree_model_set_unread_count (EMFolderTreeModel *model, + CamelStore *store, + const gchar *full, + gint unread) { - struct _EMFolderTreeModelStoreInfo *si; - GtkTreeRowReference *row; - GtkTreePath *tree_path; + EMFolderTreeModelStoreInfo *si; + GtkTreeRowReference *reference; + GtkTreeModel *tree_model; + GtkTreePath *path; + GtkTreeIter parent; GtkTreeIter iter; guint old_unread = 0; - gchar *uri, *sel_uri; g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model)); g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (full != NULL); - u(printf("set unread count %p '%s' %d\n", store, full, unread)); - if (unread < 0) return; - if (!(si = g_hash_table_lookup (model->store_hash, store))) { - u(printf(" can't find store\n")); + si = em_folder_tree_model_lookup_store_info (model, store); + if (si == NULL) return; - } - if (!(row = g_hash_table_lookup (si->full_hash, full))) { - u(printf(" can't find row\n")); + reference = g_hash_table_lookup (si->full_hash, full); + if (!gtk_tree_row_reference_valid (reference)) return; - } - tree_path = gtk_tree_row_reference_get_path (row); - if (!gtk_tree_model_get_iter ((GtkTreeModel *) model, &iter, tree_path)) { - gtk_tree_path_free (tree_path); - return; - } + tree_model = GTK_TREE_MODEL (model); - gtk_tree_path_free (tree_path); + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (tree_model, &iter, path); + gtk_tree_path_free (path); - sel_uri = em_folder_tree_model_get_selected (model); gtk_tree_model_get ( - GTK_TREE_MODEL (model), &iter, - COL_UINT_UNREAD_LAST_SEL, &old_unread, - COL_STRING_URI, &uri, -1); - if (!(g_strcmp0 (sel_uri, uri) != 0 && unread > old_unread)) - old_unread = unread; + tree_model, &iter, + COL_UINT_UNREAD_LAST_SEL, &old_unread, -1); + gtk_tree_store_set ( GTK_TREE_STORE (model), &iter, COL_UINT_UNREAD, unread, - COL_UINT_UNREAD_LAST_SEL, old_unread, -1); - - g_free (uri); - g_free (sel_uri); + COL_UINT_UNREAD_LAST_SEL, MIN (old_unread, unread), -1); - /* May be this is from where we should propagate unread count to parents etc. */ - emft_model_unread_count_changed (GTK_TREE_MODEL (model), &iter); + /* Folders are displayed with a bold weight to indicate that + * they contain unread messages. We signal that parent rows + * have changed here to update them. */ + while (gtk_tree_model_iter_parent (tree_model, &parent, &iter)) { + path = gtk_tree_model_get_path (tree_model, &parent); + gtk_tree_model_row_changed (tree_model, path, &parent); + gtk_tree_path_free (path); + iter = parent; + } } -gchar * -em_folder_tree_model_get_selected (EMFolderTreeModel *model) +EMFolderTreeModelStoreInfo * +em_folder_tree_model_lookup_store_info (EMFolderTreeModel *model, + CamelStore *store) { - xmlNodePtr node; - gchar *buf, *uri; - - node = model->state ? model->state->children : NULL; - if (!node || strcmp ((gchar *)node->name, "tree-state") != 0) - return NULL; - - node = node->children; - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "selected")) - break; - node = node->next; - } - - if (node == NULL) - return NULL; - - buf = (gchar *)xmlGetProp (node, (guchar *)"uri"); - uri = g_strdup (buf); - xmlFree (buf); + g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL); + g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); - if (uri && !*uri) { - g_free (uri); - return NULL; - } - return uri; + return g_hash_table_lookup (model->priv->store_index, store); } -void -em_folder_tree_model_set_selected (EMFolderTreeModel *model, const gchar *uri) +GtkTreeRowReference * +em_folder_tree_model_lookup_uri (EMFolderTreeModel *model, + const gchar *uri) { - xmlNodePtr root, node; - - if (model->state == NULL) - model->state = xmlNewDoc ((guchar *)"1.0"); + GtkTreeRowReference *reference; - if (!model->state->children) { - root = xmlNewDocNode (model->state, NULL, (const guchar *)"tree-state", NULL); - xmlDocSetRootElement (model->state, root); - } else { - root = model->state->children; - } - - node = root->children; - while (node != NULL) { - if (!strcmp ((gchar *)node->name, "selected")) - break; - node = node->next; - } + g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL); + g_return_val_if_fail (uri != NULL, NULL); - if (node == NULL) - node = xmlNewChild (root, NULL, (const guchar *)"selected", NULL); + reference = g_hash_table_lookup (model->priv->uri_index, uri); - xmlSetProp (node, (const guchar *)"uri", (guchar *)uri); + return gtk_tree_row_reference_valid (reference) ? reference : NULL; } |