2007-11-27 [colin] 3.1.0cvs40
[claws.git] / src / summaryview.c
index a0d4bba23e1a63b40526c29083fb3df63ff44ced..796d8c3d2502471edbaae243bba2feb80443db69 100644 (file)
@@ -125,6 +125,8 @@ static GdkPixmap *forwardedxpm;
 static GdkBitmap *forwardedxpmmask;
 static GdkPixmap *ignorethreadxpm;
 static GdkBitmap *ignorethreadxpmmask;
+static GdkPixmap *watchthreadxpm;
+static GdkBitmap *watchthreadxpmmask;
 static GdkPixmap *lockedxpm;
 static GdkBitmap *lockedxpmmask;
 static GdkPixmap *spamxpm;
@@ -311,6 +313,7 @@ static void summary_col_resized             (GtkCList               *clist,
 static void summary_reply_cb           (SummaryView            *summaryview,
                                         guint                   action,
                                         GtkWidget              *widget);
+#ifndef MAEMO
 static void summary_show_all_header_cb (SummaryView            *summaryview,
                                         guint                   action,
                                         GtkWidget              *widget);
@@ -318,13 +321,15 @@ static void summary_show_all_header_cb    (SummaryView            *summaryview,
 static void summary_add_address_cb     (SummaryView            *summaryview,
                                         guint                   action,
                                         GtkWidget              *widget);
+#endif
 static void summary_create_filter_cb   (SummaryView            *summaryview,
                                         guint                   action,
                                         GtkWidget              *widget);
+#ifndef MAEMO
 static void summary_create_processing_cb(SummaryView           *summaryview,
                                         guint                   action,
                                         GtkWidget              *widget);
-
+#endif
 static void summary_mark_clicked       (GtkWidget              *button,
                                         SummaryView            *summaryview);
 static void summary_status_clicked     (GtkWidget              *button,
@@ -439,6 +444,10 @@ static void summary_find_answers   (SummaryView    *summaryview,
 static gboolean summary_update_msg     (gpointer source, gpointer data);
 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data);
 static gboolean summary_update_folder_hook(gpointer source, gpointer data);
+static void summary_set_colorlabel_color (GtkCTree             *ctree,
+                                  GtkCTreeNode         *node,
+                                  guint                 labelcolor);
+static void summary_thread_build(SummaryView *summaryview);
 
 GtkTargetEntry summary_drag_types[2] =
 {
@@ -449,20 +458,26 @@ GtkTargetEntry summary_drag_types[2] =
 static GtkItemFactoryEntry summary_popup_entries[] =
 {
        {N_("/_Reply"),                 "<control>R", summary_reply_cb, COMPOSE_REPLY, NULL},
+#ifndef MAEMO
        {N_("/Repl_y to"),              NULL, NULL,             0, "<Branch>"},
        {N_("/Repl_y to/_all"),         "<shift><control>R", summary_reply_cb,  COMPOSE_REPLY_TO_ALL, NULL},
        {N_("/Repl_y to/_sender"),      NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
        {N_("/Repl_y to/mailing _list"),
                                        "<control>L", summary_reply_cb, COMPOSE_REPLY_TO_LIST, NULL},
        {"/---",                        NULL, NULL,             0, "<Separator>"},
+#endif
        {N_("/_Forward"),               "<control><alt>F", summary_reply_cb, COMPOSE_FORWARD_INLINE, NULL},
+#ifndef MAEMO
        {N_("/For_ward as attachment"), NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
        {N_("/Redirect"),               NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL},
+#endif
        {"/---",                        NULL, NULL,             0, "<Separator>"},
        {N_("/M_ove..."),               "<control>O", summary_move_to,  0, NULL},
        {N_("/_Copy..."),               "<shift><control>O", summary_copy_to,   0, NULL},
        {N_("/Move to _trash"),         "<control>D", summary_delete_trash,     0, NULL},
+#ifndef MAEMO
        {N_("/_Delete..."),             NULL, summary_delete, 0, NULL},
+#endif
        {"/---",                        NULL, NULL,             0, "<Separator>"},
        {N_("/_Mark"),                  NULL, NULL,             0, "<Branch>"},
        {N_("/_Mark/_Mark"),            NULL, summary_mark,     0, NULL},
@@ -473,6 +488,8 @@ static GtkItemFactoryEntry summary_popup_entries[] =
        {N_("/_Mark/Mark all read"),    NULL, summary_mark_all_read, 0, NULL},
        {N_("/_Mark/Ignore thread"),    NULL, summary_ignore_thread, 0, NULL},
        {N_("/_Mark/Unignore thread"),  NULL, summary_unignore_thread, 0, NULL},
+       {N_("/_Mark/Watch thread"),     NULL, summary_watch_thread, 0, NULL},
+       {N_("/_Mark/Unwatch thread"),   NULL, summary_unwatch_thread, 0, NULL},
        {N_("/_Mark/---"),              NULL, NULL, 0, "<Separator>"},
        {N_("/_Mark/Mark as _spam"),    NULL, summary_mark_as_spam, 1, NULL},
        {N_("/_Mark/Mark as _ham"),     NULL, summary_mark_as_spam, 0, NULL},
@@ -483,8 +500,10 @@ static GtkItemFactoryEntry summary_popup_entries[] =
        {N_("/Ta_gs"),                  NULL, NULL,             0, NULL},
 
        {"/---",                        NULL, NULL,             0, "<Separator>"},
+#ifndef MAEMO
        {N_("/Add sender to address boo_k"),
                                        NULL, summary_add_address_cb, 0, NULL},
+#endif
        {N_("/Create f_ilter rule"),    NULL, NULL,             0, "<Branch>"},
        {N_("/Create f_ilter rule/_Automatically"),
                                        NULL, summary_create_filter_cb, FILTER_BY_AUTO, NULL},
@@ -494,6 +513,7 @@ static GtkItemFactoryEntry summary_popup_entries[] =
                                        NULL, summary_create_filter_cb, FILTER_BY_TO, NULL},
        {N_("/Create f_ilter rule/by _Subject"),
                                        NULL, summary_create_filter_cb, FILTER_BY_SUBJECT, NULL},
+#ifndef MAEMO
        {N_("/Create processing rule"), NULL, NULL,             0, "<Branch>"},
        {N_("/Create processing rule/_Automatically"),
                                        NULL, summary_create_processing_cb, FILTER_BY_AUTO, NULL},
@@ -503,15 +523,20 @@ static GtkItemFactoryEntry summary_popup_entries[] =
                                        NULL, summary_create_processing_cb, FILTER_BY_TO, NULL},
        {N_("/Create processing rule/by _Subject"),
                                        NULL, summary_create_processing_cb, FILTER_BY_SUBJECT, NULL},
+#endif
        {"/---",                        NULL, NULL,             0, "<Separator>"},
        {N_("/_View"),                  NULL, NULL,             0, "<Branch>"},
        {N_("/_View/Open in new _window"),
                                        "<control><alt>N", summary_open_msg,    0, NULL},
        {N_("/_View/Message _source"),  "<control>U", summary_view_source, 0, NULL},
+#ifndef MAEMO
        {N_("/_View/All _headers"),     "<control>H", summary_show_all_header_cb, 0, "<ToggleItem>"},
+#endif
        {"/---",                        NULL, NULL,             0, "<Separator>"},
        {N_("/_Save as..."),            "<control>S", summary_save_as,   0, NULL},
+#ifndef MAEMO
        {N_("/_Print..."),              "<control>P", summary_print,   0, NULL},
+#endif
 };  /* see also list in menu_connect_identical_items() in menu.c if this changes */
 
 static const gchar *const col_label[N_SUMMARY_COLS] = {
@@ -556,11 +581,13 @@ GtkWidget *summary_get_main_widget(SummaryView *summaryview)
 }
 
 #define START_LONG_OPERATION(summaryview,force_freeze) {       \
+       if (summary_is_locked(summaryview))                     \
+               return;                                         \
        summary_lock(summaryview);                              \
        main_window_cursor_wait(summaryview->mainwin);          \
        if (force_freeze || sc_g_list_bigger(GTK_CLIST(summaryview->ctree)->selection, 1)) {\
-               froze = TRUE;                                           \
-               summary_freeze(summaryview);    \
+               froze = TRUE;                                   \
+               summary_freeze(summaryview);                    \
        }                                                       \
        folder_item_update_freeze();                            \
        inc_lock();                                             \
@@ -570,8 +597,9 @@ GtkWidget *summary_get_main_widget(SummaryView *summaryview)
 #define END_LONG_OPERATION(summaryview) {                      \
        inc_unlock();                                           \
        folder_item_update_thaw();                              \
-       if (froze)                                              \
-               summary_thaw(summaryview);      \
+       if (froze) {                                            \
+               summary_thaw(summaryview);                      \
+       }                                                       \
        main_window_cursor_normal(summaryview->mainwin);        \
        summary_unlock(summaryview);                            \
        summaryview->msginfo_update_callback_id =               \
@@ -826,6 +854,10 @@ void summary_relayout(SummaryView *summaryview)
        gtk_widget_unref(summaryview->hbox_l);
        gtk_widget_unref(summaryview->statlabel_msgs);
        quicksearch_relayout(summaryview->quicksearch);
+       if (prefs_common.show_searchbar)
+               quicksearch_show(summaryview->quicksearch);
+       else
+               quicksearch_hide(summaryview->quicksearch);
 }
 
 static void summary_set_fonts(SummaryView *summaryview)
@@ -911,6 +943,8 @@ void summary_init(SummaryView *summaryview)
                         &lockedxpm, &lockedxpmmask);
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_IGNORETHREAD,
                         &ignorethreadxpm, &ignorethreadxpmmask);
+       stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_WATCHTHREAD,
+                        &watchthreadxpm, &watchthreadxpmmask);
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP_KEY,
                         &clipkeyxpm, &clipkeyxpmmask);
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_KEY,
@@ -1103,7 +1137,9 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                }
        }
        if (!prefs_common.summary_quicksearch_sticky
-        && !prefs_common.summary_quicksearch_recurse
+        && (!prefs_common.summary_quicksearch_recurse
+         || !quicksearch_is_active(summaryview->quicksearch)
+         || (item && !quicksearch_is_in_subfolder(summaryview->quicksearch, item)))
         && !quicksearch_is_running(summaryview->quicksearch)
         && !is_refresh) {
                quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
@@ -1153,7 +1189,7 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                g_free(buf);
                debug_print("empty folder (%p %s %p %d)\n\n",
                                item, 
-                               item?item->path:"NULL",
+                               (item && item->path)?item->path:"(null)",
                                item?folder_item_parent(item):0x0,
                                item?item->no_select:FALSE);
                summary_set_hide_read_msgs_menu(summaryview, FALSE);
@@ -1386,6 +1422,14 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                                                 ? 0 : GTK_CLIST(ctree)->rows - 1);
                                }
                                break;
