aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2010-06-17 12:12:04 +0800
committerMatthew Barnes <mbarnes@redhat.com>2010-06-17 12:12:04 +0800
commit8dc6fd1344965e5bd0940e99be3af9be26e3f9bb (patch)
treea2215142e0a57bf579c099471c01f77b6b9eb358
parent53e86fe162ba90c9457ea63ce1c595e947bd93d3 (diff)
downloadgsoc2013-evolution-8dc6fd1344965e5bd0940e99be3af9be26e3f9bb.tar.gz
gsoc2013-evolution-8dc6fd1344965e5bd0940e99be3af9be26e3f9bb.tar.zst
gsoc2013-evolution-8dc6fd1344965e5bd0940e99be3af9be26e3f9bb.zip
Bug 621839 - Improve auto-selection of messages
-rw-r--r--mail/e-mail-reader.c2
-rw-r--r--mail/message-list.c163
-rw-r--r--mail/message-list.h4
-rw-r--r--modules/mail/e-mail-shell-content.c27
4 files changed, 145 insertions, 51 deletions
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 9c300fc411..502ee0d2d7 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -2111,7 +2111,7 @@ mail_reader_set_message (EMailReader *reader,
message_list = e_mail_reader_get_message_list (reader);
- message_list_select_uid (MESSAGE_LIST (message_list), uid);
+ message_list_select_uid (MESSAGE_LIST (message_list), uid, FALSE);
}
static void
diff --git a/mail/message-list.c b/mail/message-list.c
index 29776a221f..af5cd4bd5a 100644
--- a/mail/message-list.c
+++ b/mail/message-list.c
@@ -114,6 +114,12 @@ struct _MessageListPrivate {
GtkTargetList *copy_target_list;
GtkTargetList *paste_target_list;
+
+ /* This aids in automatic message selection. */
+ time_t latest_read_date;
+ const gchar *latest_read_uid;
+ time_t latest_unread_date;
+ const gchar *latest_unread_uid;
};
enum {
@@ -667,19 +673,47 @@ message_list_can_select(MessageList *ml, MessageListSelectDirection direction, g
* Selects the message with the given UID.
**/
void
-message_list_select_uid (MessageList *message_list, const gchar *uid)
+message_list_select_uid (MessageList *message_list,
+ const gchar *uid,
+ gboolean with_fallback)
{
- ETreePath node;
+ MessageListPrivate *priv;
+ GHashTable *uid_nodemap;
+ ETreePath node = NULL;
+
+ g_return_if_fail (IS_MESSAGE_LIST (message_list));
+
+ priv = message_list->priv;
+ uid_nodemap = message_list->uid_nodemap;
if (message_list->folder == NULL)
return;
+ /* Try to find the requested message UID. */
+ if (uid != NULL)
+ node = g_hash_table_lookup (uid_nodemap, uid);
+
+ /* If we're busy or waiting to regenerate the message list, cache
+ * the UID so we can try again when we're done. Otherwise if the
+ * requested message UID was not found and 'with_fallback' is set,
+ * try a couple fallbacks:
+ *
+ * 1) Most recently received unread message in the list.
+ * 2) Most recently received read message in the list.
+ */
if (message_list->regen || message_list->regen_timeout_id) {
- g_free(message_list->pending_select_uid);
- message_list->pending_select_uid = g_strdup(uid);
+ g_free (message_list->pending_select_uid);
+ message_list->pending_select_uid = g_strdup (uid);
+ message_list->pending_select_fallback = with_fallback;
+ } else if (with_fallback) {
+ if (node == NULL && priv->latest_unread_uid != NULL)
+ node = g_hash_table_lookup (
+ uid_nodemap, priv->latest_unread_uid);
+ if (node == NULL && priv->latest_read_uid != NULL)
+ node = g_hash_table_lookup (
+ uid_nodemap, priv->latest_read_uid);
}
- node = g_hash_table_lookup (message_list->uid_nodemap, uid);
if (node) {
ETree *tree;
ETreePath old_cur;
@@ -691,11 +725,17 @@ message_list_select_uid (MessageList *message_list, const gchar *uid)
e_tree_set_cursor (tree, node);
if (old_cur == node)
- g_signal_emit (message_list, message_list_signals[MESSAGE_SELECTED], 0, message_list->cursor_uid);
+ g_signal_emit (
+ message_list,
+ message_list_signals[MESSAGE_SELECTED],
+ 0, message_list->cursor_uid);
} else {
g_free (message_list->cursor_uid);
message_list->cursor_uid = NULL;
- g_signal_emit (GTK_OBJECT (message_list), message_list_signals[MESSAGE_SELECTED], 0, NULL);
+ g_signal_emit (
+ message_list,
+ message_list_signals[MESSAGE_SELECTED],
+ 0, NULL);
}
}
@@ -2836,6 +2876,11 @@ clear_tree (MessageList *ml, gboolean tfree)
g_hash_table_destroy (ml->uid_nodemap);
ml->uid_nodemap = g_hash_table_new (g_str_hash, g_str_equal);
+ ml->priv->latest_read_date = 0;
+ ml->priv->latest_read_uid = NULL;
+ ml->priv->latest_unread_date = 0;
+ ml->priv->latest_unread_uid = NULL;
+
if (ml->tree_root) {
/* we should be frozen already */
e_tree_memory_node_remove (E_TREE_MEMORY(etm), ml->tree_root);
@@ -2970,6 +3015,70 @@ find_next_selectable (MessageList *ml)
return NULL;
}
+static ETreePath *
+ml_uid_nodemap_insert (MessageList *message_list,
+ CamelMessageInfo *info,
+ ETreePath *parent_node,
+ gint row)
+{
+ ETreeMemory *tree;
+ ETreePath *node;
+ const gchar *uid;
+ time_t date;
+ guint flags;
+
+ if (parent_node == NULL)
+ parent_node = message_list->tree_root;
+
+ tree = E_TREE_MEMORY (message_list->model);
+ node = e_tree_memory_node_insert (tree, parent_node, row, info);
+
+ uid = camel_message_info_uid (info);
+ flags = camel_message_info_flags (info);
+ date = camel_message_info_date_received (info);
+
+ camel_folder_ref_message_info (message_list->folder, info);
+ g_hash_table_insert (message_list->uid_nodemap, (gpointer) uid, node);
+
+ /* Track the latest seen and unseen messages shown, used in
+ * fallback heuristics for automatic message selection. */
+ if (flags & CAMEL_MESSAGE_SEEN) {
+ if (date > message_list->priv->latest_read_date) {
+ message_list->priv->latest_read_date = date;
+ message_list->priv->latest_read_uid = uid;
+ }
+ } else {
+ if (date > message_list->priv->latest_unread_date) {
+ message_list->priv->latest_unread_date = date;
+ message_list->priv->latest_unread_uid = uid;
+ }
+ }
+
+ return node;
+}
+
+static void
+ml_uid_nodemap_remove (MessageList *message_list,
+ CamelMessageInfo *info)
+{
+ const gchar *uid;
+
+ uid = camel_message_info_uid (info);
+
+ if (uid == message_list->priv->latest_read_uid) {
+ message_list->priv->latest_read_date = 0;
+ message_list->priv->latest_read_uid = NULL;
+ }
+
+ if (uid == message_list->priv->latest_unread_uid) {
+ message_list->priv->latest_unread_date = 0;
+ message_list->priv->latest_unread_uid = NULL;
+ }
+
+ g_hash_table_remove (message_list->uid_nodemap, uid);
+ camel_folder_free_message_info (message_list->folder, info);
+}
+
/* only call if we have a tree model */
/* builds the tree structure */
@@ -3067,7 +3176,6 @@ build_tree (MessageList *ml, CamelFolderThread *thread, CamelFolderChangeInfo *c
static void
build_subtree (MessageList *ml, ETreePath parent, CamelFolderThreadNode *c, gint *row)
{
- ETreeModel *tree = ml->model;
ETreePath node;
while (c) {
@@ -3078,9 +3186,8 @@ build_subtree (MessageList *ml, ETreePath parent, CamelFolderThreadNode *c, gint
continue;
}
- node = e_tree_memory_node_insert(E_TREE_MEMORY(tree), parent, -1, (gpointer)c->message);
- g_hash_table_insert(ml->uid_nodemap, (gpointer)camel_message_info_uid(c->message), node);
- camel_folder_ref_message_info(ml->folder, (CamelMessageInfo *)c->message);
+ node = ml_uid_nodemap_insert (
+ ml, (CamelMessageInfo *) c->message, parent, -1);
if (c->child) {
build_subtree(ml, node, c->child, row);
@@ -3149,16 +3256,17 @@ tree_equal(ETreeModel *etm, ETreePath ap, CamelFolderThreadNode *bp)
static void
add_node_diff(MessageList *ml, ETreePath parent, ETreePath path, CamelFolderThreadNode *c, gint *row, gint myrow)
{
- ETreeModel *etm = ml->model;
+ CamelMessageInfo *info;
ETreePath node;
g_return_if_fail (c->message != NULL);
- /* we just update the hashtable key, umm, does this leak the info on the message node? */
- g_hash_table_remove(ml->uid_nodemap, camel_message_info_uid(c->message));
- node = e_tree_memory_node_insert(E_TREE_MEMORY(etm), parent, myrow, (gpointer)c->message);
- g_hash_table_insert(ml->uid_nodemap, (gpointer)camel_message_info_uid(c->message), node);
- camel_folder_ref_message_info(ml->folder, (CamelMessageInfo *)c->message);
+ /* XXX Casting away constness. */
+ info = (CamelMessageInfo *) c->message;
+
+ /* we just update the hashtable key */
+ ml_uid_nodemap_remove (ml, info);
+ node = ml_uid_nodemap_insert (ml, info, parent, myrow);
(*row)++;
if (c->child) {
@@ -3192,8 +3300,7 @@ remove_node_diff(MessageList *ml, ETreePath node, gint depth)
e_tree_memory_node_remove(E_TREE_MEMORY(etm), node);
g_return_if_fail (info);
- g_hash_table_remove(ml->uid_nodemap, camel_message_info_uid(info));
- camel_folder_free_message_info(ml->folder, info);
+ ml_uid_nodemap_remove (ml, info);
}
/* applies a new tree structure to an existing tree, but only by changing things
@@ -3341,12 +3448,9 @@ build_flat (MessageList *ml, GPtrArray *summary, CamelFolderChangeInfo *changes)
e_tree_memory_freeze(E_TREE_MEMORY(etm));
clear_tree (ml, FALSE);
for (i = 0; i < summary->len; i++) {
- ETreePath node;
CamelMessageInfo *info = summary->pdata[i];
- node = e_tree_memory_node_insert(E_TREE_MEMORY(etm), ml->tree_root, -1, info);
- g_hash_table_insert(ml->uid_nodemap, (gpointer)camel_message_info_uid(info), node);
- camel_folder_ref_message_info(ml->folder, info);
+ ml_uid_nodemap_insert (ml, info, NULL, -1);
}
e_tree_memory_thaw(E_TREE_MEMORY(etm));
#ifdef BROKEN_ETREE
@@ -3418,8 +3522,7 @@ build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes)
if (node) {
info = e_tree_memory_node_get_data(E_TREE_MEMORY(ml->model), node);
e_tree_memory_node_remove(E_TREE_MEMORY(ml->model), node);
- camel_folder_free_message_info(ml->folder, info);
- g_hash_table_remove(ml->uid_nodemap, changes->uid_removed->pdata[i]);
+ ml_uid_nodemap_remove (ml, info);
}
}
@@ -3429,8 +3532,7 @@ build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes)
info = camel_folder_get_message_info (ml->folder, changes->uid_added->pdata[i]);
if (info) {
d(printf(" %s\n", (gchar *)changes->uid_added->pdata[i]));
- node = e_tree_memory_node_insert (E_TREE_MEMORY (ml->model), ml->tree_root, -1, info);
- g_hash_table_insert (ml->uid_nodemap, (gpointer)camel_message_info_uid (info), node);
+ ml_uid_nodemap_insert (ml, info, NULL, -1);
}
}
@@ -4432,10 +4534,13 @@ regen_list_done (struct _regen_list_msg *m)
g_mutex_unlock (m->ml->regen_lock);
if (m->ml->regen == NULL && m->ml->pending_select_uid) {
- gchar *uid = m->ml->pending_select_uid;
+ gchar *uid;
+ gboolean with_fallback;
+ uid = m->ml->pending_select_uid;
m->ml->pending_select_uid = NULL;
- message_list_select_uid(m->ml, uid);
+ with_fallback = m->ml->pending_select_fallback;
+ message_list_select_uid (m->ml, uid, with_fallback);
g_free(uid);
} else if (m->ml->regen == NULL && m->ml->cursor_uid == NULL && m->last_row != -1) {
ETreeTableAdapter *etta = e_tree_get_table_adapter (tree);
diff --git a/mail/message-list.h b/mail/message-list.h
index 0b46e8ecd7..bc2bc0627b 100644
--- a/mail/message-list.h
+++ b/mail/message-list.h
@@ -137,6 +137,7 @@ struct _MessageList {
GList *regen;
GMutex *regen_lock; /* when writing to the regen, guard with this lock too */
gchar *pending_select_uid; /* set if we were busy regnerating while we had a select come in */
+ gboolean pending_select_fallback;
guint regen_timeout_id;
gpointer regen_timeout_msg;
@@ -191,7 +192,8 @@ gboolean message_list_can_select (MessageList *message_list,
guint32 flags,
guint32 mask);
void message_list_select_uid (MessageList *message_list,
- const gchar *uid);
+ const gchar *uid,
+ gboolean with_fallback);
void message_list_select_next_thread (MessageList *message_list);
void message_list_select_all (MessageList *message_list);
void message_list_select_thread (MessageList *message_list);
diff --git a/modules/mail/e-mail-shell-content.c b/modules/mail/e-mail-shell-content.c
index 135888d782..340e323da1 100644
--- a/modules/mail/e-mail-shell-content.c
+++ b/modules/mail/e-mail-shell-content.c
@@ -123,7 +123,6 @@ mail_shell_content_message_list_built_cb (EMailShellContent *mail_shell_content,
EShellWindow *shell_window;
EShellContent *shell_content;
GKeyFile *key_file;
- gchar *uid;
g_signal_handler_disconnect (
message_list, priv->message_list_built_id);
@@ -136,40 +135,28 @@ mail_shell_content_message_list_built_cb (EMailShellContent *mail_shell_content,
key_file = e_shell_view_get_state_key_file (shell_view);
if (message_list->cursor_uid != NULL)
- uid = NULL;
+ ; /* do nothing */
else if (message_list->folder_uri == NULL)
- uid = NULL;
+ ; /* do nothing */
- else if (e_shell_window_get_safe_mode (shell_window)) {
+ else if (e_shell_window_get_safe_mode (shell_window))
e_shell_window_set_safe_mode (shell_window, FALSE);
- uid = NULL;
- } else {
+ else {
const gchar *folder_uri;
const gchar *key;
gchar *group_name;
+ gchar *uid;
key = STATE_KEY_SELECTED_MESSAGE;
folder_uri = message_list->folder_uri;
group_name = g_strdup_printf ("Folder %s", folder_uri);
uid = g_key_file_get_string (key_file, group_name, key, NULL);
g_free (group_name);
- }
-
- if (uid != NULL) {
- CamelFolder *folder;
- CamelMessageInfo *info;
- folder = message_list->folder;
- info = camel_folder_get_message_info (folder, uid);
- if (info != NULL) {
- EMailReader *reader;
-
- reader = E_MAIL_READER (mail_shell_content);
- e_mail_reader_set_message (reader, uid);
- camel_folder_free_message_info (folder, info);
- }
+ /* Use selection fallbacks if UID is not found. */
+ message_list_select_uid (message_list, uid, TRUE);
g_free (uid);
}