0.8.11claws7
[claws.git] / src / folder.c
index bfac64ba4f6c1969168a1ad7d280aa5710865d30..afa143d64d88f67fea2b7f0dd380f5aa8fa9f659 100644 (file)
@@ -74,28 +74,36 @@ static gboolean persist_prefs_free  (gpointer key, gpointer val, gpointer data);
 void folder_item_read_cache            (FolderItem *item);
 void folder_item_free_cache            (FolderItem *item);
 
-Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
+static GSList *classlist;
+
+void folder_system_init()
+{
+       folder_register_class(mh_get_class());
+       folder_register_class(imap_get_class());
+       folder_register_class(news_get_class());
+       folder_register_class(mbox_get_class());
+}
+
+GSList *folder_get_class_list()
+{
+       return classlist;
+}
+
+void folder_register_class(FolderClass *class)
+{
+       debug_print("registering folder class %s\n", class->idstr);
+       classlist = g_slist_append(classlist, class);
+}
+
+Folder *folder_new(FolderClass *class, const gchar *name, const gchar *path)
 {
        Folder *folder = NULL;
        FolderItem *item;
 
+       g_return_val_if_fail(class != NULL, NULL);
+
        name = name ? name : path;
-       switch (type) {
-       case F_MBOX:
-               folder = mbox_folder_new(name, path);
-               break;
-       case F_MH:
-               folder = mh_folder_new(name, path);
-               break;
-       case F_IMAP:
-               folder = imap_folder_new(name, path);
-               break;
-       case F_NEWS:
-               folder = news_folder_new(name, path);
-               break;
-       default:
-               return NULL;
-       }
+       folder = class->new(name, path);
 
        /* Create root folder item */
        item = folder_item_new(folder, name, NULL);
@@ -119,18 +127,6 @@ static void folder_init(Folder *folder, const gchar *name)
        folder->draft = NULL;
        folder->queue = NULL;
        folder->trash = NULL;
-
-       /* Init Folder functions */
-       folder->item_new = NULL;
-       folder->item_destroy = NULL;
-       folder->fetch_msg = NULL;
-       folder->get_msginfo = NULL;
-       folder->get_msginfos = NULL;
-       folder->get_num_list = NULL;
-       folder->ui_func = NULL;
-       folder->ui_func_data = NULL;
-       folder->change_flags = NULL;
-       folder->check_msgnum_validity = NULL;
 }
 
 void folder_local_folder_init(Folder *folder, const gchar *name,
@@ -150,15 +146,15 @@ void folder_remote_folder_init(Folder *folder, const gchar *name,
 void folder_destroy(Folder *folder)
 {
        g_return_if_fail(folder != NULL);
-       g_return_if_fail(folder->destroy != NULL);
-
-       folder->destroy(folder);
+       g_return_if_fail(folder->class->destroy != NULL);
 
        folder_list = g_list_remove(folder_list, folder);
 
        folder_tree_destroy(folder);
        g_free(folder->name);
        g_free(folder);
+
+       folder->class->destroy(folder);
 }
 
 void folder_local_folder_destroy(LocalFolder *lfolder)
@@ -176,26 +172,12 @@ void folder_remote_folder_destroy(RemoteFolder *rfolder)
                session_destroy(rfolder->session);
 }
 
-#if 0
-Folder *mbox_folder_new(const gchar *name, const gchar *path)
-{
-       /* not yet implemented */
-       return NULL;
-}
-
-Folder *maildir_folder_new(const gchar *name, const gchar *path)
-{
-       /* not yet implemented */
-       return NULL;
-}
-#endif
-
 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
 {
        FolderItem *item = NULL;
 
-       if (folder->item_new) {
-               item = folder->item_new(folder);
+       if (folder->class->item_new) {
+               item = folder->class->item_new(folder);
        } else {
                item = g_new0(FolderItem, 1);
        }
@@ -215,6 +197,7 @@ FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path
        item->no_sub = FALSE;
        item->no_select = FALSE;
        item->collapsed = FALSE;
+       item->thread_collapsed = FALSE;
        item->threaded  = TRUE;
        item->ret_rcpt  = FALSE;
        item->opened    = FALSE;
@@ -276,8 +259,8 @@ void folder_item_destroy(FolderItem *item)
        g_free(item->path);
 
        if (item->folder != NULL) {
-               if(item->folder->item_destroy) {
-                       item->folder->item_destroy(item->folder, item);
+               if(item->folder->class->item_destroy) {
+                       item->folder->class->item_destroy(item->folder, item);
                } else {
                        g_free(item);
                }
@@ -343,20 +326,20 @@ void folder_add(Folder *folder)
 
        for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
                cur_folder = FOLDER(cur->data);
-               if (folder->type == F_MH) {
-                       if (cur_folder->type != F_MH) break;
-               } else if (folder->type == F_MBOX) {
-                       if (cur_folder->type != F_MH &&
-                           cur_folder->type != F_MBOX) break;
-               } else if (folder->type == F_IMAP) {
-                       if (cur_folder->type != F_MH &&
-                           cur_folder->type != F_MBOX &&
-                           cur_folder->type != F_IMAP) break;
-               } else if (folder->type == F_NEWS) {
-                       if (cur_folder->type != F_MH &&
-                           cur_folder->type != F_MBOX &&
-                           cur_folder->type != F_IMAP &&
-                           cur_folder->type != F_NEWS) break;
+               if (FOLDER_TYPE(folder) == F_MH) {
+                       if (FOLDER_TYPE(cur_folder) != F_MH) break;
+               } else if (FOLDER_TYPE(folder) == F_MBOX) {
+                       if (FOLDER_TYPE(cur_folder) != F_MH &&
+                           FOLDER_TYPE(cur_folder) != F_MBOX) break;
+               } else if (FOLDER_TYPE(folder) == F_IMAP) {
+                       if (FOLDER_TYPE(cur_folder) != F_MH &&
+                           FOLDER_TYPE(cur_folder) != F_MBOX &&
+                           FOLDER_TYPE(cur_folder) != F_IMAP) break;
+               } else if (FOLDER_TYPE(folder) == F_NEWS) {
+                       if (FOLDER_TYPE(cur_folder) != F_MH &&
+                           FOLDER_TYPE(cur_folder) != F_MBOX &&
+                           FOLDER_TYPE(cur_folder) != F_IMAP &&
+                           FOLDER_TYPE(cur_folder) != F_NEWS) break;
                }
        }
 
@@ -435,13 +418,13 @@ void folder_scan_tree(Folder *folder)
 {
        GHashTable *pptable;
        
-       if (!folder->scan_tree)
+       if (!folder->class->scan_tree)
                return;
        
        pptable = folder_persist_prefs_new(folder);
        folder_tree_destroy(folder);
 
-       folder->scan_tree(folder);
+       folder->class->scan_tree(folder);
 
        g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
        folder_persist_prefs_free(pptable);
@@ -455,7 +438,7 @@ FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
 {
        FolderItem *new_item;
 
-       new_item = parent->folder->create_folder(parent->folder, parent, name);
+       new_item = parent->folder->class->create_folder(parent->folder, parent, name);
        if (new_item)
                new_item->cache = msgcache_new();
 
@@ -543,7 +526,7 @@ Folder *folder_find_from_path(const gchar *path)
 
        for (list = folder_list; list != NULL; list = list->next) {
                folder = list->data;
-               if ((folder->type == F_MH || folder->type == F_MBOX) &&
+               if ((FOLDER_TYPE(folder) == F_MH || FOLDER_TYPE(folder) == F_MBOX) &&
                    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
                        return folder;
        }
@@ -551,14 +534,14 @@ Folder *folder_find_from_path(const gchar *path)
        return NULL;
 }
 
-Folder *folder_find_from_name(const gchar *name, FolderType type)
+Folder *folder_find_from_name(const gchar *name, FolderClass *class)
 {
        GList *list;
        Folder *folder;
 
        for (list = folder_list; list != NULL; list = list->next) {
                folder = list->data;
-               if (folder->type == type && strcmp2(name, folder->name) == 0)
+               if (folder->class == class && strcmp2(name, folder->name) == 0)
                        return folder;
        }
 
@@ -594,51 +577,28 @@ FolderItem *folder_find_item_from_path(const gchar *path)
        return d[1];
 }
 
-static const struct {
-       gchar *str;
-       FolderType type;
-} type_str_table[] = {
-       {"#mh"     , F_MH},
-       {"#mbox"   , F_MBOX},
-       {"#maildir", F_MAILDIR},
-       {"#imap"   , F_IMAP},
-       {"#news"   , F_NEWS}
-};
-
-static gchar *folder_get_type_string(FolderType type)
+FolderClass *folder_get_class_from_string(const gchar *str)
 {
-       gint i;
+       GSList *classlist;
 
-       for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
-            i++) {
-               if (type_str_table[i].type == type)
-                       return type_str_table[i].str;
+       classlist = folder_get_class_list();
+       for (; classlist != NULL; classlist = g_slist_next(classlist)) {
+               FolderClass *class = (FolderClass *) classlist->data;
+               if (g_strcasecmp(class->idstr, str) == 0)
+                       return class;
        }
 
        return NULL;
 }
 
-static FolderType folder_get_type_from_string(const gchar *str)
-{
-       gint i;
-
-       for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
-            i++) {
-               if (g_strcasecmp(type_str_table[i].str, str) == 0)
-                       return type_str_table[i].type;
-       }
-
-       return F_UNKNOWN;
-}
-
 gchar *folder_get_identifier(Folder *folder)
 {
        gchar *type_str;
 
        g_return_val_if_fail(folder != NULL, NULL);
 
-       type_str = folder_get_type_string(folder->type);
-       return g_strconcat(type_str, "/", folder->name, NULL);
+       type_str = folder->class->idstr;
+       return g_strconcat("#", type_str, "/", folder->name, NULL);
 }
 
 gchar *folder_item_get_identifier(FolderItem *item)
@@ -664,7 +624,7 @@ FolderItem *folder_find_item_from_identifier(const gchar *identifier)
        gchar *p;
        gchar *name;
        gchar *path;
-       FolderType type;
+       FolderClass *class;
 
        g_return_val_if_fail(identifier != NULL, NULL);
 
@@ -678,8 +638,8 @@ FolderItem *folder_find_item_from_identifier(const gchar *identifier)
                return folder_find_item_from_path(identifier);
        *p = '\0';
        p++;
-       type = folder_get_type_from_string(str);
-       if (type == F_UNKNOWN)
+       class = folder_get_class_from_string(&str[1]);
+       if (class == NULL)
                return folder_find_item_from_path(identifier);
 
        name = p;
@@ -689,7 +649,7 @@ FolderItem *folder_find_item_from_identifier(const gchar *identifier)
        *p = '\0';
        p++;
 
-       folder = folder_find_from_name(name, type);
+       folder = folder_find_from_name(name, class);
        if (!folder)
                return folder_find_item_from_path(identifier);
 
@@ -776,7 +736,7 @@ void folder_set_missing_folders(void)
 
        for (list = folder_list; list != NULL; list = list->next) {
                folder = list->data;
-               if (folder->type != F_MH) continue;
+               if (FOLDER_TYPE(folder) != F_MH) continue;
                rootitem = FOLDER_ITEM(folder->node->data);
                g_return_if_fail(rootitem != NULL);
 
@@ -784,7 +744,7 @@ void folder_set_missing_folders(void)
                    folder->queue && folder->trash)
                        continue;
 
-               if (folder->create_tree(folder) < 0) {
+               if (folder->class->create_tree(folder) < 0) {
                        g_warning("%s: can't create the folder tree.\n",
                                  LOCAL_FOLDER(folder)->rootpath);
                        continue;
@@ -917,16 +877,12 @@ void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
                flags->perm_flags = 0;
        }
        flags->tmp_flags = MSG_CACHED;
-       if (dest->folder->type == F_MH) {
+       if (FOLDER_TYPE(dest->folder) == F_MH) {
                if (dest->stype == F_QUEUE) {
                        MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
                } else if (dest->stype == F_DRAFT) {
                        MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
                }
-       } else if (dest->folder->type == F_IMAP) {
-               MSG_SET_TMP_FLAGS(*flags, MSG_IMAP);
-       } else if (dest->folder->type == F_NEWS) {
-               MSG_SET_TMP_FLAGS(*flags, MSG_NEWS);
        }
 }
 
@@ -948,7 +904,7 @@ static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
 
 gint folder_item_open(FolderItem *item)
 {
-       if(((item->folder->type == F_IMAP) && !item->no_select) || (item->folder->type == F_NEWS)) {
+       if(((FOLDER_TYPE(item->folder) == F_IMAP) && !item->no_select) || (FOLDER_TYPE(item->folder) == F_NEWS)) {
                folder_item_scan(item);
        }
 
@@ -995,7 +951,10 @@ void folder_item_close(FolderItem *item)
 gint folder_item_scan(FolderItem *item)
 {
        Folder *folder;
-       GSList *folder_list = NULL, *cache_list = NULL, *folder_list_cur, *cache_list_cur, *new_list = NULL;
+       GSList *folder_list = NULL, *cache_list = NULL;
+       GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
+       GSList *exists_list = NULL, *elem;
+       GSList *newmsg_list = NULL;
        guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0;
        guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
        gboolean update_flags = 0;
@@ -1006,18 +965,18 @@ gint folder_item_scan(FolderItem *item)
        folder = item->folder;
 
        g_return_val_if_fail(folder != NULL, -1);
-       g_return_val_if_fail(folder->get_num_list != NULL, -1);
+       g_return_val_if_fail(folder->class->get_num_list != NULL, -1);
 
        debug_print("Scanning folder %s for cache changes.\n", item->path);
 
        /* Get list of messages for folder and cache */
-       if (folder->get_num_list(item->folder, item, &folder_list) < 0) {
+       if (folder->class->get_num_list(item->folder, item, &folder_list) < 0) {
                debug_print("Error fetching list of message numbers\n");
                return(-1);
        }
 
-       if (!folder->check_msgnum_validity || 
-           folder->check_msgnum_validity(folder, item)) {
+       if (!folder->class->check_msgnum_validity || 
+           folder->class->check_msgnum_validity(folder, item)) {
                if (!item->cache)
                        folder_item_read_cache(item);
                cache_list = msgcache_get_msg_list(item->cache);
@@ -1065,18 +1024,18 @@ gint folder_item_scan(FolderItem *item)
                if (folder_cur_num < cache_cur_num) {
                        gboolean add = FALSE;
 
-                       switch(folder->type) {
+                       switch(FOLDER_TYPE(folder)) {
                                case F_NEWS:
                                        if (folder_cur_num < cache_max_num)
                                                break;
                                        
-                                       if (prefs_common.max_articles == 0) {
+                                       if (folder->account->max_articles == 0) {
                                                add = TRUE;
                                        }
 
-                                       if (folder_max_num <= prefs_common.max_articles) {
+                                       if (folder_max_num <= folder->account->max_articles) {
                                                add = TRUE;
-                                       } else if (folder_cur_num > (folder_max_num - prefs_common.max_articles)) {
+                                       } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
                                                add = TRUE;
                                        }
                                        break;
@@ -1130,37 +1089,14 @@ gint folder_item_scan(FolderItem *item)
                        MsgInfo *msginfo;
 
                        msginfo = msgcache_get_msg(item->cache, folder_cur_num);
-                       if (folder->is_msg_changed && folder->is_msg_changed(folder, item, msginfo)) {
-                               MsgInfo *newmsginfo;
-
+                       if (folder->class->is_msg_changed && folder->class->is_msg_changed(folder, item, msginfo)) {
                                msgcache_remove_msg(item->cache, msginfo->msgnum);
+                               new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
+                               procmsg_msginfo_free(msginfo);
 
-                               if (NULL != (newmsginfo = folder->get_msginfo(folder, item, folder_cur_num))) {
-                                       msgcache_add_msg(item->cache, newmsginfo);
-                                       if (MSG_IS_NEW(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags))
-                                               newcnt++;
-                                       if (MSG_IS_UNREAD(newmsginfo->flags) && !MSG_IS_IGNORE_THREAD(newmsginfo->flags))
-                                               unreadcnt++;
-                                       if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
-                                               unreadmarkedcnt++;
-                                       if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
-                                               procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
-                                       procmsg_msginfo_free(newmsginfo);
-                               }                                       
-
-                               debug_print("Updated msginfo for message %d.\n", folder_cur_num);
-                       } else {
-                               if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
-                                       newcnt++;
-                               if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
-                                       unreadcnt++;
-                               if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
-                                       unreadmarkedcnt++;
-                               if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD))
-                                       procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
-                       }
-                       totalcnt++;
-                       procmsg_msginfo_free(msginfo);
+                               debug_print("Remembering message %d to update...\n", folder_cur_num);
+                       } else
+                               exists_list = g_slist_prepend(exists_list, msginfo);
 
                        /* Move to next folder and cache number */
                        cache_list_cur = cache_list_cur->next;
@@ -1176,77 +1112,72 @@ gint folder_item_scan(FolderItem *item)
                        else
                                folder_cur_num = G_MAXINT;
 
-                       update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
-
                        continue;
                }
        }
-
-       for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur)) {
+       
+       for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
                procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
-       }
 
        g_slist_free(cache_list);
        g_slist_free(folder_list);
 
-       if (folder->get_msginfos) {
-               GSList *elem;
-               GSList *newmsg_list;
-               MsgInfo *msginfo;
-               
-               if (new_list) {
-                       newmsg_list = folder->get_msginfos(folder, item, new_list);
-                       for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
-                               msginfo = (MsgInfo *) elem->data;
-                               msgcache_add_msg(item->cache, msginfo);
-                               if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
-                                       newcnt++;
-                               if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
-                                       unreadcnt++;
-                               if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
-                                       unreadmarkedcnt++;
-                               if (procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD))
-                                       procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
-                               totalcnt++;
-                               procmsg_msginfo_free(msginfo);
-
-                               update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
+       if (new_list != NULL) {
+               if (folder->class->get_msginfos) {
+                       newmsg_list = folder->class->get_msginfos(folder, item, new_list);
+               } else if (folder->class->get_msginfo) {
+                       GSList *elem;
+       
+                       for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
+                               MsgInfo *msginfo;
+                               guint num;
+
+                               num = GPOINTER_TO_INT(elem->data);
+                               msginfo = folder->class->get_msginfo(folder, item, num);
+                               if (msginfo != NULL) {
+                                       newmsg_list = g_slist_prepend(newmsg_list, msginfo);
+                                       debug_print("Added newly found message %d to cache.\n", num);
+                               }
                        }
-                       g_slist_free(newmsg_list);
                }
-       } else if (folder->get_msginfo) {
+               g_slist_free(new_list);
+       }
+
+       if (newmsg_list != NULL) {
                GSList *elem;
-       
-               for (elem = new_list; elem != NULL; elem = g_slist_next(elem)) {
-                       MsgInfo *msginfo;
-                       guint num;
-
-                       num = GPOINTER_TO_INT(elem->data);
-                       msginfo = folder->get_msginfo(folder, item, num);
-                       if (msginfo != NULL) {
-                               msgcache_add_msg(item->cache, msginfo);
-                               if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
-                                   newcnt++;
-                               if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
-                                   unreadcnt++;
-                               if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
-                                       unreadmarkedcnt++;
-                               if (procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD))
-                                       procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
-                               totalcnt++;
-                               procmsg_msginfo_free(msginfo);
-                               debug_print("Added newly found message %d to cache.\n", num);
-                       }
 
-                       update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
-               }
+               for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem))
+                       msgcache_add_msg(item->cache, elem->data);
+
+               update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
+               
+               exists_list = g_slist_concat(exists_list, newmsg_list);
        }
 
+       for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
+               MsgInfo *msginfo;
+
+               msginfo = elem->data;
+               if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
+                       newcnt++;
+               if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
+                       unreadcnt++;
+               if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
+                       unreadmarkedcnt++;
+               if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD))
+                       procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
+               totalcnt++;
+
+               procmsg_msginfo_free(msginfo);
+       }
+       g_slist_free(exists_list);
+
        item->new = newcnt;
        item->unread = unreadcnt;
        item->total = totalcnt;
        item->unreadmarked = unreadmarkedcnt;
-       g_slist_free(new_list);
+
+       update_flags |= F_ITEM_UPDATE_MSGCNT;
 
        folder_item_update(item, update_flags);
 
@@ -1407,8 +1338,8 @@ MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
        if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
                return msginfo;
        
-       g_return_val_if_fail(folder->get_msginfo, NULL);
-       if ((msginfo = folder->get_msginfo(folder, item, num)) != NULL) {
+       g_return_val_if_fail(folder->class->get_msginfo, NULL);
+       if ((msginfo = folder->class->get_msginfo(folder, item, num)) != NULL) {
                msgcache_add_msg(item->cache, msginfo);
                return msginfo;
        }
@@ -1453,9 +1384,44 @@ gchar *folder_item_fetch_msg(FolderItem *item, gint num)
 
        folder = item->folder;
 
-       g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
+       g_return_val_if_fail(folder->class->fetch_msg != NULL, NULL);
 
-       return folder->fetch_msg(folder, item, num);
+       return folder->class->fetch_msg(folder, item, num);
+}
+
+static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
+{
+       static HeaderEntry hentry[] = {{"Message-ID:",  NULL, TRUE},
+                                      {NULL,           NULL, FALSE}};
+       FILE *fp;
+       MsgInfo *msginfo;
+       gint msgnum = 0;
+       gchar buf[BUFFSIZE];
+
+       if ((fp = fopen(file, "rb")) == NULL)
+               return 0;
+
+       if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT))
+               while (fgets(buf, sizeof(buf), fp) != NULL)
+                       if (buf[0] == '\r' || buf[0] == '\n') break;
+
+       procheader_get_header_fields(fp, hentry);
+       if (hentry[0].body) {
+               extract_parenthesis(hentry[0].body, '<', '>');
+               remove_space(hentry[0].body);
+               if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
+                       msgnum = msginfo->msgnum;
+                       procmsg_msginfo_free(msginfo);
+
+                       debug_print("found message as uid %d\n", msgnum);
+               }
+       }
+       
+       g_free(hentry[0].body);
+       hentry[0].body = NULL;
+       fclose(fp);
+
+       return msgnum;
 }
 
 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
@@ -1470,15 +1436,15 @@ gint folder_item_add_msg(FolderItem *dest, const gchar *file,
 
        folder = dest->folder;
 
-       g_return_val_if_fail(folder->add_msg != NULL, -1);
+       g_return_val_if_fail(folder->class->add_msg != NULL, -1);
 
        if (!dest->cache)
                folder_item_read_cache(dest);
 
-       num = folder->add_msg(folder, dest, file, remove_source);
+       num = folder->class->add_msg(folder, dest, file, FALSE);
 
         if (num > 0) {
-               msginfo = folder->get_msginfo(folder, dest, num);
+               msginfo = folder->class->get_msginfo(folder, dest, num);
 
                if (msginfo != NULL) {
                        if (MSG_IS_NEW(msginfo->flags))
@@ -1490,15 +1456,23 @@ gint folder_item_add_msg(FolderItem *dest, const gchar *file,
                        if (procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD))
                                procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
                        dest->total++;
-                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
 
                        msgcache_add_msg(dest->cache, msginfo);
-
                        procmsg_msginfo_free(msginfo);
+
+                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
                }
 
                 dest->last_num = num;
-        }
+        } else if (num == 0) {
+               folder_item_scan(dest);
+               num = folder_item_get_msg_num_by_file(dest, file);
+       }
+
+       if (num >= 0 && remove_source) {
+               if (unlink(file) < 0)
+                       FILE_OP_ERROR(file, "unlink");
+       }
 
        return num;
 }
@@ -1562,6 +1536,7 @@ FolderItem *folder_item_move_recursive (FolderItem *src, FolderItem *dest)
        /*copy prefs*/
        prefs_folder_item_copy_prefs(src, new_item);
        new_item->collapsed = src->collapsed;
+       new_item->thread_collapsed = src->thread_collapsed;
        new_item->threaded  = src->threaded;
        new_item->ret_rcpt  = src->ret_rcpt;
        new_item->hide_read_msgs = src->hide_read_msgs;
@@ -1586,7 +1561,7 @@ FolderItem *folder_item_move_recursive (FolderItem *src, FolderItem *dest)
        new_id = folder_item_get_identifier(new_item);
        debug_print("updating rules : %s => %s\n", old_id, new_id);
        
-       src->folder->remove_folder(src->folder, src);
+       src->folder->class->remove_folder(src->folder, src);
        folder_write_list();
 
        if (old_id != NULL && new_id != NULL)
@@ -1664,72 +1639,14 @@ gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_ite
 
 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
 {
-       Folder *folder;
-       gint num;
-       Folder *src_folder;
-
-       g_return_val_if_fail(dest != NULL, -1);
-       g_return_val_if_fail(msginfo != NULL, -1);
-
-       folder = dest->folder;
-
-       g_return_val_if_fail(folder->remove_msg != NULL, -1);
-       g_return_val_if_fail(folder->copy_msg != NULL, -1);
-
-       if (!dest->cache) folder_item_read_cache(dest);
-
-       src_folder = msginfo->folder->folder;
-
-       num = folder->copy_msg(folder, dest, msginfo);
-       
-       if (num != -1) {
-               MsgInfo *newmsginfo;
-    
-               /* Add new msginfo to dest folder */
-               if (NULL != (newmsginfo = folder->get_msginfo(folder, dest, num))) {
-                       newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
-                       
-                       if (dest->stype == F_OUTBOX || dest->stype == F_QUEUE  ||
-                           dest->stype == F_DRAFT  || dest->stype == F_TRASH)
-                               MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
-                                                    MSG_NEW|MSG_UNREAD|MSG_DELETED);
-                       msgcache_add_msg(dest->cache, newmsginfo);
-
-                       if (MSG_IS_NEW(newmsginfo->flags))
-                               dest->new++;
-                       if (MSG_IS_UNREAD(newmsginfo->flags))
-                               dest->unread++;
-                       if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
-                               dest->unreadmarked++;
-                       if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
-                               procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
-                       dest->total++;
-                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
-
-                       procmsg_msginfo_free(newmsginfo);
-               }
-
-               /* remove source message from it's folder */
-               if (src_folder->remove_msg) {
-                       src_folder->remove_msg(src_folder, msginfo->folder,
-                                              msginfo->msgnum);
-                       msgcache_remove_msg(msginfo->folder->cache, msginfo->msgnum);
+       GSList *list = NULL;
+       gint ret;
 
-                       if (MSG_IS_NEW(msginfo->flags))
-                               msginfo->folder->new--;
-                       if (MSG_IS_UNREAD(msginfo->flags))
-                               msginfo->folder->unread--;
-                       if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
-                               msginfo->folder->unreadmarked--;
-                       msginfo->folder->total--;
-                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
-               }
-       }
+       list = g_slist_append(list, msginfo);
+       ret = folder_item_move_msgs_with_dest(dest, list);
+       g_slist_free(list);
        
-       if (folder->finished_copy)
-               folder->finished_copy(folder, dest);
-
-       return num;
+       return ret;
 }
 
 /*
@@ -1758,15 +1675,16 @@ gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
        FolderItem *item;
        GSList *newmsgnums = NULL;
        GSList *l, *l2;
-       gint num;
+       gint num, lastnum = -1;
+       gboolean folderscan = FALSE;
 
        g_return_val_if_fail(dest != NULL, -1);
        g_return_val_if_fail(msglist != NULL, -1);
 
        folder = dest->folder;
 
-       g_return_val_if_fail(folder->copy_msg != NULL, -1);
-       g_return_val_if_fail(folder->remove_msg != NULL, -1);
+       g_return_val_if_fail(folder->class->copy_msg != NULL, -1);
+       g_return_val_if_fail(folder->class->remove_msg != NULL, -1);
 
        /* 
         * Copy messages to destination folder and 
@@ -1779,13 +1697,13 @@ gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
                if (!item && msginfo->folder != NULL)
                        item = msginfo->folder;
 
-               num = folder->copy_msg(folder, dest, msginfo);
+               num = folder->class->copy_msg(folder, dest, msginfo);
                newmsgnums = g_slist_append(newmsgnums, GINT_TO_POINTER(num));
        }
 
        /* Read cache for dest folder */
        if (!dest->cache) folder_item_read_cache(dest);
-       
+
        /* 
         * Fetch new MsgInfos for new messages in dest folder,
         * add them to the msgcache and update folder message counts
@@ -1795,36 +1713,60 @@ gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
                MsgInfo *msginfo = (MsgInfo *) l->data;
 
                num = GPOINTER_TO_INT(l2->data);
+               l2 = g_slist_next(l2);
 
-               if (num != -1) {
+               if (num >= 0) {
                        MsgInfo *newmsginfo;
 
-                       newmsginfo = folder->get_msginfo(folder, dest, num);
-                       if (newmsginfo) {
-                               newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
-                               if (dest->stype == F_OUTBOX ||
-                                   dest->stype == F_QUEUE  ||
-                                   dest->stype == F_DRAFT  ||
-                                   dest->stype == F_TRASH)
-                                       MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
-                                                            MSG_NEW|MSG_UNREAD|MSG_DELETED);
-                               msgcache_add_msg(dest->cache, newmsginfo);
-
-                               if (MSG_IS_NEW(newmsginfo->flags))
-                                       dest->new++;
-                               if (MSG_IS_UNREAD(newmsginfo->flags))
-                                       dest->unread++;
-                               if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
-                                       dest->unreadmarked++;
-                               if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
-                                       procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
-                               dest->total++;
-                               folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
-
-                               procmsg_msginfo_free(newmsginfo);
+                       if (num == 0) {
+                               gchar *file;
+
+                               if (!folderscan) {
+                                       folder_item_scan(dest);
+                                       folderscan = TRUE;
+                               }
+                               file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
+                               num = folder_item_get_msg_num_by_file(dest, file);
+                               g_free(file);
+                       }
+
+                       if (num > lastnum)
+                               lastnum = num;
+
+                       if (num == 0)
+                               continue;
+
+                       if (!folderscan) {
+                               newmsginfo = folder->class->get_msginfo(folder, dest, num);
+                               if (newmsginfo) {
+                                       newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
+                                       if (dest->stype == F_OUTBOX ||
+                                           dest->stype == F_QUEUE  ||
+                                           dest->stype == F_DRAFT  ||
+                                           dest->stype == F_TRASH)
+                                               MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
+                                                                    MSG_NEW|MSG_UNREAD|MSG_DELETED);
+                                       msgcache_add_msg(dest->cache, newmsginfo);
+
+                                       if (MSG_IS_NEW(newmsginfo->flags))
+                                               dest->new++;
+                                       if (MSG_IS_UNREAD(newmsginfo->flags))
+                                               dest->unread++;
+                                       if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
+                                               dest->unreadmarked++;
+                                       if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
+                                               procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
+                                       dest->total++;
+                                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
+
+                                       procmsg_msginfo_free(newmsginfo);
+                               }
+                       } else {
+                               newmsginfo = msgcache_get_msg(dest->cache, num);
+                               procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
+                               procmsg_msginfo_set_flags(newmsginfo, msginfo->flags.perm_flags, 0);
                        }
                }
-               l2 = g_slist_next(l2);
        }
 
        /*
@@ -1837,34 +1779,33 @@ gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
                MsgInfo *msginfo = (MsgInfo *) l->data;
 
                num = GPOINTER_TO_INT(l2->data);
+               l2 = g_slist_next(l2);
                
-               if (num != -1) {
-                       item->folder->remove_msg(item->folder,
-                                                msginfo->folder,
-                                                msginfo->msgnum);
+               if (num >= 0) {
+                       item->folder->class->remove_msg(item->folder,
+                                                       msginfo->folder,
+                                                       msginfo->msgnum);
                        if (!item->cache)
                                folder_item_read_cache(item);
                        msgcache_remove_msg(item->cache, msginfo->msgnum);
 
-                       if (MSG_IS_NEW(msginfo->flags))
+                       if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
                                msginfo->folder->new--;
-                       if (MSG_IS_UNREAD(msginfo->flags))
+                       if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
                                msginfo->folder->unread--;
                        if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
                                msginfo->folder->unreadmarked--;
                        msginfo->folder->total--;                       
-                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
+                       folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
                }
-
-               l2 = g_slist_next(l2);
        }
 
 
-       if (folder->finished_copy)
-               folder->finished_copy(folder, dest);
+       if (folder->class->finished_copy)
+               folder->class->finished_copy(folder, dest);
 
        g_slist_free(newmsgnums);
-       return dest->last_num;
+       return lastnum;
 }
 
 /*
@@ -1888,51 +1829,14 @@ gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
 
 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
 {
-       Folder *folder;
-       gint num;
-
-       g_return_val_if_fail(dest != NULL, -1);
-       g_return_val_if_fail(msginfo != NULL, -1);
-
-       folder = dest->folder;
-
-       g_return_val_if_fail(folder->copy_msg != NULL, -1);
+       GSList *list = NULL;
+       gint ret;
 
-       if (!dest->cache) folder_item_read_cache(dest);
+       list = g_slist_append(list, msginfo);
+       ret = folder_item_copy_msgs_with_dest(dest, list);
+       g_slist_free(list);
        
-       num = folder->copy_msg(folder, dest, msginfo);
-       if (num != -1) {
-               MsgInfo *newmsginfo;
-
-               if (NULL != (newmsginfo = folder->get_msginfo(folder, dest, num))) {
-                       newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
-                       if (dest->stype == F_OUTBOX ||
-                           dest->stype == F_QUEUE  ||
-                           dest->stype == F_DRAFT  ||
-                           dest->stype == F_TRASH)
-                               MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
-                                                    MSG_NEW|MSG_UNREAD|MSG_DELETED);
-                       msgcache_add_msg(dest->cache, newmsginfo);
-
-                       if (MSG_IS_NEW(newmsginfo->flags))
-                               dest->new++;
-                       if (MSG_IS_UNREAD(newmsginfo->flags))
-                               dest->unread++;
-                       if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
-                               dest->unreadmarked++;
-                       if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
-                               procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
-                       dest->total++;
-                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
-
-                       procmsg_msginfo_free(newmsginfo);
-               }                       
-       }
-
-       if (folder->finished_copy)
-               folder->finished_copy(folder, dest);
-
-       return num;
+       return ret;
 }
 
 /*
@@ -1958,16 +1862,17 @@ gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
 {
        Folder *folder;
-       gint num;
+       gint num, lastnum = -1;
        GSList *newmsgnums = NULL;
        GSList *l, *l2;
+       gboolean folderscan = FALSE;
 
        g_return_val_if_fail(dest != NULL, -1);
        g_return_val_if_fail(msglist != NULL, -1);
 
        folder = dest->folder;
  
-       g_return_val_if_fail(folder->copy_msg != NULL, -1);
+       g_return_val_if_fail(folder->class->copy_msg != NULL, -1);
 
        /* 
         * Copy messages to destination folder and 
@@ -1976,7 +1881,7 @@ gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
        for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
                MsgInfo * msginfo = (MsgInfo *) l->data;
 
-               num = folder->copy_msg(folder, dest, msginfo);
+               num = folder->class->copy_msg(folder, dest, msginfo);
                newmsgnums = g_slist_append(newmsgnums, GINT_TO_POINTER(num));
        }
 
@@ -1992,43 +1897,67 @@ gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
                MsgInfo *msginfo = (MsgInfo *) l->data;
 
                num = GPOINTER_TO_INT(l2->data);
+               l2 = g_slist_next(l2);
 
-               if (num != -1) {
+               if (num >= 0) {
                        MsgInfo *newmsginfo;
 
-                       newmsginfo = folder->get_msginfo(folder, dest, num);
-                       if (newmsginfo) {
-                               newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
-                               if (dest->stype == F_OUTBOX ||
-                                   dest->stype == F_QUEUE  ||
-                                   dest->stype == F_DRAFT  ||
-                                   dest->stype == F_TRASH)
-                                       MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
-                                                            MSG_NEW|MSG_UNREAD|MSG_DELETED);
-                               msgcache_add_msg(dest->cache, newmsginfo);
-
-                               if (MSG_IS_NEW(newmsginfo->flags))
-                                       dest->new++;
-                               if (MSG_IS_UNREAD(newmsginfo->flags))
-                                       dest->unread++;
-                               if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
-                                       dest->unreadmarked++;
-                               if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
-                                       procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
-                               dest->total++;
-                               folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
-
-                               procmsg_msginfo_free(newmsginfo);
+                       if (num == 0) {
+                               gchar *file;
+
+                               if (!folderscan) {
+                                       folder_item_scan(dest);
+                                       folderscan = TRUE;
+                               }
+                               file = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum);
+                               num = folder_item_get_msg_num_by_file(dest, file);
+                               g_free(file);
+                       }
+       
+                       if (num > lastnum)
+                               lastnum = num;
+
+                       if (num == 0)
+                               continue;
+
+                       if (!folderscan) {
+                               newmsginfo = folder->class->get_msginfo(folder, dest, num);
+                               if (newmsginfo) {
+                                       newmsginfo->flags.perm_flags = msginfo->flags.perm_flags;
+                                       if (dest->stype == F_OUTBOX ||
+                                           dest->stype == F_QUEUE  ||
+                                           dest->stype == F_DRAFT  ||
+                                           dest->stype == F_TRASH)
+                                               MSG_UNSET_PERM_FLAGS(newmsginfo->flags,
+                                                                    MSG_NEW|MSG_UNREAD|MSG_DELETED);
+                                       msgcache_add_msg(dest->cache, newmsginfo);
+
+                                       if (MSG_IS_NEW(newmsginfo->flags))
+                                               dest->new++;
+                                       if (MSG_IS_UNREAD(newmsginfo->flags))
+                                               dest->unread++;
+                                       if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
+                                               dest->unreadmarked++;
+                                       if (procmsg_msg_has_flagged_parent(newmsginfo, MSG_IGNORE_THREAD))
+                                               procmsg_msginfo_set_flags(newmsginfo, MSG_IGNORE_THREAD, 0);
+                                       dest->total++;
+                                       folder_item_update(dest, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
+
+                                       procmsg_msginfo_free(newmsginfo);
+                               }
+                       } else {
+                               newmsginfo = msgcache_get_msg(dest->cache, num);
+                               procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
+                               procmsg_msginfo_set_flags(newmsginfo, msginfo->flags.perm_flags, 0);
                        }
                }
-               l2 = g_slist_next(l2);
        }
        
-       if (folder->finished_copy)
-               folder->finished_copy(folder, dest);
+       if (folder->class->finished_copy)
+               folder->class->finished_copy(folder, dest);
 
        g_slist_free(newmsgnums);
-       return dest->last_num;
+       return lastnum;
 }
 
 gint folder_item_remove_msg(FolderItem *item, gint num)
@@ -2042,13 +1971,13 @@ gint folder_item_remove_msg(FolderItem *item, gint num)
        folder = item->folder;
        if (!item->cache) folder_item_read_cache(item);
 
-       ret = folder->remove_msg(folder, item, num);
+       ret = folder->class->remove_msg(folder, item, num);
 
        msginfo = msgcache_get_msg(item->cache, num);
        if (msginfo != NULL) {
-               if (MSG_IS_NEW(msginfo->flags))
+               if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
                        item->new--;
-               if (MSG_IS_UNREAD(msginfo->flags))
+               if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
                        item->unread--;
                if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
                        item->unreadmarked--;
@@ -2072,8 +2001,8 @@ gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
 
        if (!item->cache) folder_item_read_cache(item);
 
-       if (folder->remove_msgs) {
-               ret = folder->remove_msgs(folder, item, msglist);
+       if (folder->class->remove_msgs) {
+               ret = folder->class->remove_msgs(folder, item, msglist);
                if (ret == 0)
                        folder_item_scan(item);
                return ret;
@@ -2100,13 +2029,13 @@ gint folder_item_remove_all_msg(FolderItem *item)
 
        folder = item->folder;
 
-       g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
+       g_return_val_if_fail(folder->class->remove_all_msg != NULL, -1);
 
-       result = folder->remove_all_msg(folder, item);
+       result = folder->class->remove_all_msg(folder, item);
 
        if (result == 0) {
-               if (folder->finished_remove)
-                       folder->finished_remove(folder, item);
+               if (folder->class->finished_remove)
+                       folder->class->finished_remove(folder, item);
 
                folder_item_free_cache(item);
                item->cache = msgcache_new();
@@ -2129,9 +2058,9 @@ gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
 
        folder = item->folder;
 
-       g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
+       g_return_val_if_fail(folder->class->is_msg_changed != NULL, -1);
 
-       return folder->is_msg_changed(folder, item, msginfo);
+       return folder->class->is_msg_changed(folder, item, msginfo);
 }
 
 gchar *folder_item_get_cache_file(FolderItem *item)
@@ -2182,7 +2111,8 @@ static gboolean folder_build_tree(GNode *node, gpointer data)
        PrefsAccount *account = NULL;
        gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
                 threaded = TRUE, apply_sub = FALSE;
-       gboolean ret_rcpt = FALSE, hidereadmsgs = FALSE; /* CLAWS */
+       gboolean ret_rcpt = FALSE, hidereadmsgs = FALSE,
+                thread_collapsed = FALSE; /* CLAWS */
        FolderSortKey sort_key = SORT_BY_NONE;
        FolderSortType sort_type = SORT_ASCENDING;
        gint new = 0, unread = 0, total = 0, unreadmarked = 0;
@@ -2235,6 +2165,8 @@ static gboolean folder_build_tree(GNode *node, gpointer data)
                        no_select = *attr->value == '1' ? TRUE : FALSE;
                else if (!strcmp(attr->name, "collapsed"))
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
+               else if (!strcmp(attr->name, "thread_collapsed"))
+                       thread_collapsed =  *attr->value == '1' ? TRUE : FALSE;
                else if (!strcmp(attr->name, "threaded"))
                        threaded =  *attr->value == '1' ? TRUE : FALSE;
                else if (!strcmp(attr->name, "hidereadmsgs"))
@@ -2261,7 +2193,7 @@ static gboolean folder_build_tree(GNode *node, gpointer data)
                        else if (!strcmp(attr->value, "mark"))
                                sort_key = SORT_BY_MARK;
                        else if (!strcmp(attr->value, "unread"))
-                               sort_key = SORT_BY_UNREAD;
+                               sort_key = SORT_BY_STATUS;
                        else if (!strcmp(attr->value, "mime"))
                                sort_key = SORT_BY_MIME;
                        else if (!strcmp(attr->value, "to"))
@@ -2291,6 +2223,7 @@ static gboolean folder_build_tree(GNode *node, gpointer data)
        item->no_sub = no_sub;
        item->no_select = no_select;
        item->collapsed = collapsed;
+       item->thread_collapsed = thread_collapsed;
        item->threaded  = threaded;
        item->hide_read_msgs  = hidereadmsgs;
        item->ret_rcpt  = ret_rcpt;
@@ -2321,12 +2254,12 @@ static gboolean folder_read_folder_func(GNode *node, gpointer data)
        Folder *folder;
        XMLNode *xmlnode;
        GList *list;
-       FolderType type = F_UNKNOWN;
+       FolderClass *class = NULL;
        const gchar *name = NULL;
        const gchar *path = NULL;
        PrefsAccount *account = NULL;
        gboolean collapsed = FALSE, threaded = TRUE, apply_sub = FALSE;
-       gboolean ret_rcpt = FALSE; /* CLAWS */
+       gboolean ret_rcpt = FALSE, thread_collapsed = FALSE; /* CLAWS */
 
        if (g_node_depth(node) != 2) return FALSE;
        g_return_val_if_fail(node->data != NULL, FALSE);
@@ -2342,23 +2275,16 @@ static gboolean folder_read_folder_func(GNode *node, gpointer data)
                XMLAttr *attr = list->data;
 
                if (!attr || !attr->name || !attr->value) continue;
-               if (!strcmp(attr->name, "type")) {
-                       if (!strcasecmp(attr->value, "mh"))
-                               type = F_MH;
-                       else if (!strcasecmp(attr->value, "mbox"))
-                               type = F_MBOX;
-                       else if (!strcasecmp(attr->value, "maildir"))
-                               type = F_MAILDIR;
-                       else if (!strcasecmp(attr->value, "imap"))
-                               type = F_IMAP;
-                       else if (!strcasecmp(attr->value, "news"))
-                               type = F_NEWS;
-               } else if (!strcmp(attr->name, "name"))
+               if (!strcmp(attr->name, "type"))
+                       class = folder_get_class_from_string(attr->value);
+               else if (!strcmp(attr->name, "name"))
                        name = attr->value;
                else if (!strcmp(attr->name, "path"))
                        path = attr->value;
                else if (!strcmp(attr->name, "collapsed"))
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
+               else if (!strcmp(attr->name, "thread_collapsed"))
+                       thread_collapsed = *attr->value == '1' ? TRUE : FALSE;
                else if (!strcmp(attr->name, "threaded"))
                        threaded = *attr->value == '1' ? TRUE : FALSE;
                else if (!strcmp(attr->name, "account_id")) {
@@ -2371,16 +2297,17 @@ static gboolean folder_read_folder_func(GNode *node, gpointer data)
                        ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
        }
 
-       folder = folder_new(type, name, path);
+       folder = folder_new(class, name, path);
        g_return_val_if_fail(folder != NULL, FALSE);
        folder->account = account;
-       if (account && (type == F_IMAP || type == F_NEWS))
+       if (account != NULL)
                account->folder = REMOTE_FOLDER(folder);
        node->data = folder->node->data;
        g_node_destroy(folder->node);
        folder->node = node;
        folder_add(folder);
        FOLDER_ITEM(node->data)->collapsed = collapsed;
+       FOLDER_ITEM(node->data)->thread_collapsed = thread_collapsed;
        FOLDER_ITEM(node->data)->threaded  = threaded;
        FOLDER_ITEM(node->data)->account   = account;
        FOLDER_ITEM(node->data)->apply_sub = apply_sub;
@@ -2415,8 +2342,6 @@ static void folder_write_list_recursive(GNode *node, gpointer data)
        FILE *fp = (FILE *)data;
        FolderItem *item;
        gint i, depth;
-       static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
-                                          "news", "unknown"};
        static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
                                                 "draft", "queue", "trash"};
        static gchar *sort_key_str[] = {"none", "number", "size", "date",
@@ -2435,10 +2360,10 @@ static void folder_write_list_recursive(GNode *node, gpointer data)
        if (depth == 1) {
                Folder *folder = item->folder;
 
-               fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
+               fprintf(fp, "<folder type=\"%s\"", folder->class->idstr);
                if (folder->name)
                        PUT_ESCAPE_STR(fp, "name", folder->name);
-               if (folder->type == F_MH || folder->type == F_MBOX)
+               if (FOLDER_TYPE(folder) == F_MH || FOLDER_TYPE(folder) == F_MBOX)
                        PUT_ESCAPE_STR(fp, "path",
                                       LOCAL_FOLDER(folder)->rootpath);
                if (item->collapsed && node->children)
@@ -2464,6 +2389,12 @@ static void folder_write_list_recursive(GNode *node, gpointer data)
                        fputs(" no_select=\"1\"", fp);
                if (item->collapsed && node->children)
                        fputs(" collapsed=\"1\"", fp);
+               else
+                       fputs(" collapsed=\"0\"", fp);
+               if (item->thread_collapsed)
+                       fputs(" thread_collapsed=\"1\"", fp);
+               else
+                       fputs(" thread_collapsed=\"0\"", fp);
                if (item->threaded)
                        fputs(" threaded=\"1\"", fp);
                else
@@ -2600,14 +2531,14 @@ static void folder_create_processing_folder(void)
                                      G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
                                      NULL);
 
-       processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
+       processing_folder = folder_new(mh_get_class(), "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
        g_assert(processing_folder);
 
        if (!is_dir_exist(tmpname)) {
                debug_print("*TMP* creating %s\n", tmpname);
-               processing_folder_item = processing_folder->create_folder(processing_folder,
-                                                                         processing_folder->node->data,
-                                                                         PROCESSING_FOLDER);
+               processing_folder_item = processing_folder->class->create_folder(processing_folder,
+                                                                                processing_folder->node->data,
+                                                                                PROCESSING_FOLDER);
                g_assert(processing_folder_item);                                                                         
        }
        else {
@@ -2671,6 +2602,7 @@ void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
        prefs_folder_item_read_config(item); 
         
        item->collapsed = pp->collapsed;
+       item->thread_collapsed = pp->thread_collapsed;
        item->threaded  = pp->threaded;
        item->ret_rcpt  = pp->ret_rcpt;
        item->hide_read_msgs = pp->hide_read_msgs;
@@ -2695,6 +2627,7 @@ static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
                pp = g_new0(PersistPrefs, 1);
                g_return_if_fail(pp != NULL);
                pp->collapsed = item->collapsed;
+               pp->thread_collapsed = item->thread_collapsed;
                pp->threaded  = item->threaded;
                pp->ret_rcpt  = item->ret_rcpt; 
                pp->hide_read_msgs = item->hide_read_msgs;