+                       case ACTION_FIRST_LIST:
+                               if (GTK_CLIST(ctree)->row_list != NULL) {
+                                       node = gtk_ctree_node_nth
+                                               (ctree,
+                                                item->sort_type == SORT_ASCENDING
+                                                ? 0 : GTK_CLIST(ctree)->rows - 1);
+                               }
+                               break;
                        case ACTION_NOTHING:
                        case ACTION_UNSET:
                                node = NULL;
@@ -1483,8 +1527,9 @@ void summary_lock(SummaryView *summaryview)
 
 void summary_unlock(SummaryView *summaryview)
 {
-       if (summaryview->lock_count)
+       if (summaryview->lock_count) {
                summaryview->lock_count--;
+       }
 }
 
 gboolean summary_is_locked(SummaryView *summaryview)
@@ -1547,7 +1592,9 @@ void summary_set_menu_sensitive(SummaryView *summaryview)
        GtkItemFactory *ifactory = summaryview->popupfactory;
        SensitiveCond state;
        gboolean sensitive;
+#ifndef MAEMO
        GtkWidget *menuitem;
+#endif
        gint i;
 
        static const struct {
@@ -1555,19 +1602,25 @@ void summary_set_menu_sensitive(SummaryView *summaryview)
                SensitiveCond cond;
        } entry[] = {
                {"/Reply"                       , M_HAVE_ACCOUNT|M_TARGET_EXIST},
+#ifndef MAEMO
                {"/Reply to"                    , M_HAVE_ACCOUNT|M_TARGET_EXIST},
                {"/Reply to/all"                , M_HAVE_ACCOUNT|M_TARGET_EXIST},
                {"/Reply to/sender"             , M_HAVE_ACCOUNT|M_TARGET_EXIST},
                {"/Reply to/mailing list"       , M_HAVE_ACCOUNT|M_TARGET_EXIST},
+#endif
 
                {"/Forward"                     , M_HAVE_ACCOUNT|M_TARGET_EXIST},
+#ifndef MAEMO
                {"/Forward as attachment"       , M_HAVE_ACCOUNT|M_TARGET_EXIST},
                {"/Redirect"                    , M_HAVE_ACCOUNT|M_TARGET_EXIST},
+#endif
 
                {"/Move..."                     , M_TARGET_EXIST|M_ALLOW_DELETE|M_NOT_NEWS},
                {"/Copy..."                     , M_TARGET_EXIST|M_EXEC},
                {"/Move to trash"               , M_TARGET_EXIST|M_ALLOW_DELETE|M_NOT_NEWS},
+#ifndef MAEMO
                {"/Delete..."                   , M_TARGET_EXIST|M_ALLOW_DELETE},
+#endif
 
                {"/Mark"                        , M_TARGET_EXIST},
                {"/Mark/Mark"                   , M_TARGET_EXIST},
@@ -1583,16 +1636,24 @@ void summary_set_menu_sensitive(SummaryView *summaryview)
                {"/Color label"                 , M_TARGET_EXIST},
                {"/Tags"                        , M_TARGET_EXIST},
 
+#ifndef MAEMO
                {"/Add sender to address book"  , M_SINGLE_TARGET_EXIST},
+#endif
                {"/Create filter rule"          , M_SINGLE_TARGET_EXIST|M_UNLOCKED},
+#ifndef MAEMO
                {"/Create processing rule"      , M_SINGLE_TARGET_EXIST|M_UNLOCKED},
+#endif
 
                {"/View"                        , M_SINGLE_TARGET_EXIST},
                {"/View/Open in new window"     , M_SINGLE_TARGET_EXIST},
                {"/View/Message source"         , M_SINGLE_TARGET_EXIST},
+#ifndef MAEMO
                {"/View/All headers"            , M_SINGLE_TARGET_EXIST},
+#endif
                {"/Save as..."                  , M_TARGET_EXIST},
+#ifndef MAEMO
                {"/Print..."                    , M_TARGET_EXIST},
+#endif
                {NULL, 0}
        };
 
