aboutsummaryrefslogtreecommitdiffstats
path: root/mail/message-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/message-list.c')
-rw-r--r--mail/message-list.c291
1 files changed, 278 insertions, 13 deletions
diff --git a/mail/message-list.c b/mail/message-list.c
index 80d521957c..3d67b798c6 100644
--- a/mail/message-list.c
+++ b/mail/message-list.c
@@ -101,8 +101,11 @@ static void save_tree_state(MessageList *ml);
static void folder_changed (CamelObject *o, gpointer event_data, gpointer user_data);
static void message_changed (CamelObject *o, gpointer event_data, gpointer user_data);
+static void hide_save_state(MessageList *ml);
+static void hide_load_state(MessageList *ml);
+
/* note: @changes is owned/freed by the caller */
-static void mail_do_regenerate_messagelist (MessageList *list, const gchar *search, CamelFolderChangeInfo *changes);
+static void mail_do_regenerate_messagelist (MessageList *list, const char *search, const char *hideexpr, CamelFolderChangeInfo *changes);
/* macros for working with id's (stored in the tree nodes) */
#define id_is_uid(id) (id[0] == 'u')/* is this a uid id? */
@@ -1086,6 +1089,10 @@ message_list_init (GtkObject *object)
message_list->uid_rowmap = g_hash_table_new (g_str_hash, g_str_equal);
message_list->uid_pool = e_mempool_new(1024, 512, E_MEMPOOL_ALIGN_BYTE);
+ message_list->hidden = NULL;
+ message_list->hidden_pool = NULL;
+ message_list->hide_before = ML_HIDE_NONE_START;
+ message_list->hide_after = ML_HIDE_NONE_END;
}
static void
@@ -1096,6 +1103,7 @@ message_list_destroy (GtkObject *object)
if (message_list->folder) {
save_tree_state(message_list);
save_header_state(message_list);
+ hide_save_state(message_list);
}
gtk_object_unref (GTK_OBJECT (message_list->table_model));
@@ -1109,6 +1117,7 @@ message_list_destroy (GtkObject *object)
if (message_list->seen_id)
gtk_timeout_remove (message_list->seen_id);
+ mail_tool_camel_lock_up();
if (message_list->folder) {
camel_object_unhook_event((CamelObject *)message_list->folder, "folder_changed",
folder_changed, message_list);
@@ -1116,6 +1125,14 @@ message_list_destroy (GtkObject *object)
message_changed, message_list);
camel_object_unref (CAMEL_OBJECT (message_list->folder));
}
+
+ if (message_list->hidden) {
+ g_hash_table_destroy(message_list->hidden);
+ e_mempool_destroy(message_list->hidden_pool);
+ message_list->hidden = NULL;
+ message_list->hidden_pool = NULL;
+ }
+ mail_tool_camel_lock_down();
GTK_OBJECT_CLASS (message_list_parent_class)->destroy (object);
}
@@ -1310,6 +1327,7 @@ save_tree_state(MessageList *ml)
ETreePath *node;
ETreePath *child;
FILE *out;
+ int rem;
filename = mail_config_folder_to_cachename(ml->folder, "treestate-");
out = fopen(filename, "w");
@@ -1319,7 +1337,11 @@ save_tree_state(MessageList *ml)
if (node && child) {
save_node_state(ml, out, child);
}
+ rem = ftell(out) == 0;
fclose(out);
+ /* remove the file if it was empty, should probably check first, but this is easier */
+ if (rem)
+ unlink(filename);
}
g_free(filename);
}
@@ -1371,17 +1393,22 @@ build_tree (MessageList *ml, CamelFolderThread *thread, CamelFolderChangeInfo *c
ml->tree_root = e_tree_model_node_insert(etm, NULL, 0, NULL);
e_tree_model_node_set_expanded(etm, ml->tree_root, TRUE);
}
-
+
+#define BROKEN_ETREE /* avoid some broken code in etree(?) by not using the incremental update */
+
top = e_tree_model_node_get_first_child(etm, ml->tree_root);
+#ifndef BROKEN_ETREE
if (top == NULL || changes == NULL) {
+#endif
e_tree_model_freeze(etm);
clear_tree (ml);
build_subtree(ml, ml->tree_root, thread->tree, &row, expanded_nodes);
e_tree_model_thaw(etm);
+#ifndef BROKEN_ETREE
} else {
build_subtree_diff(ml, ml->tree_root, top, thread->tree, &row, expanded_nodes);
}
-
+#endif
free_tree_state(expanded_nodes);
#ifdef TIMEIT
@@ -1683,9 +1710,11 @@ build_flat (MessageList *ml, GPtrArray *uids, CamelFolderChangeInfo *changes)
gettimeofday(&start, NULL);
#endif
+#ifndef BROKEN_ETREE
if (changes) {
build_flat_diff(ml, changes);
} else {
+#endif
e_tree_model_freeze(tree);
clear_tree (ml);
for (i = 0; i < uids->len; i++) {
@@ -1694,7 +1723,9 @@ build_flat (MessageList *ml, GPtrArray *uids, CamelFolderChangeInfo *changes)
g_hash_table_insert (ml->uid_rowmap, id_uid(uid), GINT_TO_POINTER (i));
}
e_tree_model_thaw(tree);
+#ifndef BROKEN_ETREE
}
+#endif
#ifdef TIMEIT
gettimeofday(&end, NULL);
@@ -1836,7 +1867,7 @@ main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data)
printf("folder changed event, changes = %p\n", changes);
- mail_do_regenerate_messagelist(ml, ml->search, changes);
+ mail_do_regenerate_messagelist(ml, ml->search, NULL, changes);
}
static void
@@ -1896,6 +1927,7 @@ message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder)
camel_exception_init (&ex);
if (message_list->folder) {
+ hide_save_state(message_list);
camel_object_unhook_event((CamelObject *)message_list->folder, "folder_changed",
folder_changed, message_list);
camel_object_unhook_event((CamelObject *)message_list->folder, "message_changed",
@@ -1915,8 +1947,10 @@ message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder)
camel_object_ref (CAMEL_OBJECT (camel_folder));
+ hide_load_state(message_list);
+
clear_tree(message_list);
- mail_do_regenerate_messagelist (message_list, message_list->search, NULL);
+ mail_do_regenerate_messagelist (message_list, message_list->search, NULL, NULL);
}
E_MAKE_TYPE (message_list, "MessageList", MessageList, message_list_class_init, message_list_init, PARENT_TYPE);
@@ -2024,7 +2058,7 @@ message_list_set_threaded(MessageList *ml, gboolean threaded)
if (ml->threaded ^ threaded) {
ml->threaded = threaded;
- mail_do_regenerate_messagelist(ml, ml->search, NULL);
+ mail_do_regenerate_messagelist(ml, ml->search, NULL, NULL);
}
}
@@ -2038,7 +2072,155 @@ message_list_set_search(MessageList *ml, const char *search)
if (search != NULL && ml->search !=NULL && strcmp(search, ml->search)==0)
return;
- mail_do_regenerate_messagelist(ml, search, NULL);
+ mail_do_regenerate_messagelist(ml, search, NULL, NULL);
+}
+
+/* returns the number of messages displayable *after* expression hiding has taken place */
+unsigned int message_list_length(MessageList *ml)
+{
+ return ml->hide_unhidden;
+}
+
+/* add a new expression to hide, or set the range.
+ @expr: A new search expression - all matching messages will be hidden. May be %NULL.
+ @lower: Use ML_HIDE_NONE_START to specify no messages hidden from the start of the list.
+ @upper: Use ML_HIDE_NONE_END to specify no message hidden from the end of the list.
+
+ For either @upper or @lower, use ML_HIDE_SAME, to keep the previously set hide range.
+ If either range is negative, then the range is taken from the end of the available list
+ of messages, once other hiding has been performed. Use message_list_length() to find out
+ how many messages are available for hiding.
+
+ Example: hide_add(ml, NULL, -100, ML_HIDE_NONE_END) -> hide all but the last (most recent)
+ 100 messages.
+*/
+void message_list_hide_add(MessageList *ml, const char *expr, unsigned int lower, unsigned int upper)
+{
+ mail_tool_camel_lock_up();
+ if (lower != ML_HIDE_SAME)
+ ml->hide_before = lower;
+ if (upper != ML_HIDE_SAME)
+ ml->hide_after = upper;
+ mail_tool_camel_lock_down();
+
+ mail_do_regenerate_messagelist(ml, ml->search, expr, NULL);
+}
+
+/* hide specific uid's */
+void message_list_hide_uids(MessageList *ml, GPtrArray *uids)
+{
+ int i;
+ char *uid;
+
+ /* first see if we need to do any work, if so, then do it all at once */
+ for (i=0;i<uids->len;i++) {
+ if (g_hash_table_lookup(ml->uid_rowmap, uids->pdata[i])) {
+ mail_tool_camel_lock_up();
+ if (ml->hidden == NULL) {
+ ml->hidden = g_hash_table_new(g_str_hash, g_str_equal);
+ ml->hidden_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
+ }
+
+ uid = e_mempool_strdup(ml->hidden_pool, uids->pdata[i]);
+ g_hash_table_insert(ml->hidden, uid, uid);
+ for (;i<uids->len;i++) {
+ if (g_hash_table_lookup(ml->uid_rowmap, uids->pdata[i])) {
+ uid = e_mempool_strdup(ml->hidden_pool, uids->pdata[i]);
+ g_hash_table_insert(ml->hidden, uid, uid);
+ }
+ }
+ mail_tool_camel_lock_down();
+ mail_do_regenerate_messagelist(ml, ml->search, NULL, NULL);
+ break;
+ }
+ }
+}
+
+/* no longer hide any messages */
+void message_list_hide_clear(MessageList *ml)
+{
+ mail_tool_camel_lock_up();
+ if (ml->hidden) {
+ g_hash_table_destroy(ml->hidden);
+ e_mempool_destroy(ml->hidden_pool);
+ ml->hidden = NULL;
+ ml->hidden_pool = NULL;
+ }
+ ml->hide_before = ML_HIDE_NONE_START;
+ ml->hide_after = ML_HIDE_NONE_END;
+ mail_tool_camel_lock_down();
+
+ mail_do_regenerate_messagelist(ml, ml->search, NULL, NULL);
+}
+
+#define HIDE_STATE_VERSION (1)
+
+/* version 1 file is:
+ uintf 1
+ uintf hide_before
+ uintf hide_after
+ string* uids
+*/
+
+static void hide_load_state(MessageList *ml)
+{
+ char *filename;
+ FILE *in;
+ guint32 version, lower, upper;
+
+ filename = mail_config_folder_to_cachename(ml->folder, "hidestate-");
+ in = fopen(filename, "r");
+ if (in) {
+ camel_folder_summary_decode_fixed_int32(in, &version);
+ if (version == HIDE_STATE_VERSION) {
+ mail_tool_camel_lock_up();
+ if (ml->hidden == NULL) {
+ ml->hidden = g_hash_table_new(g_str_hash, g_str_equal);
+ ml->hidden_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
+ }
+ camel_folder_summary_decode_fixed_int32(in, &lower);
+ ml->hide_before = lower;
+ camel_folder_summary_decode_fixed_int32(in, &upper);
+ ml->hide_after = upper;
+ while (!feof(in)) {
+ char *olduid, *uid;
+
+ if (camel_folder_summary_decode_string(in, &olduid) != -1) {
+ uid = e_mempool_strdup(ml->hidden_pool, olduid);
+ g_hash_table_insert(ml->hidden, uid, uid);
+ }
+ }
+ mail_tool_camel_lock_down();
+ }
+ fclose(in);
+ }
+ g_free(filename);
+}
+
+static void hide_save_1(char *uid, char *keydata, FILE *out)
+{
+ camel_folder_summary_encode_string(out, uid);
+}
+
+/* save the hide state. Note that messages are hidden by uid, if the uid's change, then
+ this will become invalid, but is easy to reset in the ui */
+static void hide_save_state(MessageList *ml)
+{
+ char *filename;
+ FILE *out;
+
+ filename = mail_config_folder_to_cachename(ml->folder, "hidestate-");
+ mail_tool_camel_lock_up();
+ if (ml->hidden == NULL && ml->hide_before == ML_HIDE_NONE_START && ml->hide_after == ML_HIDE_NONE_END) {
+ unlink(filename);
+ } else if ( (out = fopen(filename, "w")) ) {
+ camel_folder_summary_encode_fixed_int32(out, HIDE_STATE_VERSION);
+ camel_folder_summary_encode_fixed_int32(out, ml->hide_before);
+ camel_folder_summary_encode_fixed_int32(out, ml->hide_after);
+ if (ml->hidden)
+ g_hash_table_foreach(ml->hidden, (GHFunc)hide_save_1, out);
+ fclose(out);
+ }
}
/* ** REGENERATE MESSAGELIST ********************************************** */
@@ -2047,12 +2229,14 @@ typedef struct regenerate_messagelist_input_s {
MessageList *ml;
CamelFolder *folder;
char *search;
+ char *hideexpr;
CamelFolderChangeInfo *changes;
gboolean dotree; /* we are building a tree */
} regenerate_messagelist_input_t;
typedef struct regenerate_messagelist_data_s {
- GPtrArray *uids;
+ GPtrArray *uids, /* list of uid's to use, if realuids is NULL, this is the actual uid's from search, else a simple ptrarray */
+ *realuids; /* actual uid's from search/get_uid's, or NULL */
CamelFolderThread *tree;
CamelFolderChangeInfo *changes;
} regenerate_messagelist_data_t;
@@ -2087,6 +2271,8 @@ static void do_regenerate_messagelist (gpointer in_data, gpointer op_data, Camel
{
regenerate_messagelist_input_t *input = (regenerate_messagelist_input_t *) in_data;
regenerate_messagelist_data_t *data = (regenerate_messagelist_data_t *) op_data;
+ int i;
+ GPtrArray *uids, *uidnew;
mail_tool_camel_lock_up();
@@ -2101,6 +2287,75 @@ static void do_regenerate_messagelist (gpointer in_data, gpointer op_data, Camel
return;
}
+ /* see if we have a new expression to hide on */
+ if (input->hideexpr) {
+ uidnew = camel_folder_search_by_expression(input->ml->folder, input->hideexpr, ex);
+ /* well, lets not abort just because this faileld ... */
+ camel_exception_clear(ex);
+
+ if (uidnew) {
+ if (input->ml->hidden == NULL) {
+ input->ml->hidden = g_hash_table_new(g_str_hash, g_str_equal);
+ input->ml->hidden_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
+ }
+
+ for (i=0;i<uidnew->len;i++) {
+ if (g_hash_table_lookup(input->ml->hidden, uidnew->pdata[i]) == 0) {
+ char *uid = e_mempool_strdup(input->ml->hidden_pool, uidnew->pdata[i]);
+ g_hash_table_insert(input->ml->hidden, uid, uid);
+ }
+ }
+ camel_folder_search_free(input->ml->folder, uidnew);
+ }
+ }
+
+ input->ml->hide_unhidden = data->uids->len;
+
+ /* what semantics do we want from hide_before, hide_after?
+ probably <0 means measure from the end of the list */
+
+ /* perform uid hiding */
+ if (input->ml->hidden || input->ml->hide_before != ML_HIDE_NONE_START || input->ml->hide_after != ML_HIDE_NONE_END) {
+ int start, end;
+ uidnew = g_ptr_array_new();
+ uids = data->uids;
+
+ /* first, hide matches */
+ if (input->ml->hidden) {
+ for (i=0;i<uids->len;i++) {
+ if (g_hash_table_lookup(input->ml->hidden, uids->pdata[i]) == 0)
+ g_ptr_array_add(uidnew, uids->pdata[i]);
+ }
+ }
+
+ /* then calculate the subrange visible and chop it out */
+ input->ml->hide_unhidden = uidnew->len;
+
+ if (input->ml->hide_before != ML_HIDE_NONE_START || input->ml->hide_after != ML_HIDE_NONE_END) {
+ GPtrArray *uid2 = g_ptr_array_new();
+
+ start = input->ml->hide_before;
+ if (start < 0)
+ start += input->ml->hide_unhidden;
+ end = input->ml->hide_after;
+ if (end < 0)
+ end += input->ml->hide_unhidden;
+
+ start = MAX(start, 0);
+ end = MIN(end, uidnew->len);
+ for (i=start;i<end;i++) {
+ g_ptr_array_add(uid2, uidnew->pdata[i]);
+ }
+
+ g_ptr_array_free(uidnew, TRUE);
+ uidnew = uid2;
+ }
+ data->realuids = uids;
+ data->uids = uidnew;
+ } else {
+ data->realuids = NULL;
+ }
+
if (input->dotree && data->uids)
data->tree = camel_folder_thread_messages_new(input->ml->folder, data->uids);
else
@@ -2113,7 +2368,7 @@ static void cleanup_regenerate_messagelist (gpointer in_data, gpointer op_data,
{
regenerate_messagelist_input_t *input = (regenerate_messagelist_input_t *) in_data;
regenerate_messagelist_data_t *data = (regenerate_messagelist_data_t *) op_data;
-
+ GPtrArray *uids;
ETreeModel *etm;
etm = E_TREE_MODEL (input->ml->table_model);
@@ -2128,10 +2383,17 @@ static void cleanup_regenerate_messagelist (gpointer in_data, gpointer op_data,
else
build_flat(input->ml, data->uids, input->changes);
+ /* work out if we have aux uid's to free, otherwise free the real ones */
+ uids = data->realuids;
+ if (uids)
+ g_ptr_array_free(data->uids, TRUE);
+ else
+ uids = data->uids;
+
if (input->search)
- camel_folder_search_free (input->ml->folder, data->uids);
+ camel_folder_search_free (input->ml->folder, uids);
else
- camel_folder_free_uids (input->ml->folder, data->uids);
+ camel_folder_free_uids (input->ml->folder, uids);
/* update what we have as our search string */
if (input->ml->search && input->ml->search != input->search)
@@ -2141,6 +2403,8 @@ static void cleanup_regenerate_messagelist (gpointer in_data, gpointer op_data,
if (data->tree)
camel_folder_thread_messages_destroy(data->tree);
+ g_free(input->hideexpr);
+
if (input->changes)
camel_folder_change_info_free(input->changes);
@@ -2158,7 +2422,7 @@ static const mail_operation_spec op_regenerate_messagelist =
/* if changes == NULL, then update the whole list, otherwise just update the changes */
static void
-mail_do_regenerate_messagelist (MessageList *list, const gchar *search, CamelFolderChangeInfo *changes)
+mail_do_regenerate_messagelist (MessageList *list, const gchar *search, const char *hideexpr, CamelFolderChangeInfo *changes)
{
regenerate_messagelist_input_t *input;
@@ -2170,7 +2434,7 @@ mail_do_regenerate_messagelist (MessageList *list, const gchar *search, CamelFol
/* see if we need to goto the child thread at all anyway */
/* currently the only case is the flat view with updates and no search */
- if (search == NULL && changes != NULL && !list->threaded) {
+ if (hideexpr == NULL && search == NULL && changes != NULL && !list->threaded) {
build_flat_diff(list, changes);
camel_folder_change_info_free(changes);
return;
@@ -2179,6 +2443,7 @@ mail_do_regenerate_messagelist (MessageList *list, const gchar *search, CamelFol
input = g_new (regenerate_messagelist_input_t, 1);
input->ml = list;
input->search = g_strdup (search);
+ input->hideexpr = g_strdup(hideexpr);
input->changes = changes;
input->dotree = list->threaded;