diff options
-rw-r--r-- | camel/ChangeLog | 25 | ||||
-rw-r--r-- | camel/camel-folder-thread.c | 409 | ||||
-rw-r--r-- | camel/camel-folder-thread.h | 21 | ||||
-rw-r--r-- | camel/camel-vtrash-folder.c | 3 |
4 files changed, 278 insertions, 180 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index d27c61eb27..928f87acca 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,28 @@ +2002-06-11 Not Zed <NotZed@Ximian.com> + + * camel-vtrash-folder.c (camel_vtrash_folder_class_init): Our + parent class is camel_vee_folder, not camel_folder, fix setting of + parent folder (unused anyway, but removes a run-time warning). + +2002-06-07 Not Zed <NotZed@Ximian.com> + + * camel-pgp-context.c (camel_pgp_context_init): Duh! The protocol + is application/pgp-signature. TODO: need to have a signed and an + encrypted protocol specified separately. + +2002-06-06 Not Zed <NotZed@Ximian.com> + + * camel-folder-thread.c (thread_summary): Do the work here. + (camel_folder_thread_messages_remove): Implement. Remove some + uid's from the tree. Disalbe from build, for now. + (remove_uid_node_rec): Actually do the work. + (camel_folder_thread_messages_add): Implement. Add some summary + items to the tree. Disable from build though. + (camel_folder_thread_messages_apply): Make a thread tree built + using camel_folder_thread_new() map to a new set of uids, + preserving some order. Meant primarily to manage deleted + added + uids in a user-friendly manner. + 2002-06-09 Jeffrey Stedfast <fejj@ximian.com> * camel-multipart-signed.c (camel_multipart_signed_verify): Use diff --git a/camel/camel-folder-thread.c b/camel/camel-folder-thread.c index 2427047ed9..1401d39f21 100644 --- a/camel/camel-folder-thread.c +++ b/camel/camel-folder-thread.c @@ -425,30 +425,13 @@ static gint id_equal(void *a, void *b) return ((CamelSummaryMessageID *)a)->id.id == ((CamelSummaryMessageID *)b)->id.id; } -/** - * camel_folder_thread_messages_new: - * @folder: - * @uids: The subset of uid's to thread. If NULL. then thread all - * uid's in @folder. - * - * Thread a (subset) of the messages in a folder. And sort the result - * in summary order. - * - * This function is probably to be removed soon. - * - * Return value: A CamelFolderThread contianing a tree of CamelFolderThreadNode's - * which represent the threaded structure of the messages. - **/ -CamelFolderThread * -camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) +/* perform actual threading */ +static void +thread_summary(CamelFolderThread *thread, GPtrArray *summary) { GHashTable *id_table, *no_id_table; int i; CamelFolderThreadNode *c, *child, *head; - CamelFolderThread *thread; - GHashTable *wanted = NULL; - GPtrArray *summary; - #ifdef TIMEIT struct timeval start, end; unsigned long diff; @@ -456,29 +439,10 @@ camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) gettimeofday(&start, NULL); #endif - thread = g_malloc(sizeof(*thread)); - thread->tree = NULL; - thread->node_chunks = e_memchunk_new(32, sizeof(CamelFolderThreadNode)); - thread->folder = folder; - camel_object_ref((CamelObject *)folder); - - /* wanted is the list of what we want, we put it in a hash for quick lookup */ - if (uids) { - wanted = g_hash_table_new(g_str_hash, g_str_equal); - for (i=0;i<uids->len;i++) - g_hash_table_insert(wanted, uids->pdata[i], uids->pdata[i]); - } - - thread->summary = summary = camel_folder_get_summary(folder); - id_table = g_hash_table_new((GHashFunc)id_hash, (GCompareFunc)id_equal); no_id_table = g_hash_table_new(NULL, NULL); for (i=0;i<summary->len;i++) { CamelMessageInfo *mi = summary->pdata[i]; - const char *uid = camel_message_info_uid(mi); - - if (wanted && g_hash_table_lookup(wanted, uid) == 0) - continue; if (mi->message_id.id.id) { c = g_hash_table_lookup(id_table, &mi->message_id); @@ -588,174 +552,141 @@ camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) diff = end.tv_sec * 1000 + end.tv_usec/1000; diff -= start.tv_sec * 1000 + start.tv_usec/1000; printf("Message threading %d messages took %ld.%03ld seconds\n", - uids->len, diff / 1000, diff % 1000); + summary->len, diff / 1000, diff % 1000); #endif - - return thread; } /** - * camel_folder_thread_messages_new_summary: - * @summary: Array of CamelMessageInfo's to thread. + * camel_folder_thread_messages_new: + * @folder: + * @uids: The subset of uid's to thread. If NULL. then thread all + * uid's in @folder. * - * Thread a list of MessageInfo's. The summary must remain valid for the - * life of the CamelFolderThread created by this function, and it is upto the - * caller to ensure this. + * Thread a (subset) of the messages in a folder. And sort the result + * in summary order. * + * This function is probably to be removed soon. + * * Return value: A CamelFolderThread contianing a tree of CamelFolderThreadNode's * which represent the threaded structure of the messages. **/ CamelFolderThread * -camel_folder_thread_messages_new_summary(GPtrArray *summary) +camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids) { - GHashTable *id_table, *no_id_table; - int i; - CamelFolderThreadNode *c, *child, *head; CamelFolderThread *thread; - -#ifdef TIMEIT - struct timeval start, end; - unsigned long diff; - - gettimeofday(&start, NULL); -#endif + GHashTable *wanted = NULL; + GPtrArray *summary; + GPtrArray *fsummary; + int i; thread = g_malloc(sizeof(*thread)); + thread->refcount = 1; thread->tree = NULL; thread->node_chunks = e_memchunk_new(32, sizeof(CamelFolderThreadNode)); - thread->folder = NULL; - thread->summary = NULL; - - id_table = g_hash_table_new((GHashFunc)id_hash, (GCompareFunc)id_equal); - no_id_table = g_hash_table_new(NULL, NULL); - for (i=0;i<summary->len;i++) { - CamelMessageInfo *mi = summary->pdata[i]; + thread->folder = folder; + camel_object_ref((CamelObject *)folder); - if (mi->message_id.id.id) { - c = g_hash_table_lookup(id_table, &mi->message_id); - /* check for duplicate messages */ - if (c && c->order) { - /* if duplicate, just make out it is a no-id message, but try and insert it - into the right spot in the tree */ - d(printf("doing: (duplicate message id)\n")); - c = e_memchunk_alloc0(thread->node_chunks); - g_hash_table_insert(no_id_table, (void *)mi, c); - } else if (!c) { - d(printf("doing : %08x%08x (%s)\n", mi->message_id.id.part.hi, mi->message_id.id.part.lo, camel_message_info_subject(mi))); - c = e_memchunk_alloc0(thread->node_chunks); - g_hash_table_insert(id_table, (void *)&mi->message_id, c); - } - } else { - d(printf("doing : (no message id)\n")); - c = e_memchunk_alloc0(thread->node_chunks); - g_hash_table_insert(no_id_table, (void *)mi, c); - } + /* get all of the summary items of interest in summary order*/ + if (uids) { + wanted = g_hash_table_new(g_str_hash, g_str_equal); + for (i=0;i<uids->len;i++) + g_hash_table_insert(wanted, uids->pdata[i], uids->pdata[i]); + } - c->message = mi; - c->order = i+1; - child = c; - if (mi->references) { - int j; + fsummary = camel_folder_get_summary(folder); + thread->summary = summary = g_ptr_array_new(); - d(printf("references:\n")); - for (j=0;j<mi->references->size;j++) { - /* should never be empty, but just incase */ - if (mi->references->references[j].id.id == 0) - continue; + for (i=0;i<fsummary->len;i++) { + CamelMessageInfo *info = fsummary->pdata[i]; - c = g_hash_table_lookup(id_table, &mi->references->references[j]); - if (c == NULL) { - d(printf("not found\n")); - c = e_memchunk_alloc0(thread->node_chunks); - g_hash_table_insert(id_table, &mi->references->references[j], c); - } - if (c!=child) - container_parent_child(c, child); - child = c; - } + if (wanted == NULL || g_hash_table_lookup(wanted, camel_message_info_uid(info)) != NULL) { + camel_folder_ref_message_info(folder, info); + g_ptr_array_add(summary, info); } } - d(printf("\n\n")); - /* build a list of root messages (no parent) */ - head = NULL; - g_hash_table_foreach(id_table, hashloop, &head); - g_hash_table_foreach(no_id_table, hashloop, &head); - - g_hash_table_destroy(id_table); - g_hash_table_destroy(no_id_table); - - /* remove empty parent nodes */ - prune_empty(thread, &head); - - /* find any siblings which missed out */ - group_root_set(thread, &head); + camel_folder_free_summary(folder, fsummary); -#if 0 - printf("finished\n"); - i = camel_folder_thread_messages_dump(head); - printf("%d count, %d items in tree\n", uids->len, i); -#endif + thread_summary(thread, summary); - sort_thread(&head); + g_hash_table_destroy(wanted); - /* remove any phantom nodes, this could possibly be put in group_root_set()? */ - c = (CamelFolderThreadNode *)&head; - while (c && c->next) { - CamelFolderThreadNode *scan, *newtop; + return thread; +} - child = c->next; - if (child->message == NULL) { - newtop = child->child; - /* unlink pseudo node */ - c->next = newtop; +/* add any still there, in the existing order */ +static void +add_present_rec(CamelFolderThread *thread, GHashTable *have, GPtrArray *summary, CamelFolderThreadNode *node) +{ + while (node) { + const char *uid = camel_message_info_uid(node->message); - /* link its siblings onto the end of its children */ - scan = (CamelFolderThreadNode *)&newtop->child; - while (scan->next) - scan = scan->next; - scan->next = newtop->next; - /* and link the now 'real' node into the list */ - newtop->next = child->next; - c = newtop; - e_memchunk_free(thread->node_chunks, child); + if (g_hash_table_lookup(have, (char *)uid)) { + g_hash_table_remove(have, (char *)camel_message_info_uid(node->message)); + g_ptr_array_add(summary, node->message); } else { - c = child; + camel_folder_free_message_info(thread->folder, (CamelMessageInfo *)node->message); } - } - /* this is only debug assertion stuff */ - c = (CamelFolderThreadNode *)&head; - while (c->next) { - c = c->next; - if (c->message == NULL) - g_warning("threading missed removing a pseudo node: %s\n", c->root_subject); + if (node->child) + add_present_rec(thread, have, summary, node->child); + node = node->next; } +} - thread->tree = head; +void +camel_folder_thread_messages_apply(CamelFolderThread *thread, GPtrArray *uids) +{ + int i; + GPtrArray *all; + GHashTable *table; + CamelMessageInfo *info; -#ifdef TIMEIT - gettimeofday(&end, NULL); - diff = end.tv_sec * 1000 + end.tv_usec/1000; - diff -= start.tv_sec * 1000 + start.tv_usec/1000; - printf("Message threading %d messages took %ld.%03ld seconds\n", - summary->len, diff / 1000, diff % 1000); -#endif + all = g_ptr_array_new(); + table = g_hash_table_new(g_str_hash, g_str_equal); + for (i=0;i<uids->len;i++) + g_hash_table_insert(table, uids->pdata[i], uids->pdata[i]); - return thread; + add_present_rec(thread, table, all, thread->tree); + + /* add any new ones, in supplied order */ + for (i=0;i<uids->len;i++) + if (g_hash_table_lookup(table, uids->pdata[i]) && (info = camel_folder_get_message_info(thread->folder, uids->pdata[i]))) + g_ptr_array_add(all, info); + + g_hash_table_destroy(table); + thread_summary(thread, all); + + g_ptr_array_free(thread->summary, TRUE); + thread->summary = all; +} + +void +camel_folder_thread_messages_ref(CamelFolderThread *thread) +{ + thread->refcount++; } /** - * camel_folder_thread_messages_destroy: + * camel_folder_thread_messages_unref: * @thread: * * Free all memory associated with the thread descriptor @thread. **/ void -camel_folder_thread_messages_destroy(CamelFolderThread *thread) +camel_folder_thread_messages_unref(CamelFolderThread *thread) { + if (thread->refcount > 1) { + thread->refcount--; + return; + } + if (thread->folder) { - camel_folder_free_summary(thread->folder, thread->summary); + int i; + + for (i=0;i<thread->summary->len;i++) + camel_folder_free_message_info(thread->folder, thread->summary->pdata[i]); + g_ptr_array_free(thread->summary, TRUE); camel_object_unref((CamelObject *)thread->folder); } e_memchunk_destroy(thread->node_chunks); @@ -763,21 +694,157 @@ camel_folder_thread_messages_destroy(CamelFolderThread *thread) } #if 0 -/* intended for incremental update. Not implemented yet as, well, its probbaly - not worth it (memory overhead vs speed, may as well just rethread the whole - lot?) +/** + * camel_folder_thread_messages_new_summary: + * @summary: Array of CamelMessageInfo's to thread. + * + * Thread a list of MessageInfo's. The summary must remain valid for the + * life of the CamelFolderThread created by this function, and it is upto the + * caller to ensure this. + * + * Return value: A CamelFolderThread contianing a tree of CamelFolderThreadNode's + * which represent the threaded structure of the messages. + **/ +CamelFolderThread * +camel_folder_thread_messages_new_summary(GPtrArray *summary) +{ + CamelFolderThread *thread; + +#ifdef TIMEIT + struct timeval start, end; + unsigned long diff; + + gettimeofday(&start, NULL); +#endif + + thread = g_malloc(sizeof(*thread)); + thread->refcount = 1; + thread->tree = NULL; + thread->node_chunks = e_memchunk_new(32, sizeof(CamelFolderThreadNode)); + thread->folder = NULL; + thread->summary = NULL; + + thread_summary(thread, summary); + + return thread; +} + +/* scan the list in depth-first fashion */ +static void +build_summary_rec(GHashTable *have, GPtrArray *summary, CamelFolderThreadNode *node) +{ + while (node) { + if (node->message) + g_hash_table_insert(have, (char *)camel_message_info_uid(node->message), node->message); + g_ptr_array_add(summary, node); + if (node->child) + build_summary_rec(have, summary, node->child); + node = node->next; + } +} - But it might be implemented at a later date. -*/ void -camel_folder_thread_messages_add(CamelFolderThread *thread, CamelFolder *folder, GPtrArray *uids) +camel_folder_thread_messages_add(CamelFolderThread *thread, GPtrArray *summary) { - + GPtrArray *all; + int i; + GHashTable *table; + + /* Instead of working out all the complex in's and out's of + trying to do an incremental summary generation, just redo the whole + thing with the summary in the current order - so it comes out + in the same order */ + + all = g_ptr_array_new(); + table = g_hash_table_new(g_str_hash, g_str_equal); + build_summary_rec(table, all, thread->tree); + for (i=0;i<summary->len;i++) { + CamelMessageInfo *info = summary->pdata[i]; + + /* check its not already there, we dont want duplicates */ + if (g_hash_table_lookup(table, camel_message_info_uid(info)) == NULL) + g_ptr_array_add(all, info); + } + g_hash_table_destroy(table); + + /* reset the tree, and rebuild fully */ + thread->tree = NULL; + e_memchunk_empty(thread->node_chunks); + thread_summary(thread, all, NULL); +} + +static void +remove_uid_node_rec(CamelFolderThread *thread, GHashTable *table, CamelFolderThreadNode **list, CamelFolderThreadNode *parent) +{ + CamelFolderThreadNode *prev = NULL; + CamelFolderThreadNode *node, *next, *child, *rest; + + node = (CamelFolderThreadNode *)list; + next = node->next; + while (next) { + + if (next->child) + remove_uid_node_rec(thread, table, &next->child, next); + + /* do we have a node to remove? */ + if (next->message && g_hash_table_lookup(table, (char *)camel_message_info_uid(node->message))) { + child = next->child; + if (child) { + /* + node + next + child + lchild + rest + + becomes: + node + child + lchild + rest + */ + + rest = next->next; + node->next = child; + e_memchunk_free(thread->node_chunks, next); + next = child; + do { + lchild = child; + child->parent = parent; + child = child->next; + } while (child); + lchild->next = rest; + } else { + /* + node + next + rest + becomes: + node + rest */ + node->next = next->next; + e_memchunk_free(thread->node_chunks, next); + next = node->next; + } + } else { + node = next; + next = node->next; + } + } } void -thread_messages_remove(CamelFolderThread *thread, CamelFolder *folder, GPtrArray *uids) +camel_folder_thread_messages_remove(CamelFolderThread *thread, GPtrArray *uids) { - + GHashTable *table; + int i; + + table = g_hash_table_new(g_str_hash, g_str_equal); + for (i=0;i<uids->len;i++) + g_hash_table_insert(table, uids->pdata[i], uids->pdata[i]); + + remove_uid_node_rec(thread, table, &thread->tree, NULL); + g_hash_table_destroy(table); } + #endif diff --git a/camel/camel-folder-thread.h b/camel/camel-folder-thread.h index 5999b7bab8..3b40fe9d9c 100644 --- a/camel/camel-folder-thread.h +++ b/camel/camel-folder-thread.h @@ -40,22 +40,29 @@ typedef struct _CamelFolderThreadNode { int order; } CamelFolderThreadNode; -typedef struct CamelFolderThread { +typedef struct _CamelFolderThread { + int refcount; + struct _CamelFolderThreadNode *tree; struct _EMemChunk *node_chunks; CamelFolder *folder; GPtrArray *summary; } CamelFolderThread; +/* interface 1: using uid's */ CamelFolderThread *camel_folder_thread_messages_new(CamelFolder *folder, GPtrArray *uids); +void camel_folder_thread_messages_apply(CamelFolderThread *thread, GPtrArray *uids); -/* new improved interface (believe it or not!) */ +/* interface 2: using messageinfo's. Currently disabled. */ +#if 0 +/* new improved interface */ CamelFolderThread *camel_folder_thread_messages_new_summary(GPtrArray *summary); -/* -void camel_folder_thread_messages_add(CamelFolderThread *threads, CamelFolder *folder, GPtrArray *uids); -void camel_folder_thread_messages_remove(CamelFolderThread *threads, CamelFolder *folder, GPtrArray *uids); -*/ -void camel_folder_thread_messages_destroy(CamelFolderThread *threads); +void camel_folder_thread_messages_add(CamelFolderThread *thread, GPtrArray *summary); +void camel_folder_thread_messages_remove(CamelFolderThread *thread, GPtrArray *uids); +#endif + +void camel_folder_thread_messages_ref(CamelFolderThread *threads); +void camel_folder_thread_messages_unref(CamelFolderThread *threads); /* debugging function only */ int camel_folder_threaded_messages_dump(CamelFolderThreadNode *c); diff --git a/camel/camel-vtrash-folder.c b/camel/camel-vtrash-folder.c index 5c6e993c4f..aa21ce3c4c 100644 --- a/camel/camel-vtrash-folder.c +++ b/camel/camel-vtrash-folder.c @@ -47,8 +47,7 @@ camel_vtrash_folder_class_init (CamelVTrashFolderClass *klass) { CamelFolderClass *folder_class = (CamelFolderClass *) klass; - camel_vtrash_folder_parent = - CAMEL_VEE_FOLDER_CLASS (camel_type_get_global_classfuncs (camel_folder_get_type ())); + camel_vtrash_folder_parent = CAMEL_VEE_FOLDER_CLASS(camel_vee_folder_get_type()); folder_class->append_message = vtrash_append_message; folder_class->transfer_messages_to = vtrash_transfer_messages_to; |