@@ -1607,6 +1668,7 @@ void summary_set_menu_sensitive(SummaryView *summaryview)
 
 
        summary_lock(summaryview);
+#ifndef MAEMO
        menuitem = gtk_item_factory_get_widget(ifactory, "/View/All headers");
        if (summaryview->messageview 
        &&  summaryview->messageview->mimeview
@@ -1614,6 +1676,7 @@ void summary_set_menu_sensitive(SummaryView *summaryview)
                gtk_check_menu_item_set_active
                        (GTK_CHECK_MENU_ITEM(menuitem),
                         summaryview->messageview->mimeview->textview->show_all_headers);
+#endif
        summary_unlock(summaryview);
 }
 
@@ -2453,6 +2516,7 @@ static void summary_status_show(SummaryView *summaryview)
                g_free(str);
        }
 
+       summary_set_menu_sensitive(summaryview);
        toolbar_main_set_sensitive(summaryview->mainwin);
 }
 
@@ -2535,6 +2599,9 @@ static void summary_set_column_titles(SummaryView *summaryview)
 
                hbox  = gtk_hbox_new(FALSE, 4);
                label = gtk_label_new(title);
+#ifdef MAEMO
+       gtk_widget_set_size_request(hbox, -1, 20);
+#endif
 
                if (justify == GTK_JUSTIFY_RIGHT)
                        gtk_box_pack_end(GTK_BOX(hbox), label,
@@ -2616,7 +2683,10 @@ void summary_reflect_prefs(void)
        }
 
        summary_set_column_titles(summaryview);
-       summary_show(summaryview, summaryview->folder_item);
+       summary_relayout(summaryview);
+       
+       if (summaryview->folder_item)
+               summary_show(summaryview, summaryview->folder_item);
 }
 
 void summary_sort(SummaryView *summaryview,
@@ -3453,6 +3523,9 @@ static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
        if (MSG_IS_IGNORE_THREAD(flags)) {
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
                                          ignorethreadxpm, ignorethreadxpmmask);
+       } else if (MSG_IS_WATCH_THREAD(flags)) {
+               gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
+                                         watchthreadxpm, watchthreadxpmmask);
        } else if (MSG_IS_SPAM(flags)) {
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
                                          spamxpm, spamxpmmask);
@@ -3961,13 +4034,10 @@ void summary_delete(SummaryView *summaryview)
        if (!prefs_common.live_dangerously) {
                gchar *buf = NULL;
                int num = g_list_length(GTK_CLIST(summaryview->ctree)->selection);
-               if (num == 1)
-                       buf = g_strdup_printf(_(
-                               "Do you really want to delete the selected message?"));
-               else
-                       buf = g_strdup_printf(_(
-                               "Do you really want to delete the %d selected messages?"), 
-                               num);
+               buf = g_strdup_printf(ngettext(
+                       "Do you really want to delete the selected message?",
+                       "Do you really want to delete the %d selected messages?", num), 
+                       num);
                aval = alertpanel(_("Delete message(s)"),
                                  buf,
                                  GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL);
@@ -4127,6 +4197,12 @@ void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
                return;
        }
 
+       if (to_folder->no_select) {
+               alertpanel_error(_("The destination folder can only be used to "
+                                  "store subfolders."));
+               return;
+       }
+
        START_LONG_OPERATION(summaryview, FALSE); 
 
        for (cur = GTK_CLIST(summaryview->ctree)->selection;
@@ -4218,6 +4294,12 @@ void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
                return;
        }
 
+       if (to_folder->no_select) {
+               alertpanel_error(_("The destination folder can only be used to "
+                                  "store subfolders."));
+               return;
+       }
+
        START_LONG_OPERATION(summaryview, FALSE);
 
        for (cur = GTK_CLIST(summaryview->ctree)->selection;
@@ -4247,17 +4329,43 @@ void summary_copy_to(SummaryView *summaryview)
 
 void summary_add_address(SummaryView *summaryview)
 {
-       MsgInfo *msginfo;
+       MsgInfo *msginfo, *full_msginfo;
        gchar *from;
+       GtkWidget *image = NULL;
+       GdkPixbuf *picture = NULL;
 
        msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
                                              summaryview->selected);
-       if (!msginfo) return;
+       if (!msginfo || !msginfo->from) 
+               return;
 
        Xstrdup_a(from, msginfo->from, return);
        eliminate_address_comment(from);
        extract_address(from);
-       addressbook_add_contact(msginfo->fromname, from, NULL);
+       
+       full_msginfo = procmsg_msginfo_get_full_info(msginfo);
+       if (full_msginfo &&
+           full_msginfo->extradata &&
+           full_msginfo->extradata->face) {
+               image = face_get_from_header(full_msginfo->extradata->face);
+       } 
+#if HAVE_LIBCOMPFACE
+       else if (full_msginfo &&
+                full_msginfo->extradata &&
+                full_msginfo->extradata->xface) {
+               image = xface_get_from_header(full_msginfo->extradata->xface,
+                               &summaryview->ctree->style->white,
+                               summaryview->mainwin->window->window);  
+       }
+#endif
+       procmsg_msginfo_free(full_msginfo);
+       if (image)
+               picture = gtk_image_get_pixbuf(GTK_IMAGE(image));
+
+       addressbook_add_contact(msginfo->fromname, from, NULL, picture);
+
+       if (image)
+               gtk_widget_destroy(image);
 }
 
 void summary_select_all(SummaryView *summaryview)
@@ -4389,7 +4497,7 @@ void summary_save_as(SummaryView *summaryview)
 void summary_print(SummaryView *summaryview)
 {
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
-#ifndef USE_GNOMEPRINT
+#if !defined(USE_GNOMEPRINT) && !GTK_CHECK_VERSION(2,10,0)
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
        MsgInfo *msginfo;
        gchar *cmdline = NULL;
@@ -4409,7 +4517,7 @@ void summary_print(SummaryView *summaryview)
        g_free(msg);
 
        if (clist->selection == NULL) return;
-#ifndef USE_GNOMEPRINT
+#if !defined(USE_GNOMEPRINT) && !GTK_CHECK_VERSION(2,10,0)
        cmdline = input_dialog(_("Print"),
                               _("Enter the print command line:\n"
                                 "('%s' will be replaced with file name)"),
@@ -4705,7 +4813,7 @@ static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
 
 /* thread functions */
 
-void summary_thread_build(SummaryView *summaryview)
+static void summary_thread_build(SummaryView *summaryview)
 {
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
        GtkCTreeNode *node;
@@ -5020,10 +5128,15 @@ void summary_filter(SummaryView *summaryview, gboolean selected_only)
        } else {
                mlist = folder_item_get_msg_list(summaryview->folder_item);
        }
+       
+       folder_item_set_batch(summaryview->folder_item, TRUE);
        for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
                summary_filter_func((MsgInfo *)cur_list->data);
        }
+       folder_item_set_batch(summaryview->folder_item, FALSE);
+       
        filtering_move_and_copy_msgs(mlist);
+       
        for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
                procmsg_msginfo_free((MsgInfo *)cur_list->data);
        }
@@ -5207,7 +5320,6 @@ static gboolean summary_set_row_tag(SummaryView *summaryview, GtkCTreeNode *row,
 
        procmsg_msginfo_update_tags(msginfo, set, id);
        
-       
        if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
                tags_str = procmsg_msginfo_get_tags_str(msginfo);
                gtk_ctree_node_set_text(ctree, row, 
@@ -5233,10 +5345,12 @@ void summary_set_tag(SummaryView *summaryview, gint tag_id,
        gboolean froze = FALSE;
        gboolean redisplay = FALSE;
        START_LONG_OPERATION(summaryview, FALSE);
+       folder_item_set_batch(summaryview->folder_item, TRUE);
        for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
                redisplay |= summary_set_row_tag(summaryview,
                                           GTK_CTREE_NODE(cur->data), FALSE, set, real_id);
        }
+       folder_item_set_batch(summaryview->folder_item, FALSE);
        END_LONG_OPERATION(summaryview);
        if (redisplay)
                summary_redisplay_msg(summaryview);
@@ -5487,15 +5601,32 @@ static void summary_tags_menu_item_apply_tags_activate_cb(GtkWidget *widget,
        tag_apply_open(summary_get_selection(summaryview));     
 }
 
+static gint summary_tag_cmp_list(gconstpointer a, gconstpointer b)
+{
+       gint id_a = GPOINTER_TO_INT(a);
+       gint id_b = GPOINTER_TO_INT(b);
+       const gchar *tag_a = tags_get_tag(id_a);
+       const gchar *tag_b = tags_get_tag(id_b);
+       
+       if (tag_a == NULL)
+               return tag_b == NULL ? 0:1;
+       
+       if (tag_b == NULL)
+               return tag_a == NULL ? 0:1;
+
+       return g_utf8_collate(tag_a, tag_b);
+}
+
 static void summary_tags_menu_create(SummaryView *summaryview, gboolean refresh)
 {
        GtkWidget *label_menuitem;
        GtkWidget *menu;
        GtkWidget *item;
        GSList *cur = tags_get_list();
-       GSList *orig = cur;
+       GSList *orig = NULL;
        gboolean existing_tags = FALSE;
 
+       cur = orig = g_slist_sort(cur, summary_tag_cmp_list);
        label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
                                                   "/Tags");
        g_signal_connect(G_OBJECT(label_menuitem), "activate",
@@ -5559,6 +5690,137 @@ static gboolean summary_popup_menu(GtkWidget *widget, gpointer data)
        return TRUE;
 }
 
+#if GTK_CHECK_VERSION(2,12,0) && !MAEMO
+static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column)
+{
+       MsgFlags flags;
+
+       if (!info)
+               return NULL;
+
+       flags = info->flags;
+
+       switch(summaryview->col_state[column].type) {
+               case S_COL_STATUS:
+                       if (MSG_IS_IGNORE_THREAD(flags)) {
+                               return _("Ignored thread");
+                       } else if (MSG_IS_WATCH_THREAD(flags)) {
+                               return _("Watched thread");
+                       } else if (MSG_IS_SPAM(flags)) {
+                               return _("Spam");
+                       } else if (MSG_IS_NEW(flags)) {
+                               return _("New");
+                       } else if (MSG_IS_UNREAD(flags)) {
+                               return _("Unread");
+                       } else if (MSG_IS_REPLIED(flags)) {
+                               return _("Replied");
+                       } else if (MSG_IS_FORWARDED(flags)) {
+                               return _("Forwarded");
+                       } else {
+                               return NULL;
+                       }
+               case S_COL_MARK:
+                       if (MSG_IS_DELETED(flags)) {
+                               return _("Deleted");
+                       } else if (MSG_IS_MARKED(flags)) {
+                               return _("Marked");
+                       } else if (MSG_IS_MOVE(flags)) {
+                               return _("To be moved");
+                       } else if (MSG_IS_COPY(flags)) {
+                               return _("To be copied");
+                       } else {
+                               return NULL;
+                       }
+               case S_COL_LOCKED:
+                       if (MSG_IS_LOCKED(flags)) {
+                               return _("Locked");
+                       } else {
+                               return NULL;
+                       }
+               case S_COL_MIME:
+                       if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
+                               return _("Signed, has attachment(s)");
+                       } else if (MSG_IS_SIGNED(flags)) {
+                               return _("Signed");
+                       } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
+                               return _("Encrypted, has attachment(s)");
+                       } else if (MSG_IS_ENCRYPTED(flags)) {
+                               return _("Encrypted");
+                       } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
+                               return _("Has attachment(s)");
+                       } else {
+                               return NULL;
+                       }
+               default:
+                       return NULL;
+       }
+}
+static gboolean tooltip_cb (GtkWidget  *widget,
+                            gint        x,
+                            gint        y,
+                            gboolean    keyboard_mode,
+                            GtkTooltip *tooltip,
+                            gpointer    user_data) 
+{
+       GtkCTree *ctree = GTK_CTREE(widget);
+       SummaryView *summaryview = (SummaryView *)user_data;
+       gint row = -1, column = -1;
+       int offset = prefs_common.show_col_headers ? 24:0;
+       GtkCTreeNode *node = NULL;
+       gchar *text = NULL;
+       gchar *formatted = NULL;
+       MsgInfo *info = NULL;
+       GdkRectangle rect;
+
+       if (!prefs_common.show_tooltips)
+               return FALSE;
+
+       if (y - offset < 0)
+               return FALSE;
+
+       if (!gtk_clist_get_selection_info(GTK_CLIST(ctree), x, y - offset,
+                                         &row, &column))
+               return FALSE;
+
+       if ((node = gtk_ctree_node_nth(ctree, row)) == NULL)
+               return FALSE;
+
+       if ((info = gtk_ctree_node_get_row_data(ctree, node)) == NULL)
+               return FALSE;
+
+       switch (gtk_ctree_node_get_cell_type(ctree, node, column)) {
+               case GTK_CELL_TEXT:
+                       if (gtk_ctree_node_get_text(ctree, node, column, &text) != TRUE)
+                               return FALSE;
+                       break;
+               case GTK_CELL_PIXTEXT:
+                       if (gtk_ctree_node_get_pixtext(ctree, node, column, &text, 
+                               NULL, NULL, NULL) != TRUE)
+                               return FALSE;
+                       break;
+               default: 
+                       if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL)
+                               return FALSE;
+       }
+       
+       if (!text || !*text)
+               return FALSE;
+
+       formatted = g_strdup(text);
+       g_strstrip(formatted);
+       
+       gtk_tooltip_set_text (tooltip, formatted);
+       g_free(formatted);
+       
+       rect.x = x - 2;
+       rect.y = y - 2;
+       rect.width = 12;
+       rect.height= 12;        
+       gtk_tooltip_set_tip_area(tooltip, &rect);
+       
+       return TRUE;
+}
+#endif
 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
 {
        GtkWidget *ctree;
@@ -5582,9 +5844,8 @@ static GtkWidget *summary_ctree_create(SummaryView *summaryview)
        ctree = gtk_sctree_new_with_titles
                (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
 
-#ifdef MAEMO
-       gtk_clist_column_titles_hide(GTK_CLIST(ctree));
-#endif
+       if (prefs_common.show_col_headers == FALSE)
+               gtk_clist_column_titles_hide(GTK_CLIST(ctree));
 
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_EXTENDED);
        gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MARK],
@@ -5668,7 +5929,7 @@ static GtkWidget *summary_ctree_create(SummaryView *summaryview)
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE,   summary_score_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED,  summary_locked_clicked);
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TAGS,    summary_tags_clicked);
-
+       
 #undef CLIST_BUTTON_SIGNAL_CONNECT
 
        g_signal_connect(G_OBJECT(ctree), "tree_select_row",
@@ -5726,6 +5987,12 @@ static GtkWidget *summary_ctree_create(SummaryView *summaryview)
                         G_CALLBACK(summary_drag_motion_cb),
                         summaryview);
 
+#if GTK_CHECK_VERSION(2,12,0) && !MAEMO
+       g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL);
+       g_signal_connect(G_OBJECT(ctree), "query-tooltip", 
+                        G_CALLBACK(tooltip_cb),
+                       summaryview);
+#endif
        return ctree;
 }
 
@@ -5881,8 +6148,12 @@ static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
                case GDK_KP_Enter:
                        handled = TRUE;
                        if (summaryview->displayed != summaryview->selected) {
+#ifndef MAEMO
                                summary_display_msg(summaryview,
                                                    summaryview->selected);
+#else
+                               summary_open_row(NULL, summaryview);
+#endif
                                break;
                        }
                        mimeview_scroll_one_line(messageview->mimeview, mod_pressed);
@@ -6197,7 +6468,7 @@ static void summary_reply_cb(SummaryView *summaryview, guint action,
        compose_reply_from_messageview(msgview, msginfo_list, action);
        g_slist_free(msginfo_list);
 }
-
+#ifndef MAEMO
 static void summary_show_all_header_cb(SummaryView *summaryview,
                                       guint action, GtkWidget *widget)
 {
@@ -6212,19 +6483,19 @@ static void summary_add_address_cb(SummaryView *summaryview,
 {
        summary_add_address(summaryview);
 }
-
+#endif
 static void summary_create_filter_cb(SummaryView *summaryview,
                                     guint action, GtkWidget *widget)
 {
        summary_filter_open(summaryview, (PrefsFilterType)action, 0);
 }
-
+#ifndef MAEMO
 static void summary_create_processing_cb(SummaryView *summaryview,
                                         guint action, GtkWidget *widget)
 {
        summary_filter_open(summaryview, (PrefsFilterType)action, 1);
 }
-
+#endif
 static void summary_sort_by_column_click(SummaryView *summaryview,
                                         FolderSortKey sort_key)
 {
@@ -6658,6 +6929,7 @@ static void summary_ignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpoin
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
        g_return_if_fail(msginfo);
 
+       summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
        summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
 
        summary_set_row_marks(summaryview, row);
@@ -6744,6 +7016,100 @@ void summary_toggle_ignore_thread(SummaryView *summaryview)
                summary_ignore_thread(summaryview);
 }
 
+static void summary_watch_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
+{
+       SummaryView *summaryview = (SummaryView *) data;
+       MsgInfo *msginfo;
+
+       msginfo = gtk_ctree_node_get_row_data(ctree, row);
+       g_return_if_fail(msginfo);
+
+       summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0);
+
+       summary_set_row_marks(summaryview, row);
+       debug_print("Message %d is marked as watch thread\n",
+           msginfo->msgnum);
+}
+
+void summary_watch_thread(SummaryView *summaryview)
+{
+       GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
+       GList *cur;
+       gboolean froze = FALSE;
+
+       START_LONG_OPERATION(summaryview, FALSE);
+       for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
+               gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), 
+                                       GTK_CTREE_FUNC(summary_watch_thread_func), 
+                                       summaryview);
+
+       END_LONG_OPERATION(summaryview);
+
+       summary_status_show(summaryview);
+}
+
+static void summary_unwatch_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
+{
+       SummaryView *summaryview = (SummaryView *) data;
+       MsgInfo *msginfo;
+
+       msginfo = gtk_ctree_node_get_row_data(ctree, row);
+       g_return_if_fail(msginfo);
+
+       summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
+
+       summary_set_row_marks(summaryview, row);
+       debug_print("Message %d is marked as unwatch thread\n",
+           msginfo->msgnum);
+}
+
+void summary_unwatch_thread(SummaryView *summaryview)
+{
+       GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
+       GList *cur;
+       gboolean froze = FALSE;
+
+       START_LONG_OPERATION(summaryview, FALSE);
+       for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
+               gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), 
+                                       GTK_CTREE_FUNC(summary_unwatch_thread_func), 
+                                       summaryview);
+
+       END_LONG_OPERATION(summaryview);
+
+       summary_status_show(summaryview);
+}
+
+static void summary_check_watch_thread_func
+               (GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
+{
+       MsgInfo *msginfo;
+       gint *found_watch = (gint *) data;
+
+       if (*found_watch) return;
+       else {
+               msginfo = gtk_ctree_node_get_row_data(ctree, row);
+               *found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags);
+       }               
+}
+
+void summary_toggle_watch_thread(SummaryView *summaryview)
+{
+       GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
+       GList *cur;
+       gint found_watch = 0;
+
+       for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
+               gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data),
+                                       GTK_CTREE_FUNC(summary_check_watch_thread_func),
+                                       &found_watch);
+
+       if (found_watch) 
+               summary_unwatch_thread(summaryview);
+       else 
+               summary_watch_thread(summaryview);
+}
+
 void summary_toggle_show_read_messages(SummaryView *summaryview)
 {
        FolderItemUpdateData source;
@@ -6787,6 +7153,7 @@ void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP, &clipxpm, &clipxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_LOCKED, &lockedxpm, &lockedxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm, &ignorethreadxpmmask);
+       stock_pixmap_gdk(ctree, STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm, &watchthreadxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm, &clipkeyxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_KEY, &keyxpm, &keyxpmmask);
        stock_pixmap_gdk(ctree, STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm, &gpgsignedxpmmask);
@@ -7057,7 +7424,9 @@ gint summaryview_export_mbox_list(SummaryView *summaryview)
        gchar *mbox = filesel_select_file_save(_("Export to mbox file"), NULL);
        gint ret;
        
-       if (mbox == NULL || list == NULL)
+       if (mbox == NULL)
+               return -2;
+       if (list == NULL)
                return -1;
                
        ret = export_list_to_mbox(list, mbox);