talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

summaryview.c (200606B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2025 the Claws Mail team and Hiroyuki Yamamoto
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "defs.h"
     20 
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 #include <gdk/gdkkeysyms.h>
     24 #include <gtk/gtk.h>
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <ctype.h>
     30 
     31 #include "main.h"
     32 #include "menu.h"
     33 #include "mbox.h"
     34 #include "mainwindow.h"
     35 #include "folderview.h"
     36 #include "summaryview.h"
     37 #include "messageview.h"
     38 #include "mimeview.h"
     39 #include "foldersel.h"
     40 #include "procmsg.h"
     41 #include "procheader.h"
     42 #include "sourcewindow.h"
     43 #include "prefs_common.h"
     44 #include "prefs_summary_column.h"
     45 #include "prefs_summary_open.h"
     46 #include "account.h"
     47 #include "compose.h"
     48 #include "file-utils.h"
     49 #include "utils.h"
     50 #include "gtkutils.h"
     51 #include "stock_pixmap.h"
     52 #include "filesel.h"
     53 #include "alertpanel.h"
     54 #include "inputdialog.h"
     55 #include "statusbar.h"
     56 #include "folder.h"
     57 #include "inc.h"
     58 #include "imap.h"
     59 #include "addressbook.h"
     60 #include "addr_compl.h"
     61 #include "folder_item_prefs.h"
     62 #include "string_match.h"
     63 #include "toolbar.h"
     64 #include "hooks.h"
     65 #include "folderutils.h"
     66 #include "log.h"
     67 #include "manual.h"
     68 #include "manage_window.h"
     69 
     70 #define SUMMARY_COL_MARK_WIDTH		10
     71 #define SUMMARY_COL_STATUS_WIDTH	13
     72 #define SUMMARY_COL_LOCKED_WIDTH	13
     73 #define SUMMARY_COL_MIME_WIDTH		11
     74 
     75 static int normal_row_height = -1;
     76 static GtkStyle *bold_style;
     77 
     78 static GdkPixbuf *markxpm;
     79 static GdkPixbuf *deletedxpm;
     80 static GdkPixbuf *movedxpm;
     81 static GdkPixbuf *copiedxpm;
     82 
     83 static GdkPixbuf *newxpm;
     84 static GdkPixbuf *unreadxpm;
     85 static GdkPixbuf *repliedxpm;
     86 static GdkPixbuf *forwardedxpm;
     87 static GdkPixbuf *repliedandforwardedxpm;
     88 static GdkPixbuf *ignorethreadxpm;
     89 static GdkPixbuf *watchthreadxpm;
     90 static GdkPixbuf *lockedxpm;
     91 static GdkPixbuf *spamxpm;
     92 
     93 static GdkPixbuf *clipxpm;
     94 static GdkPixbuf *keyxpm;
     95 static GdkPixbuf *clipkeyxpm;
     96 static GdkPixbuf *keysignxpm;
     97 static GdkPixbuf *gpgsignedxpm;
     98 static GdkPixbuf *clipgpgsignedxpm;
     99 
    100 static void summary_free_msginfo_func	(GtkCMCTree		*ctree,
    101 					 GtkCMCTreeNode		*node,
    102 					 gpointer		 data);
    103 static void summary_set_marks_func	(GtkCMCTree		*ctree,
    104 					 GtkCMCTreeNode		*node,
    105 					 gpointer		 data);
    106 
    107 void  summary_set_menu_sensitive	(SummaryView		*summaryview);
    108 guint summary_get_msgnum		(SummaryView		*summaryview,
    109 					 GtkCMCTreeNode		*node);
    110 
    111 
    112 static void summary_set_hide_menu (SummaryView *summaryview,
    113 				   const gchar *menu_item,
    114 				   guint action);
    115 
    116 static GtkCMCTreeNode *summary_find_prev_msg
    117 					(SummaryView		*summaryview,
    118 					 GtkCMCTreeNode		*current_node,
    119 					 gboolean	 	 start_from_prev);
    120 static GtkCMCTreeNode *summary_find_next_msg
    121 					(SummaryView		*summaryview,
    122 					 GtkCMCTreeNode		*current_node,
    123 					 gboolean	 	 start_from_next);
    124 
    125 static GtkCMCTreeNode *summary_find_prev_flagged_msg
    126 					(SummaryView	*summaryview,
    127 					 GtkCMCTreeNode	*current_node,
    128 					 MsgPermFlags	 flags,
    129 					 gboolean	 start_from_prev);
    130 static GtkCMCTreeNode *summary_find_next_flagged_msg
    131 					(SummaryView	*summaryview,
    132 					 GtkCMCTreeNode	*current_node,
    133 					 MsgPermFlags	 flags,
    134 					 gboolean	 start_from_next);
    135 
    136 static GtkCMCTreeNode *summary_find_msg_by_msgnum
    137 					(SummaryView		*summaryview,
    138 					 guint			 msgnum);
    139 
    140 static void summary_update_status	(SummaryView		*summaryview);
    141 
    142 /* display functions */
    143 static void summary_status_show		(SummaryView		*summaryview);
    144 static void summary_set_column_titles	(SummaryView		*summaryview);
    145 static void summary_set_ctree_from_list	(SummaryView		*summaryview,
    146 					 GSList			*mlist,
    147 					 guint			selected_msgnum);
    148 static inline void summary_set_header	(SummaryView		*summaryview,
    149 					 gchar			*text[],
    150 					 MsgInfo		*msginfo);
    151 static void summary_display_msg		(SummaryView		*summaryview,
    152 					 GtkCMCTreeNode		*row);
    153 static void summary_display_msg_full	(SummaryView		*summaryview,
    154 					 GtkCMCTreeNode		*row,
    155 					 gboolean		 new_window,
    156 					 gboolean		 all_headers,
    157 					 gboolean		 from_sep_msgview);
    158 static void summary_set_row_marks	(SummaryView		*summaryview,
    159 					 GtkCMCTreeNode		*row);
    160 
    161 /* message handling */
    162 static void summary_mark_row		(SummaryView		*summaryview,
    163 					 GtkCMCTreeNode		*row);
    164 static void summary_lock_row		(SummaryView		*summaryview,
    165 					 GtkCMCTreeNode		*row);
    166 static void summary_unlock_row		(SummaryView		*summaryview,
    167 					 GtkCMCTreeNode		*row);
    168 static void summary_mark_row_as_read	(SummaryView		*summaryview,
    169 					 GtkCMCTreeNode		*row);
    170 static void summary_mark_row_as_unread	(SummaryView		*summaryview,
    171 					 GtkCMCTreeNode		*row);
    172 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed);
    173 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed);
    174 static void summary_delete_row		(SummaryView		*summaryview,
    175 					 GtkCMCTreeNode		*row);
    176 static void summary_unmark_row		(SummaryView		*summaryview,
    177 					 GtkCMCTreeNode		*row);
    178 static void summary_move_row_to		(SummaryView		*summaryview,
    179 					 GtkCMCTreeNode		*row,
    180 					 FolderItem		*to_folder);
    181 static void summary_copy_row_to		(SummaryView		*summaryview,
    182 					 GtkCMCTreeNode		*row,
    183 					 FolderItem		*to_folder);
    184 
    185 static gint summary_execute_move	(SummaryView		*summaryview);
    186 static void summary_execute_move_func	(GtkCMCTree		*ctree,
    187 					 GtkCMCTreeNode		*node,
    188 					 gpointer		 data);
    189 static void summary_execute_copy	(SummaryView		*summaryview);
    190 static void summary_execute_copy_func	(GtkCMCTree		*ctree,
    191 					 GtkCMCTreeNode		*node,
    192 					 gpointer		 data);
    193 static void summary_execute_delete	(SummaryView		*summaryview);
    194 static void summary_execute_delete_func	(GtkCMCTree		*ctree,
    195 					 GtkCMCTreeNode		*node,
    196 					 gpointer		 data);
    197 static void summary_execute_expunge	(SummaryView		*summaryview);
    198 
    199 static void summary_thread_init		(SummaryView		*summaryview);
    200 
    201 static void summary_unthread_for_exec		(SummaryView	*summaryview);
    202 static void summary_unthread_for_exec_func	(GtkCMCTree	*ctree,
    203 						 GtkCMCTreeNode	*node,
    204 						 gpointer	 data);
    205 
    206 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp,
    207 			      GSList * mlist);
    208 
    209 static GtkWidget *summary_ctree_create	(SummaryView	*summaryview);
    210 
    211 /* callback functions */
    212 static gint summary_toggle_pressed	(GtkWidget		*eventbox,
    213 					 GdkEventButton		*event,
    214 					 SummaryView		*summaryview);
    215 
    216 static gint summary_folder_eventbox_pressed
    217 					(GtkWidget		*eventbox,
    218 					 GdkEventButton		*event,
    219 					 SummaryView		*summaryview);
    220 static gboolean summary_button_pressed	(GtkWidget		*ctree,
    221 					 GdkEventButton		*event,
    222 					 SummaryView		*summaryview);
    223 static gboolean summary_button_released	(GtkWidget		*ctree,
    224 					 GdkEventButton		*event,
    225 					 SummaryView		*summaryview);
    226 static gboolean summary_key_pressed	(GtkWidget		*ctree,
    227 					 GdkEventKey		*event,
    228 					 SummaryView		*summaryview);
    229 static void summary_tree_expanded	(GtkCMCTree		*ctree,
    230 					 GtkCMCTreeNode		*node,
    231 					 SummaryView		*summaryview);
    232 static void summary_tree_collapsed	(GtkCMCTree		*ctree,
    233 					 GtkCMCTreeNode		*node,
    234 					 SummaryView		*summaryview);
    235 static void summary_selected		(GtkCMCTree		*ctree,
    236 					 GtkCMCTreeNode		*row,
    237 					 gint			 column,
    238 					 SummaryView		*summaryview);
    239 static void summary_unselected		(GtkCMCTree		*ctree,
    240 					 GtkCMCTreeNode		*row,
    241 					 gint			 column,
    242 					 SummaryView		*summaryview);
    243 static void summary_col_resized		(GtkCMCList		*clist,
    244 					 gint			 column,
    245 					 gint			 width,
    246 					 SummaryView		*summaryview);
    247 static void summary_mark_clicked	(GtkWidget		*button,
    248 					 SummaryView		*summaryview);
    249 static void summary_status_clicked	(GtkWidget		*button,
    250 					 SummaryView		*summaryview);
    251 static void summary_mime_clicked	(GtkWidget		*button,
    252 					 SummaryView		*summaryview);
    253 static void summary_num_clicked		(GtkWidget		*button,
    254 					 SummaryView		*summaryview);
    255 static void summary_score_clicked       (GtkWidget *button,
    256 					 SummaryView *summaryview);
    257 static void summary_size_clicked	(GtkWidget		*button,
    258 					 SummaryView		*summaryview);
    259 static void summary_date_clicked	(GtkWidget		*button,
    260 					 SummaryView		*summaryview);
    261 static void summary_from_clicked	(GtkWidget		*button,
    262 					 SummaryView		*summaryview);
    263 static void summary_to_clicked		(GtkWidget		*button,
    264 					 SummaryView		*summaryview);
    265 static void summary_subject_clicked	(GtkWidget		*button,
    266 					 SummaryView		*summaryview);
    267 static void summary_score_clicked	(GtkWidget		*button,
    268 					 SummaryView		*summaryview);
    269 static void summary_locked_clicked	(GtkWidget		*button,
    270 					 SummaryView		*summaryview);
    271 
    272 /* custom compare functions for sorting */
    273 static gint summary_cmp_by_mark		(GtkCMCList		*clist,
    274 					 gconstpointer		 ptr1,
    275 					 gconstpointer		 ptr2);
    276 static gint summary_cmp_by_status	(GtkCMCList		*clist,
    277 					 gconstpointer		 ptr1,
    278 					 gconstpointer		 ptr2);
    279 static gint summary_cmp_by_mime		(GtkCMCList		*clist,
    280 					 gconstpointer		 ptr1,
    281 					 gconstpointer		 ptr2);
    282 static gint summary_cmp_by_num		(GtkCMCList		*clist,
    283 					 gconstpointer		 ptr1,
    284 					 gconstpointer		 ptr2);
    285 static gint summary_cmp_by_size		(GtkCMCList		*clist,
    286 					 gconstpointer		 ptr1,
    287 					 gconstpointer		 ptr2);
    288 static gint summary_cmp_by_date		(GtkCMCList		*clist,
    289 					 gconstpointer		 ptr1,
    290 					 gconstpointer		 ptr2);
    291 static gint summary_cmp_by_thread_date	(GtkCMCList		*clist,
    292 					 gconstpointer		 ptr1,
    293 					 gconstpointer		 ptr2);
    294 static gint summary_cmp_by_from		(GtkCMCList		*clist,
    295 					 gconstpointer		 ptr1,
    296 					 gconstpointer		 ptr2);
    297 static gint summary_cmp_by_simplified_subject
    298 					(GtkCMCList 		*clist,
    299 					 gconstpointer 		 ptr1,
    300 					 gconstpointer 		 ptr2);
    301 static gint summary_cmp_by_score	(GtkCMCList		*clist,
    302 					 gconstpointer		 ptr1,
    303 					 gconstpointer		 ptr2);
    304 static gint summary_cmp_by_label	(GtkCMCList		*clist,
    305 					 gconstpointer		 ptr1,
    306 					 gconstpointer		 ptr2);
    307 static gint summary_cmp_by_to		(GtkCMCList		*clist,
    308 					 gconstpointer		 ptr1,
    309 					 gconstpointer		 ptr2);
    310 static gint summary_cmp_by_subject	(GtkCMCList		*clist,
    311 					 gconstpointer		 ptr1,
    312 					 gconstpointer		 ptr2);
    313 static gint summary_cmp_by_locked	(GtkCMCList 		*clist,
    314 				         gconstpointer 		 ptr1,
    315 					 gconstpointer 		 ptr2);
    316 
    317 static void summary_find_answers	(SummaryView 	*summaryview,
    318 					 MsgInfo	*msg);
    319 
    320 static gboolean summary_update_msg	(gpointer source, gpointer data);
    321 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data);
    322 static gboolean summary_update_folder_hook(gpointer source, gpointer data);
    323 static void summary_thread_build(SummaryView *summaryview);
    324 
    325 GtkTargetEntry summary_drag_types[3] =
    326 {
    327 	{"text/uri-list", 0, TARGET_MAIL_URI_LIST},
    328 	{"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
    329 	{"claws-mail/msg-path-list", 0, TARGET_MAIL_CM_PATH_LIST},
    330 };
    331 
    332 static void summary_reedit_cb(GtkAction *gaction, gpointer data);
    333 static void summary_reply_cb(GtkAction *gaction, gpointer data);
    334 
    335 /* Only submenus and specifically-handled menu entries here */
    336 static GtkActionEntry summary_popup_entries[] =
    337 {
    338 	{"SummaryViewPopup",                      NULL, "SummaryViewPopup", NULL, NULL, NULL },
    339 	{"SummaryViewPopup/Reedit",               NULL, N_("Re-edit"), NULL, NULL, G_CALLBACK(summary_reedit_cb) },
    340 	{"SummaryViewPopup/Reply",                NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY */
    341 	{"SummaryViewPopup/ReplyTo",              NULL, N_("Repl_y to"), NULL, NULL, NULL },
    342 	{"SummaryViewPopup/ReplyTo/All",          NULL, N_("_All"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_ALL */
    343 	{"SummaryViewPopup/ReplyTo/Sender",       NULL, N_("_Sender"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_SENDER */
    344 	{"SummaryViewPopup/ReplyTo/List",         NULL, N_("Mailing _list"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_LIST */
    345 
    346 	{"SummaryViewPopup/Forward",              NULL, N_("_Forward"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_INLINE */
    347 	{"SummaryViewPopup/ForwardAtt",           NULL, N_("For_ward as attachment"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_AS_ATTACH */
    348 	{"SummaryViewPopup/Redirect",             NULL, N_("R_edirect"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REDIRECT */
    349 	{"SummaryViewPopup/Marks",                NULL, N_("_Marks"), NULL, NULL, NULL },
    350 	{"SummaryViewPopup/View",                 NULL, N_("_View"), NULL, NULL, NULL },
    351 };
    352 
    353 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data);
    354 static void summary_header_set_displayed_columns_cb(GtkAction *gaction, gpointer data);
    355 
    356 static GtkActionEntry summary_header_popup_entries[] =
    357 {
    358 	{"SummaryViewHeaderPopup",                     NULL, "SummaryViewHeaderPopup", NULL, NULL, NULL },
    359 	{"SummaryViewHeaderPopup/SetDisplayedColumns", NULL, N_("_Set displayed columns"), NULL, NULL, G_CALLBACK(summary_header_set_displayed_columns_cb) }
    360 };
    361 
    362 static GtkToggleActionEntry summary_header_popup_toggle_entries[] =
    363 {
    364 	{"SummaryViewHeaderPopup/LockColumnHeaders",     NULL, N_("_Lock column headers"), NULL, NULL, G_CALLBACK(summary_header_lock_sorting_cb), FALSE }
    365 };
    366 
    367 static const gchar *const col_label[N_SUMMARY_COLS] = {
    368 	"",            /* S_COL_MARK    */
    369 	N_("S"),       /* S_COL_STATUS  */
    370 	"",            /* S_COL_MIME    */
    371 	N_("Subject"), /* S_COL_SUBJECT */
    372 	N_("From"),    /* S_COL_FROM    */
    373 	N_("To"),      /* S_COL_TO      */
    374 	N_("Date"),    /* S_COL_DATE    */
    375 	N_("Size"),    /* S_COL_SIZE    */
    376 	N_("#"),       /* S_COL_NUMBER  */
    377 	N_("Score"),   /* S_COL_SCORE   */
    378 	"",            /* S_COL_LOCKED  */
    379 };
    380 
    381 void summary_freeze(SummaryView *summaryview)
    382 {
    383 	if (summaryview)
    384 		gtk_cmclist_freeze(GTK_CMCLIST(summaryview->ctree));
    385 }
    386 
    387 void summary_thaw(SummaryView *summaryview)
    388 {
    389 	if (summaryview)
    390 		gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
    391 }
    392 
    393 void summary_thaw_with_status(SummaryView *summaryview)
    394 {
    395 	if (summaryview) {
    396 		summary_status_show(summaryview);
    397 		gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
    398 	}
    399 }
    400 
    401 void summary_grab_focus(SummaryView *summaryview)
    402 {
    403 	if (summaryview)
    404 		gtk_widget_grab_focus(summaryview->ctree);
    405 }
    406 
    407 GtkWidget *summary_get_main_widget(SummaryView *summaryview)
    408 {
    409 	if (summaryview)
    410 		return summaryview->ctree;
    411 	else
    412 		return NULL;
    413 }
    414 
    415 #define START_LONG_OPERATION(summaryview,force_freeze) {	\
    416 	summary_lock(summaryview);				\
    417 	main_window_cursor_wait(summaryview->mainwin);		\
    418 	if (force_freeze || sc_g_list_bigger(GTK_CMCLIST(summaryview->ctree)->selection, 1)) {\
    419 		froze = TRUE;						\
    420 		summary_freeze(summaryview);	\
    421 	}							\
    422 	folder_item_update_freeze();				\
    423 	inc_lock();						\
    424 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,		\
    425 		      summaryview->msginfo_update_callback_id);	\
    426 }
    427 #define END_LONG_OPERATION(summaryview) {			\
    428 	inc_unlock();						\
    429 	folder_item_update_thaw();				\
    430 	if (froze) 						\
    431 		summary_thaw(summaryview);	\
    432 	main_window_cursor_normal(summaryview->mainwin);	\
    433 	summary_unlock(summaryview);				\
    434 	summaryview->msginfo_update_callback_id =		\
    435 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 	\
    436 		summary_update_msg, (gpointer) summaryview);	\
    437 }
    438 
    439 SummaryView *summary_create(MainWindow *mainwin)
    440 {
    441 	SummaryView *summaryview;
    442 	GtkWidget *vbox;
    443 	GtkWidget *scrolledwin;
    444 	GtkWidget *ctree;
    445 	GtkWidget *hbox;
    446 	GtkWidget *hbox_l;
    447 	GtkWidget *stat_box;
    448 	GtkWidget *stat_box2;
    449 	GtkWidget *stat_vbox;
    450 	GtkWidget *statlabel_folder;
    451 	GtkWidget *statlabel_select;
    452 	GtkWidget *statlabel_msgs;
    453 	GtkWidget *hbox_spc;
    454 	GtkWidget *toggle_eventbox;
    455 	GtkWidget *toggle_arrow;
    456 
    457 	debug_print("Creating summary view...\n");
    458 	summaryview = g_new0(SummaryView, 1);
    459 
    460 #define SUMMARY_VBOX_SPACING 3
    461 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, SUMMARY_VBOX_SPACING);
    462 	gtk_widget_set_name(GTK_WIDGET(vbox), "summaryview");
    463 
    464 	/* create status label */
    465 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    466 	gtk_widget_show(hbox);
    467 
    468 	stat_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    469 	gtk_widget_show(stat_vbox);
    470 
    471 	stat_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    472 	gtk_widget_show(stat_box);
    473 
    474 	stat_box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    475 	gtk_widget_show(stat_box2);
    476 
    477 	gtk_box_pack_start(GTK_BOX(hbox), stat_vbox, TRUE, TRUE, 0);
    478 	gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box, TRUE, TRUE, 0);
    479 	gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box2, TRUE, TRUE, 0);
    480 
    481 	hbox_l = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    482 	gtk_widget_show(hbox_l);
    483 	gtk_box_pack_start(GTK_BOX(stat_box), hbox_l, TRUE, TRUE, 0);
    484 
    485 	statlabel_folder = gtk_label_new("");
    486 	gtk_widget_show(statlabel_folder);
    487 	gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
    488 	statlabel_select = gtk_label_new("");
    489 	gtk_widget_show(statlabel_select);
    490 	gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
    491 
    492 	/* toggle view button */
    493 	toggle_eventbox = gtk_event_box_new();
    494 	gtk_widget_show(toggle_eventbox);
    495 
    496 	gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, HSPACING_NARROW);
    497 
    498 	toggle_arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU);
    499 	gtk_widget_show(toggle_arrow);
    500 	gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
    501 	g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
    502 			 G_CALLBACK(summary_toggle_pressed),
    503 			 summaryview);
    504 
    505 	statlabel_msgs = gtk_label_new("");
    506 	gtk_widget_show(statlabel_msgs);
    507 	gtk_box_pack_end(GTK_BOX(stat_box), statlabel_msgs, FALSE, FALSE, HSPACING_NARROW);
    508 
    509 	hbox_spc = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    510 	gtk_widget_show(hbox_spc);
    511 	gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
    512 
    513 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
    514 	gtk_widget_show(scrolledwin);
    515 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
    516 				       GTK_POLICY_AUTOMATIC,
    517 				       GTK_POLICY_AUTOMATIC);
    518 	summaryview->mainwidget_book = gtk_notebook_new();
    519         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
    520         gtk_notebook_set_show_border(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
    521 
    522 	gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
    523 		scrolledwin);
    524 	gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
    525 
    526 	ctree = summary_ctree_create(summaryview);
    527 	gtk_widget_show(ctree);
    528 
    529 	gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
    530 					    GTK_CMCLIST(ctree)->hadjustment);
    531 	gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
    532 					    GTK_CMCLIST(ctree)->vadjustment);
    533 	gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
    534 
    535 	/* status label */
    536 	gtk_widget_show_all(stat_vbox);
    537 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    538 
    539 	/* create popup menu */
    540 
    541 	gtk_action_group_add_actions(mainwin->action_group,
    542 			summary_popup_entries,
    543 			G_N_ELEMENTS(summary_popup_entries),
    544 			(gpointer)summaryview);
    545 
    546 	gtk_action_group_add_actions(mainwin->action_group,
    547 			summary_header_popup_entries,
    548 			G_N_ELEMENTS(summary_header_popup_entries),
    549 			(gpointer)summaryview);
    550 
    551 	gtk_action_group_add_toggle_actions(mainwin->action_group,
    552 			summary_header_popup_toggle_entries,
    553 			G_N_ELEMENTS(summary_header_popup_toggle_entries),
    554 			(gpointer)summaryview);
    555 
    556 	summaryview->ui_manager = gtk_ui_manager_new();
    557 	summaryview->action_group = cm_menu_create_action_group_full(summaryview->ui_manager,"Menu", summary_popup_entries,
    558 			G_N_ELEMENTS(summary_popup_entries), (gpointer)summaryview);
    559 
    560 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewPopup", "SummaryViewPopup", GTK_UI_MANAGER_MENU)
    561 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reedit", "SummaryViewPopup/Reedit", GTK_UI_MANAGER_MENUITEM)
    562 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reply", "SummaryViewPopup/Reply", GTK_UI_MANAGER_MENUITEM)
    563 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ReplyTo", "SummaryViewPopup/ReplyTo", GTK_UI_MANAGER_MENU)
    564 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
    565 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Forward", "SummaryViewPopup/Forward", GTK_UI_MANAGER_MENUITEM)
    566 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ForwardAtt", "SummaryViewPopup/ForwardAtt", GTK_UI_MANAGER_MENUITEM)
    567 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Redirect", "SummaryViewPopup/Redirect", GTK_UI_MANAGER_MENUITEM)
    568 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
    569 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Move", "Message/Move", GTK_UI_MANAGER_MENUITEM)
    570 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Copy", "Message/Copy", GTK_UI_MANAGER_MENUITEM)
    571 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Trash", "Message/Trash", GTK_UI_MANAGER_MENUITEM)
    572 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Delete", "Message/Delete", GTK_UI_MANAGER_MENUITEM)
    573 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
    574 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Marks", "SummaryViewPopup/Marks", GTK_UI_MANAGER_MENU)
    575 
    576 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
    577 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "AddSenderToAB", "Tools/AddSenderToAB", GTK_UI_MANAGER_MENUITEM)
    578 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator5", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
    579 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "View", "SummaryViewPopup/View", GTK_UI_MANAGER_MENU)
    580 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "SaveAs", "File/SaveAs", GTK_UI_MANAGER_MENUITEM)
    581 
    582 	/* submenus - replyto */
    583 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "All", "SummaryViewPopup/ReplyTo/All", GTK_UI_MANAGER_MENUITEM)
    584 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "Sender", "SummaryViewPopup/ReplyTo/Sender", GTK_UI_MANAGER_MENUITEM)
    585 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "MailingList", "SummaryViewPopup/ReplyTo/List", GTK_UI_MANAGER_MENUITEM)
    586 
    587 	/* submenus - mark */
    588 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Mark", "Message/Marks/Mark", GTK_UI_MANAGER_MENUITEM)
    589 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Unmark", "Message/Marks/Unmark", GTK_UI_MANAGER_MENUITEM)
    590 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator1", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR)
    591 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkRead", "Message/Marks/MarkRead", GTK_UI_MANAGER_MENUITEM)
    592 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkUnread", "Message/Marks/MarkUnread", GTK_UI_MANAGER_MENUITEM)
    593 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator2", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR)
    594 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkAllRead", "Message/Marks/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
    595 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkAllUnread", "Message/Marks/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
    596 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator3", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR)
    597 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "IgnoreThread", "Message/Marks/IgnoreThread", GTK_UI_MANAGER_MENUITEM)
    598 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "UnignoreThread", "Message/Marks/UnignoreThread", GTK_UI_MANAGER_MENUITEM)
    599 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "WatchThread", "Message/Marks/WatchThread", GTK_UI_MANAGER_MENUITEM)
    600 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "UnwatchThread", "Message/Marks/UnwatchThread", GTK_UI_MANAGER_MENUITEM)
    601 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator4", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR)
    602 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Lock", "Message/Marks/Lock", GTK_UI_MANAGER_MENUITEM)
    603 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Unlock", "Message/Marks/Unlock", GTK_UI_MANAGER_MENUITEM)
    604 
    605 	/* submenus - view */
    606 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "OpenNewWindow", "View/OpenNewWindow", GTK_UI_MANAGER_MENUITEM)
    607 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "MessageSource", "View/MessageSource", GTK_UI_MANAGER_MENUITEM)
    608 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "AllHeaders", "View/AllHeaders", GTK_UI_MANAGER_MENUITEM)
    609 
    610 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewHeaderPopup", "SummaryViewHeaderPopup", GTK_UI_MANAGER_MENU)
    611 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "LockColumnHeaders", "SummaryViewHeaderPopup/LockColumnHeaders", GTK_UI_MANAGER_MENUITEM)
    612 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "SetDisplayedColumns", "SummaryViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
    613 
    614 	summaryview->popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
    615 				gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewPopup")) );
    616 	summaryview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
    617 				gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup")) );
    618 
    619 	summaryview->vbox = vbox;
    620 	summaryview->scrolledwin = scrolledwin;
    621 	summaryview->ctree = ctree;
    622 	summaryview->hbox = hbox;
    623 	summaryview->hbox_l = hbox_l;
    624 	summaryview->hbox_spc = hbox_spc;
    625 	summaryview->stat_box = stat_box;
    626 	summaryview->stat_box2 = stat_box2;
    627 	summaryview->statlabel_folder = statlabel_folder;
    628 	summaryview->statlabel_select = statlabel_select;
    629 	summaryview->statlabel_msgs = statlabel_msgs;
    630 	summaryview->toggle_eventbox = toggle_eventbox;
    631 	summaryview->toggle_arrow = toggle_arrow;
    632 	summaryview->lock_count = 0;
    633 	summaryview->msginfo_update_callback_id =
    634 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview);
    635 	summaryview->folder_item_update_callback_id =
    636 		hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
    637 				summary_update_folder_item_hook,
    638 				(gpointer) summaryview);
    639 	summaryview->folder_update_callback_id =
    640 		hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
    641 				summary_update_folder_hook,
    642 				(gpointer) summaryview);
    643 
    644 	summaryview->target_list = gtk_target_list_new(summary_drag_types, 3);
    645 
    646 	/* CLAWS: need this to get the SummaryView * from
    647 	 * the CList */
    648 	g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview);
    649 
    650 	gtk_widget_show_all(vbox);
    651 
    652 	gtk_widget_show(vbox);
    653 	return summaryview;
    654 }
    655 
    656 void summary_relayout(SummaryView *summaryview)
    657 {
    658 	gtk_widget_realize(summaryview->stat_box);
    659 
    660 	g_object_ref(summaryview->hbox_l);
    661 	g_object_ref(summaryview->statlabel_msgs);
    662 
    663 	gtk_container_remove(GTK_CONTAINER(
    664 		gtk_widget_get_parent(summaryview->hbox_l)), summaryview->hbox_l);
    665 	gtk_container_remove(GTK_CONTAINER(
    666 		gtk_widget_get_parent(summaryview->statlabel_msgs)), summaryview->statlabel_msgs);
    667 
    668 	switch (prefs_common.layout_mode) {
    669 	case NORMAL_LAYOUT:
    670 		gtk_box_pack_start(GTK_BOX(summaryview->stat_box),
    671 				summaryview->hbox_l, TRUE, TRUE, 0);
    672 		gtk_box_pack_end(GTK_BOX(summaryview->stat_box),
    673 				summaryview->statlabel_msgs, FALSE, FALSE, HSPACING_NARROW);
    674 		gtk_widget_show_all(summaryview->stat_box);
    675 		gtk_widget_show_all(summaryview->stat_box2);
    676 		gtk_widget_show(summaryview->toggle_eventbox);
    677 		break;
    678 	case VERTICAL_LAYOUT:
    679 		gtk_box_pack_start(GTK_BOX(summaryview->stat_box),
    680 				summaryview->hbox_l, TRUE, TRUE, 0);
    681 		gtk_box_pack_start(GTK_BOX(summaryview->stat_box2),
    682 				summaryview->statlabel_msgs, FALSE, FALSE, HSPACING_NARROW);
    683 		gtk_widget_show_all(summaryview->stat_box);
    684 		gtk_widget_show_all(summaryview->stat_box2);
    685 		gtk_widget_show(summaryview->toggle_eventbox);
    686 		gtk_widget_show(summaryview->statlabel_msgs);
    687 		break;
    688 	}
    689 	summary_set_column_order(summaryview);
    690 
    691 	g_object_unref(summaryview->hbox_l);
    692 	g_object_unref(summaryview->statlabel_msgs);
    693 }
    694 
    695 static void summary_set_folder_pixmap(SummaryView *summaryview, StockPixmap icon)
    696 {
    697 	GtkWidget *pixmap;
    698 	if (!summaryview->folder_pixmap_eventbox) {
    699 		summaryview->folder_pixmap_eventbox = gtk_event_box_new();
    700 		gtk_widget_show(summaryview->folder_pixmap_eventbox);
    701 		gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, FALSE, FALSE, 4);
    702 		gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, 0); /* search_toggle before */
    703 		g_signal_connect(G_OBJECT(summaryview->folder_pixmap_eventbox), "button_press_event",
    704 			 G_CALLBACK(summary_folder_eventbox_pressed),
    705 			 summaryview);
    706 	}
    707 	if (summaryview->folder_pixmap)
    708 		gtk_widget_destroy(summaryview->folder_pixmap);
    709 
    710 	pixmap = stock_pixmap_widget(icon);
    711 	gtk_container_add(GTK_CONTAINER(summaryview->folder_pixmap_eventbox), pixmap);
    712 	gtk_widget_show(pixmap);
    713 	summaryview->folder_pixmap = pixmap;
    714 }
    715 
    716 void summary_init(SummaryView *summaryview)
    717 {
    718 	GtkWidget *pixmap;
    719 
    720 	gtk_widget_realize(summaryview->ctree);
    721 	stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
    722 	stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
    723 	stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
    724 	stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
    725 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
    726 	stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
    727 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
    728 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
    729 	stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
    730 	stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
    731 	stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
    732 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
    733 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
    734 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
    735 	stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
    736 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
    737 	stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
    738 	stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
    739 	stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
    740 
    741 	summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
    742 
    743 	/* Init summaryview prefs */
    744 	summaryview->sort_key = SORT_BY_NONE;
    745 	summaryview->sort_type = SORT_ASCENDING;
    746 
    747 	/* Init summaryview extra data */
    748 	summaryview->simplify_subject_preg = NULL;
    749 	summary_clear_list(summaryview);
    750 	summary_set_column_titles(summaryview);
    751 	main_create_mailing_list_menu (summaryview->mainwin, NULL);
    752 	summary_set_menu_sensitive(summaryview);
    753 
    754 	summaryview->header_menu_lock = FALSE;
    755 }
    756 
    757 #define CURRENTLY_DISPLAYED(m) \
    758 ( (m->msgnum == displayed_msgnum) \
    759   && (!g_ascii_strcasecmp(m->folder->name,item->name)) )
    760 
    761 #define FOLDER_SHOWS_TO_HDR(i) \
    762 ( i && (folder_has_parent_of_type(i, F_OUTBOX) \
    763         || folder_has_parent_of_type(i, F_DRAFT) \
    764         || folder_has_parent_of_type(i, F_QUEUE)) )
    765 
    766 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
    767 {
    768 	gboolean show_from = FALSE, show_to = FALSE;
    769 	gboolean showing_from = FALSE, showing_to = FALSE;
    770 	gint from_pos = 0, to_pos = 0;
    771 	SummaryColumnState *col_state = summaryview->col_state;
    772 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
    773 
    774 	if (!item || ((prefs_common.layout_mode == VERTICAL_LAYOUT) && prefs_common.two_line_vert) )
    775 		return;
    776 	if (FOLDER_SHOWS_TO_HDR(item))
    777 		show_to = TRUE;
    778 	else
    779 		show_from = TRUE;
    780 
    781 	from_pos = summaryview->col_pos[S_COL_FROM];
    782 	to_pos = summaryview->col_pos[S_COL_TO];
    783 	showing_from = col_state[from_pos].visible;
    784 	showing_to = col_state[to_pos].visible;
    785 
    786 	if (showing_from && showing_to) {
    787 		debug_print("showing both\n");
    788 		return;
    789 	}
    790 
    791 	if (!showing_from && !showing_to) {
    792 		debug_print("showing none\n");
    793 		return;
    794 	}
    795 
    796 	debug_print("showing %s %s, must show %s %s\n",
    797 		showing_from?"From":"",
    798 		showing_to?"To":"",
    799 		show_from?"From":"",
    800 		show_to?"To":"");
    801 
    802 	if (showing_from == show_from && showing_to == show_to)
    803 		return;
    804 	/* else we'll switch both */
    805 
    806 	debug_print("switching columns\n");
    807 	col_state[from_pos].type = S_COL_TO;
    808 	col_state[from_pos].visible = show_to;
    809 
    810 	col_state[to_pos].type = S_COL_FROM;
    811 	col_state[to_pos].visible = show_from;
    812 
    813 	summaryview->col_pos[S_COL_TO] = from_pos;
    814 	summaryview->col_pos[S_COL_FROM] = to_pos;
    815 
    816 	gtk_cmclist_set_column_visibility
    817 		(GTK_CMCLIST(ctree), from_pos, col_state[from_pos].visible);
    818 	gtk_cmclist_set_column_visibility
    819 		(GTK_CMCLIST(ctree), to_pos, col_state[to_pos].visible);
    820 
    821 	summary_set_column_titles(summaryview);
    822 }
    823 
    824 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
    825 {
    826 	int u = 0, n = 0, m = 0, t = 0, r = 0, f = 0, l = 0, i = 0, w = 0;
    827 	GSList *cur;
    828 	for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
    829 		MsgInfo * msginfo = (MsgInfo *) cur->data;
    830 		t++;
    831 		if (MSG_IS_NEW(msginfo->flags))
    832 			n++;
    833 		if (MSG_IS_UNREAD(msginfo->flags))
    834 			u++;
    835 		if (MSG_IS_MARKED(msginfo->flags))
    836 			m++;
    837 		if (MSG_IS_REPLIED(msginfo->flags))
    838 			r++;
    839 		if (MSG_IS_FORWARDED(msginfo->flags))
    840 			f++;
    841 		if (MSG_IS_LOCKED(msginfo->flags))
    842 			l++;
    843 		if (MSG_IS_IGNORE_THREAD(msginfo->flags))
    844 			i++;
    845 		if (MSG_IS_WATCH_THREAD(msginfo->flags))
    846 			w++;
    847 	}
    848 	if (t != item->total_msgs
    849 	||  n != item->new_msgs
    850 	||  u != item->unread_msgs
    851 	||  m != item->marked_msgs
    852 	||  r != item->replied_msgs
    853 	||  f != item->forwarded_msgs
    854 	||  l != item->locked_msgs
    855 	||  i != item->ignored_msgs
    856 	||  w != item->watched_msgs
    857 	||  (m == 0 && item->unreadmarked_msgs != 0)
    858 	||  item->unreadmarked_msgs < 0) {
    859 		debug_print("Inconsistency\n");
    860 		folder_item_scan_full(item, FALSE);
    861 		return FALSE;
    862 	}
    863 	return TRUE;
    864 }
    865 
    866 gboolean summaryview_search_root_progress(gpointer data, guint at, guint matched, guint total)
    867 {
    868 	gint interval = 100;
    869 
    870 	statusbar_progress_all(at, total, interval);
    871 
    872 	if (at % interval == 0)
    873 		GTK_EVENTS_FLUSH();
    874 
    875 	return TRUE;
    876 }
    877 
    878 gboolean summary_show(SummaryView *summaryview, FolderItem *item, gboolean avoid_refresh)
    879 {
    880 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
    881 	GtkCMCTreeNode *node = NULL;
    882 	GSList *mlist = NULL;
    883 	gchar *buf;
    884 	gboolean is_refresh;
    885 	guint selected_msgnum = 0;
    886 	guint displayed_msgnum = 0;
    887 	GSList *cur;
    888         GSList *not_killed;
    889 	gboolean hidden_removed = FALSE;
    890 
    891 	if (summary_is_locked(summaryview)) return FALSE;
    892 
    893 	if (!summaryview->mainwin)
    894 		return FALSE;
    895 	summary_switch_from_to(summaryview, item);
    896 
    897 	inc_lock();
    898 	summary_lock(summaryview);
    899 
    900 	menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
    901 
    902 	utils_free_regex();
    903 
    904 	is_refresh = (item == summaryview->folder_item && !avoid_refresh) ? TRUE : FALSE;
    905 
    906 	if (item && item->folder->klass->item_opened) {
    907 		item->folder->klass->item_opened(item);
    908 	}
    909 
    910 	if (!is_refresh) {
    911 		main_create_mailing_list_menu (summaryview->mainwin, NULL);
    912 	} else {
    913 		selected_msgnum = summary_get_msgnum(summaryview,
    914 						     summaryview->selected);
    915 		displayed_msgnum = summary_get_msgnum(summaryview,
    916 						      summaryview->displayed);
    917 	}
    918 
    919 	/* process the marks if any */
    920 	if (!is_refresh &&
    921 			(summaryview->mainwin->lock_count == 0 &&
    922 			 (summaryview->moved > 0 || summaryview->copied > 0))) {
    923 		AlertValue val;
    924 		gboolean changed = FALSE;
    925 
    926 		val = alertpanel(_("Process mark"),
    927 				 _("Some marks are left. Process them?"),
    928 				 NULL, _("_No"), NULL, _("_Yes"), NULL, _("_Cancel"),
    929 				 ALERTFOCUS_FIRST);
    930 		if (G_ALERTALTERNATE == val) {
    931 			summary_unlock(summaryview);
    932 			summary_execute(summaryview);
    933 			summary_lock(summaryview);
    934 			changed = TRUE;
    935 		} else if (G_ALERTDEFAULT == val) {
    936 			/* DO NOTHING */
    937 		} else {
    938 			summary_unlock(summaryview);
    939 			inc_unlock();
    940 			return FALSE;
    941 		}
    942 		if (changed)
    943 			folder_update_op_count();
    944 	}
    945 
    946 	summary_freeze(summaryview);
    947 
    948 	summary_clear_list(summaryview);
    949 
    950 	buf = NULL;
    951 	if (!item || !item->path || !folder_item_parent(item) || item->no_select) {
    952 		g_free(buf);
    953 		debug_print("empty folder (%p %s %p %d)\n",
    954 				item,
    955 				(item && item->path)?item->path:"(null)",
    956 				item?folder_item_parent(item):0x0,
    957 				item?item->no_select:FALSE);
    958 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE);
    959 		summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE);
    960 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE);
    961 		summary_clear_all(summaryview);
    962 		summaryview->folder_item = item;
    963 		summary_thaw(summaryview);
    964 		summary_unlock(summaryview);
    965 		inc_unlock();
    966 		return TRUE;
    967 	}
    968 	g_free(buf);
    969 
    970 	if (!is_refresh)
    971 		messageview_clear(summaryview->messageview);
    972 
    973 	summaryview->folder_item = item;
    974 	item->opened = TRUE;
    975 
    976 	buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
    977 	debug_print("%s\n", buf);
    978 	STATUSBAR_PUSH(summaryview->mainwin, buf);
    979 	g_free(buf);
    980 
    981 	main_window_cursor_wait(summaryview->mainwin);
    982 
    983 	mlist = folder_item_get_msg_list(item);
    984 
    985 	if (!summary_check_consistency(item, mlist)) {
    986 		debug_print("reloading due to inconsistency\n");
    987 		procmsg_msg_list_free(mlist);
    988 		mlist = folder_item_get_msg_list(item);
    989 	}
    990 
    991 	if ((summaryview->folder_item->hide_read_msgs
    992              || summaryview->folder_item->hide_del_msgs
    993              || summaryview->folder_item->hide_read_threads)) {
    994 		GSList *not_killed;
    995 
    996 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
    997 						summaryview->folder_item->hide_read_msgs);
    998 		summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
    999 						summaryview->folder_item->hide_del_msgs);
   1000 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
   1001 						summaryview->folder_item->hide_read_threads);
   1002 		not_killed = NULL;
   1003 		for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
   1004 			MsgInfo * msginfo = (MsgInfo *) cur->data;
   1005 
   1006 			if (!msginfo->hidden) {
   1007 				if (MSG_IS_DELETED(msginfo->flags) && summaryview->folder_item->hide_del_msgs) {
   1008 					procmsg_msginfo_free(&msginfo);
   1009 					continue;
   1010 				}
   1011 				if (summaryview->folder_item->hide_read_msgs) {
   1012 					if (MSG_IS_UNREAD(msginfo->flags) &&
   1013 					    !MSG_IS_IGNORE_THREAD(msginfo->flags))
   1014 						not_killed = g_slist_prepend(not_killed, msginfo);
   1015 					else if (MSG_IS_MARKED(msginfo->flags) ||
   1016 						 MSG_IS_LOCKED(msginfo->flags))
   1017 						not_killed = g_slist_prepend(not_killed, msginfo);
   1018 					else if (is_refresh &&
   1019 						(msginfo->msgnum == selected_msgnum ||
   1020 						 msginfo->msgnum == displayed_msgnum))
   1021 						not_killed = g_slist_prepend(not_killed, msginfo);
   1022 					else
   1023 						procmsg_msginfo_free(&msginfo);
   1024 				} else {
   1025 					not_killed = g_slist_prepend(not_killed, msginfo);
   1026 				}
   1027 			 } else
   1028 			 	procmsg_msginfo_free(&msginfo);
   1029 		}
   1030 		hidden_removed = TRUE;
   1031 		g_slist_free(mlist);
   1032 		mlist = not_killed;
   1033 	} else {
   1034 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE);
   1035 		summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE);
   1036 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE);
   1037 	}
   1038 
   1039 	if (!hidden_removed) {
   1040 		not_killed = NULL;
   1041 		for (cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
   1042 			MsgInfo * msginfo = (MsgInfo *) cur->data;
   1043 			if (!msginfo->hidden)
   1044 				not_killed = g_slist_prepend(not_killed, msginfo);
   1045 			else
   1046 				procmsg_msginfo_free(&msginfo);
   1047 		}
   1048 		g_slist_free(mlist);
   1049 		mlist = not_killed;
   1050 	}
   1051 
   1052 	STATUSBAR_POP(summaryview->mainwin);
   1053 
   1054 	/* set ctree and hash table from the msginfo list, and
   1055 	   create the thread */
   1056 	summary_set_ctree_from_list(summaryview, mlist, selected_msgnum);
   1057 
   1058 	g_slist_free(mlist);
   1059 
   1060 	if (is_refresh) {
   1061 		summaryview->displayed =
   1062 			summary_find_msg_by_msgnum(summaryview,
   1063 						   displayed_msgnum);
   1064 		if (!summaryview->displayed)
   1065 			messageview_clear(summaryview->messageview);
   1066 		summary_unlock(summaryview);
   1067 
   1068 		summary_select_by_msgnum(summaryview, selected_msgnum, FALSE);
   1069 
   1070 		summary_lock(summaryview);
   1071 		if (!summaryview->selected) {
   1072 			/* no selected message - select first unread
   1073 			   message, but do not display it */
   1074 			node = summary_find_next_flagged_msg(summaryview, NULL,
   1075 							     MSG_UNREAD, FALSE);
   1076 			if (node == NULL && GTK_CMCLIST(ctree)->row_list != NULL)
   1077 				node = gtk_cmctree_node_nth
   1078 					(ctree,
   1079 					 item->sort_type == SORT_DESCENDING
   1080 					 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
   1081 			summary_unlock(summaryview);
   1082 
   1083 			summary_select_node(summaryview, node, OPEN_SELECTED_ON_FOLDER_OPEN);
   1084 
   1085 			summary_lock(summaryview);
   1086 		}
   1087 	} else {
   1088 		/* backward compat */
   1089 		int i = 0;
   1090 		gboolean set = FALSE, stop = FALSE;
   1091 		for (i = 0; i < 6; i++) {
   1092 			EntryAction act = prefs_common.summary_select_prio[i];
   1093 
   1094 			if (act != ACTION_UNSET) {
   1095 				set = TRUE;
   1096 				break;
   1097 			}
   1098 		}
   1099 		if (!set)
   1100 			prefs_summary_open_set_defaults();
   1101 
   1102  		for (i = 0; i < 6 && node == NULL; i++) {
   1103 			EntryAction act = prefs_common.summary_select_prio[i];
   1104 
   1105 			switch(act) {
   1106 			case ACTION_OLDEST_MARKED:
   1107 				if (summaryview->sort_type == SORT_ASCENDING)
   1108 					node = summary_find_next_flagged_msg(summaryview, NULL,
   1109 					     MSG_MARKED, FALSE);
   1110 				else
   1111 					node = summary_find_prev_flagged_msg(summaryview, NULL,
   1112 					     MSG_MARKED, FALSE);
   1113 				break;
   1114 			case ACTION_NEWEST_MARKED:
   1115 				if (summaryview->sort_type == SORT_ASCENDING)
   1116 					node = summary_find_prev_flagged_msg(summaryview, NULL,
   1117 					     MSG_MARKED, FALSE);
   1118 				else
   1119 					node = summary_find_next_flagged_msg(summaryview, NULL,
   1120 					     MSG_MARKED, FALSE);
   1121 				break;
   1122 			case ACTION_OLDEST_NEW:
   1123 				if (summaryview->sort_type == SORT_ASCENDING)
   1124 					node = summary_find_next_flagged_msg(summaryview, NULL,
   1125 					     MSG_NEW, FALSE);
   1126 				else
   1127 					node = summary_find_prev_flagged_msg(summaryview, NULL,
   1128 					     MSG_NEW, FALSE);
   1129 				break;
   1130 			case ACTION_NEWEST_NEW:
   1131 				if (summaryview->sort_type == SORT_ASCENDING)
   1132 					node = summary_find_prev_flagged_msg(summaryview, NULL,
   1133 					     MSG_NEW, FALSE);
   1134 				else
   1135 					node = summary_find_next_flagged_msg(summaryview, NULL,
   1136 					     MSG_NEW, FALSE);
   1137 				break;
   1138 			case ACTION_OLDEST_UNREAD:
   1139 				if (summaryview->sort_type == SORT_ASCENDING)
   1140 					node = summary_find_next_flagged_msg(summaryview, NULL,
   1141 					     MSG_UNREAD, FALSE);
   1142 				else
   1143 					node = summary_find_prev_flagged_msg(summaryview, NULL,
   1144 					     MSG_UNREAD, FALSE);
   1145 				break;
   1146 			case ACTION_NEWEST_UNREAD:
   1147 				if (summaryview->sort_type == SORT_ASCENDING)
   1148 					node = summary_find_prev_flagged_msg(summaryview, NULL,
   1149 					     MSG_UNREAD, FALSE);
   1150 				else
   1151 					node = summary_find_next_flagged_msg(summaryview, NULL,
   1152 					     MSG_UNREAD, FALSE);
   1153 				break;
   1154 			case ACTION_LAST_OPENED:
   1155 				if (summaryview->folder_item) {
   1156 					node = summary_find_msg_by_msgnum(summaryview,
   1157 							summaryview->folder_item->last_seen);
   1158 				}
   1159 				break;
   1160 			case ACTION_NEWEST_LIST:
   1161 				if (GTK_CMCLIST(ctree)->row_list != NULL) {
   1162 					node = gtk_cmctree_node_nth
   1163 						(ctree,
   1164 						 item->sort_type == SORT_DESCENDING
   1165 						 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
   1166 				}
   1167 				break;
   1168 			case ACTION_OLDEST_LIST:
   1169 				if (GTK_CMCLIST(ctree)->row_list != NULL) {
   1170 					node = gtk_cmctree_node_nth
   1171 						(ctree,
   1172 						 item->sort_type == SORT_ASCENDING
   1173 						 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
   1174 				}
   1175 				break;
   1176 			case ACTION_NOTHING:
   1177 			case ACTION_UNSET:
   1178 				node = NULL;
   1179 				stop = TRUE;
   1180 				break;
   1181 			}
   1182 
   1183 			if (stop || node)
   1184 				break;
   1185 		}
   1186 
   1187 		summary_unlock(summaryview);
   1188 
   1189 		if (node)
   1190 			summary_select_node(summaryview, node,
   1191 					OPEN_SELECTED_ON_FOLDER_OPEN);
   1192 
   1193 		summary_lock(summaryview);
   1194 	}
   1195 
   1196 	summary_status_show(summaryview);
   1197 	summary_set_menu_sensitive(summaryview);
   1198 	toolbar_main_set_sensitive(summaryview->mainwin);
   1199 
   1200 	summary_thaw(summaryview);
   1201 	STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
   1202 	STATUSBAR_POP(summaryview->mainwin);
   1203 	main_window_cursor_normal(summaryview->mainwin);
   1204 	summary_unlock(summaryview);
   1205 	inc_unlock();
   1206 	return TRUE;
   1207 }
   1208 
   1209 #undef CURRENTLY_DISPLAYED
   1210 
   1211 static void summary_cancel_mark_read_timeout(SummaryView *summaryview) {
   1212 	if (summaryview->mark_as_read_timeout_tag != 0) {
   1213 		g_source_remove(summaryview->mark_as_read_timeout_tag);
   1214 		summaryview->mark_as_read_timeout_tag = 0;
   1215 	}
   1216 }
   1217 
   1218 void summary_clear_list(SummaryView *summaryview)
   1219 {
   1220 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
   1221 	gint optimal_width;
   1222 
   1223 	summary_freeze(summaryview);
   1224 
   1225 	gtk_cmctree_pre_recursive(GTK_CMCTREE(summaryview->ctree),
   1226 				NULL, summary_free_msginfo_func, NULL);
   1227 
   1228 	if (summaryview->folder_item) {
   1229 		summaryview->folder_item->opened = FALSE;
   1230 		summaryview->folder_item = NULL;
   1231 	}
   1232 
   1233 	summary_cancel_mark_read_timeout(summaryview);
   1234 
   1235 	summaryview->display_msg = FALSE;
   1236 
   1237 	summaryview->selected = NULL;
   1238 	summaryview->displayed = NULL;
   1239 	summaryview->total_size = 0;
   1240 	summaryview->deleted = summaryview->moved = 0;
   1241 	summaryview->copied = 0;
   1242 	if (summaryview->msgid_table) {
   1243 		g_hash_table_destroy(summaryview->msgid_table);
   1244 		summaryview->msgid_table = NULL;
   1245 	}
   1246 	if (summaryview->subject_table) {
   1247 		g_hash_table_destroy(summaryview->subject_table);
   1248 		summaryview->subject_table = NULL;
   1249 	}
   1250 	summaryview->mlist = NULL;
   1251 
   1252 	gtk_cmclist_clear(clist);
   1253 	if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
   1254 		optimal_width = gtk_cmclist_optimal_column_width
   1255 			(clist, summaryview->col_pos[S_COL_SUBJECT]);
   1256 		gtk_cmclist_set_column_width
   1257 			(clist, summaryview->col_pos[S_COL_SUBJECT],
   1258 			 optimal_width);
   1259 	}
   1260 
   1261 	summary_thaw(summaryview);
   1262 }
   1263 
   1264 void summary_clear_all(SummaryView *summaryview)
   1265 {
   1266 	mimeview_clear(summaryview->messageview->mimeview);
   1267 	messageview_clear(summaryview->messageview);
   1268 	summary_clear_list(summaryview);
   1269 	summary_set_menu_sensitive(summaryview);
   1270 	toolbar_main_set_sensitive(summaryview->mainwin);
   1271 	summary_status_show(summaryview);
   1272 }
   1273 
   1274 void summary_lock(SummaryView *summaryview)
   1275 {
   1276 	summaryview->lock_count++;
   1277 }
   1278 
   1279 void summary_unlock(SummaryView *summaryview)
   1280 {
   1281 	if (summaryview->lock_count)
   1282 		summaryview->lock_count--;
   1283 }
   1284 
   1285 gboolean summary_is_locked(SummaryView *summaryview)
   1286 {
   1287 	return summaryview->lock_count > 0;
   1288 }
   1289 
   1290 SummarySelection summary_get_selection_type(SummaryView *summaryview)
   1291 {
   1292 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
   1293 	SummarySelection selection;
   1294 
   1295 	if (!clist->row_list)
   1296 		selection = SUMMARY_NONE;
   1297 	else if (!clist->selection)
   1298 		selection = SUMMARY_SELECTED_NONE;
   1299 	else if (!clist->selection->next)
   1300 		selection = SUMMARY_SELECTED_SINGLE;
   1301 	else
   1302 		selection = SUMMARY_SELECTED_MULTIPLE;
   1303 
   1304 	return selection;
   1305 }
   1306 
   1307 /*!
   1308  *\return	MsgInfo	* Selected message if there's one selected;
   1309  *		if multiple selected, or none, return NULL.
   1310  */
   1311 MsgInfo *summary_get_selected_msg(SummaryView *summaryview)
   1312 {
   1313 	/* summaryview->selected may be valid when multiple
   1314 	 * messages were selected */
   1315 	GList *sellist = GTK_CMCLIST(summaryview->ctree)->selection;
   1316 
   1317 	if (sellist == NULL || sellist->next)
   1318 		return NULL;
   1319 
   1320 	return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data);
   1321 }
   1322 
   1323 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
   1324 {
   1325 	GSList *mlist = NULL;
   1326 	GList *cur;
   1327 	MsgInfo *msginfo;
   1328 
   1329 	for (cur = GTK_CMCLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL;
   1330 	     cur = cur->next) {
   1331 		msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
   1332 		mlist = g_slist_prepend(mlist,
   1333 				procmsg_msginfo_new_ref(msginfo));
   1334 	}
   1335 
   1336 	mlist = g_slist_reverse(mlist);
   1337 
   1338 	return mlist;
   1339 }
   1340 
   1341 void summary_set_menu_sensitive(SummaryView *summaryview)
   1342 {
   1343 	SensitiveCondMask state;
   1344 
   1345 	main_window_set_menu_sensitive(summaryview->mainwin);
   1346 
   1347 	state = main_window_get_current_state(summaryview->mainwin);
   1348 
   1349 #define SET_SENSITIVE(entry_str, ...) \
   1350 { \
   1351 	SensitiveCondMask cond = main_window_get_mask(__VA_ARGS__, -1); \
   1352 	cm_menu_set_sensitive_full(summaryview->mainwin->ui_manager, \
   1353 			(const gchar *) entry_str, \
   1354 			((cond & state) == cond)); \
   1355 }
   1356 	SET_SENSITIVE("Menus/SummaryViewPopup/Reedit", M_ALLOW_REEDIT);
   1357 	SET_SENSITIVE("Menus/SummaryViewPopup/Reply", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1358 
   1359 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1360 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/All", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1361 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/Sender", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1362 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/MailingList", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1363 
   1364 	SET_SENSITIVE("Menus/SummaryViewPopup/Forward", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1365 
   1366 	SET_SENSITIVE("Menus/SummaryViewPopup/ForwardAtt", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1367 	SET_SENSITIVE("Menus/SummaryViewPopup/Redirect", M_HAVE_ACCOUNT, M_TARGET_EXIST);
   1368 
   1369 	SET_SENSITIVE("Menus/SummaryViewPopup/Move", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS);
   1370 	SET_SENSITIVE("Menus/SummaryViewPopup/Copy", M_TARGET_EXIST, M_EXEC);
   1371 	SET_SENSITIVE("Menus/SummaryViewPopup/Trash", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS, M_NOT_TRASH);
   1372 	SET_SENSITIVE("Menus/SummaryViewPopup/Delete", M_TARGET_EXIST, M_ALLOW_DELETE);
   1373 
   1374 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks", M_TARGET_EXIST);
   1375 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Mark", M_TARGET_EXIST);
   1376 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Unmark", M_TARGET_EXIST);
   1377 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkRead", M_TARGET_EXIST);
   1378 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkUnread", M_TARGET_EXIST);
   1379 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkAllRead", M_TARGET_EXIST);
   1380 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkAllUnread", M_TARGET_EXIST);
   1381 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/IgnoreThread", M_TARGET_EXIST);
   1382 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/UnignoreThread", M_TARGET_EXIST);
   1383 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/WatchThread", M_TARGET_EXIST);
   1384 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/UnwatchThread", M_TARGET_EXIST);
   1385 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Lock", M_TARGET_EXIST);
   1386 	SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Unlock", M_TARGET_EXIST);
   1387 
   1388 	SET_SENSITIVE("Menus/SummaryViewPopup/View", M_SINGLE_TARGET_EXIST);
   1389 	SET_SENSITIVE("Menus/SummaryViewPopup/View/OpenNewWindow", M_SINGLE_TARGET_EXIST);
   1390 	SET_SENSITIVE("Menus/SummaryViewPopup/View/MessageSource", M_SINGLE_TARGET_EXIST);
   1391 	SET_SENSITIVE("Menus/SummaryViewPopup/View/AllHeaders", M_SINGLE_TARGET_EXIST);
   1392 	SET_SENSITIVE("Menus/SummaryViewPopup/SaveAs", M_TARGET_EXIST);
   1393 #undef SET_SENSITIVE
   1394 
   1395 	summary_lock(summaryview);
   1396 	if (summaryview->messageview
   1397 	&&  summaryview->messageview->mimeview
   1398 	&&  summaryview->messageview->mimeview->textview)
   1399 		cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager, "Menus/SummaryViewPopup/View/AllHeaders",
   1400 			prefs_common.show_all_headers);
   1401 	summary_unlock(summaryview);
   1402 }
   1403 
   1404 void summary_select_prev(SummaryView *summaryview)
   1405 {
   1406 	GtkCMCTreeNode *node = summaryview->selected;
   1407 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1408 
   1409 	if (summaryview->sort_type == SORT_ASCENDING)
   1410 		node = gtkut_ctree_node_prev(ctree, node);
   1411 	else
   1412 		node = gtkut_ctree_node_next(ctree, node);
   1413 
   1414 	if (node && node != summaryview->selected)
   1415 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1416 }
   1417 
   1418 void summary_select_next(SummaryView *summaryview)
   1419 {
   1420 	GtkCMCTreeNode *node = summaryview->selected;
   1421 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1422 
   1423 	if (summaryview->sort_type == SORT_ASCENDING)
   1424 		node = gtkut_ctree_node_next(ctree, node);
   1425 	else
   1426 		node = gtkut_ctree_node_prev(ctree, node);
   1427 
   1428 	if (node && node != summaryview->selected)
   1429 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1430 }
   1431 
   1432 void summary_select_prev_unread(SummaryView *summaryview)
   1433 {
   1434 	GtkCMCTreeNode *node;
   1435 
   1436 	if (summaryview->sort_type == SORT_ASCENDING)
   1437 		node = summary_find_prev_flagged_msg
   1438 			(summaryview, summaryview->selected, MSG_UNREAD, TRUE);
   1439 	else
   1440 		node = summary_find_next_flagged_msg
   1441 			(summaryview, summaryview->selected, MSG_UNREAD, TRUE);
   1442 
   1443 	if (!node || node == summaryview->selected) {
   1444 		AlertValue val = 0;
   1445 
   1446  		switch (prefs_common.next_unread_msg_dialog) {
   1447  			case NEXTUNREADMSGDIALOG_ALWAYS:
   1448 				val = alertpanel(_("No more unread messages"),
   1449 						 _("No unread message found. "
   1450 						   "Search from the end?"),
   1451 						 NULL, _("_No"), NULL, _("_Yes"),
   1452 						 NULL, NULL, ALERTFOCUS_SECOND);
   1453  				break;
   1454  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
   1455  				val = G_ALERTALTERNATE;
   1456  				break;
   1457  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
   1458  				val = !G_ALERTALTERNATE;
   1459  				break;
   1460  			default:
   1461  				debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n");
   1462  		}
   1463 		if (val != G_ALERTALTERNATE) return;
   1464 		if (summaryview->sort_type == SORT_ASCENDING)
   1465 			node = summary_find_prev_flagged_msg(summaryview, NULL,
   1466 							     MSG_UNREAD, FALSE);
   1467 		else
   1468 			node = summary_find_next_flagged_msg(summaryview, NULL,
   1469 							     MSG_UNREAD, FALSE);
   1470 	}
   1471 
   1472 	if (!node)
   1473 		alertpanel_notice(_("No unread messages."));
   1474 	else
   1475 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1476 }
   1477 
   1478 void summary_select_next_unread(SummaryView *summaryview)
   1479 {
   1480 	GtkCMCTreeNode *node = summaryview->selected;
   1481 
   1482 	if (summaryview->sort_type == SORT_ASCENDING)
   1483 		node = summary_find_next_flagged_msg
   1484 			(summaryview, node, MSG_UNREAD, TRUE);
   1485 	else
   1486 		node = summary_find_prev_flagged_msg
   1487 			(summaryview, node, MSG_UNREAD, TRUE);
   1488 
   1489 	if (node)
   1490 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1491 	else {
   1492 		AlertValue val = 0;
   1493 
   1494  		switch (prefs_common.next_unread_msg_dialog) {
   1495  			case NEXTUNREADMSGDIALOG_ALWAYS:
   1496 				val = alertpanel(_("No more unread messages"),
   1497 						 _("No unread message found. "
   1498 						   "Go to next folder?"),
   1499 						 NULL, _("_No"), NULL, _("_Yes"),
   1500 						 NULL, NULL, ALERTFOCUS_SECOND);
   1501  				break;
   1502  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
   1503  				val = G_ALERTALTERNATE;
   1504  				break;
   1505  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
   1506  				val = G_ALERTOTHER;
   1507  				break;
   1508  			default:
   1509  				debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n");
   1510  		}
   1511 
   1512 		if (val == G_ALERTALTERNATE)
   1513 			folderview_select_next_with_flag(summaryview->folderview, MSG_UNREAD);
   1514 	}
   1515 }
   1516 
   1517 void summary_select_prev_new(SummaryView *summaryview)
   1518 {
   1519 	GtkCMCTreeNode *node;
   1520 
   1521 	if (summaryview->sort_type == SORT_ASCENDING)
   1522 		node = summary_find_prev_flagged_msg
   1523 			(summaryview, summaryview->selected, MSG_NEW, TRUE);
   1524 	else
   1525 		node = summary_find_next_flagged_msg
   1526 			(summaryview, summaryview->selected, MSG_NEW, TRUE);
   1527 
   1528 	if (!node || node == summaryview->selected) {
   1529 		AlertValue val = 0;
   1530 
   1531  		switch (prefs_common.next_unread_msg_dialog) {
   1532  			case NEXTUNREADMSGDIALOG_ALWAYS:
   1533 				val = alertpanel(_("No more new messages"),
   1534 						 _("No new message found. "
   1535 						   "Search from the end?"),
   1536 						 NULL, _("_No"), NULL, _("_Yes"),
   1537 						 NULL, NULL, ALERTFOCUS_SECOND);
   1538  				break;
   1539  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
   1540  				val = G_ALERTALTERNATE;
   1541  				break;
   1542  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
   1543  				val = !G_ALERTALTERNATE;
   1544  				break;
   1545  			default:
   1546  				debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n");
   1547  		}
   1548 		if (val != G_ALERTALTERNATE) return;
   1549 		if (summaryview->sort_type == SORT_ASCENDING)
   1550 			node = summary_find_prev_flagged_msg(summaryview, NULL,
   1551 						     	     MSG_NEW, FALSE);
   1552 		else
   1553 			node = summary_find_next_flagged_msg(summaryview, NULL,
   1554 							     MSG_NEW, FALSE);
   1555 	}
   1556 
   1557 	if (!node)
   1558 		alertpanel_notice(_("No new messages."));
   1559 	else
   1560 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1561 }
   1562 
   1563 void summary_select_next_new(SummaryView *summaryview)
   1564 {
   1565 	GtkCMCTreeNode *node = summaryview->selected;
   1566 
   1567 	if (summaryview->sort_type == SORT_ASCENDING)
   1568 		node = summary_find_next_flagged_msg
   1569 			(summaryview, node, MSG_NEW, TRUE);
   1570 	else
   1571 		node = summary_find_prev_flagged_msg
   1572 			(summaryview, node, MSG_NEW, TRUE);
   1573 
   1574 	if (node)
   1575 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1576 	else {
   1577 		AlertValue val = 0;
   1578 
   1579  		switch (prefs_common.next_unread_msg_dialog) {
   1580  			case NEXTUNREADMSGDIALOG_ALWAYS:
   1581 				val = alertpanel(_("No more new messages"),
   1582 						 _("No new message found. "
   1583 						   "Go to next folder?"),
   1584 						 NULL, _("_No"), NULL, _("_Yes"),
   1585 						 NULL, NULL, ALERTFOCUS_SECOND);
   1586  				break;
   1587  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
   1588  				val = G_ALERTALTERNATE;
   1589  				break;
   1590  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
   1591  				val = G_ALERTOTHER;
   1592  				break;
   1593  			default:
   1594  				debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n");
   1595  		}
   1596 		if (val == G_ALERTALTERNATE)
   1597 			folderview_select_next_with_flag(summaryview->folderview, MSG_NEW);
   1598 	}
   1599 }
   1600 
   1601 void summary_select_prev_marked(SummaryView *summaryview)
   1602 {
   1603 	GtkCMCTreeNode *node;
   1604 
   1605 	if (summaryview->sort_type == SORT_ASCENDING)
   1606 		node = summary_find_prev_flagged_msg
   1607 			(summaryview, summaryview->selected, MSG_MARKED, TRUE);
   1608 	else
   1609 		node = summary_find_next_flagged_msg
   1610 			(summaryview, summaryview->selected, MSG_MARKED, TRUE);
   1611 
   1612 	if (!node) {
   1613 		AlertValue val;
   1614 
   1615 		val = alertpanel(_("No more marked messages"),
   1616 				 _("No marked message found. "
   1617 				   "Search from the end?"),
   1618 				 NULL, _("_No"), NULL, _("_Yes"),
   1619 				 NULL, NULL, ALERTFOCUS_SECOND);
   1620 		if (val != G_ALERTALTERNATE) return;
   1621 		node = summary_find_prev_flagged_msg(summaryview, NULL,
   1622 						     MSG_MARKED, TRUE);
   1623 	}
   1624 
   1625 	if (!node)
   1626 		alertpanel_notice(_("No marked messages."));
   1627 	else
   1628 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1629 }
   1630 
   1631 void summary_select_next_marked(SummaryView *summaryview)
   1632 {
   1633 	GtkCMCTreeNode *node = summaryview->selected;
   1634 
   1635 	if (summaryview->sort_type == SORT_ASCENDING)
   1636 		node = summary_find_next_flagged_msg
   1637 			(summaryview, node, MSG_MARKED, TRUE);
   1638 	else
   1639 		node = summary_find_prev_flagged_msg
   1640 			(summaryview, node, MSG_MARKED, TRUE);
   1641 
   1642 	if (node)
   1643 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1644 	else {
   1645 		AlertValue val = 0;
   1646 
   1647  		switch (prefs_common.next_unread_msg_dialog) {
   1648  			case NEXTUNREADMSGDIALOG_ALWAYS:
   1649 				val = alertpanel(_("No more marked messages"),
   1650 						 _("No marked message found. "
   1651 						   "Go to next folder?"),
   1652 						 NULL, _("_No"), NULL, _("_Yes"),
   1653 						 NULL, NULL, ALERTFOCUS_SECOND);
   1654  				break;
   1655  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
   1656  				val = G_ALERTALTERNATE;
   1657  				break;
   1658  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
   1659  				val = G_ALERTOTHER;
   1660  				break;
   1661  			default:
   1662  				debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n");
   1663  		}
   1664 		if (val == G_ALERTALTERNATE)
   1665 			folderview_select_next_with_flag(summaryview->folderview, MSG_MARKED);
   1666 	}
   1667 }
   1668 
   1669 void summary_select_prev_labeled(SummaryView *summaryview)
   1670 {
   1671 	GtkCMCTreeNode *node;
   1672 
   1673 	if (summaryview->sort_type == SORT_ASCENDING)
   1674 		node = summary_find_prev_flagged_msg
   1675 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
   1676 	else
   1677 		node = summary_find_next_flagged_msg
   1678 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
   1679 
   1680 	if (!node) {
   1681 		AlertValue val;
   1682 
   1683 		val = alertpanel(_("No more labeled messages"),
   1684 				 _("No labeled message found. "
   1685 				   "Search from the end?"),
   1686 				 NULL, _("_No"), NULL, _("_Yes"),
   1687 				 NULL, NULL, ALERTFOCUS_SECOND);
   1688 		if (val != G_ALERTALTERNATE) return;
   1689 		node = summary_find_prev_flagged_msg(summaryview, NULL,
   1690 						     MSG_CLABEL_FLAG_MASK, TRUE);
   1691 	}
   1692 
   1693 	if (!node)
   1694 		alertpanel_notice(_("No labeled messages."));
   1695 	else
   1696 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1697 }
   1698 
   1699 void summary_select_next_labeled(SummaryView *summaryview)
   1700 {
   1701 	GtkCMCTreeNode *node;
   1702 
   1703 	if (summaryview->sort_type == SORT_ASCENDING)
   1704 		node = summary_find_next_flagged_msg
   1705 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
   1706 	else
   1707 		node = summary_find_prev_flagged_msg
   1708 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
   1709 
   1710 	if (!node) {
   1711 		AlertValue val;
   1712 
   1713 		val = alertpanel(_("No more labeled messages"),
   1714 				 _("No labeled message found. "
   1715 				   "Search from the beginning?"),
   1716 				 NULL, _("_No"), NULL, _("_Yes"),
   1717 				 NULL, NULL, ALERTFOCUS_SECOND);
   1718 		if (val != G_ALERTALTERNATE) return;
   1719 		if (summaryview->sort_type == SORT_ASCENDING)
   1720 			node = summary_find_next_flagged_msg(summaryview, NULL,
   1721 						    	     MSG_CLABEL_FLAG_MASK, TRUE);
   1722 		else
   1723 			node = summary_find_prev_flagged_msg(summaryview, NULL,
   1724 						    	     MSG_CLABEL_FLAG_MASK, TRUE);
   1725 	}
   1726 
   1727 	if (!node)
   1728 		alertpanel_notice(_("No labeled messages."));
   1729 	else
   1730 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1731 }
   1732 
   1733 void summary_select_parent(SummaryView *summaryview)
   1734 {
   1735 	GtkCMCTreeNode *node = NULL;
   1736 
   1737 	if (summaryview->selected)
   1738 		node = GTK_CMCTREE_ROW(summaryview->selected)->parent;
   1739 	if (node)
   1740 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
   1741 }
   1742 
   1743 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum,
   1744 		gboolean show)
   1745 {
   1746 	GtkCMCTreeNode *node;
   1747 
   1748 	node = summary_find_msg_by_msgnum(summaryview, msgnum);
   1749 	summary_select_node(summaryview, node, show);
   1750 }
   1751 
   1752 void summary_select_by_msg_list(SummaryView	*summaryview, GSList *msginfos)
   1753 {
   1754 	GtkCMCTree *ctree;
   1755 	GSList *msgnum_list, *walk;
   1756 	gboolean froze = FALSE;
   1757 
   1758 	ctree = GTK_CMCTREE(summaryview->ctree);
   1759 
   1760 	msgnum_list = procmsg_get_number_list_for_msgs(msginfos);
   1761 
   1762 	START_LONG_OPERATION(summaryview, FALSE);
   1763 	for(walk = msgnum_list; walk; walk = walk->next) {
   1764 		GtkCMCTreeNode *node;
   1765 		node = summary_find_msg_by_msgnum(summaryview, GPOINTER_TO_UINT(walk->data));
   1766 		if (node != NULL)
   1767 			gtk_cmctree_select(ctree, node);
   1768 	}
   1769 	END_LONG_OPERATION(summaryview);
   1770 	g_slist_free(msgnum_list);
   1771 }
   1772 
   1773 typedef struct _PostponedSelectData
   1774 {
   1775 	GtkCMCTree *ctree;
   1776 	GtkCMCTreeNode *row;
   1777 	GtkCMCTreeNode *node;
   1778 	GtkScrollType type;
   1779 	gint column;
   1780 	SummaryView *summaryview;
   1781 	gboolean display_msg;
   1782 } PostponedSelectData;
   1783 
   1784 static gboolean summary_select_retry(void *data)
   1785 {
   1786 	PostponedSelectData *psdata = (PostponedSelectData *)data;
   1787 	debug_print("trying again\n");
   1788 	if (psdata->row)
   1789 		summary_selected(psdata->ctree, psdata->row,
   1790 			    psdata->column, psdata->summaryview);
   1791 	else if (psdata->node)
   1792 		summary_select_node(psdata->summaryview, psdata->node,
   1793 			    psdata->display_msg);
   1794 	g_free(psdata);
   1795 	return FALSE;
   1796 }
   1797 
   1798 /**
   1799  * summary_select_node:
   1800  * @summaryview: Summary view.
   1801  * @node: Summary tree node.
   1802  * @display_msg: whether to also display the message
   1803  *
   1804  * Select @node (bringing it into view by scrolling and expanding its
   1805  * thread, if necessary) and unselect all others.
   1806  **/
   1807 
   1808 void summary_select_node(SummaryView *summaryview, GtkCMCTreeNode *node,
   1809 			 gboolean display_msg)
   1810 {
   1811 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1812 
   1813 	/* If msgview is hidden, we never want to automatically display
   1814 	 * a selected message, since that would unhide the msgview. */
   1815 	if (!messageview_is_visible(summaryview->messageview))
   1816 		display_msg = FALSE;
   1817 
   1818 	if (summary_is_locked(summaryview)
   1819 	&& !GTK_SCTREE(ctree)->selecting_range
   1820 	&& summaryview->messageview->mimeview
   1821 	&& summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
   1822 	&& summaryview->messageview->mimeview->textview->loading) {
   1823 		PostponedSelectData *data = g_new0(PostponedSelectData, 1);
   1824 		summaryview->messageview->mimeview->textview->stop_loading = TRUE;
   1825 
   1826 		data->ctree = ctree;
   1827 		data->row = NULL;
   1828 		data->node = node;
   1829 		data->summaryview = summaryview;
   1830 		data->display_msg = display_msg;
   1831 		debug_print("postponing open of message till end of load\n");
   1832 		g_timeout_add(100, summary_select_retry, data);
   1833 		return;
   1834 	}
   1835 	if (summary_is_locked(summaryview)) {
   1836 		return;
   1837 	}
   1838 	if (!summaryview->folder_item)
   1839 		return;
   1840 	if (node) {
   1841 		gtkut_ctree_expand_parent_all(ctree, node);
   1842 
   1843 		summary_lock(summaryview);
   1844 		GTK_EVENTS_FLUSH();
   1845 		summary_unlock(summaryview);
   1846 
   1847 		gtkut_ctree_node_move_if_on_the_edge(ctree, node, -1);
   1848 
   1849 		if (display_msg && summaryview->displayed == node)
   1850 			summaryview->displayed = NULL;
   1851 		summaryview->display_msg = display_msg;
   1852 		gtk_sctree_select(GTK_SCTREE(ctree), node);
   1853 		if (summaryview->selected == NULL)
   1854 			summaryview->selected = node;
   1855 	}
   1856 }
   1857 
   1858 guint summary_get_msgnum(SummaryView *summaryview, GtkCMCTreeNode *node)
   1859 {
   1860 	GtkCMCTree *ctree =NULL;
   1861 	MsgInfo *msginfo;
   1862 
   1863 	if (!summaryview)
   1864 		return 0;
   1865 	ctree = GTK_CMCTREE(summaryview->ctree);
   1866 	if (!node)
   1867 		return 0;
   1868 	msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   1869 	if (msginfo)
   1870 		return msginfo->msgnum;
   1871 	else
   1872 		return -1;
   1873 }
   1874 
   1875 static GtkCMCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
   1876 					     GtkCMCTreeNode *current_node,
   1877 					     gboolean start_from_prev)
   1878 {
   1879 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1880 	GtkCMCTreeNode *node;
   1881 	MsgInfo *msginfo;
   1882 
   1883 	if (current_node) {
   1884 		if (start_from_prev)
   1885 			node = gtkut_ctree_node_prev(ctree, current_node);
   1886 		else
   1887 			node = current_node;
   1888 	} else
   1889 		node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
   1890 
   1891 	for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
   1892 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   1893 		if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
   1894 	}
   1895 
   1896 	return node;
   1897 }
   1898 
   1899 static GtkCMCTreeNode *summary_find_next_msg(SummaryView *summaryview,
   1900 					    GtkCMCTreeNode *current_node,
   1901 					    gboolean start_from_next)
   1902 {
   1903 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1904 	GtkCMCTreeNode *node;
   1905 	MsgInfo *msginfo;
   1906 
   1907 	if (current_node) {
   1908 		if (start_from_next)
   1909 			node = gtkut_ctree_node_next(ctree, current_node);
   1910 		else
   1911 			node = current_node;
   1912 	} else
   1913 		node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   1914 
   1915 	for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
   1916 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   1917 		if (msginfo && !MSG_IS_DELETED(msginfo->flags)
   1918 		&& !MSG_IS_MOVE(msginfo->flags)) break;
   1919 	}
   1920 
   1921 	return node;
   1922 }
   1923 
   1924 static GtkCMCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
   1925 						   GtkCMCTreeNode *current_node,
   1926 						   MsgPermFlags flags,
   1927 						   gboolean start_from_prev)
   1928 {
   1929 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1930 	GtkCMCTreeNode *node;
   1931 	MsgInfo *msginfo;
   1932 
   1933 	if (current_node) {
   1934 		if (start_from_prev)
   1935 			node = gtkut_ctree_node_prev(ctree, current_node);
   1936 		else
   1937 			node = current_node;
   1938 	} else
   1939 		node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
   1940 
   1941 	for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
   1942 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   1943 		if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
   1944 	}
   1945 
   1946 	return node;
   1947 }
   1948 
   1949 static GtkCMCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
   1950 						   GtkCMCTreeNode *current_node,
   1951 						   MsgPermFlags flags,
   1952 						   gboolean start_from_next)
   1953 {
   1954 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1955 	GtkCMCTreeNode *node;
   1956 	MsgInfo *msginfo;
   1957 
   1958 	if (current_node) {
   1959 		if (start_from_next)
   1960 			node = gtkut_ctree_node_next(ctree, current_node);
   1961 		else
   1962 			node = current_node;
   1963 	} else
   1964 		node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   1965 
   1966 	for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
   1967 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   1968 		/* Find msg with matching flags but ignore messages with
   1969 		   ignore flags, if searching for new or unread messages */
   1970 		if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) &&
   1971 		    !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags))
   1972 			)
   1973 			break;
   1974 	}
   1975 
   1976 	return node;
   1977 }
   1978 
   1979 static GtkCMCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
   1980 						guint msgnum)
   1981 {
   1982 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   1983 	GtkCMCTreeNode *node;
   1984 	MsgInfo *msginfo;
   1985 
   1986 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   1987 
   1988 	for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
   1989 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   1990 		if (msginfo && msginfo->msgnum == msgnum) break;
   1991 	}
   1992 
   1993 	return node;
   1994 }
   1995 
   1996 static guint attract_hash_func(gconstpointer key)
   1997 {
   1998 	gchar *str;
   1999 	gchar *p;
   2000 	guint h;
   2001 
   2002 	Xstrdup_a(str, (const gchar *)key, return 0);
   2003 	trim_subject(str);
   2004 
   2005 	p = str;
   2006 	h = *p;
   2007 
   2008 	if (h) {
   2009 		for (p += 1; *p != '\0'; p++)
   2010 			h = (h << 5) - h + *p;
   2011 	}
   2012 
   2013 	return h;
   2014 }
   2015 
   2016 static gint attract_compare_func(gconstpointer a, gconstpointer b)
   2017 {
   2018 	return subject_compare((const gchar *)a, (const gchar *)b) == 0;
   2019 }
   2020 
   2021 void summary_attract_by_subject(SummaryView *summaryview)
   2022 {
   2023 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   2024 	GtkCMCList *clist = GTK_CMCLIST(ctree);
   2025 	GtkCMCTreeNode *src_node;
   2026 	GtkCMCTreeNode *dst_node, *sibling;
   2027 	GtkCMCTreeNode *tmp;
   2028 	MsgInfo *src_msginfo, *dst_msginfo;
   2029 	GHashTable *subject_table;
   2030 
   2031 	debug_print("Attracting messages by subject...\n");
   2032 	STATUSBAR_PUSH(summaryview->mainwin,
   2033 		       _("Attracting messages by subject..."));
   2034 
   2035 	main_window_cursor_wait(summaryview->mainwin);
   2036 	summary_freeze(summaryview);
   2037 
   2038 	subject_table = g_hash_table_new(attract_hash_func,
   2039 					 attract_compare_func);
   2040 
   2041 	for (src_node = GTK_CMCTREE_NODE(clist->row_list);
   2042 	     src_node != NULL;
   2043 	     src_node = tmp) {
   2044 		tmp = GTK_CMCTREE_ROW(src_node)->sibling;
   2045 		src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
   2046 		if (!src_msginfo) continue;
   2047 		if (!src_msginfo->subject) continue;
   2048 
   2049 		/* find attracting node */
   2050 		dst_node = g_hash_table_lookup(subject_table,
   2051 					       src_msginfo->subject);
   2052 
   2053 		if (dst_node) {
   2054 			dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
   2055 
   2056 			/* if the time difference is more than 20 days,
   2057 			   don't attract */
   2058 			if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
   2059 			    > 60 * 60 * 24 * 20)
   2060 				continue;
   2061 
   2062 			sibling = GTK_CMCTREE_ROW(dst_node)->sibling;
   2063 			if (src_node != sibling)
   2064 				gtk_cmctree_move(ctree, src_node, NULL, sibling);
   2065 		}
   2066 
   2067 		g_hash_table_insert(subject_table,
   2068 				    src_msginfo->subject, src_node);
   2069 	}
   2070 
   2071 	g_hash_table_destroy(subject_table);
   2072 
   2073 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
   2074 
   2075 	summary_thaw(summaryview);
   2076 
   2077 	debug_print("Attracting messages by subject done.\n");
   2078 	STATUSBAR_POP(summaryview->mainwin);
   2079 
   2080 	main_window_cursor_normal(summaryview->mainwin);
   2081 }
   2082 
   2083 static void summary_free_msginfo_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   2084 				      gpointer data)
   2085 {
   2086 	MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   2087 
   2088 	if (msginfo)
   2089 		procmsg_msginfo_free(&msginfo);
   2090 }
   2091 
   2092 static void summary_set_marks_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   2093 				   gpointer data)
   2094 {
   2095 	SummaryView *summaryview = data;
   2096 	MsgInfo *msginfo;
   2097 
   2098 	msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   2099 
   2100 	cm_return_if_fail(msginfo != NULL);
   2101 
   2102 	if (MSG_IS_DELETED(msginfo->flags))
   2103 		summaryview->deleted++;
   2104 
   2105 	summaryview->total_size += msginfo->size;
   2106 
   2107 	summary_set_row_marks(summaryview, node);
   2108 }
   2109 
   2110 static void summary_update_status(SummaryView *summaryview)
   2111 {
   2112 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   2113 	GtkCMCTreeNode *node;
   2114 	MsgInfo *msginfo;
   2115 
   2116 	summaryview->total_size =
   2117 	summaryview->deleted = summaryview->moved = summaryview->copied = 0;
   2118 
   2119 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   2120 	     node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
   2121 		msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   2122 
   2123 		if (!msginfo)
   2124 			continue;
   2125 
   2126 		if (MSG_IS_DELETED(msginfo->flags))
   2127 			summaryview->deleted++;
   2128 		if (MSG_IS_MOVE(msginfo->flags))
   2129 			summaryview->moved++;
   2130 		if (MSG_IS_COPY(msginfo->flags))
   2131 			summaryview->copied++;
   2132 		summaryview->total_size += msginfo->size;
   2133 	}
   2134 }
   2135 
   2136 static void summary_status_show(SummaryView *summaryview)
   2137 {
   2138 	gchar *str;
   2139 	gchar *del, *mv, *cp;
   2140 	gchar *sel;
   2141 	gchar *spc;
   2142 	gchar *itstr;
   2143 	GList *rowlist, *cur;
   2144 	guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0;
   2145 	goffset sel_size = 0, n_size = 0;
   2146 	MsgInfo *msginfo;
   2147 	gchar *name;
   2148 
   2149 	if (!summaryview->folder_item) {
   2150 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), "");
   2151 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), "");
   2152 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs),   "");
   2153 		toolbar_main_set_sensitive(summaryview->mainwin);
   2154 		return;
   2155 	}
   2156 	gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_folder), PANGO_ELLIPSIZE_END);
   2157 	gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_select), PANGO_ELLIPSIZE_END);
   2158 	gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_msgs), PANGO_ELLIPSIZE_START);
   2159 
   2160 	rowlist = GTK_CMCLIST(summaryview->ctree)->selection;
   2161 	for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
   2162 		msginfo = gtk_cmctree_node_get_row_data
   2163 			(GTK_CMCTREE(summaryview->ctree),
   2164 			 GTK_CMCTREE_NODE(cur->data));
   2165 		if (msginfo) {
   2166 			sel_size += msginfo->size;
   2167 			n_selected++;
   2168 		}
   2169 	}
   2170 
   2171 	if (summaryview->folder_item->hide_read_msgs
   2172 	|| summaryview->folder_item->hide_del_msgs
   2173 	|| summaryview->folder_item->hide_read_threads) {
   2174 		rowlist = GTK_CMCLIST(summaryview->ctree)->row_list;
   2175 		for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
   2176 			msginfo = gtk_cmctree_node_get_row_data
   2177 				(GTK_CMCTREE(summaryview->ctree),
   2178 				 GTK_CMCTREE_NODE(cur));
   2179 			if (msginfo) {
   2180 				n_size += msginfo->size;
   2181 				n_total++;
   2182 				if (MSG_IS_NEW(msginfo->flags))
   2183 					n_new++;
   2184 				if (MSG_IS_UNREAD(msginfo->flags))
   2185 					n_unread++;
   2186 			}
   2187 		}
   2188 	} else {
   2189 		n_new = summaryview->folder_item->new_msgs;
   2190 		n_unread = summaryview->folder_item->unread_msgs;
   2191 	}
   2192 
   2193 	name = folder_item_get_name(summaryview->folder_item);
   2194 	gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
   2195 	g_free(name);
   2196 
   2197 	if (summaryview->deleted)
   2198 		del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
   2199 	else
   2200 		del = g_strdup("");
   2201 	if (summaryview->moved)
   2202 		mv = g_strdup_printf(_("%s%d moved"),
   2203 				     summaryview->deleted ? ", " : "",
   2204 				     summaryview->moved);
   2205 	else
   2206 		mv = g_strdup("");
   2207 	if (summaryview->copied)
   2208 		cp = g_strdup_printf(_("%s%d copied"),
   2209 				     summaryview->deleted ||
   2210 				     summaryview->moved ? ", " : "",
   2211 				     summaryview->copied);
   2212 	else
   2213 		cp = g_strdup("");
   2214 
   2215 	if (summaryview->deleted || summaryview->moved || summaryview->copied)
   2216 		spc = "    ";
   2217 	else
   2218 		spc = "";
   2219 
   2220 	if (n_selected) {
   2221 		sel = g_strdup_printf(" (%s)", to_human_readable((goffset)sel_size));
   2222 		itstr = strdup(" items selected");
   2223 	} else {
   2224 		sel = g_strdup("");
   2225 		itstr = g_strdup("");
   2226 	}
   2227 
   2228 	str = g_strconcat(n_selected ? itos(n_selected) : "",
   2229 					itstr, sel, spc, del, mv, cp, NULL);
   2230 	g_free(sel);
   2231 	g_free(del);
   2232 	g_free(mv);
   2233 	g_free(cp);
   2234 	g_free(itstr);
   2235 
   2236 	gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
   2237 	g_free(str);
   2238 
   2239 	str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
   2240 				      n_new, n_unread, n_total,
   2241 				      to_human_readable((goffset)n_size));
   2242 	gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str);
   2243 	g_free(str);
   2244 
   2245 	summary_set_menu_sensitive(summaryview);
   2246 	toolbar_main_set_sensitive(summaryview->mainwin);
   2247 }
   2248 
   2249 static void summary_set_column_titles(SummaryView *summaryview)
   2250 {
   2251 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
   2252 	GtkWidget *hbox;
   2253 	GtkWidget *label;
   2254 	GtkWidget *arrow;
   2255 	gint pos;
   2256 	const gchar *title;
   2257 	SummaryColumnType type;
   2258 	GtkJustification justify;
   2259 
   2260 	static FolderSortKey sort_by[N_SUMMARY_COLS] = {
   2261 		SORT_BY_MARK,
   2262 		SORT_BY_STATUS,
   2263 		SORT_BY_MIME,
   2264 		SORT_BY_SUBJECT,
   2265 		SORT_BY_FROM,
   2266 		SORT_BY_TO,
   2267 		SORT_BY_DATE,
   2268 		SORT_BY_SIZE,
   2269 		SORT_BY_NUMBER,
   2270 		SORT_BY_SCORE,
   2271 		SORT_BY_LOCKED,
   2272 		SORT_BY_TAGS
   2273 	};
   2274 
   2275 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
   2276 		type = summaryview->col_state[pos].type;
   2277 		title = col_label[type];
   2278 
   2279 		/* CLAWS: mime and unread are single char headers */
   2280 		justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
   2281 			? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
   2282 
   2283 		if (type == S_COL_MIME) {
   2284 			label = gtk_image_new_from_pixbuf(clipxpm);
   2285 			gtk_widget_show(label);
   2286 			gtk_cmclist_set_column_widget(clist, pos, label);
   2287 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Attachment"));
   2288 			continue;
   2289 		} else if (type == S_COL_MARK) {
   2290 			label = gtk_image_new_from_pixbuf(markxpm);
   2291 			gtk_widget_show(label);
   2292 			gtk_cmclist_set_column_widget(clist, pos, label);
   2293 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, C_("Column Header", "_Mark"));
   2294 			continue;
   2295 		} else if (type == S_COL_LOCKED) {
   2296 			label = gtk_image_new_from_pixbuf(lockedxpm);
   2297 			gtk_widget_show(label);
   2298 			gtk_cmclist_set_column_widget(clist, pos, label);
   2299 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Locked"));
   2300 			continue;
   2301 		} else if (type == S_COL_STATUS) {
   2302 			gtk_cmclist_set_column_title(clist, pos, title);
   2303 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Status"));
   2304 			continue;
   2305 		}
   2306 
   2307 		hbox  = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
   2308 		label = gtk_label_new(title);
   2309 
   2310 		if (justify == GTK_JUSTIFY_RIGHT)
   2311 			gtk_box_pack_end(GTK_BOX(hbox), label,
   2312 					 FALSE, FALSE, 0);
   2313 		else
   2314 			gtk_box_pack_start(GTK_BOX(hbox), label,
   2315 					   FALSE, FALSE, 0);
   2316 
   2317 		if (summaryview->sort_key == sort_by[type] ||
   2318 		    (summaryview->sort_key == SORT_BY_THREAD_DATE &&
   2319 		     sort_by[SORT_BY_DATE] && type == S_COL_DATE)) {
   2320 			arrow = gtk_image_new_from_icon_name
   2321 				(summaryview->sort_type == SORT_ASCENDING
   2322 				 ? "pan-down-symbolic" : "pan-up-symbolic",
   2323 				 GTK_ICON_SIZE_MENU);
   2324 			gtk_widget_set_size_request(GTK_WIDGET(arrow), 10, 10);
   2325 			if (justify == GTK_JUSTIFY_RIGHT)
   2326 				gtk_box_pack_start(GTK_BOX(hbox), arrow,
   2327 						   FALSE, FALSE, 0);
   2328 			else
   2329 				gtk_box_pack_end(GTK_BOX(hbox), arrow,
   2330 						 FALSE, FALSE, 0);
   2331 		}
   2332 
   2333 		gtk_widget_show_all(hbox);
   2334 		gtk_cmclist_set_column_widget(clist, pos, hbox);
   2335 	}
   2336 }
   2337 
   2338 void summary_sort(SummaryView *summaryview,
   2339 		  FolderSortKey sort_key, FolderSortType sort_type)
   2340 {
   2341 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   2342 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
   2343 	GtkCMCListCompareFunc cmp_func = NULL;
   2344 	g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
   2345 				       G_CALLBACK(summary_tree_expanded), summaryview);
   2346 	summary_freeze(summaryview);
   2347 
   2348 	switch (sort_key) {
   2349 	case SORT_BY_MARK:
   2350 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mark;
   2351 		break;
   2352 	case SORT_BY_STATUS:
   2353 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_status;
   2354 		break;
   2355 	case SORT_BY_MIME:
   2356 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mime;
   2357 		break;
   2358 	case SORT_BY_NUMBER:
   2359 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_num;
   2360 		break;
   2361 	case SORT_BY_SIZE:
   2362 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_size;
   2363 		break;
   2364 	case SORT_BY_DATE:
   2365 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_date;
   2366 		break;
   2367 	case SORT_BY_THREAD_DATE:
   2368 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_thread_date;
   2369 		break;
   2370 	case SORT_BY_FROM:
   2371 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_from;
   2372 		break;
   2373 	case SORT_BY_SUBJECT:
   2374 		if (summaryview->simplify_subject_preg)
   2375 			cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_simplified_subject;
   2376 		else
   2377 			cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_subject;
   2378 		break;
   2379 	case SORT_BY_SCORE:
   2380 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_score;
   2381 		break;
   2382 	case SORT_BY_LABEL:
   2383 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_label;
   2384 		break;
   2385 	case SORT_BY_TO:
   2386 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_to;
   2387 		break;
   2388 	case SORT_BY_LOCKED:
   2389 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_locked;
   2390 		break;
   2391 	case SORT_BY_NONE:
   2392 		break;
   2393 	default:
   2394 		goto unlock;
   2395 	}
   2396 
   2397 	summaryview->sort_key = sort_key;
   2398 	summaryview->sort_type = sort_type;
   2399 
   2400 	summary_set_column_titles(summaryview);
   2401 	summary_set_menu_sensitive(summaryview);
   2402 
   2403 	/* allow fallback to don't sort */
   2404 	if (summaryview->sort_key == SORT_BY_NONE)
   2405 		goto unlock;
   2406 
   2407 	if (cmp_func != NULL) {
   2408 		debug_print("Sorting summary...\n");
   2409 		STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
   2410 
   2411 		main_window_cursor_wait(summaryview->mainwin);
   2412 
   2413 		gtk_cmclist_set_compare_func(clist, cmp_func);
   2414 
   2415 		gtk_cmclist_set_sort_type(clist, (GtkSortType)sort_type);
   2416 		gtk_sctree_sort_recursive(ctree, NULL);
   2417 
   2418 		gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
   2419 
   2420 		main_window_cursor_normal(summaryview->mainwin);
   2421 
   2422 		debug_print("Sorting summary done.\n");
   2423 		STATUSBAR_POP(summaryview->mainwin);
   2424 	}
   2425 unlock:
   2426 	summary_thaw(summaryview);
   2427 	g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
   2428 				       G_CALLBACK(summary_tree_expanded), summaryview);
   2429 }
   2430 
   2431 static gboolean summary_update_thread_age(GNode *node, gpointer data)
   2432 {
   2433 	MsgInfo *msginfo = node->data;
   2434 	time_t *most_recent = (time_t *)data;
   2435 
   2436 	if (msginfo->date_t > *most_recent) {
   2437 		*most_recent = msginfo->date_t;
   2438 	}
   2439 	return FALSE;
   2440 }
   2441 
   2442 static void summary_find_thread_age(GNode *gnode)
   2443 {
   2444 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
   2445 	time_t most_recent;
   2446 
   2447 	if (!msginfo)
   2448 		return;
   2449 	most_recent = msginfo->thread_date = msginfo->date_t;
   2450 
   2451 	g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_thread_age, &most_recent);
   2452 
   2453 	msginfo->thread_date = most_recent;
   2454 }
   2455 
   2456 static gboolean summary_update_is_read(GNode *node, gpointer data)
   2457 {
   2458 	MsgInfo *msginfo = node->data;
   2459 	gboolean *all_read = (gboolean *)data;
   2460 
   2461 	if (MSG_IS_UNREAD(msginfo->flags)) {
   2462 		*all_read = FALSE;
   2463 		return TRUE;
   2464 	}
   2465 	return FALSE;
   2466 }
   2467 
   2468 static gboolean summary_thread_is_read(GNode *gnode)
   2469 {
   2470 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
   2471 	gboolean all_read = TRUE;
   2472 
   2473 	if (!msginfo)
   2474 		return all_read;
   2475 
   2476 	g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_is_read, &all_read);
   2477     return all_read;
   2478 }
   2479 
   2480 typedef struct  _ThreadSelectedData {
   2481 	guint msgnum;
   2482 	gboolean is_selected;
   2483 } ThreadSelectedData;
   2484 
   2485 static gboolean summary_update_is_selected(GNode *gnode, gpointer data)
   2486 {
   2487 	ThreadSelectedData *selected = (ThreadSelectedData *)data;
   2488 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
   2489 
   2490 	if (msginfo->msgnum == selected->msgnum) {
   2491 		selected->is_selected = TRUE;
   2492 		return TRUE;
   2493 	}
   2494 
   2495 	return FALSE;
   2496 }
   2497 
   2498 static gboolean summary_thread_is_selected(GNode *gnode, guint selected_msgnum)
   2499 {
   2500 	ThreadSelectedData selected;
   2501 
   2502 	selected.msgnum = selected_msgnum;
   2503 	selected.is_selected = FALSE;
   2504 	g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1,
   2505 			summary_update_is_selected, &selected);
   2506 	return selected.is_selected;
   2507 }
   2508 
   2509 static gboolean summary_insert_gnode_func(GtkCMCTree *ctree, guint depth, GNode *gnode,
   2510 				   GtkCMCTreeNode *cnode, gpointer data)
   2511 {
   2512 	SummaryView *summaryview = (SummaryView *)data;
   2513 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
   2514 	gchar *text[N_SUMMARY_COLS];
   2515 	gint *col_pos = summaryview->col_pos;
   2516 	const gchar *msgid = msginfo->msgid;
   2517 	GHashTable *msgid_table = summaryview->msgid_table;
   2518 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
   2519 
   2520 	summary_set_header(summaryview, text, msginfo);
   2521 
   2522 	gtk_cmctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
   2523 				NULL, NULL, FALSE, summaryview->threaded && !summaryview->thread_collapsed);
   2524 #define SET_TEXT(col) {						\
   2525 	gtk_cmctree_node_set_text(ctree, cnode, col_pos[col], 	\
   2526 				text[col_pos[col]]);		\
   2527 }
   2528 
   2529 	if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
   2530 		SET_TEXT(S_COL_NUMBER);
   2531 	if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
   2532 		SET_TEXT(S_COL_SCORE);
   2533 	if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
   2534 		SET_TEXT(S_COL_SIZE);
   2535 	if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible)
   2536 		SET_TEXT(S_COL_DATE);
   2537 	if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
   2538 		SET_TEXT(S_COL_FROM);
   2539 	if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
   2540 		SET_TEXT(S_COL_TO);
   2541 	if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible)
   2542 		SET_TEXT(S_COL_TAGS);
   2543 
   2544 	if (vert_layout && prefs_common.two_line_vert)
   2545 		g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
   2546 
   2547 #undef SET_TEXT
   2548 
   2549 	GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
   2550 	summary_set_marks_func(ctree, cnode, summaryview);
   2551 
   2552 	if (msgid && msgid[0] != '\0')
   2553 		g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
   2554 
   2555 	return TRUE;
   2556 }
   2557 
   2558 static void summary_set_ctree_from_list(SummaryView *summaryview,
   2559 					GSList *mlist, guint selected_msgnum)
   2560 {
   2561 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   2562 	MsgInfo *msginfo;
   2563 	GtkCMCTreeNode *node = NULL;
   2564 	GHashTable *msgid_table;
   2565 	GHashTable *subject_table = NULL;
   2566 	GSList * cur;
   2567 	GdkDisplay *display;
   2568 
   2569 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
   2570 
   2571 	if (!mlist) return;
   2572 
   2573 	display = gdk_display_get_default();
   2574 
   2575 	debug_print("Setting summary from message data...\n");
   2576 	STATUSBAR_PUSH(summaryview->mainwin,
   2577 		       _("Setting summary from message data..."));
   2578 	gdk_display_flush(display);
   2579 
   2580 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
   2581 				       G_CALLBACK(summary_tree_expanded), summaryview);
   2582 
   2583 	msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
   2584 	summaryview->msgid_table = msgid_table;
   2585 
   2586 	if (prefs_common.thread_by_subject) {
   2587 		subject_table = g_hash_table_new(g_str_hash, g_str_equal);
   2588 		summaryview->subject_table = subject_table;
   2589 	} else {
   2590 		summaryview->subject_table = NULL;
   2591 	}
   2592 
   2593 	if (prefs_common.use_addr_book)
   2594 		start_address_completion(NULL);
   2595 
   2596 	if (summaryview->threaded) {
   2597 		GNode *root, *gnode;
   2598 		root = procmsg_get_thread_tree(mlist);
   2599 
   2600 
   2601 		for (gnode = root->children; gnode != NULL;
   2602 		     gnode = gnode->next) {
   2603 			if (!summaryview->folder_item->hide_read_threads ||
   2604 			    !summary_thread_is_read(gnode) ||
   2605 			    summary_thread_is_selected(gnode, selected_msgnum)) {
   2606 				summary_find_thread_age(gnode);
   2607 				node = gtk_sctree_insert_gnode
   2608 					(ctree, NULL, node, gnode,
   2609 					 summary_insert_gnode_func, summaryview);
   2610 			}
   2611 		}
   2612 
   2613 		g_node_destroy(root);
   2614 
   2615 	} else {
   2616 		gchar *text[N_SUMMARY_COLS];
   2617 		cur = mlist;
   2618 		for (; mlist != NULL; mlist = mlist->next) {
   2619 			msginfo = (MsgInfo *)mlist->data;
   2620 
   2621 			summary_set_header(summaryview, text, msginfo);
   2622 
   2623 			node = gtk_sctree_insert_node
   2624 				(ctree, NULL, node, text, 2,
   2625 				 NULL, NULL,
   2626 				 FALSE, FALSE);
   2627 			if (vert_layout && prefs_common.two_line_vert)
   2628 				g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
   2629 
   2630 			GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
   2631 			summary_set_marks_func(ctree, node, summaryview);
   2632 
   2633 			if (msginfo->msgid && msginfo->msgid[0] != '\0')
   2634 				g_hash_table_insert(msgid_table,
   2635 						    msginfo->msgid, node);
   2636 
   2637 			if (prefs_common.thread_by_subject)
   2638 				subject_table_insert(subject_table,
   2639 					     msginfo->subject,
   2640 					     node);
   2641 		}
   2642 		mlist = cur;
   2643 	}
   2644 
   2645 	if (prefs_common.enable_hscrollbar &&
   2646 	    summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
   2647 		gint optimal_width;
   2648 
   2649 		optimal_width = gtk_cmclist_optimal_column_width
   2650 			(GTK_CMCLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
   2651 		gtk_cmclist_set_column_width(GTK_CMCLIST(ctree),
   2652 					   summaryview->col_pos[S_COL_SUBJECT],
   2653 					   optimal_width);
   2654 	}
   2655 
   2656 	if (prefs_common.use_addr_book)
   2657 		end_address_completion();
   2658 
   2659 	debug_print("Setting summary from message data done.\n");
   2660 	STATUSBAR_POP(summaryview->mainwin);
   2661 	if (debug_get_mode()) {
   2662 		debug_print("\tmsgid hash table size = %d\n",
   2663 			    g_hash_table_size(msgid_table));
   2664 		if (prefs_common.thread_by_subject)
   2665 			debug_print("\tsubject hash table size = %d\n",
   2666 			    g_hash_table_size(subject_table));
   2667 	}
   2668 
   2669 	summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type);
   2670 
   2671 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   2672 
   2673 	if (prefs_common.bold_unread || prefs_common.bold_marked) {
   2674 		while (node) {
   2675 			GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
   2676 			if (GTK_CMCTREE_ROW(node)->children)
   2677 				summary_set_row_marks(summaryview, node);
   2678 			node = next;
   2679 		}
   2680 	}
   2681 
   2682 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
   2683 				       G_CALLBACK(summary_tree_expanded), summaryview);
   2684 }
   2685 
   2686 static gchar *summary_complete_address(const gchar *addr)
   2687 {
   2688 	gint count;
   2689 	gchar *res, *tmp, *email_addr;
   2690 
   2691 	if (addr == NULL || !strchr(addr, '@'))
   2692 		return NULL;
   2693 
   2694 	Xstrdup_a(email_addr, addr, return NULL);
   2695 	extract_address(email_addr);
   2696 	if (!*email_addr)
   2697 		return NULL;
   2698 
   2699 	/*
   2700 	 * completion stuff must be already initialized
   2701 	 */
   2702 	res = NULL;
   2703 	if (1 < (count = complete_address(email_addr))) {
   2704 		tmp = get_complete_address(1);
   2705 		res = procheader_get_fromname(tmp);
   2706 		g_free(tmp);
   2707 	}
   2708 
   2709 	return res;
   2710 }
   2711 
   2712 static inline void summary_set_header(SummaryView *summaryview, gchar *text[],
   2713 			       MsgInfo *msginfo)
   2714 {
   2715 	static gchar date_modified[80];
   2716 	static gchar col_score[11];
   2717 	static gchar from_buf[BUFFSIZE], to_buf[BUFFSIZE];
   2718 	static gchar tmp2[BUFFSIZE+4], tmp3[BUFFSIZE];
   2719 	gint *col_pos = summaryview->col_pos;
   2720 	gchar *from_text = NULL, *to_text = NULL;
   2721 	gboolean should_swap = FALSE;
   2722 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
   2723 	static const gchar *color_dim_rgb = NULL;
   2724 	if (!color_dim_rgb)
   2725 		color_dim_rgb = gtkut_gdk_rgba_to_string(&summaryview->color_dim);
   2726 	text[col_pos[S_COL_FROM]]   = "";
   2727 	text[col_pos[S_COL_TO]]     = "";
   2728 	text[col_pos[S_COL_SUBJECT]]= "";
   2729 	text[col_pos[S_COL_MARK]]   = "";
   2730 	text[col_pos[S_COL_STATUS]] = "";
   2731 	text[col_pos[S_COL_MIME]]   = "";
   2732 	text[col_pos[S_COL_LOCKED]] = "";
   2733 	text[col_pos[S_COL_DATE]]   = "";
   2734 	text[col_pos[S_COL_TAGS]]   = "";
   2735 	if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
   2736 		text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
   2737 	else
   2738 		text[col_pos[S_COL_NUMBER]] = "";
   2739 
   2740 	/* slow! */
   2741 	if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
   2742 		text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size);
   2743 	else
   2744 		text[col_pos[S_COL_SIZE]] = "";
   2745 
   2746 	if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
   2747 		text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score);
   2748 	else
   2749 		text[col_pos[S_COL_SCORE]] = "";
   2750 
   2751 	/* slow! */
   2752 	if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible ||
   2753 	    (vert_layout && prefs_common.two_line_vert)) {
   2754 		if (msginfo->date_t && msginfo->date_t > 0) {
   2755 			procheader_date_get_localtime(date_modified,
   2756 						      sizeof(date_modified),
   2757 						      msginfo->date_t);
   2758 			text[col_pos[S_COL_DATE]] = date_modified;
   2759 		} else if (msginfo->date)
   2760 			text[col_pos[S_COL_DATE]] = msginfo->date;
   2761 		else
   2762 			text[col_pos[S_COL_DATE]] = _("(No Date)");
   2763 	}
   2764 
   2765 	if (prefs_common.swap_from && msginfo->from && msginfo->to
   2766 	&&  !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) {
   2767 		gchar *addr = NULL;
   2768 
   2769 		addr = g_strdup(msginfo->from);
   2770 
   2771 		if (addr) {
   2772 			extract_address(addr);
   2773 			if (account_find_from_address(addr)) {
   2774 				should_swap = TRUE;
   2775 			}
   2776 			g_free(addr);
   2777 		}
   2778 	}
   2779 
   2780 	if (!prefs_common.use_addr_book) {
   2781 		if (prefs_common.summary_from_show == SHOW_NAME)
   2782 			from_text = msginfo->fromname;
   2783 		else if (prefs_common.summary_from_show == SHOW_BOTH)
   2784 			from_text = msginfo->from;
   2785 		else {
   2786 			from_text = msginfo->from;
   2787 			extract_address(from_text);
   2788 		}
   2789 		if (!from_text)
   2790 			from_text = _("(No From)");
   2791 	} else {
   2792 		gchar *tmp = summary_complete_address(msginfo->from);
   2793 		if (tmp) {
   2794 			strncpy2(from_buf, tmp, sizeof(from_buf));
   2795 			g_free(tmp);
   2796 			from_text = from_buf;
   2797 		} else {
   2798 			if (prefs_common.summary_from_show == SHOW_NAME)
   2799 				from_text = msginfo->fromname;
   2800 			else if (prefs_common.summary_from_show == SHOW_BOTH)
   2801 				from_text = msginfo->from;
   2802 			else {
   2803 				from_text = msginfo->from;
   2804 				if (from_text)
   2805 					extract_address(from_text);
   2806 			}
   2807 			if (!from_text)
   2808 				from_text = _("(No From)");
   2809 		}
   2810 	}
   2811 
   2812 	to_text = msginfo->to ? msginfo->to :
   2813 		   (msginfo->cc ? msginfo->cc : NULL);
   2814 
   2815 	if (!to_text)
   2816 		to_text = _("(No Recipient)");
   2817 	else {
   2818 		if (prefs_common.summary_from_show == SHOW_NAME) {
   2819 			gchar *tmp = procheader_get_fromname(to_text);
   2820 			/* need to keep to_text pointing to stack, so heap-allocated
   2821 			 * string from procheader_get_fromname() will be copied to to_buf */
   2822 			if (tmp != NULL) {
   2823 				strncpy2(to_buf, tmp, sizeof(to_buf));
   2824 				g_free(tmp);
   2825 				to_text = to_buf;
   2826 			}
   2827 		} else if (prefs_common.summary_from_show == SHOW_ADDR)
   2828 			extract_address(to_text);
   2829 	}
   2830 
   2831 	text[col_pos[S_COL_TO]] = to_text;
   2832 	if (!should_swap) {
   2833 		text[col_pos[S_COL_FROM]] = from_text;
   2834 	} else {
   2835 		if (prefs_common.use_addr_book) {
   2836 			gchar *tmp = summary_complete_address(to_text);
   2837 			/* need to keep to_text pointing to stack, so heap-allocated
   2838 			 * string from summary_complete_address() will be copied to to_buf */
   2839 			if (tmp) {
   2840 				strncpy2(to_buf, tmp, sizeof(to_buf));
   2841 				g_free(tmp);
   2842 				to_text = to_buf;
   2843 			} else {
   2844 				to_text = to_text ? to_text : _("(No From)");
   2845 			}
   2846 		}
   2847 		snprintf(tmp2, BUFFSIZE+4, "➜ %s", to_text);
   2848 		tmp2[BUFFSIZE-1]='\0';
   2849 		text[col_pos[S_COL_FROM]] = tmp2;
   2850 	}
   2851 
   2852 	if (summaryview->simplify_subject_preg != NULL)
   2853 		text[col_pos[S_COL_SUBJECT]] = msginfo->subject ?
   2854 			string_remove_match(tmp3, BUFFSIZE, msginfo->subject,
   2855 					summaryview->simplify_subject_preg) :
   2856 			_("(No Subject)");
   2857 	else
   2858 		text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
   2859 			_("(No Subject)");
   2860 	if (vert_layout && prefs_common.two_line_vert) {
   2861 		if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) {
   2862 			gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
   2863 									"<span color='%s' style='italic'>",
   2864 									_("From: %s, on %s"), "</span>", NULL),
   2865 					text[col_pos[S_COL_SUBJECT]],
   2866 					color_dim_rgb,
   2867 					text[col_pos[S_COL_FROM]],
   2868 					text[col_pos[S_COL_DATE]]);
   2869 			text[col_pos[S_COL_SUBJECT]] = tmp;
   2870 		} else {
   2871 			gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
   2872 									 "<span color='%s' style='italic'>",
   2873 									_("To: %s, on %s"), "</span>", NULL),
   2874 					text[col_pos[S_COL_SUBJECT]],
   2875 					color_dim_rgb,
   2876 					text[col_pos[S_COL_TO]],
   2877 					text[col_pos[S_COL_DATE]]);
   2878 			text[col_pos[S_COL_SUBJECT]] = tmp;
   2879 		}
   2880 	}
   2881 }
   2882 
   2883 static void summary_display_msg(SummaryView *summaryview, GtkCMCTreeNode *row)
   2884 {
   2885 	summary_display_msg_full(summaryview, row, FALSE, FALSE, FALSE);
   2886 }
   2887 
   2888 static gboolean defer_change(gpointer data);
   2889 typedef enum {
   2890 	FLAGS_UNSET,
   2891 	FLAGS_SET,
   2892 	FLAGS_CHANGE
   2893 } ChangeType;
   2894 typedef struct _ChangeData {
   2895 	MsgInfo *info;
   2896 	ChangeType op;
   2897 	MsgPermFlags set_flags;
   2898 	MsgTmpFlags  set_tmp_flags;
   2899 	MsgPermFlags unset_flags;
   2900 	MsgTmpFlags  unset_tmp_flags;
   2901 } ChangeData;
   2902 
   2903 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
   2904 {
   2905 	if (!msginfo->folder || !msginfo->folder->processing_pending) {
   2906 		debug_print("flags: doing unset now\n");
   2907 		procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags);
   2908 	} else {
   2909 		ChangeData *unset_data = g_new0(ChangeData, 1);
   2910 		unset_data->info = msginfo;
   2911 		unset_data->op = FLAGS_UNSET;
   2912 		unset_data->unset_flags = flags;
   2913 		unset_data->unset_tmp_flags = tmp_flags;
   2914 		debug_print("flags: deferring unset\n");
   2915 		g_timeout_add(100, defer_change, unset_data);
   2916 	}
   2917 }
   2918 
   2919 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
   2920 {
   2921 	if (!msginfo->folder || !msginfo->folder->processing_pending) {
   2922 		debug_print("flags: doing set now\n");
   2923 		procmsg_msginfo_set_flags(msginfo, flags, tmp_flags);
   2924 	} else {
   2925 		ChangeData *set_data = g_new0(ChangeData, 1);
   2926 		set_data->info = msginfo;
   2927 		set_data->op = FLAGS_SET;
   2928 		set_data->set_flags = flags;
   2929 		set_data->set_tmp_flags = tmp_flags;
   2930 		debug_print("flags: deferring set\n");
   2931 		g_timeout_add(100, defer_change, set_data);
   2932 	}
   2933 }
   2934 
   2935 static void summary_msginfo_change_flags(MsgInfo *msginfo,
   2936 		MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags,
   2937 		MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags)
   2938 {
   2939 	if (!msginfo->folder || !msginfo->folder->processing_pending) {
   2940 		debug_print("flags: doing change now\n");
   2941 		procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags,
   2942 			rem_flags, rem_tmp_flags);
   2943 	} else {
   2944 		ChangeData *change_data = g_new0(ChangeData, 1);
   2945 		change_data->info = msginfo;
   2946 		change_data->op = FLAGS_CHANGE;
   2947 		change_data->set_flags = add_flags;
   2948 		change_data->set_tmp_flags = add_tmp_flags;
   2949 		change_data->unset_flags = rem_flags;
   2950 		change_data->unset_tmp_flags = rem_tmp_flags;
   2951 		debug_print("flags: deferring change\n");
   2952 		g_timeout_add(100, defer_change, change_data);
   2953 	}
   2954 }
   2955 
   2956 gboolean defer_change(gpointer data)
   2957 {
   2958 	ChangeData *chg = (ChangeData *)data;
   2959 	if (chg->info->folder && chg->info->folder->processing_pending) {
   2960 		debug_print("flags: trying later\n");
   2961 		return TRUE; /* try again */
   2962 	} else {
   2963 		debug_print("flags: finally doing it\n");
   2964 		switch(chg->op) {
   2965 		case FLAGS_UNSET:
   2966 			procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags);
   2967 			break;
   2968 		case FLAGS_SET:
   2969 			procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags);
   2970 			break;
   2971 		case FLAGS_CHANGE:
   2972 			procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags,
   2973 				chg->unset_flags, chg->unset_tmp_flags);
   2974 			break;
   2975 		default:
   2976 			g_warning("unknown change op");
   2977 		}
   2978 		g_free(chg);
   2979 	}
   2980 	return FALSE;
   2981 }
   2982 
   2983 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo,
   2984 				      GtkCMCTreeNode *row)
   2985 {
   2986 	cm_return_if_fail(summaryview != NULL);
   2987 	cm_return_if_fail(msginfo != NULL);
   2988 	cm_return_if_fail(row != NULL);
   2989 
   2990 	if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
   2991 		summary_msginfo_unset_flags
   2992 			(msginfo, MSG_NEW | MSG_UNREAD, 0);
   2993 		summary_set_row_marks(summaryview, row);
   2994 		summary_status_show(summaryview);
   2995 	}
   2996 }
   2997 
   2998 typedef struct  {
   2999 	MsgInfo *msginfo;
   3000 	SummaryView *summaryview;
   3001 } MarkAsReadData;
   3002 
   3003 static int msginfo_mark_as_read_timeout(void *data)
   3004 {
   3005 	MarkAsReadData *mdata = (MarkAsReadData *)data;
   3006 	if (!mdata)
   3007 		return FALSE;
   3008 
   3009 	summary_lock(mdata->summaryview);
   3010 	if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview))
   3011 		msginfo_mark_as_read(mdata->summaryview, mdata->msginfo,
   3012 				     mdata->summaryview->selected);
   3013 	procmsg_msginfo_free(&(mdata->msginfo));
   3014 
   3015 	mdata->summaryview->mark_as_read_timeout_tag = 0;
   3016 	summary_unlock(mdata->summaryview);
   3017 
   3018 	g_free(mdata);
   3019 	return FALSE;
   3020 }
   3021 
   3022 static void summary_display_msg_full(SummaryView *summaryview,
   3023 				     GtkCMCTreeNode *row,
   3024 				     gboolean new_window, gboolean all_headers,
   3025 				     gboolean from_sep_msgview)
   3026 {
   3027 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3028 	MsgInfo *msginfo;
   3029 	gint val;
   3030 	if (!new_window) {
   3031 		if (summaryview->displayed == row &&
   3032 		    messageview_is_visible(summaryview->messageview))
   3033 			return;
   3034 		else if (summaryview->messageview)
   3035 			summaryview->messageview->filtered = FALSE;
   3036 	}
   3037 	cm_return_if_fail(row != NULL);
   3038 
   3039 	if (summary_is_locked(summaryview)) return;
   3040 	summary_lock(summaryview);
   3041 
   3042 	STATUSBAR_POP(summaryview->mainwin);
   3043 	GTK_EVENTS_FLUSH();
   3044 
   3045 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3046 
   3047 	if (!msginfo) {
   3048 		debug_print("NULL msginfo\n");
   3049 		summary_unlock(summaryview);
   3050 		return;
   3051 	}
   3052 
   3053 	if (new_window) {
   3054 		MessageView *msgview = messageview_create_with_new_window(summaryview->mainwin);
   3055 		val = messageview_show(msgview, msginfo, all_headers);
   3056 	} else {
   3057 		MessageView *msgview = summaryview->messageview;
   3058 		summaryview->displayed = row;
   3059 		if (!messageview_is_visible(msgview) &&
   3060 		    gtk_window_is_active(GTK_WINDOW(summaryview->mainwin->window))) {
   3061 			main_window_toggle_message_view(summaryview->mainwin);
   3062 			GTK_EVENTS_FLUSH();
   3063 		}
   3064 		val = messageview_show(msgview, msginfo, all_headers);
   3065 		if (mimeview_tree_is_empty(msgview->mimeview))
   3066 			gtk_widget_grab_focus(summaryview->ctree);
   3067 		gtkut_ctree_node_move_if_on_the_edge(ctree, row,
   3068 			GTK_CMCLIST(summaryview->ctree)->focus_row);
   3069 	}
   3070 
   3071 	if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) {
   3072 		if (!prefs_common.mark_as_read_on_new_window &&
   3073 		    prefs_common.mark_as_read_delay) {
   3074 			MarkAsReadData *data = g_new0(MarkAsReadData, 1);
   3075 			data->summaryview = summaryview;
   3076 			data->msginfo = procmsg_msginfo_new_ref(msginfo);
   3077 			if (summaryview->mark_as_read_timeout_tag != 0)
   3078 				g_source_remove(summaryview->mark_as_read_timeout_tag);
   3079 			summaryview->mark_as_read_timeout_tag =
   3080 				g_timeout_add_seconds(prefs_common.mark_as_read_delay,
   3081 					msginfo_mark_as_read_timeout, data);
   3082 		} else if (new_window || !prefs_common.mark_as_read_on_new_window) {
   3083 			msginfo_mark_as_read(summaryview, msginfo, row);
   3084 		} else if (from_sep_msgview &&
   3085 			   prefs_common.mark_as_read_on_new_window) {
   3086 			msginfo_mark_as_read(summaryview, msginfo, row);
   3087 		}
   3088 	}
   3089 
   3090 	summary_set_menu_sensitive(summaryview);
   3091 	toolbar_main_set_sensitive(summaryview->mainwin);
   3092 	messageview_set_menu_sensitive(summaryview->messageview);
   3093 
   3094 	summary_unlock(summaryview);
   3095 }
   3096 
   3097 void summary_display_msg_selected(SummaryView *summaryview,
   3098 				  gboolean all_headers)
   3099 {
   3100 	if (summary_is_locked(summaryview)) return;
   3101 	summaryview->displayed = NULL;
   3102 	summary_display_msg_full(summaryview, summaryview->selected, FALSE,
   3103 				 all_headers, FALSE);
   3104 }
   3105 
   3106 void summary_redisplay_msg(SummaryView *summaryview)
   3107 {
   3108 	GtkCMCTreeNode *node;
   3109 
   3110 	if (summaryview->displayed) {
   3111 		node = summaryview->displayed;
   3112 		summaryview->displayed = NULL;
   3113 		summary_display_msg(summaryview, node);
   3114 	}
   3115 }
   3116 
   3117 void summary_open_msg(SummaryView *summaryview, gboolean new_window,
   3118 		      gboolean from_sep_msgview)
   3119 {
   3120 	if (!summaryview->selected) return;
   3121 
   3122 	/* CLAWS: if separate message view, don't open a new window
   3123 	 * but rather use the current separated message view */
   3124 	summary_display_msg_full(summaryview, summaryview->selected,
   3125 				 new_window, FALSE, from_sep_msgview);
   3126 }
   3127 
   3128 void summary_view_source(SummaryView * summaryview)
   3129 {
   3130 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3131 	MsgInfo *msginfo;
   3132 	SourceWindow *srcwin;
   3133 
   3134 	if (!summaryview->selected) return;
   3135 
   3136 	srcwin = source_window_create();
   3137 	msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
   3138 	source_window_show(srcwin);
   3139 	source_window_show_msg(srcwin, msginfo);
   3140 }
   3141 
   3142 void summary_reedit(SummaryView *summaryview)
   3143 {
   3144 	MsgInfo *msginfo;
   3145 
   3146 	if (!summaryview->selected) return;
   3147 	if (!summaryview->folder_item) return;
   3148 	if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
   3149 		return;
   3150 
   3151 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
   3152 					      summaryview->selected);
   3153 	if (!msginfo) return;
   3154 
   3155 	compose_reedit(msginfo, FALSE);
   3156 }
   3157 
   3158 gboolean summary_is_list(SummaryView *summaryview)
   3159 {
   3160 	return (gtk_notebook_get_current_page(
   3161 		GTK_NOTEBOOK(summaryview->mainwidget_book)) == 0);
   3162 }
   3163 
   3164 void summary_toggle_view(SummaryView *summaryview)
   3165 {
   3166 	if (summary_is_locked(summaryview))
   3167 		return;
   3168 	if (!messageview_is_visible(summaryview->messageview) &&
   3169 	    summaryview->selected && summary_is_list(summaryview))
   3170 		summary_display_msg(summaryview,
   3171 				    summaryview->selected);
   3172 	else
   3173 		main_window_toggle_message_view(summaryview->mainwin);
   3174 }
   3175 
   3176 static gboolean summary_search_unread_recursive(GtkCMCTree *ctree,
   3177 						GtkCMCTreeNode *node)
   3178 {
   3179 	MsgInfo *msginfo;
   3180 
   3181 	if (node) {
   3182 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
   3183 		if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
   3184 			return TRUE;
   3185 		node = GTK_CMCTREE_ROW(node)->children;
   3186 	} else
   3187 		node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   3188 
   3189 	while (node) {
   3190 		if (summary_search_unread_recursive(ctree, node) == TRUE)
   3191 			return TRUE;
   3192 		node = GTK_CMCTREE_ROW(node)->sibling;
   3193 	}
   3194 
   3195 	return FALSE;
   3196 }
   3197 
   3198 static gboolean summary_have_unread_children(SummaryView *summaryview,
   3199 					     GtkCMCTreeNode *node)
   3200 {
   3201 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3202 
   3203 	if (!node) return FALSE;
   3204 
   3205 	node = GTK_CMCTREE_ROW(node)->children;
   3206 
   3207 	while (node) {
   3208 		if (summary_search_unread_recursive(ctree, node) == TRUE)
   3209 			return TRUE;
   3210 		node = GTK_CMCTREE_ROW(node)->sibling;
   3211 	}
   3212 	return FALSE;
   3213 }
   3214 
   3215 static void summary_set_row_marks(SummaryView *summaryview, GtkCMCTreeNode *row)
   3216 {
   3217 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3218 	GtkStyle *style = NULL;
   3219 	MsgInfo *msginfo;
   3220 	MsgFlags flags;
   3221 	gint *col_pos = summaryview->col_pos;
   3222 
   3223 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3224 	if (!msginfo) return;
   3225 
   3226 	flags = msginfo->flags;
   3227 
   3228 	gtk_cmctree_node_set_foreground(ctree, row, NULL);
   3229 
   3230 	/* set new/unread column */
   3231 	if (MSG_IS_IGNORE_THREAD(flags)) {
   3232 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3233 					  ignorethreadxpm);
   3234 	} else if (MSG_IS_WATCH_THREAD(flags)) {
   3235 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3236 					  watchthreadxpm);
   3237 	} else if (MSG_IS_SPAM(flags)) {
   3238 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3239 					  spamxpm);
   3240 	} else if (MSG_IS_NEW(flags)) {
   3241 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3242 					  newxpm);
   3243 	} else if (MSG_IS_UNREAD(flags)) {
   3244 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3245 					  unreadxpm);
   3246 	} else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
   3247 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3248 					  repliedandforwardedxpm);
   3249 	} else if (MSG_IS_REPLIED(flags)) {
   3250 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3251 					  repliedxpm);
   3252 	} else if (MSG_IS_FORWARDED(flags)) {
   3253 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
   3254 					  forwardedxpm);
   3255 	} else {
   3256 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_STATUS],
   3257 					"");
   3258 	}
   3259 
   3260 	if ((((prefs_common.bold_unread && MSG_IS_UNREAD(flags)) ||
   3261 	     (prefs_common.bold_marked && MSG_IS_MARKED(flags))) &&
   3262 	    !MSG_IS_IGNORE_THREAD(flags)) ||
   3263 	    (!GTK_CMCTREE_ROW(row)->expanded &&
   3264 	      GTK_CMCTREE_ROW(row)->children &&
   3265 	      summary_have_unread_children(summaryview, row)))
   3266 		style = bold_style;
   3267 
   3268 	/* set mark column */
   3269 	if (MSG_IS_DELETED(flags)) {
   3270 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
   3271 					  deletedxpm);
   3272 		if (style)
   3273 			style = bold_style;
   3274 		gtk_cmctree_node_set_foreground
   3275 			(ctree, row, &summaryview->color_dim);
   3276 	} else if (MSG_IS_MARKED(flags)) {
   3277 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
   3278 					  markxpm);
   3279 	} else if (MSG_IS_MOVE(flags)) {
   3280 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
   3281 					  movedxpm);
   3282 		if (!msginfo->to_folder ||
   3283 		    !folder_has_parent_of_type(msginfo->to_folder, F_TRASH)) {
   3284 			if (style)
   3285 				style = bold_style;
   3286 			gtk_cmctree_node_set_foreground
   3287 				(ctree, row, &summaryview->color_marked);
   3288 		} else {
   3289 			if (style)
   3290 				style = bold_style;
   3291 			gtk_cmctree_node_set_foreground
   3292 				(ctree, row, &summaryview->color_dim);
   3293 		}
   3294 	} else if (MSG_IS_COPY(flags)) {
   3295 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
   3296 					  copiedxpm);
   3297 		if (style)
   3298 			style = bold_style;
   3299 		gtk_cmctree_node_set_foreground
   3300 			(ctree, row, &summaryview->color_marked);
   3301 	} else {
   3302 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "");
   3303 	}
   3304 
   3305 	if (MSG_IS_LOCKED(flags)) {
   3306 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_LOCKED],
   3307 					  lockedxpm);
   3308 	}
   3309 	else {
   3310 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], "");
   3311 	}
   3312 
   3313 	if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
   3314 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
   3315 					  clipgpgsignedxpm);
   3316 	} else if (MSG_IS_SIGNED(flags)) {
   3317 		if (MSG_IS_ENCRYPTED(flags)) {
   3318 			gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
   3319 					  keysignxpm);
   3320 		} else {
   3321 			gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
   3322 						  gpgsignedxpm);
   3323 		}
   3324 	} else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
   3325 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
   3326 					  clipkeyxpm);
   3327 	} else if (MSG_IS_ENCRYPTED(flags)) {
   3328 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
   3329 					  keyxpm);
   3330 	} else if (MSG_IS_WITH_ATTACHMENT(flags)) {
   3331 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
   3332 					  clipxpm);
   3333 	} else {
   3334 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MIME], "");
   3335 	}
   3336 
   3337 	gtk_cmctree_node_set_row_style(ctree, row, style);
   3338 }
   3339 
   3340 static void summary_mark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
   3341 {
   3342 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3343 	MsgInfo *msginfo;
   3344 
   3345 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3346 	cm_return_if_fail(msginfo);
   3347 	if (MSG_IS_DELETED(msginfo->flags))
   3348 		summaryview->deleted--;
   3349 	if (MSG_IS_MOVE(msginfo->flags))
   3350 		summaryview->moved--;
   3351 	if (MSG_IS_COPY(msginfo->flags))
   3352 		summaryview->copied--;
   3353 
   3354 	procmsg_msginfo_set_to_folder(msginfo, NULL);
   3355 	summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED,
   3356 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
   3357 	summary_set_row_marks(summaryview, row);
   3358 	debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum);
   3359 }
   3360 
   3361 static void summary_lock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
   3362 {
   3363 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3364 	MsgInfo *msginfo;
   3365 
   3366 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3367 	cm_return_if_fail(msginfo);
   3368 	if (MSG_IS_DELETED(msginfo->flags))
   3369 		summaryview->deleted--;
   3370 	if (MSG_IS_MOVE(msginfo->flags)) {
   3371 		summaryview->moved--;
   3372 	}
   3373 	if (MSG_IS_COPY(msginfo->flags)) {
   3374 		summaryview->copied--;
   3375 	}
   3376 	procmsg_msginfo_set_to_folder(msginfo, NULL);
   3377 	summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED,
   3378 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
   3379 
   3380 	summary_set_row_marks(summaryview, row);
   3381 	debug_print("Message %d is locked\n", msginfo->msgnum);
   3382 }
   3383 
   3384 static void summary_unlock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
   3385 {
   3386 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3387 	MsgInfo *msginfo;
   3388 
   3389 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3390 	cm_return_if_fail(msginfo);
   3391 	if (!MSG_IS_LOCKED(msginfo->flags))
   3392 		return;
   3393 	procmsg_msginfo_set_to_folder(msginfo, NULL);
   3394 	summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
   3395 	summary_set_row_marks(summaryview, row);
   3396 	debug_print("Message %d is unlocked\n", msginfo->msgnum);
   3397 }
   3398 
   3399 void summary_mark(SummaryView *summaryview)
   3400 {
   3401 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3402 	GList *cur;
   3403 	gboolean froze = FALSE;
   3404 
   3405 	if (summary_is_locked(summaryview))
   3406 		return;
   3407 	START_LONG_OPERATION(summaryview, FALSE);
   3408 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3409 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   3410 		summary_mark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
   3411 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3412 	END_LONG_OPERATION(summaryview);
   3413 
   3414 	summary_status_show(summaryview);
   3415 }
   3416 
   3417 static void summary_mark_row_as_read(SummaryView *summaryview,
   3418 				     GtkCMCTreeNode *row)
   3419 {
   3420 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3421 	MsgInfo *msginfo;
   3422 
   3423 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3424 	cm_return_if_fail(msginfo);
   3425 
   3426 	if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
   3427 		return;
   3428 
   3429 	summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
   3430 	summary_set_row_marks(summaryview, row);
   3431 	debug_print("Message %d is marked as read\n",
   3432 		msginfo->msgnum);
   3433 }
   3434 
   3435 static void summary_mark_row_as_unread(SummaryView *summaryview,
   3436 				     GtkCMCTreeNode *row)
   3437 {
   3438 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3439 	MsgInfo *msginfo;
   3440 
   3441 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3442 	cm_return_if_fail(msginfo);
   3443 
   3444 	if(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))
   3445 		return;
   3446 
   3447 	summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0);
   3448 	summary_set_row_marks(summaryview, row);
   3449 	debug_print("Message %d is marked as unread\n",
   3450 		msginfo->msgnum);
   3451 }
   3452 
   3453 void summary_mark_as_read(SummaryView *summaryview)
   3454 {
   3455 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3456 	GList *cur;
   3457 	gboolean froze = FALSE;
   3458 
   3459 	if (summary_is_locked(summaryview))
   3460 		return;
   3461 
   3462 	if (!summaryview->folder_item)
   3463 		return;
   3464 
   3465 	if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) &&
   3466 	     summaryview->folder_item->total_msgs > 1) &&
   3467 	    !summary_mark_all_read_confirm(TRUE))
   3468 		return;
   3469 
   3470 	START_LONG_OPERATION(summaryview, FALSE);
   3471 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3472 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   3473 		summary_mark_row_as_read(summaryview,
   3474 					 GTK_CMCTREE_NODE(cur->data));
   3475 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3476 	END_LONG_OPERATION(summaryview);
   3477 
   3478 	summary_status_show(summaryview);
   3479 }
   3480 
   3481 void summary_mark_as_unread(SummaryView *summaryview)
   3482 {
   3483 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3484 	GList *cur;
   3485 	gboolean froze = FALSE;
   3486 
   3487 	if (summary_is_locked(summaryview))
   3488 		return;
   3489 
   3490 	if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) &&
   3491 	     summaryview->folder_item->total_msgs > 1) &&
   3492 	    !summary_mark_all_unread_confirm(TRUE))
   3493 		return;
   3494 
   3495 	START_LONG_OPERATION(summaryview, FALSE);
   3496 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3497 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   3498 		summary_mark_row_as_unread(summaryview,
   3499 					 GTK_CMCTREE_NODE(cur->data));
   3500 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3501 	END_LONG_OPERATION(summaryview);
   3502 
   3503 	summary_status_show(summaryview);
   3504 }
   3505 
   3506 void summary_msgs_lock(SummaryView *summaryview)
   3507 {
   3508 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3509 	GList *cur;
   3510 	gboolean froze = FALSE;
   3511 
   3512 	if (summary_is_locked(summaryview))
   3513 		return;
   3514 	START_LONG_OPERATION(summaryview, FALSE);
   3515 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   3516 		summary_lock_row(summaryview,
   3517 					 GTK_CMCTREE_NODE(cur->data));
   3518 	END_LONG_OPERATION(summaryview);
   3519 
   3520 	summary_status_show(summaryview);
   3521 }
   3522 
   3523 void summary_msgs_unlock(SummaryView *summaryview)
   3524 {
   3525 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3526 	GList *cur;
   3527 	gboolean froze = FALSE;
   3528 
   3529 	if (summary_is_locked(summaryview))
   3530 		return;
   3531 	START_LONG_OPERATION(summaryview, FALSE);
   3532 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   3533 		summary_unlock_row(summaryview,
   3534 				   GTK_CMCTREE_NODE(cur->data));
   3535 	END_LONG_OPERATION(summaryview);
   3536 
   3537 	summary_status_show(summaryview);
   3538 }
   3539 
   3540 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed)
   3541 {
   3542 	/* ask_if_needed is FALSE when user-asking is performed by caller,
   3543 	   commonly when the caller is a mark-as-read-recursive func */
   3544 	if (ask_if_needed && prefs_common.ask_mark_all_read) {
   3545 		AlertValue val = alertpanel_full(_("Mark all as read"),
   3546 			  _("Do you really want to mark all mails in this folder as read?"),
   3547 			  NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST,
   3548 			  TRUE, NULL, ALERT_QUESTION);
   3549 
   3550 		if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
   3551 			return FALSE;
   3552 		else if (val & G_ALERTDISABLE)
   3553 			prefs_common.ask_mark_all_read = FALSE;
   3554 	}
   3555 	return TRUE;
   3556 }
   3557 
   3558 void summary_mark_all_read(SummaryView *summaryview, gboolean ask_if_needed)
   3559 {
   3560 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3561 	GtkCMCTreeNode *node;
   3562 	gboolean froze = FALSE;
   3563 
   3564 	if (summary_is_locked(summaryview))
   3565 		return;
   3566 
   3567 	if (!summary_mark_all_read_confirm(ask_if_needed))
   3568 		return;
   3569 
   3570 	START_LONG_OPERATION(summaryview, TRUE);
   3571 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3572 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
   3573 		node = gtkut_ctree_node_next(ctree, node))
   3574 		summary_mark_row_as_read(summaryview, node);
   3575 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3576 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
   3577 		node = gtkut_ctree_node_next(ctree, node)) {
   3578 		if (!GTK_CMCTREE_ROW(node)->expanded)
   3579 			summary_set_row_marks(summaryview, node);
   3580 	}
   3581 	END_LONG_OPERATION(summaryview);
   3582 
   3583 	summary_status_show(summaryview);
   3584 }
   3585 
   3586 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed)
   3587 {
   3588 	/* ask_if_needed is FALSE when user-asking is performed by caller,
   3589 	   commonly when the caller is a mark-as-unread-recursive func */
   3590 	if (ask_if_needed && prefs_common.ask_mark_all_read) {
   3591 		AlertValue val = alertpanel_full(_("Mark all as unread"),
   3592 			  _("Do you really want to mark all mails in this folder as unread?"),
   3593 			  NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST,
   3594 			  TRUE, NULL, ALERT_QUESTION);
   3595 
   3596 		if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
   3597 			return FALSE;
   3598 		else if (val & G_ALERTDISABLE)
   3599 			prefs_common.ask_mark_all_read = FALSE;
   3600 	}
   3601 	return TRUE;
   3602 }
   3603 
   3604 void summary_mark_all_unread(SummaryView *summaryview, gboolean ask_if_needed)
   3605 {
   3606 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3607 	GtkCMCTreeNode *node;
   3608 	gboolean froze = FALSE;
   3609 
   3610 	if (summary_is_locked(summaryview))
   3611 		return;
   3612 
   3613 	if (!summary_mark_all_unread_confirm(ask_if_needed))
   3614 		return;
   3615 
   3616 	START_LONG_OPERATION(summaryview, TRUE);
   3617 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3618 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
   3619 		node = gtkut_ctree_node_next(ctree, node))
   3620 		summary_mark_row_as_unread(summaryview, node);
   3621 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3622 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
   3623 		node = gtkut_ctree_node_next(ctree, node)) {
   3624 		if (!GTK_CMCTREE_ROW(node)->expanded)
   3625 			summary_set_row_marks(summaryview, node);
   3626 	}
   3627 	END_LONG_OPERATION(summaryview);
   3628 
   3629 	summary_status_show(summaryview);
   3630 }
   3631 
   3632 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo)
   3633 {
   3634 	GList * cur;
   3635 	gboolean found;
   3636 
   3637 	switch (FOLDER_TYPE(summaryview->folder_item->folder)) {
   3638 
   3639 	case F_NEWS:
   3640 
   3641 		/*
   3642 		  security : checks if one the accounts correspond to
   3643 		  the author of the post
   3644 		*/
   3645 
   3646 		found = FALSE;
   3647 		for(cur = account_get_list() ; cur != NULL ; cur = cur->next) {
   3648 			PrefsAccount * account;
   3649 			gchar * from_name;
   3650 
   3651 			account = cur->data;
   3652 			if (account->name && *account->name)
   3653 				from_name =
   3654 					g_strdup_printf("%s <%s>",
   3655 							account->name,
   3656 							account->address);
   3657 			else
   3658 				from_name =
   3659 					g_strdup_printf("%s",
   3660 							account->address);
   3661 
   3662 			if (g_utf8_collate(from_name, msginfo->from) == 0) {
   3663 				g_free(from_name);
   3664 				found = TRUE;
   3665 				break;
   3666 			}
   3667 			g_free(from_name);
   3668 		}
   3669 
   3670 		if (!found) {
   3671 			alertpanel_error(_("You're not the author of the article."));
   3672 		}
   3673 
   3674 		return found;
   3675 
   3676 	default:
   3677 		return TRUE;
   3678 	}
   3679 }
   3680 
   3681 static void summary_delete_row(SummaryView *summaryview, GtkCMCTreeNode *row)
   3682 {
   3683 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3684 	MsgInfo *msginfo;
   3685 
   3686 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3687 	cm_return_if_fail(msginfo);
   3688 
   3689 	if (MSG_IS_LOCKED(msginfo->flags)) return;
   3690 
   3691 	if (MSG_IS_DELETED(msginfo->flags)) return;
   3692 
   3693 	if (MSG_IS_MOVE(msginfo->flags))
   3694 		summaryview->moved--;
   3695 	if (MSG_IS_COPY(msginfo->flags))
   3696 		summaryview->copied--;
   3697 
   3698 	procmsg_msginfo_set_to_folder(msginfo, NULL);
   3699 	summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED,
   3700 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
   3701 	summaryview->deleted++;
   3702 
   3703 	if (!prefs_common.immediate_exec &&
   3704 	    !folder_has_parent_of_type(summaryview->folder_item, F_TRASH)) {
   3705 		summary_set_row_marks(summaryview, row);
   3706 	}
   3707 	debug_print("Message %s/%d is set to delete\n",
   3708 		    msginfo->folder->path, msginfo->msgnum);
   3709 }
   3710 
   3711 void summary_cancel(SummaryView *summaryview)
   3712 {
   3713 	MsgInfo * msginfo;
   3714 
   3715 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
   3716 					      summaryview->selected);
   3717 	if (!msginfo) return;
   3718 
   3719 	if (!check_permission(summaryview, msginfo))
   3720 		return;
   3721 
   3722 	if (summary_is_locked(summaryview)) return;
   3723 
   3724 	summary_lock(summaryview);
   3725 
   3726 	summary_freeze(summaryview);
   3727 
   3728 	summary_update_status(summaryview);
   3729 	summary_status_show(summaryview);
   3730 
   3731 	summary_thaw(summaryview);
   3732 
   3733 	summary_unlock(summaryview);
   3734 }
   3735 
   3736 void summary_delete(SummaryView *summaryview)
   3737 {
   3738 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3739 	FolderItem *item = summaryview->folder_item;
   3740 	GList *cur;
   3741 	GtkCMCTreeNode *sel_last = NULL;
   3742 	GtkCMCTreeNode *node;
   3743 	AlertValue aval;
   3744 	gboolean froze = FALSE;
   3745 
   3746 	if (!item) return;
   3747 
   3748 	if (summary_is_locked(summaryview)) return;
   3749 
   3750 	if (!summaryview->folder_item) return;
   3751 
   3752 	START_LONG_OPERATION(summaryview, FALSE);
   3753 
   3754 	if (!prefs_common.live_dangerously) {
   3755 		guint num = g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
   3756 		char buf[BUFSIZ];
   3757 		snprintf(buf, sizeof(buf), "Delete the selected %d messages?", num);
   3758 		aval = alertpanel("Delete messages", buf,
   3759 				  NULL, "_Cancel", NULL, "_Delete", NULL, NULL, ALERTFOCUS_SECOND);
   3760 		if (aval != G_ALERTALTERNATE) {
   3761 			END_LONG_OPERATION(summaryview);
   3762 			return;
   3763 		}
   3764 	}
   3765 
   3766 	main_window_cursor_wait(summaryview->mainwin);
   3767 
   3768 	/* next code sets current row focus right. We need to find a row
   3769 	 * that is not deleted. */
   3770 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3771 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
   3772 		sel_last = GTK_CMCTREE_NODE(cur->data);
   3773 		summary_delete_row(summaryview, sel_last);
   3774 	}
   3775 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3776 	END_LONG_OPERATION(summaryview);
   3777 
   3778 	if (summaryview->sort_type == SORT_ASCENDING) {
   3779 		node = summary_find_next_msg(summaryview, sel_last, TRUE);
   3780 		if (!node || prefs_common.next_on_delete == FALSE)
   3781 			node = summary_find_prev_msg(summaryview, sel_last,TRUE);
   3782 	} else {
   3783 		node = summary_find_prev_msg(summaryview, sel_last,TRUE);
   3784 		if (!node || prefs_common.next_on_delete == FALSE)
   3785 			node = summary_find_next_msg(summaryview, sel_last,TRUE);
   3786 	}
   3787 	summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
   3788 
   3789 	if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) {
   3790 		summary_execute(summaryview);
   3791 		/* after deleting, the anchor may be at an invalid row
   3792 		 * so reset it to the node we found earlier */
   3793 		gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node);
   3794 	} else
   3795 		summary_status_show(summaryview);
   3796 
   3797 
   3798 	main_window_cursor_normal(summaryview->mainwin);
   3799 }
   3800 
   3801 void summary_delete_trash(SummaryView *summaryview)
   3802 {
   3803 	FolderItem *to_folder = NULL;
   3804 	PrefsAccount *ac;
   3805 	if (!summaryview->folder_item ||
   3806 	    FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
   3807 
   3808 	if (NULL != (ac = account_find_from_item(summaryview->folder_item)))
   3809 		to_folder = account_get_special_folder(ac, F_TRASH);
   3810 
   3811 	if (to_folder == NULL)
   3812 		to_folder = summaryview->folder_item->folder->trash;
   3813 
   3814 	if (to_folder == NULL || to_folder == summaryview->folder_item
   3815 	    || folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
   3816 		summary_delete(summaryview);
   3817 	else
   3818 		summary_move_selected_to(summaryview, to_folder);
   3819 }
   3820 
   3821 
   3822 static void summary_unmark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
   3823 {
   3824 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3825 	MsgInfo *msginfo;
   3826 
   3827 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3828 	cm_return_if_fail(msginfo);
   3829 	if (MSG_IS_DELETED(msginfo->flags))
   3830 		summaryview->deleted--;
   3831 	if (MSG_IS_MOVE(msginfo->flags))
   3832 		summaryview->moved--;
   3833 	if (MSG_IS_COPY(msginfo->flags))
   3834 		summaryview->copied--;
   3835 
   3836 	procmsg_msginfo_set_to_folder(msginfo, NULL);
   3837 	summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED,
   3838 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
   3839 	summary_set_row_marks(summaryview, row);
   3840 
   3841 	debug_print("Message %s/%d is unmarked\n",
   3842 		    msginfo->folder->path, msginfo->msgnum);
   3843 }
   3844 
   3845 void summary_unmark(SummaryView *summaryview)
   3846 {
   3847 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3848 	GList *cur;
   3849 	gboolean froze = FALSE;
   3850 
   3851 	if (summary_is_locked(summaryview))
   3852 		return;
   3853 	START_LONG_OPERATION(summaryview, FALSE);
   3854 	folder_item_set_batch(summaryview->folder_item, TRUE);
   3855 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   3856 		summary_unmark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
   3857 	folder_item_set_batch(summaryview->folder_item, FALSE);
   3858 	END_LONG_OPERATION(summaryview);
   3859 
   3860 	summary_status_show(summaryview);
   3861 }
   3862 
   3863 static void summary_move_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
   3864 				FolderItem *to_folder)
   3865 {
   3866 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3867 	MsgInfo *msginfo;
   3868 
   3869 	cm_return_if_fail(to_folder != NULL);
   3870 
   3871 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3872 	cm_return_if_fail(msginfo);
   3873 	if (MSG_IS_LOCKED(msginfo->flags))
   3874 		return;
   3875 
   3876 	procmsg_msginfo_set_to_folder(msginfo, to_folder);
   3877 	if (MSG_IS_DELETED(msginfo->flags))
   3878 		summaryview->deleted--;
   3879 	if (MSG_IS_COPY(msginfo->flags)) {
   3880 		summaryview->copied--;
   3881 	}
   3882 	if (!MSG_IS_MOVE(msginfo->flags)) {
   3883 		summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED,
   3884 			MSG_COPY | MSG_MOVE_DONE);
   3885 		summaryview->moved++;
   3886 	} else {
   3887 		summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY);
   3888 	}
   3889 
   3890 	if (!prefs_common.immediate_exec) {
   3891 		summary_set_row_marks(summaryview, row);
   3892 	}
   3893 
   3894 	debug_print("Message %d is set to move to %s\n",
   3895 		    msginfo->msgnum, to_folder->path);
   3896 }
   3897 
   3898 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
   3899 {
   3900 	GList *cur;
   3901 	GtkCMCTreeNode *sel_last = NULL;
   3902 	gboolean froze = FALSE;
   3903 
   3904 	if (!to_folder) return;
   3905 	if (!summaryview->folder_item ||
   3906 	    FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
   3907 
   3908 	if (summary_is_locked(summaryview)) return;
   3909 
   3910 	if (summaryview->folder_item == to_folder) {
   3911 		alertpanel_error(_("Destination is same as current folder."));
   3912 		return;
   3913 	}
   3914 
   3915 	if (to_folder->no_select) {
   3916 		alertpanel_error(_("The destination folder can only be used to "
   3917 				   "store subfolders."));
   3918 		return;
   3919 	}
   3920 
   3921 	START_LONG_OPERATION(summaryview, FALSE);
   3922 
   3923 	for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
   3924 	     cur != NULL && cur->data != NULL; cur = cur->next) {
   3925 		sel_last = GTK_CMCTREE_NODE(cur->data);
   3926 		summary_move_row_to
   3927 			(summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
   3928 	}
   3929 	END_LONG_OPERATION(summaryview);
   3930 
   3931 	if (prefs_common.immediate_exec) {
   3932 		summary_execute(summaryview);
   3933 	} else {
   3934 		GtkCMCTreeNode *node = NULL;
   3935 		if (summaryview->sort_type == SORT_ASCENDING) {
   3936 			node = summary_find_next_msg(summaryview, sel_last,TRUE);
   3937 			if (!node || prefs_common.next_on_delete == FALSE)
   3938 				node = summary_find_prev_msg(summaryview, sel_last,TRUE);
   3939 		} else {
   3940 			node = summary_find_prev_msg(summaryview, sel_last,TRUE);
   3941 			if (!node || prefs_common.next_on_delete == FALSE)
   3942 				node = summary_find_next_msg(summaryview, sel_last,TRUE);
   3943 		}
   3944 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
   3945 		summary_status_show(summaryview);
   3946 	}
   3947 
   3948 	if (!summaryview->selected) { /* this was the last message */
   3949 		GtkCMCTreeNode *node = gtk_cmctree_node_nth (GTK_CMCTREE(summaryview->ctree),
   3950 							 GTK_CMCLIST(summaryview->ctree)->rows - 1);
   3951 		if (node)
   3952 			summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
   3953 	}
   3954 
   3955 }
   3956 
   3957 void summary_move_to(SummaryView *summaryview)
   3958 {
   3959 	if (!summaryview->folder_item) return;
   3960 
   3961 	char *title = "Select destination folder";
   3962 	FolderItem *dst = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE, title);
   3963 	summary_move_selected_to(summaryview, dst);
   3964 }
   3965 
   3966 static void summary_copy_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
   3967 				FolderItem *to_folder)
   3968 {
   3969 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   3970 	MsgInfo *msginfo;
   3971 
   3972 	cm_return_if_fail(to_folder != NULL);
   3973 
   3974 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   3975 	cm_return_if_fail(msginfo);
   3976 	procmsg_msginfo_set_to_folder(msginfo, to_folder);
   3977 	if (MSG_IS_DELETED(msginfo->flags))
   3978 		summaryview->deleted--;
   3979 	if (MSG_IS_MOVE(msginfo->flags)) {
   3980 		summaryview->moved--;
   3981 	}
   3982 
   3983 	if (!MSG_IS_COPY(msginfo->flags)) {
   3984 		summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED,
   3985 			MSG_MOVE | MSG_MOVE_DONE);
   3986 		summaryview->copied++;
   3987 	} else {
   3988 		summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE);
   3989 	}
   3990 	if (!prefs_common.immediate_exec) {
   3991 		summary_set_row_marks(summaryview, row);
   3992 	}
   3993 
   3994 	debug_print("Message %d is set to copy to %s\n",
   3995 		    msginfo->msgnum, to_folder->path);
   3996 }
   3997 
   3998 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
   3999 {
   4000 	GList *cur;
   4001 	gboolean froze = FALSE;
   4002 
   4003 	if (!to_folder) return;
   4004 	if (!summaryview->folder_item) return;
   4005 
   4006 	if (summary_is_locked(summaryview)) return;
   4007 
   4008 	if (summaryview->folder_item == to_folder) {
   4009 		alertpanel_error("Destination to copy is same as current folder.");
   4010 		return;
   4011 	}
   4012 
   4013 	if (to_folder->no_select) {
   4014 		alertpanel_error("The destination folder can only be used to store subfolders.");
   4015 		return;
   4016 	}
   4017 
   4018 	START_LONG_OPERATION(summaryview, FALSE);
   4019 
   4020 	for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
   4021 	     cur != NULL && cur->data != NULL; cur = cur->next)
   4022 		summary_copy_row_to
   4023 			(summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
   4024 
   4025 	END_LONG_OPERATION(summaryview);
   4026 
   4027 	if (prefs_common.immediate_exec) {
   4028 		summary_execute(summaryview);
   4029 		return;
   4030 	}
   4031 	summary_status_show(summaryview);
   4032 }
   4033 
   4034 void summary_copy_to(SummaryView *summaryview)
   4035 {
   4036 	if (!summaryview->folder_item) return;
   4037 
   4038 	char *title = "Select destination folder";
   4039 	FolderItem *dst = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE, title);
   4040 	summary_copy_selected_to(summaryview, dst);
   4041 }
   4042 
   4043 void summary_add_address(SummaryView *summaryview)
   4044 {
   4045 	MsgInfo *msginfo, *full_msginfo;
   4046 	gchar *from;
   4047 
   4048 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
   4049 					      summaryview->selected);
   4050 	if (!msginfo || !msginfo->from)
   4051 		return;
   4052 
   4053 	Xstrdup_a(from, msginfo->from, return);
   4054 	eliminate_address_comment(from);
   4055 	extract_address(from);
   4056 
   4057 	full_msginfo = procmsg_msginfo_get_full_info(msginfo);
   4058 
   4059 	procmsg_msginfo_free(&full_msginfo);
   4060 
   4061 	addressbook_add_contact(msginfo->fromname, from, NULL, NULL);
   4062 }
   4063 
   4064 void summary_select_all(SummaryView *summaryview)
   4065 {
   4066 	GtkCMCTreeNode *node;
   4067 
   4068 	if (!summaryview->folder_item) return;
   4069 
   4070 	if (GTK_CMCLIST(summaryview->ctree)->focus_row < 0) {
   4071 		/* If no row is selected, select (but do not open) the first
   4072 		 * row, to get summaryview into correct state for selecting all. */
   4073 		debug_print("summary_select_all: no row selected, selecting first one\n");
   4074 		if (GTK_CMCLIST(summaryview->ctree)->row_list != NULL) {
   4075 			node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
   4076 			summary_select_node(summaryview, node, FALSE);
   4077 		}
   4078 	}
   4079 
   4080 	/* Now select all rows while locking the summaryview for
   4081 	 * faster performance. */
   4082 	summary_lock(summaryview);
   4083 	gtk_cmclist_select_all(GTK_CMCLIST(summaryview->ctree));
   4084 	summary_unlock(summaryview);
   4085 
   4086 	summary_status_show(summaryview);
   4087 }
   4088 
   4089 void summary_unselect_all(SummaryView *summaryview)
   4090 {
   4091 	summary_lock(summaryview);
   4092 	gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
   4093 	summary_unlock(summaryview);
   4094 	summary_status_show(summaryview);
   4095 }
   4096 
   4097 void summary_select_thread(SummaryView *summaryview, gboolean delete_thread,
   4098 			   gboolean trash_thread)
   4099 {
   4100 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4101 	GtkCMCTreeNode *node = NULL;
   4102 	gboolean froze = FALSE;
   4103 	GList *cur = NULL;
   4104 	GList *copy = NULL;
   4105 	if (!GTK_CMCLIST(summaryview->ctree)->selection)
   4106 		return;
   4107 
   4108 
   4109 	START_LONG_OPERATION(summaryview, FALSE);
   4110 	copy = g_list_copy(GTK_CMCLIST(summaryview->ctree)->selection);
   4111 	for (cur = copy; cur != NULL && cur->data != NULL;
   4112 	     cur = cur->next) {
   4113 		node = GTK_CMCTREE_NODE(cur->data);
   4114 		if (!node)
   4115 			continue;
   4116 		while (GTK_CMCTREE_ROW(node)->parent != NULL)
   4117 			node = GTK_CMCTREE_ROW(node)->parent;
   4118 
   4119 		gtk_cmctree_select_recursive(ctree, node);
   4120 	}
   4121 	g_list_free(copy);
   4122 	END_LONG_OPERATION(summaryview);
   4123 
   4124 	if (trash_thread) {
   4125 		if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
   4126 			summary_delete(summaryview);
   4127 		else
   4128 			summary_delete_trash(summaryview);
   4129 	} else if (delete_thread)
   4130 		summary_delete(summaryview);
   4131 
   4132 	summary_status_show(summaryview);
   4133 }
   4134 
   4135 void summary_save_as(SummaryView *summaryview)
   4136 {
   4137 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4138 	MsgInfo *msginfo;
   4139 	gchar *filename = NULL;
   4140 	gchar *src, *dest;
   4141 	gchar *tmp;
   4142 	gchar *filedir = NULL;
   4143 	gchar *converted_filename = NULL;
   4144 	gchar * filepath = NULL;
   4145 
   4146 	AlertValue aval = 0;
   4147 
   4148 	if (!summaryview->selected) return;
   4149 	msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
   4150 	if (!msginfo) return;
   4151 
   4152 	if (msginfo->subject) {
   4153 		Xstrdup_a(filename, msginfo->subject, return);
   4154 		subst_for_filename(filename);
   4155 	}
   4156 
   4157 	manage_window_focus_in(summaryview->window, NULL, NULL);
   4158 
   4159 	if (filename && !g_utf8_validate(filename, -1, NULL)) {
   4160 		converted_filename = conv_codeset_strdup(filename,
   4161 					       conv_get_locale_charset_str(),
   4162 					       CS_UTF_8);
   4163 		if (!converted_filename) {
   4164 			g_warning("summary_save_as(): failed to convert character set");
   4165 		} else {
   4166 			filename = converted_filename;
   4167 		}
   4168 	}
   4169 	if (!filename)
   4170 		return;
   4171 
   4172 	if (prefs_common.attach_save_dir && *prefs_common.attach_save_dir)
   4173 		filepath = g_strconcat(prefs_common.attach_save_dir, G_DIR_SEPARATOR_S, filename, NULL);
   4174 	dest = filesel_select_file_save(_("Save as"), filepath ? filepath : filename);
   4175 	if (filepath)
   4176 		g_free(filepath);
   4177 	if (converted_filename)
   4178 		g_free(converted_filename);
   4179 	if (!dest)
   4180 		return;
   4181 
   4182 	if (is_file_exist(dest)) {
   4183 		aval = alertpanel("Append or Overwrite?", "",
   4184 				  NULL, "_Append", NULL, "_Overwrite",
   4185 				  NULL, "_Cancel", ALERTFOCUS_FIRST);
   4186 		if (aval != 0 && aval != 1)
   4187 			return;
   4188 	}
   4189 
   4190 	src = procmsg_get_message_file(msginfo);
   4191 	tmp = g_path_get_basename(dest);
   4192 
   4193 	if ( aval==0 ) { /* append */
   4194 		if (append_file(src, dest, TRUE) < 0)
   4195 			alertpanel_error("Couldn't save the file '%s'.", tmp);
   4196 	} else { /* overwrite */
   4197 		if (copy_file(src, dest, TRUE) < 0)
   4198 			alertpanel_error("Couldn't save the file '%s'.", tmp);
   4199 	}
   4200 	g_free(src);
   4201 
   4202 	/*
   4203 	 * If two or more msgs are selected,
   4204 	 * append them to the output file.
   4205 	 */
   4206 	if (GTK_CMCLIST(ctree)->selection->next) {
   4207 		GList *item;
   4208 		for (item = GTK_CMCLIST(ctree)->selection->next; item != NULL; item=item->next) {
   4209 			msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(item->data));
   4210 			if (!msginfo) break;
   4211 			src = procmsg_get_message_file(msginfo);
   4212 			if (append_file(src, dest, TRUE) < 0) {
   4213 				alertpanel_error("Couldn't save the file '%s'.", tmp);
   4214 			}
   4215 			g_free(src);
   4216 		}
   4217 	}
   4218 
   4219 	filedir = g_path_get_dirname(dest);
   4220 	if (filedir) {
   4221 		if (strcmp(filedir, ".")) {
   4222 			g_free(prefs_common.attach_save_dir);
   4223 			prefs_common.attach_save_dir = g_filename_to_utf8(filedir, -1, NULL, NULL, NULL);
   4224 		}
   4225         g_free(filedir);
   4226 	}
   4227 
   4228 	g_free(dest);
   4229 	g_free(tmp);
   4230 }
   4231 
   4232 gboolean summary_execute(SummaryView *summaryview)
   4233 {
   4234 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4235 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
   4236 	GtkCMCTreeNode *node, *next;
   4237 	GtkCMCTreeNode *new_selected = NULL;
   4238 	gint move_val = -1;
   4239 
   4240 	if (!summaryview->folder_item) return FALSE;
   4241 
   4242 	if (summary_is_locked(summaryview)) return FALSE;
   4243 	summary_lock(summaryview);
   4244 
   4245 	summary_freeze(summaryview);
   4246 
   4247 	main_window_cursor_wait(summaryview->mainwin);
   4248 
   4249 	if (summaryview->threaded)
   4250 		summary_unthread_for_exec(summaryview);
   4251 
   4252 	folder_item_update_freeze();
   4253 	move_val = summary_execute_move(summaryview);
   4254 	summary_execute_copy(summaryview);
   4255 	summary_execute_delete(summaryview);
   4256 
   4257 	node = GTK_CMCTREE_NODE(clist->row_list);
   4258 	for (; node != NULL; node = next) {
   4259 		next = gtkut_ctree_node_next(ctree, node);
   4260 		if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
   4261 
   4262 		if (node == summaryview->displayed) {
   4263 			messageview_clear(summaryview->messageview);
   4264 			summary_cancel_mark_read_timeout(summaryview);
   4265 			summaryview->displayed = NULL;
   4266 		}
   4267 		if (GTK_CMCTREE_ROW(node)->children != NULL) {
   4268 			next = NULL;
   4269 			if (GTK_CMCTREE_ROW(node)->sibling) {
   4270 				next = GTK_CMCTREE_ROW(node)->sibling;
   4271 			} else {
   4272 				GtkCMCTreeNode *parent = NULL;
   4273 				for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
   4274 				     parent = GTK_CMCTREE_ROW(parent)->parent) {
   4275 					if (GTK_CMCTREE_ROW(parent)->sibling) {
   4276 						next = GTK_CMCTREE_ROW(parent)->sibling;
   4277 					}
   4278 				}
   4279 			}
   4280 		}
   4281 
   4282 		if (!new_selected &&
   4283 		    gtkut_ctree_node_is_selected(ctree, node)) {
   4284 			summary_unselect_all(summaryview);
   4285 			if (summaryview->sort_type == SORT_ASCENDING) {
   4286 				new_selected = summary_find_next_msg(summaryview, node,TRUE);
   4287 				if (!new_selected || prefs_common.next_on_delete == FALSE)
   4288 					new_selected = summary_find_prev_msg(summaryview, node,TRUE);
   4289 			} else {
   4290 				new_selected = summary_find_prev_msg(summaryview, node,TRUE);
   4291 				if (!new_selected || prefs_common.next_on_delete == FALSE)
   4292 					new_selected = summary_find_next_msg(summaryview, node,TRUE);
   4293 			}
   4294 		}
   4295 
   4296 		gtk_sctree_remove_node((GtkSCTree *)ctree, node);
   4297 	}
   4298 
   4299 	folder_item_update_thaw();
   4300 
   4301 	if (new_selected) {
   4302 		summary_unlock(summaryview);
   4303 		summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
   4304 		summary_lock(summaryview);
   4305 	}
   4306 
   4307 	if (summaryview->threaded) {
   4308 		summary_thread_build(summaryview);
   4309 		summary_thread_init(summaryview);
   4310 	}
   4311 
   4312 	summary_thaw(summaryview);
   4313 
   4314 	summaryview->selected = clist->selection ?
   4315 		GTK_CMCTREE_NODE(clist->selection->data) : NULL;
   4316 
   4317 	if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
   4318 		menu_set_insensitive_all
   4319 			(GTK_MENU_SHELL(summaryview->popupmenu));
   4320 		folderview_grab_focus(summaryview->folderview);
   4321 	} else {
   4322 		menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
   4323 		gtk_widget_grab_focus(summaryview->ctree);
   4324 	}
   4325 	summary_update_status(summaryview);
   4326 	summary_status_show(summaryview);
   4327 
   4328 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
   4329 
   4330 	summary_unlock(summaryview);
   4331 
   4332 	main_window_cursor_normal(summaryview->mainwin);
   4333 
   4334 	if (move_val < 0)
   4335 		summary_show(summaryview, summaryview->folder_item, FALSE);
   4336 	return TRUE;
   4337 }
   4338 
   4339 gboolean summary_expunge(SummaryView *summaryview)
   4340 {
   4341 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4342 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
   4343 	GtkCMCTreeNode *node, *next;
   4344 	GtkCMCTreeNode *new_selected = NULL;
   4345 
   4346 	if (!summaryview->folder_item) return FALSE;
   4347 
   4348 	if (summary_is_locked(summaryview)) return FALSE;
   4349 
   4350 	summary_lock(summaryview);
   4351 
   4352 	summary_freeze(summaryview);
   4353 
   4354 	main_window_cursor_wait(summaryview->mainwin);
   4355 
   4356 	if (summaryview->threaded)
   4357 		summary_unthread_for_exec(summaryview);
   4358 
   4359 	folder_item_update_freeze();
   4360 	summary_execute_expunge(summaryview);
   4361 
   4362 	node = GTK_CMCTREE_NODE(clist->row_list);
   4363 	for (; node != NULL; node = next) {
   4364 		next = gtkut_ctree_node_next(ctree, node);
   4365 		if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
   4366 
   4367 		if (node == summaryview->displayed) {
   4368 			messageview_clear(summaryview->messageview);
   4369 			summary_cancel_mark_read_timeout(summaryview);
   4370 			summaryview->displayed = NULL;
   4371 		}
   4372 		if (GTK_CMCTREE_ROW(node)->children != NULL) {
   4373 			next = NULL;
   4374 			if (GTK_CMCTREE_ROW(node)->sibling) {
   4375 				next = GTK_CMCTREE_ROW(node)->sibling;
   4376 			} else {
   4377 				GtkCMCTreeNode *parent = NULL;
   4378 				for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
   4379 				     parent = GTK_CMCTREE_ROW(parent)->parent) {
   4380 					if (GTK_CMCTREE_ROW(parent)->sibling) {
   4381 						next = GTK_CMCTREE_ROW(parent)->sibling;
   4382 					}
   4383 				}
   4384 			}
   4385 		}
   4386 
   4387 		if (!new_selected &&
   4388 		    gtkut_ctree_node_is_selected(ctree, node)) {
   4389 			summary_unselect_all(summaryview);
   4390 			new_selected = summary_find_next_msg(summaryview, node,TRUE);
   4391 			if (!new_selected)
   4392 				new_selected = summary_find_prev_msg
   4393 					(summaryview, node,TRUE);
   4394 		}
   4395 
   4396 		gtk_sctree_remove_node((GtkSCTree *)ctree, node);
   4397 	}
   4398 
   4399 	folder_item_update_thaw();
   4400 
   4401 	if (new_selected) {
   4402 		summary_unlock(summaryview);
   4403 		summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
   4404 		summary_lock(summaryview);
   4405 	}
   4406 
   4407 	if (summaryview->threaded) {
   4408 		summary_thread_build(summaryview);
   4409 		summary_thread_init(summaryview);
   4410 	}
   4411 
   4412 	summary_thaw(summaryview);
   4413 
   4414 	summaryview->selected = clist->selection ?
   4415 		GTK_CMCTREE_NODE(clist->selection->data) : NULL;
   4416 
   4417 	if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
   4418 		menu_set_insensitive_all
   4419 			(GTK_MENU_SHELL(summaryview->popupmenu));
   4420 		folderview_grab_focus(summaryview->folderview);
   4421 	} else {
   4422 		menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
   4423 		gtk_widget_grab_focus(summaryview->ctree);
   4424 	}
   4425 
   4426 	summary_update_status(summaryview);
   4427 	summary_status_show(summaryview);
   4428 
   4429 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
   4430 
   4431 	summary_unlock(summaryview);
   4432 
   4433 	main_window_cursor_normal(summaryview->mainwin);
   4434 
   4435 	return TRUE;
   4436 }
   4437 
   4438 static gint summary_execute_move(SummaryView *summaryview)
   4439 {
   4440 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4441 	GSList *cur;
   4442 	gint val = -1;
   4443 	/* search moving messages and execute */
   4444 
   4445 	gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_move_func,
   4446 			summaryview);
   4447 
   4448 	if (summaryview->mlist) {
   4449 		hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
   4450 			summaryview->msginfo_update_callback_id);
   4451 		val = procmsg_move_messages(summaryview->mlist);
   4452 		summaryview->msginfo_update_callback_id =
   4453 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
   4454 			summary_update_msg, (gpointer) summaryview);
   4455 
   4456 		for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
   4457 			procmsg_msginfo_free((MsgInfo **)&(cur->data));
   4458 		}
   4459 
   4460 		g_slist_free(summaryview->mlist);
   4461 		summaryview->mlist = NULL;
   4462 		return val;
   4463 	}
   4464 	return 0;
   4465 }
   4466 
   4467 static void summary_execute_move_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   4468 				      gpointer data)
   4469 {
   4470 	SummaryView *summaryview = data;
   4471 	MsgInfo *msginfo;
   4472 
   4473 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   4474 
   4475 	if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
   4476 		summaryview->mlist =
   4477 			g_slist_prepend(summaryview->mlist, msginfo);
   4478 		gtk_cmctree_node_set_row_data(ctree, node, NULL);
   4479 
   4480 		if (msginfo->msgid && *msginfo->msgid &&
   4481 		    node == g_hash_table_lookup(summaryview->msgid_table,
   4482 						msginfo->msgid))
   4483 			g_hash_table_remove(summaryview->msgid_table,
   4484 					    msginfo->msgid);
   4485 		if (prefs_common.thread_by_subject &&
   4486 		    msginfo->subject && *msginfo->subject &&
   4487 		    node == subject_table_lookup(summaryview->subject_table,
   4488 						 msginfo->subject)) {
   4489 			subject_table_remove(summaryview->subject_table,
   4490 					     msginfo->subject);
   4491 		}
   4492 	}
   4493 }
   4494 
   4495 static void summary_execute_copy(SummaryView *summaryview)
   4496 {
   4497 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4498 
   4499 	/* search copying messages and execute */
   4500 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
   4501 		summaryview->msginfo_update_callback_id);
   4502 	gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
   4503 				summaryview);
   4504 
   4505 	if (summaryview->mlist) {
   4506 		summaryview->mlist = g_slist_reverse(summaryview->mlist);
   4507 		procmsg_copy_messages(summaryview->mlist);
   4508 
   4509 		g_slist_free(summaryview->mlist);
   4510 		summaryview->mlist = NULL;
   4511 	}
   4512 	summaryview->msginfo_update_callback_id =
   4513 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
   4514 			summary_update_msg, (gpointer) summaryview);
   4515 }
   4516 
   4517 static void summary_execute_copy_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   4518 				      gpointer data)
   4519 {
   4520 	SummaryView *summaryview = data;
   4521 	MsgInfo *msginfo;
   4522 
   4523 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   4524 
   4525 	if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
   4526 		summaryview->mlist =
   4527 			g_slist_prepend(summaryview->mlist, msginfo);
   4528 
   4529 		summary_msginfo_unset_flags(msginfo, 0, MSG_COPY);
   4530 		summary_set_row_marks(summaryview, node);
   4531 	}
   4532 }
   4533 
   4534 static void summary_execute_delete(SummaryView *summaryview)
   4535 {
   4536 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4537 	GSList *cur;
   4538 
   4539 	/* search deleting messages and execute */
   4540 	gtk_cmctree_pre_recursive
   4541 		(ctree, NULL, summary_execute_delete_func, summaryview);
   4542 
   4543 	if (!summaryview->mlist) return;
   4544 
   4545 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
   4546 		summaryview->msginfo_update_callback_id);
   4547 
   4548 	folder_item_remove_msgs(summaryview->folder_item,
   4549 				summaryview->mlist);
   4550 
   4551 	summaryview->msginfo_update_callback_id =
   4552 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
   4553 			summary_update_msg, (gpointer) summaryview);
   4554 
   4555 	for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
   4556 			procmsg_msginfo_free((MsgInfo **)&(cur->data));
   4557 	}
   4558 
   4559 	g_slist_free(summaryview->mlist);
   4560 	summaryview->mlist = NULL;
   4561 }
   4562 
   4563 static void summary_execute_delete_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   4564 					gpointer data)
   4565 {
   4566 	SummaryView *summaryview = data;
   4567 	MsgInfo *msginfo;
   4568 
   4569 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   4570 
   4571 	if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
   4572 		summaryview->mlist =
   4573 			g_slist_prepend(summaryview->mlist, msginfo);
   4574 		gtk_cmctree_node_set_row_data(ctree, node, NULL);
   4575 
   4576 		if (msginfo->msgid && *msginfo->msgid &&
   4577 		    node == g_hash_table_lookup(summaryview->msgid_table,
   4578 						msginfo->msgid)) {
   4579 			g_hash_table_remove(summaryview->msgid_table,
   4580 					    msginfo->msgid);
   4581 		}
   4582 		if (prefs_common.thread_by_subject &&
   4583 		    msginfo->subject && *msginfo->subject &&
   4584 		    node == subject_table_lookup(summaryview->subject_table,
   4585 						 msginfo->subject)) {
   4586 			subject_table_remove(summaryview->subject_table,
   4587 					     msginfo->subject);
   4588 		}
   4589 	}
   4590 }
   4591 
   4592 static void summary_execute_expunge_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   4593 					gpointer data)
   4594 {
   4595 	SummaryView *summaryview = data;
   4596 	MsgInfo *msginfo;
   4597 
   4598 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   4599 
   4600 	if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
   4601 		summaryview->mlist =
   4602 			g_slist_prepend(summaryview->mlist, msginfo);
   4603 		gtk_cmctree_node_set_row_data(ctree, node, NULL);
   4604 
   4605 		if (msginfo->msgid && *msginfo->msgid &&
   4606 		    node == g_hash_table_lookup(summaryview->msgid_table,
   4607 						msginfo->msgid)) {
   4608 			g_hash_table_remove(summaryview->msgid_table,
   4609 					    msginfo->msgid);
   4610 		}
   4611 		if (prefs_common.thread_by_subject &&
   4612 		    msginfo->subject && *msginfo->subject &&
   4613 		    node == subject_table_lookup(summaryview->subject_table,
   4614 						 msginfo->subject)) {
   4615 			subject_table_remove(summaryview->subject_table,
   4616 					     msginfo->subject);
   4617 		}
   4618 	}
   4619 }
   4620 
   4621 static void summary_execute_expunge(SummaryView *summaryview)
   4622 {
   4623 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4624 	GSList *cur;
   4625 
   4626 	gtk_cmctree_pre_recursive
   4627 		(ctree, NULL, summary_execute_expunge_func, summaryview);
   4628 
   4629 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
   4630 		summaryview->msginfo_update_callback_id);
   4631 
   4632 	folder_item_expunge(summaryview->folder_item);
   4633 
   4634 	summaryview->msginfo_update_callback_id =
   4635 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
   4636 			summary_update_msg, (gpointer) summaryview);
   4637 	for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
   4638 		procmsg_msginfo_free((MsgInfo **)&(cur->data));
   4639 
   4640 	g_slist_free(summaryview->mlist);
   4641 	summaryview->mlist = NULL;
   4642 }
   4643 
   4644 /* thread functions */
   4645 
   4646 static void summary_thread_build(SummaryView *summaryview)
   4647 {
   4648 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4649 	GtkCMCTreeNode *node;
   4650 	GtkCMCTreeNode *next;
   4651 	GtkCMCTreeNode *parent;
   4652 	MsgInfo *msginfo;
   4653         GSList *reflist;
   4654 
   4655 	summary_lock(summaryview);
   4656 
   4657 	debug_print("Building threads...\n");
   4658 	STATUSBAR_PUSH(summaryview->mainwin, "Building threads...");
   4659 	main_window_cursor_wait(summaryview->mainwin);
   4660 
   4661 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
   4662 				       G_CALLBACK(summary_tree_expanded), summaryview);
   4663 	summary_freeze(summaryview);
   4664 
   4665 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   4666 	while (node) {
   4667 		next = GTK_CMCTREE_ROW(node)->sibling;
   4668 
   4669 		msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   4670 
   4671 		parent = NULL;
   4672 
   4673 		if (msginfo && msginfo->inreplyto) {
   4674 			parent = g_hash_table_lookup(summaryview->msgid_table,
   4675 						     msginfo->inreplyto);
   4676 
   4677 			if (!parent && msginfo->references) {
   4678 				for (reflist = msginfo->references;
   4679 				     reflist != NULL; reflist = reflist->next)
   4680 					if ((parent = g_hash_table_lookup
   4681 						(summaryview->msgid_table,
   4682 						 reflist->data)))
   4683 						break;
   4684 			}
   4685 		}
   4686 
   4687 		if (msginfo && prefs_common.thread_by_subject && parent == NULL) {
   4688 			parent = subject_table_lookup
   4689 				(summaryview->subject_table,
   4690 				 msginfo->subject);
   4691 		}
   4692 
   4693 		if (parent && parent != node && parent != GTK_CMCTREE_ROW(node)->parent) {
   4694 			gtk_cmctree_move(ctree, node, parent, NULL);
   4695 		}
   4696 
   4697 		node = next;
   4698 	}
   4699 
   4700 	gtkut_ctree_set_focus_row(ctree, summaryview->selected);
   4701 
   4702 	summary_thaw(summaryview);
   4703 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
   4704 					 G_CALLBACK(summary_tree_expanded), summaryview);
   4705 
   4706 	debug_print("Building threads done.\n");
   4707 	STATUSBAR_POP(summaryview->mainwin);
   4708 	main_window_cursor_normal(summaryview->mainwin);
   4709 
   4710 	summaryview->threaded = TRUE;
   4711 
   4712 	summary_unlock(summaryview);
   4713 }
   4714 
   4715 static void summary_thread_init(SummaryView *summaryview)
   4716 {
   4717 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4718 	GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   4719 	GtkCMCTreeNode *next;
   4720 	if (!summaryview->thread_collapsed) {
   4721 		g_signal_handlers_block_by_func(G_OBJECT(ctree),
   4722 				       G_CALLBACK(summary_tree_expanded), summaryview);
   4723 		while (node) {
   4724 			next = GTK_CMCTREE_ROW(node)->sibling;
   4725 			if (GTK_CMCTREE_ROW(node)->children)
   4726 				gtk_cmctree_expand_recursive(ctree, node);
   4727 			node = next;
   4728 		}
   4729 		g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
   4730 				       G_CALLBACK(summary_tree_expanded), summaryview);
   4731 	}
   4732 }
   4733 
   4734 static void summary_unthread_for_exec(SummaryView *summaryview)
   4735 {
   4736 	GtkCMCTreeNode *node;
   4737 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4738 	gboolean froze = FALSE;
   4739 
   4740 	debug_print("Unthreading for execution...\n");
   4741 
   4742 	START_LONG_OPERATION(summaryview, TRUE);
   4743 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   4744 	     node != NULL; node = GTK_CMCTREE_NODE_NEXT(node)) {
   4745 		summary_unthread_for_exec_func(ctree, node, summaryview);
   4746 	}
   4747 
   4748 	END_LONG_OPERATION(summaryview);
   4749 
   4750 	debug_print("Unthreading for execution done.\n");
   4751 }
   4752 
   4753 static void summary_unthread_for_exec_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   4754 					   gpointer data)
   4755 {
   4756 	MsgInfo *msginfo;
   4757 	GtkCMCTreeNode *top_parent;
   4758 	GtkCMCTreeNode *child;
   4759 	GtkCMCTreeNode *sibling;
   4760 	SummaryView * summaryview = (SummaryView *)data;
   4761 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
   4762 
   4763 	if (!msginfo ||
   4764 	    (!MSG_IS_MOVE(msginfo->flags) &&
   4765 	     !MSG_IS_DELETED(msginfo->flags)))
   4766 		return;
   4767 	child = GTK_CMCTREE_ROW(node)->children;
   4768 	if (!child) return;
   4769 
   4770 	if (node == summaryview->selected)
   4771 		summaryview->selected = NULL;
   4772 	if (node == summaryview->displayed)
   4773 		summaryview->displayed = NULL;
   4774 
   4775 	summary_cancel_mark_read_timeout(summaryview);
   4776 
   4777 	for (top_parent = node;
   4778 	     GTK_CMCTREE_ROW(top_parent)->parent != NULL;
   4779 	     top_parent = GTK_CMCTREE_ROW(top_parent)->parent)
   4780 		;
   4781 	sibling = GTK_CMCTREE_ROW(top_parent)->sibling;
   4782 
   4783 	GTK_SCTREE(ctree)->sorting = TRUE;
   4784 	while (child != NULL) {
   4785 		GtkCMCTreeNode *next_child;
   4786 		MsgInfo *cinfo = GTKUT_CTREE_NODE_GET_ROW_DATA(child);
   4787 
   4788 		next_child = GTK_CMCTREE_ROW(child)->sibling;
   4789 
   4790 		if (!MSG_IS_MOVE(cinfo->flags) && !MSG_IS_DELETED(cinfo->flags)) {
   4791 			gtk_cmctree_move(ctree, child,
   4792 				NULL,
   4793 				sibling);
   4794 		} else {
   4795 			if (child == summaryview->displayed) {
   4796 				messageview_clear(summaryview->messageview);
   4797 				summaryview->displayed = NULL;
   4798 			}
   4799 			if (child == summaryview->selected) {
   4800 				messageview_clear(summaryview->messageview);
   4801 				summaryview->selected = NULL;
   4802 			}
   4803 		}
   4804 		child = next_child;
   4805 	}
   4806 	GTK_SCTREE(ctree)->sorting = FALSE;
   4807 }
   4808 
   4809 void summary_expand_threads(SummaryView *summaryview)
   4810 {
   4811 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4812 	GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   4813 	GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
   4814 
   4815 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
   4816 				       G_CALLBACK(summary_tree_expanded), summaryview);
   4817 	summary_freeze(summaryview);
   4818 	GTK_SCTREE(ctree)->sorting = TRUE;
   4819 
   4820 	while (node) {
   4821 		if (GTK_CMCTREE_ROW(node)->children) {
   4822 			gtk_cmctree_expand(ctree, node);
   4823 			summary_set_row_marks(summaryview, node);
   4824 		}
   4825 		node = GTK_CMCTREE_NODE_NEXT(node);
   4826 	}
   4827 
   4828 	GTK_SCTREE(ctree)->sorting = FALSE;
   4829 	if (focus_node) {
   4830 		GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
   4831 	}
   4832 	summary_thaw(summaryview);
   4833 
   4834 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
   4835 					 G_CALLBACK(summary_tree_expanded), summaryview);
   4836 
   4837 	summaryview->thread_collapsed = FALSE;
   4838 
   4839 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
   4840 }
   4841 
   4842 void summary_collapse_threads(SummaryView *summaryview)
   4843 {
   4844 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   4845 	GtkCMCTreeNode *node = NULL;
   4846 	GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
   4847 
   4848 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
   4849 				       G_CALLBACK(summary_tree_collapsed), summaryview);
   4850 	summary_freeze(summaryview);
   4851 	GTK_SCTREE(ctree)->sorting = TRUE;
   4852 
   4853 	node = focus_node;
   4854 	while (node && GTK_CMCTREE_ROW(node)->parent) {
   4855 		focus_node = node = GTK_CMCTREE_ROW(node)->parent;
   4856 	}
   4857 	summary_select_node(summaryview, node, OPEN_SELECTED_ON_DIRECTIONAL);
   4858 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
   4859 	while (node) {
   4860 		if (GTK_CMCTREE_ROW(node)->children) {
   4861 			gtk_cmctree_collapse(ctree, node);
   4862 			summary_set_row_marks(summaryview, node);
   4863 		}
   4864 		node = GTK_CMCTREE_ROW(node)->sibling;
   4865 	}
   4866 
   4867 	GTK_SCTREE(ctree)->sorting = FALSE;
   4868 	if (focus_node) {
   4869 		GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
   4870 	}
   4871 	GTK_SCTREE(ctree)->anchor_row =
   4872 			gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row);
   4873 	summary_thaw(summaryview);
   4874 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
   4875 					 G_CALLBACK(summary_tree_collapsed), summaryview);
   4876 
   4877 	summaryview->thread_collapsed = TRUE;
   4878 
   4879 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
   4880 }
   4881 
   4882 void summaryview_destroy(SummaryView *summaryview)
   4883 {
   4884 	if(summaryview->simplify_subject_preg) {
   4885 		regfree(summaryview->simplify_subject_preg);
   4886 		g_free(summaryview->simplify_subject_preg);
   4887 		summaryview->simplify_subject_preg = NULL;
   4888 	}
   4889 }
   4890 
   4891 static gboolean summary_popup_menu(GtkWidget *widget, gpointer data)
   4892 {
   4893 	SummaryView *summaryview = (SummaryView *)data;
   4894 	summaryview->display_msg = messageview_is_visible(summaryview->messageview);
   4895 
   4896 	gtk_menu_popup_at_pointer(GTK_MENU(summaryview->popupmenu), NULL);
   4897 
   4898 	return TRUE;
   4899 }
   4900 
   4901 static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column)
   4902 {
   4903 	MsgFlags flags;
   4904 	if (!info)
   4905 		return NULL;
   4906 
   4907 	flags = info->flags;
   4908 
   4909 	switch(summaryview->col_state[column].type) {
   4910 		case S_COL_STATUS:
   4911 			if (MSG_IS_IGNORE_THREAD(flags)) {
   4912 				return "Ignored thread";
   4913 			} else if (MSG_IS_WATCH_THREAD(flags)) {
   4914 				return "Watched thread";
   4915 			} else if (MSG_IS_SPAM(flags)) {
   4916 				return "Spam";
   4917 			} else if (MSG_IS_NEW(flags)) {
   4918 				return "New";
   4919 			} else if (MSG_IS_UNREAD(flags)) {
   4920 				return "Unread";
   4921 			} else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
   4922 				return "Replied but also forwarded - click to see reply";
   4923 			} else if (MSG_IS_REPLIED(flags)) {
   4924 				return "Replied - click to see reply";
   4925 			} else if (MSG_IS_FORWARDED(flags)) {
   4926 				return "Forwarded";
   4927 			} else {
   4928 				return NULL;
   4929 			}
   4930 		case S_COL_MARK:
   4931 			if (MSG_IS_DELETED(flags)) {
   4932 				return "Deleted";
   4933 			} else if (MSG_IS_MARKED(flags)) {
   4934 				return "Marked";
   4935 			} else if (MSG_IS_MOVE(flags)) {
   4936 				return "To be moved";
   4937 			} else if (MSG_IS_COPY(flags)) {
   4938 				return "To be copied";
   4939 			} else {
   4940 				return NULL;
   4941 			}
   4942 		case S_COL_LOCKED:
   4943 			if (MSG_IS_LOCKED(flags)) {
   4944 				return "Locked";
   4945 			} else {
   4946 				return NULL;
   4947 			}
   4948 		case S_COL_MIME:
   4949 			if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
   4950 				return "Signed, has attachment(s)";
   4951 			} else if (MSG_IS_SIGNED(flags)) {
   4952 				return "Signed";
   4953 			} else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
   4954 				return "Encrypted, has attachment(s)";
   4955 			} else if (MSG_IS_ENCRYPTED(flags)) {
   4956 				return "Encrypted";
   4957 			} else if (MSG_IS_WITH_ATTACHMENT(flags)) {
   4958 				return "Has attachment(s)";
   4959 			} else {
   4960 				return NULL;
   4961 			}
   4962 		default:
   4963 			return NULL;
   4964 	}
   4965 }
   4966 
   4967 static gboolean tooltip_cb (GtkWidget  *widget,
   4968                             gint        x,
   4969                             gint        y,
   4970                             gboolean    keyboard_mode,
   4971                             GtkTooltip *tooltip,
   4972                             gpointer    user_data)
   4973 {
   4974 	GtkCMCTree *ctree = GTK_CMCTREE(widget);
   4975 	SummaryView *summaryview = (SummaryView *)user_data;
   4976 	gint row = -1, column = -1;
   4977 	int offset = prefs_common.show_col_headers ? 24:0;
   4978 	GtkCMCTreeNode *node = NULL;
   4979 	gchar *text = NULL;
   4980 	gchar *formatted = NULL;
   4981 	MsgInfo *info = NULL;
   4982 	GdkRectangle rect;
   4983 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
   4984 	if (!prefs_common.show_tooltips)
   4985 		return FALSE;
   4986 
   4987 	if (y - offset < 0)
   4988 		return FALSE;
   4989 
   4990 	if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), x, y - offset,
   4991 					  &row, &column))
   4992 		return FALSE;
   4993 
   4994 	if ((node = gtk_cmctree_node_nth(ctree, row)) == NULL)
   4995 		return FALSE;
   4996 
   4997 	if ((info = gtk_cmctree_node_get_row_data(ctree, node)) == NULL)
   4998 		return FALSE;
   4999 
   5000 	switch (gtk_cmctree_node_get_cell_type(ctree, node, column)) {
   5001 		case GTK_CMCELL_TEXT:
   5002 			if (gtk_cmctree_node_get_text(ctree, node, column, &text) != TRUE)
   5003 				return FALSE;
   5004 			if (column == summaryview->col_pos[S_COL_FROM])
   5005 				text = info->from;
   5006 			else if (column == summaryview->col_pos[S_COL_TO])
   5007 				text = info->to;
   5008 			break;
   5009 		case GTK_CMCELL_PIXTEXT:
   5010 			if (gtk_cmctree_node_get_pixtext(ctree, node, column, &text,
   5011 				NULL, NULL) != TRUE)
   5012 				return FALSE;
   5013 			break;
   5014 		default:
   5015 			if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL)
   5016 				return FALSE;
   5017 	}
   5018 
   5019 	if (!text || !*text)
   5020 		return FALSE;
   5021 
   5022 	formatted = g_strdup(text);
   5023 	g_strstrip(formatted);
   5024 
   5025 	if (vert_layout && prefs_common.two_line_vert)
   5026 		gtk_tooltip_set_markup (tooltip, formatted);
   5027 	else
   5028 	                gtk_tooltip_set_text (tooltip, formatted);
   5029 	g_free(formatted);
   5030 
   5031 	rect.x = x - 2;
   5032 	rect.y = y - 2;
   5033 	rect.width = 12;
   5034 	rect.height= 12;
   5035 	gtk_tooltip_set_tip_area(tooltip, &rect);
   5036 
   5037 	return TRUE;
   5038 }
   5039 
   5040 static gboolean summary_header_button_pressed(GtkWidget *widget,
   5041 		GdkEvent *_event,
   5042 		gpointer user_data)
   5043 {
   5044 	GdkEventButton *event = (GdkEventButton *)_event;
   5045 	SummaryView *summaryview = (SummaryView *)user_data;
   5046 
   5047 	cm_return_val_if_fail(summaryview != NULL, FALSE);
   5048 
   5049 	/* Only handle single button presses. */
   5050 	if (event->type == GDK_2BUTTON_PRESS ||
   5051 			event->type == GDK_3BUTTON_PRESS)
   5052 		return FALSE;
   5053 
   5054 	/* Handle right-click for context menu */
   5055 	if (event->button == 3) {
   5056 		/* Set up any menu items that need setting up. */
   5057 		summaryview->header_menu_lock = TRUE;
   5058 		cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager,
   5059 				"Menus/SummaryViewHeaderPopup/LockColumnHeaders",
   5060 				prefs_common_get_prefs()->summary_col_lock);
   5061 		summaryview->header_menu_lock = FALSE;
   5062 
   5063 		gtk_menu_popup_at_pointer(GTK_MENU(summaryview->headerpopupmenu), NULL);
   5064 		return TRUE;
   5065 	}
   5066 
   5067 	return FALSE;
   5068 }
   5069 
   5070 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
   5071 {
   5072 	GtkWidget *ctree;
   5073 	gint *col_pos = summaryview->col_pos;
   5074 	SummaryColumnState *col_state;
   5075 	gchar *titles[N_SUMMARY_COLS];
   5076 	SummaryColumnType type;
   5077 	gint pos;
   5078 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
   5079 	memset(titles, 0, sizeof(titles));
   5080 
   5081 	col_state = prefs_summary_column_get_config();
   5082 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
   5083 		summaryview->col_state[pos] = col_state[pos];
   5084 		type = col_state[pos].type;
   5085 		col_pos[type] = pos;
   5086 		titles[pos] = "dummy";
   5087 	}
   5088 	col_state = summaryview->col_state;
   5089 
   5090 	ctree = gtk_sctree_new_with_titles
   5091 		(N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
   5092 
   5093 	gtk_widget_set_name(GTK_WIDGET(ctree), "summaryview_sctree");
   5094 
   5095 	if (prefs_common.show_col_headers == FALSE)
   5096 		gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
   5097 
   5098 	gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_MULTIPLE);
   5099 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
   5100 					   GTK_JUSTIFY_CENTER);
   5101 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
   5102 					   GTK_JUSTIFY_CENTER);
   5103 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
   5104 					   GTK_JUSTIFY_CENTER);
   5105 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
   5106 					   GTK_JUSTIFY_CENTER);
   5107 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
   5108 					   GTK_JUSTIFY_RIGHT);
   5109 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
   5110 					   GTK_JUSTIFY_RIGHT);
   5111 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
   5112 					   GTK_JUSTIFY_RIGHT);
   5113 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
   5114 				   prefs_common.summary_col_size[S_COL_MARK]);
   5115 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
   5116 				   prefs_common.summary_col_size[S_COL_STATUS]);
   5117 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
   5118 				   prefs_common.summary_col_size[S_COL_LOCKED]);
   5119 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
   5120 				   prefs_common.summary_col_size[S_COL_MIME]);
   5121 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SUBJECT],
   5122 				   prefs_common.summary_col_size[S_COL_SUBJECT]);
   5123 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_FROM],
   5124 				   prefs_common.summary_col_size[S_COL_FROM]);
   5125 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TO],
   5126 				   prefs_common.summary_col_size[S_COL_TO]);
   5127 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_DATE],
   5128 				   prefs_common.summary_col_size[S_COL_DATE]);
   5129 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
   5130 				   prefs_common.summary_col_size[S_COL_SIZE]);
   5131 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
   5132 				   prefs_common.summary_col_size[S_COL_NUMBER]);
   5133 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
   5134 				   prefs_common.summary_col_size[S_COL_SCORE]);
   5135 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TAGS],
   5136 				   prefs_common.summary_col_size[S_COL_TAGS]);
   5137 
   5138 	gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
   5139 			     GTK_CMCTREE_EXPANDER_TRIANGLE);
   5140 
   5141 	gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
   5142 
   5143 	gtk_cmctree_set_indent(GTK_CMCTREE(ctree), 12);
   5144 	g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview);
   5145 
   5146 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
   5147 		gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[pos].button,
   5148 				       FALSE);
   5149 		if (((pos == summaryview->col_pos[S_COL_FROM] && !FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
   5150 		     (pos == summaryview->col_pos[S_COL_TO] && FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
   5151 		     pos == summaryview->col_pos[S_COL_DATE]) && vert_layout &&
   5152 			    prefs_common.two_line_vert)
   5153 			gtk_cmclist_set_column_visibility
   5154 				(GTK_CMCLIST(ctree), pos, FALSE);
   5155 		else
   5156 			gtk_cmclist_set_column_visibility
   5157 				(GTK_CMCLIST(ctree), pos, col_state[pos].visible);
   5158 	}
   5159 	if (prefs_common.two_line_vert)
   5160 		gtk_sctree_set_use_markup(GTK_SCTREE(ctree), summaryview->col_pos[S_COL_SUBJECT], vert_layout);
   5161 
   5162 	/* connect signal to the buttons for sorting */
   5163 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
   5164 	g_signal_connect \
   5165 		(G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
   5166 		 "button-press-event", \
   5167 		 G_CALLBACK(summary_header_button_pressed), \
   5168 		 summaryview); \
   5169 	g_signal_connect \
   5170 		(G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
   5171 		 "clicked", \
   5172 		 G_CALLBACK(func), \
   5173 		 summaryview);
   5174 
   5175 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked)
   5176 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked)
   5177 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked)
   5178 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked)
   5179 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked)
   5180 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked)
   5181 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked)
   5182 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO     , summary_to_clicked)
   5183 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked)
   5184 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE  , summary_score_clicked)
   5185 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED , summary_locked_clicked)
   5186 
   5187 #undef CLIST_BUTTON_SIGNAL_CONNECT
   5188 
   5189 	g_signal_connect(G_OBJECT(ctree), "tree_select_row",
   5190 			 G_CALLBACK(summary_selected), summaryview);
   5191 	g_signal_connect(G_OBJECT(ctree), "tree_unselect_row",
   5192 			 G_CALLBACK(summary_unselected), summaryview);
   5193 	g_signal_connect(G_OBJECT(ctree), "button_press_event",
   5194 			 G_CALLBACK(summary_button_pressed),
   5195 			 summaryview);
   5196 	g_signal_connect(G_OBJECT(ctree), "popup-menu",
   5197 			 G_CALLBACK(summary_popup_menu), summaryview);
   5198 	g_signal_connect(G_OBJECT(ctree), "button_release_event",
   5199 			 G_CALLBACK(summary_button_released),
   5200 			 summaryview);
   5201 	g_signal_connect(G_OBJECT(ctree), "key_press_event",
   5202 			 G_CALLBACK(summary_key_pressed), summaryview);
   5203 	g_signal_connect(G_OBJECT(ctree), "resize_column",
   5204 			 G_CALLBACK(summary_col_resized), summaryview);
   5205         g_signal_connect(G_OBJECT(ctree), "open_row",
   5206 			 G_CALLBACK(summary_open_row), summaryview);
   5207 
   5208 	g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
   5209 			       G_CALLBACK(summary_tree_expanded),
   5210 			       summaryview);
   5211 	g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
   5212 			       G_CALLBACK(summary_tree_collapsed),
   5213 			       summaryview);
   5214 
   5215 	g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL);
   5216 	g_signal_connect(G_OBJECT(ctree), "query-tooltip",
   5217 			 G_CALLBACK(tooltip_cb),
   5218 			summaryview);
   5219 	return ctree;
   5220 }
   5221 
   5222 void summary_set_column_order(SummaryView *summaryview)
   5223 {
   5224 	GtkWidget *ctree;
   5225 	GtkWidget *scrolledwin = summaryview->scrolledwin;
   5226 	FolderItem *item;
   5227 	guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected);
   5228 	guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed);
   5229 
   5230 	item = summaryview->folder_item;
   5231 
   5232 	summary_clear_all(summaryview);
   5233 	gtk_widget_destroy(summaryview->ctree);
   5234 
   5235 	summaryview->ctree = ctree = summary_ctree_create(summaryview);
   5236 	summary_set_column_titles(summaryview);
   5237 	gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
   5238 					    GTK_CMCLIST(ctree)->hadjustment);
   5239 	gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
   5240 					    GTK_CMCLIST(ctree)->vadjustment);
   5241 	gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
   5242 	gtk_widget_show(ctree);
   5243 
   5244 	summary_show(summaryview, item, FALSE);
   5245 
   5246 	summary_select_by_msgnum(summaryview, selected_msgnum, FALSE);
   5247 
   5248 	summaryview->selected = summary_find_msg_by_msgnum(summaryview, selected_msgnum);
   5249 	summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum);
   5250 	if (!summaryview->displayed)
   5251 		messageview_clear(summaryview->messageview);
   5252 	else
   5253 		summary_redisplay_msg(summaryview);
   5254 
   5255 	/* get normal row height */
   5256 	gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
   5257 	normal_row_height = GTK_CMCLIST(ctree)->row_height;
   5258 
   5259 	if ((prefs_common.layout_mode == VERTICAL_LAYOUT) &&
   5260 	    prefs_common.two_line_vert) {
   5261 		gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 2*normal_row_height + 2);
   5262 	}
   5263 }
   5264 
   5265 
   5266 /* callback functions */
   5267 
   5268 static gint summary_folder_eventbox_pressed(GtkWidget *eventbox, GdkEventButton *event,
   5269 					    SummaryView *summaryview)
   5270 {
   5271 	if (event) {
   5272 		folderview_grab_focus(summaryview->folderview);
   5273 		mainwindow_exit_folder(summaryview->mainwin);
   5274 	}
   5275 	return TRUE;
   5276 }
   5277 
   5278 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
   5279 				   SummaryView *summaryview)
   5280 {
   5281 	if (event)
   5282 		summary_toggle_view(summaryview);
   5283 	return TRUE;
   5284 }
   5285 
   5286 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
   5287 				       SummaryView *summaryview)
   5288 {
   5289 	if (!event) return FALSE;
   5290 	if (event->window != GTK_CMCLIST(ctree)->clist_window) return FALSE;
   5291 
   5292 	if (event->button == 3) {
   5293 		/* right clicked */
   5294 		summary_set_menu_sensitive(summaryview);
   5295 		gtk_menu_popup_at_pointer(GTK_MENU(summaryview->popupmenu), NULL);
   5296 	} else if (event->button == 2) {
   5297 		summaryview->display_msg = messageview_is_visible(summaryview->messageview);
   5298 	} else if (event->button == 1) {
   5299 		if (!prefs_common.emulate_emacs &&
   5300 		    messageview_is_visible(summaryview->messageview))
   5301 			summaryview->display_msg = TRUE;
   5302 	}
   5303 
   5304 	return FALSE;
   5305 }
   5306 
   5307 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
   5308 					SummaryView *summaryview)
   5309 {
   5310 	return FALSE;
   5311 }
   5312 
   5313 gboolean summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
   5314 {
   5315 	if (!summaryview)
   5316 		return FALSE;
   5317 	if (summary_is_list(summaryview))
   5318 		return summary_key_pressed(summaryview->ctree, event, summaryview);
   5319 	else
   5320 		return FALSE;
   5321 }
   5322 
   5323 #define BREAK_ON_MODIFIER_KEY() \
   5324 	if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
   5325 
   5326 /* Copied from gtkcmclist.c, if it changes there, it has to change
   5327  * here as well. This is an ugly hack, there must be a better way to
   5328  * find out how much to move for page up/down. */
   5329 #define CELL_SPACING 1
   5330 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
   5331 				    SummaryView *summaryview)
   5332 {
   5333 	GtkCMCTree *ctree = GTK_CMCTREE(widget);
   5334 	GtkCMCTreeNode *node;
   5335 	GtkAdjustment *adj;
   5336 	gboolean mod_pressed;
   5337 
   5338 	if (!event)
   5339 		return TRUE;
   5340 
   5341 	mod_pressed =
   5342 		((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
   5343 
   5344 	if (summaryview->selected) {
   5345 		gboolean handled = FALSE;
   5346 		switch (event->keyval) {
   5347 		case GDK_KEY_space:
   5348 			handled = TRUE;
   5349 			if (event->state & GDK_CONTROL_MASK) {
   5350 				handled = FALSE;
   5351 				break;
   5352 			}
   5353 			if (summaryview->displayed != summaryview->selected) {
   5354 					summary_display_msg(summaryview, summaryview->selected);
   5355 			} else {
   5356 				if (mod_pressed) {
   5357 					summary_select_prev_unread(summaryview);
   5358 				} else {
   5359 					summary_select_next_unread(summaryview);
   5360 				}
   5361 			}
   5362 			break;
   5363 		case GDK_KEY_Return:
   5364 		case GDK_KEY_KP_Enter:
   5365 			handled = TRUE;
   5366 			if (summaryview->displayed != summaryview->selected)
   5367 				summary_display_msg(summaryview, summaryview->selected);
   5368 			break;
   5369 		}
   5370 
   5371 		if (handled)
   5372 			return FALSE;
   5373 	}
   5374 	if (summary_is_locked(summaryview))
   5375 		return TRUE;
   5376 
   5377 	switch (event->keyval) {
   5378 	case GDK_KEY_Left:		/* Move focus */
   5379 		adj = gtk_scrolled_window_get_hadjustment
   5380 			(GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
   5381 		if (gtk_adjustment_get_lower(adj) != gtk_adjustment_get_value(adj))
   5382 			break;
   5383 		folderview_grab_focus(summaryview->folderview);
   5384 		mainwindow_exit_folder(summaryview->mainwin);
   5385 		return TRUE;
   5386 	case GDK_KEY_Home: case GDK_KEY_KP_Home:
   5387 	case GDK_KEY_End: case GDK_KEY_KP_End:
   5388 	case GDK_KEY_Up: case GDK_KEY_KP_Up:
   5389 	case GDK_KEY_Down: case GDK_KEY_KP_Down:
   5390 	case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
   5391 	case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
   5392 		if ((node = summaryview->selected) != NULL) {
   5393 			GtkCMCTreeNode *next = NULL;
   5394 			switch (event->keyval) {
   5395 				case GDK_KEY_Home: case GDK_KEY_KP_Home:
   5396 					next = gtk_cmctree_node_nth(ctree, 0);
   5397 					break;
   5398 				case GDK_KEY_End: case GDK_KEY_KP_End:
   5399 					next = gtk_cmctree_node_nth(ctree,
   5400 							g_list_length(GTK_CMCLIST(ctree)->row_list)-1);
   5401 					break;
   5402 				case GDK_KEY_Up: case GDK_KEY_KP_Up:
   5403 					next = gtk_cmctree_node_nth(ctree,
   5404 							MAX(0, GTK_CMCLIST(ctree)->focus_row - 1));
   5405 					break;
   5406 				case GDK_KEY_Down: case GDK_KEY_KP_Down:
   5407 					next = gtk_cmctree_node_nth(ctree,
   5408 							MIN(GTK_CMCLIST(ctree)->focus_row + 1, GTK_CMCLIST(ctree)->rows - 1));
   5409 					break;
   5410 				case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
   5411 					next = gtk_cmctree_node_nth(ctree,
   5412 							MAX(0, GTK_CMCLIST(ctree)->focus_row -
   5413 								(2 * GTK_CMCLIST(ctree)->clist_window_height -
   5414 								 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
   5415 								(2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
   5416 					break;
   5417 				case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
   5418 					next = gtk_cmctree_node_nth(ctree,
   5419 							MIN(GTK_CMCLIST(ctree)->rows - 1, GTK_CMCLIST(ctree)->focus_row +
   5420 								(2 * GTK_CMCLIST(ctree)->clist_window_height -
   5421 								 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
   5422 								(2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
   5423 					break;
   5424 			}
   5425 
   5426 			if (next != NULL &&
   5427 					next != gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row)) {
   5428 				gtk_sctree_select_with_state
   5429 					(GTK_SCTREE(ctree), next, (event->state & ~GDK_CONTROL_MASK) );
   5430 
   5431 				/* Deprecated - what are the non-deprecated equivalents? */
   5432 				if (gtk_cmctree_node_is_visible(GTK_CMCTREE(ctree), next) != GTK_VISIBILITY_FULL)
   5433 					gtkut_ctree_node_move_if_on_the_edge(GTK_CMCTREE(ctree), next, -1);
   5434 				if (!mod_pressed)
   5435 					summary_select_node(summaryview, summaryview->selected, OPEN_SELECTED_ON_DIRECTIONAL);
   5436 				summaryview->selected = next;
   5437 			}
   5438 		}
   5439 		return TRUE;
   5440 	default:
   5441 		break;
   5442 	}
   5443 
   5444 	if (!summaryview->selected) {
   5445 		node = gtk_cmctree_node_nth(ctree, 0);
   5446 		if (node)
   5447 			gtk_sctree_select(GTK_SCTREE(ctree), node);
   5448 		else
   5449 			return TRUE;
   5450 	}
   5451 
   5452 	switch (event->keyval) {
   5453 	case GDK_KEY_Delete:
   5454 		BREAK_ON_MODIFIER_KEY();
   5455 		summary_delete_trash(summaryview);
   5456 		break;
   5457 	default:
   5458 		break;
   5459 	}
   5460 	return FALSE;
   5461 }
   5462 #undef CELL_SPACING
   5463 
   5464 void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
   5465 {
   5466 	if (FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
   5467 		summary_reedit(summaryview);
   5468 	else
   5469 		summary_open_msg(summaryview, TRUE, FALSE);
   5470 
   5471 	summaryview->display_msg = FALSE;
   5472 }
   5473 
   5474 static void summary_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   5475 				  SummaryView *summaryview)
   5476 {
   5477 	summary_set_row_marks(summaryview, node);
   5478 	if (prefs_common.bold_unread) {
   5479 		while (node) {
   5480 			GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
   5481 			if (GTK_CMCTREE_ROW(node)->children)
   5482 				summary_set_row_marks(summaryview, node);
   5483 			node = next;
   5484 		}
   5485 	}
   5486 }
   5487 
   5488 static void summary_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
   5489 				   SummaryView *summaryview)
   5490 {
   5491 	gtk_sctree_select(GTK_SCTREE(ctree), node);
   5492 	summary_set_row_marks(summaryview, node);
   5493 }
   5494 
   5495 static void summary_unselected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
   5496 			       gint column, SummaryView *summaryview)
   5497 {
   5498 	if (summary_is_locked(summaryview)
   5499 	||  GTK_SCTREE(ctree)->selecting_range) {
   5500 		return;
   5501 	}
   5502 
   5503 	summary_status_show(summaryview);
   5504 }
   5505 
   5506 static void summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
   5507 			     gint column, SummaryView *summaryview)
   5508 {
   5509 	const GList *list, *cur;
   5510 	MessageView *msgview;
   5511 	MsgInfo *msginfo;
   5512 	gboolean marked_unread = FALSE;
   5513 
   5514 	if (summary_is_locked(summaryview)
   5515 	&& !GTK_SCTREE(ctree)->selecting_range
   5516 	&& summaryview->messageview
   5517 	&& summaryview->messageview->mimeview
   5518 	&& summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
   5519 	&& summaryview->messageview->mimeview->textview->loading) {
   5520 		PostponedSelectData *data = g_new0(PostponedSelectData, 1);
   5521 		summaryview->messageview->mimeview->textview->stop_loading = TRUE;
   5522 
   5523 		data->ctree = ctree;
   5524 		data->row = row;
   5525 		data->node = NULL;
   5526 		data->column = column;
   5527 		data->summaryview = summaryview;
   5528 		debug_print("postponing open of message till end of load\n");
   5529 		g_timeout_add(100, summary_select_retry, data);
   5530 		return;
   5531 	}
   5532 	if (summary_is_locked(summaryview)
   5533 	||  GTK_SCTREE(ctree)->selecting_range) {
   5534 		return;
   5535 	}
   5536 
   5537 	summary_status_show(summaryview);
   5538 
   5539 	if (GTK_CMCLIST(ctree)->selection &&
   5540 	    GTK_CMCLIST(ctree)->selection->next) {
   5541 		summaryview->display_msg = FALSE;
   5542 		summary_set_menu_sensitive(summaryview);
   5543 		toolbar_main_set_sensitive(summaryview->mainwin);
   5544 		return;
   5545 	}
   5546 
   5547 	summaryview->selected = row;
   5548 
   5549 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   5550 	cm_return_if_fail(msginfo != NULL);
   5551 
   5552 	main_create_mailing_list_menu (summaryview->mainwin, msginfo);
   5553 
   5554 	switch (column < 0 ? column : summaryview->col_state[column].type) {
   5555 	case S_COL_MARK:
   5556 		if (!MSG_IS_DELETED(msginfo->flags) &&
   5557 		    !MSG_IS_MOVE(msginfo->flags) &&
   5558 		    !MSG_IS_COPY(msginfo->flags)) {
   5559 			if (MSG_IS_MARKED(msginfo->flags)) {
   5560 				summary_unmark_row(summaryview, row);
   5561 				summary_status_show(summaryview);
   5562 			} else {
   5563 				summary_mark_row(summaryview, row);
   5564 				summary_status_show(summaryview);
   5565 			}
   5566 		}
   5567 		break;
   5568 	case S_COL_STATUS:
   5569 		if (MSG_IS_UNREAD(msginfo->flags)) {
   5570 			summary_mark_row_as_read(summaryview, row);
   5571 			summary_status_show(summaryview);
   5572 		} else if (!MSG_IS_REPLIED(msginfo->flags) &&
   5573 			 !MSG_IS_FORWARDED(msginfo->flags)) {
   5574 			marked_unread = TRUE;
   5575 		}
   5576 		break;
   5577 	case S_COL_LOCKED:
   5578 		if (MSG_IS_LOCKED(msginfo->flags)) {
   5579 			summary_unlock_row(summaryview, row);
   5580 			summary_status_show(summaryview);
   5581 		}
   5582 		else {
   5583 			summary_lock_row(summaryview, row);
   5584 			summary_status_show(summaryview);
   5585 		}
   5586 		break;
   5587 	default:
   5588 		break;
   5589 	}
   5590 
   5591 	list = messageview_get_msgview_list();
   5592 	for (cur = list; cur != NULL; cur = cur->next) {
   5593 		msgview = (MessageView *) cur->data;
   5594 
   5595 		if (msgview->new_window && msgview->update_needed) {
   5596 			MsgInfo *new_msginfo = summary_get_selected_msg(summaryview);
   5597 			messageview_show(msgview, new_msginfo, msgview->all_headers);
   5598 			msgview->update_needed = FALSE;
   5599 		}
   5600 	}
   5601 
   5602 	if (summaryview->display_msg) {
   5603 		summaryview->display_msg = FALSE;
   5604 		if (summaryview->displayed != row) {
   5605 			summary_display_msg(summaryview, row);
   5606 			if (marked_unread) {
   5607 				summary_mark_row_as_unread(summaryview, row);
   5608 				summary_status_show(summaryview);
   5609 			}
   5610 			return;
   5611 		}
   5612 	}
   5613 
   5614 	if (marked_unread) {
   5615 		summary_mark_row_as_unread(summaryview, row);
   5616 		summary_status_show(summaryview);
   5617 	}
   5618 
   5619 	summary_set_menu_sensitive(summaryview);
   5620 	toolbar_main_set_sensitive(summaryview->mainwin);
   5621 }
   5622 
   5623 static void summary_col_resized(GtkCMCList *clist, gint column, gint width,
   5624 				SummaryView *summaryview)
   5625 {
   5626 	SummaryColumnType type = summaryview->col_state[column].type;
   5627 
   5628 	prefs_common.summary_col_size[type] = width;
   5629 }
   5630 
   5631 
   5632 /*
   5633  * \brief get List of msginfo selected in SummaryView
   5634  *
   5635  * \param summaryview
   5636  *
   5637  * \return GSList holding MsgInfo
   5638  */
   5639 GSList *summary_get_selection(SummaryView *summaryview)
   5640 {
   5641 	GList *sel = NULL;
   5642 	GSList *msginfo_list = NULL;
   5643 
   5644 	cm_return_val_if_fail(summaryview != NULL, NULL);
   5645 
   5646 	sel = GTK_CMCLIST(summaryview->ctree)->selection;
   5647 
   5648 	cm_return_val_if_fail(sel != NULL, NULL);
   5649 
   5650 	for ( ; sel != NULL; sel = sel->next)
   5651 		msginfo_list =
   5652 			g_slist_prepend(msginfo_list,
   5653 				       gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
   5654 								   GTK_CMCTREE_NODE(sel->data)));
   5655 	return g_slist_reverse(msginfo_list);
   5656 }
   5657 
   5658 /*
   5659  * \brief get number of messages currently selected in SummaryView
   5660  *
   5661  * \param summaryview
   5662  *
   5663  * \return number of messages currently selected
   5664  */
   5665 guint summary_get_selection_count(SummaryView *summaryview)
   5666 {
   5667 	cm_return_val_if_fail(summaryview != NULL, 0);
   5668 	return g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
   5669 }
   5670 
   5671 static void summary_sort_by_column_click(SummaryView *summaryview,
   5672 					 FolderSortKey sort_key)
   5673 {
   5674 	if (prefs_common.summary_col_lock) {
   5675 		debug_print("summaryview columns locked, ignoring\n");
   5676 		return;
   5677 	}
   5678 
   5679 	GtkCMCTreeNode *node = NULL;
   5680 	if (summaryview->sort_key == sort_key)
   5681 		summary_sort(summaryview, sort_key,
   5682 			     summaryview->sort_type == SORT_ASCENDING
   5683 			     ? SORT_DESCENDING : SORT_ASCENDING);
   5684 	else
   5685 		summary_sort(summaryview, sort_key, summaryview->sort_type);
   5686 
   5687 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(summaryview->ctree)->row_list);
   5688 
   5689 	summary_freeze(summaryview);
   5690 	if (prefs_common.bold_unread || prefs_common.bold_marked) {
   5691 		while (node) {
   5692 			GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
   5693 			if (GTK_CMCTREE_ROW(node)->children)
   5694 				summary_set_row_marks(summaryview, node);
   5695 			node = next;
   5696 		}
   5697 	}
   5698 	summary_thaw(summaryview);
   5699 }
   5700 
   5701 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
   5702 {
   5703 	summary_sort_by_column_click(summaryview, SORT_BY_MARK);
   5704 }
   5705 
   5706 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview)
   5707 {
   5708 	summary_sort_by_column_click(summaryview, SORT_BY_STATUS);
   5709 }
   5710 
   5711 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
   5712 {
   5713 	summary_sort_by_column_click(summaryview, SORT_BY_MIME);
   5714 }
   5715 
   5716 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
   5717 {
   5718 	summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
   5719 }
   5720 
   5721 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
   5722 {
   5723 	summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
   5724 }
   5725 
   5726 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
   5727 {
   5728 	if (summaryview->sort_key == SORT_BY_THREAD_DATE)
   5729 		summary_sort_by_column_click(summaryview, SORT_BY_THREAD_DATE);
   5730 	else
   5731 		summary_sort_by_column_click(summaryview, SORT_BY_DATE);
   5732 }
   5733 
   5734 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
   5735 {
   5736 	if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
   5737 		summary_sort_by_column_click(summaryview, SORT_BY_FROM);
   5738 	else
   5739 		summary_sort_by_column_click(summaryview, SORT_BY_TO);
   5740 }
   5741 
   5742 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
   5743 {
   5744 	if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
   5745 		summary_sort_by_column_click(summaryview, SORT_BY_TO);
   5746 	else
   5747 		summary_sort_by_column_click(summaryview, SORT_BY_FROM);
   5748 }
   5749 
   5750 static void summary_subject_clicked(GtkWidget *button,
   5751 				    SummaryView *summaryview)
   5752 {
   5753 	summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
   5754 }
   5755 
   5756 static void summary_score_clicked(GtkWidget *button,
   5757 				  SummaryView *summaryview)
   5758 {
   5759 	summary_sort_by_column_click(summaryview, SORT_BY_SCORE);
   5760 }
   5761 
   5762 static void summary_locked_clicked(GtkWidget *button,
   5763 				   SummaryView *summaryview)
   5764 {
   5765 	summary_sort_by_column_click(summaryview, SORT_BY_LOCKED);
   5766 }
   5767 
   5768 /* custom compare functions for sorting */
   5769 
   5770 static gint summary_cmp_by_date(GtkCMCList *clist,
   5771 		      gconstpointer ptr1, gconstpointer ptr2)
   5772 {
   5773 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
   5774 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
   5775 	gint res;
   5776 	if (!msginfo1 || !msginfo2)
   5777 		return -1;
   5778 
   5779 	res = (msginfo1->date_t - msginfo2->date_t);
   5780 	if (res == 0)
   5781 		res = msginfo1->msgnum - msginfo2->msgnum;
   5782 	return res;
   5783 }
   5784 
   5785 #define CMP_FUNC_DEF(func_name, val)					 \
   5786 static gint func_name(GtkCMCList *clist,				 \
   5787 		      gconstpointer ptr1, gconstpointer ptr2)		 \
   5788 {									 \
   5789 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;		 \
   5790 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;		 \
   5791 	gint res;							 \
   5792 	if (!msginfo1 || !msginfo2)					 \
   5793 		return -1;						 \
   5794 									 \
   5795 	res = (val);							 \
   5796 	return (res != 0) ? res:summary_cmp_by_date(clist, ptr1, ptr2);	 \
   5797 }
   5798 
   5799 CMP_FUNC_DEF(summary_cmp_by_mark,
   5800 	     MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
   5801 CMP_FUNC_DEF(summary_cmp_by_status,
   5802 	     (-(MSG_IS_SPAM(msginfo1->flags))+(MSG_IS_UNREAD(msginfo1->flags)<<1)+(MSG_IS_NEW(msginfo1->flags)<<2))
   5803 	     - (-(MSG_IS_SPAM(msginfo2->flags))+(MSG_IS_UNREAD(msginfo2->flags)<<1)+(MSG_IS_NEW(msginfo2->flags)<<2)) )
   5804 CMP_FUNC_DEF(summary_cmp_by_mime,
   5805 	     MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags))
   5806 CMP_FUNC_DEF(summary_cmp_by_label,
   5807 	     MSG_GET_COLORLABEL(msginfo1->flags) -
   5808 	     MSG_GET_COLORLABEL(msginfo2->flags))
   5809 CMP_FUNC_DEF(summary_cmp_by_locked,
   5810 	     MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags))
   5811 
   5812 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
   5813 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
   5814 
   5815 #undef CMP_FUNC_DEF
   5816 
   5817 static gint summary_cmp_by_subject(GtkCMCList *clist,
   5818 				   gconstpointer ptr1,
   5819 				   gconstpointer ptr2)
   5820 {
   5821 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
   5822 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
   5823 	gint res;
   5824 
   5825 	if (!msginfo1->subject)
   5826 		return (msginfo2->subject != NULL);
   5827 	if (!msginfo2->subject)
   5828 		return -1;
   5829 
   5830 	res = subject_compare_for_sort
   5831 		(msginfo1->subject, msginfo2->subject);
   5832 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
   5833 }
   5834 
   5835 static gint summary_cmp_by_thread_date(GtkCMCList *clist,
   5836 				   gconstpointer ptr1,
   5837 				   gconstpointer ptr2)
   5838 {
   5839 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
   5840 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
   5841 	gint thread_diff = msginfo1->thread_date - msginfo2->thread_date;
   5842 
   5843 	if (msginfo1->thread_date > 0 && msginfo2->thread_date > 0)
   5844 		return thread_diff;
   5845 	else
   5846 		return msginfo1->date_t - msginfo2->date_t;
   5847 }
   5848 
   5849 static gint summary_cmp_by_from(GtkCMCList *clist, gconstpointer ptr1,
   5850 				gconstpointer ptr2)
   5851 {
   5852 	const gchar *str1, *str2;
   5853 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
   5854 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
   5855 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
   5856 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
   5857 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
   5858 	gint res;
   5859 
   5860 	cm_return_val_if_fail(sv, -1);
   5861 	if (sv->col_state[sv->col_pos[S_COL_FROM]].visible) {
   5862 		str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text;
   5863 		str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text;
   5864 	} else {
   5865 		str1 = msginfo1->from;
   5866 		str2 = msginfo2->from;
   5867 	}
   5868 
   5869 	if (!str1)
   5870 		return str2 != NULL;
   5871 
   5872 	if (!str2)
   5873  		return -1;
   5874 
   5875 	res = g_utf8_collate(str1, str2);
   5876 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
   5877 }
   5878 
   5879 static gint summary_cmp_by_to(GtkCMCList *clist, gconstpointer ptr1,
   5880 				gconstpointer ptr2)
   5881 {
   5882 	const gchar *str1, *str2;
   5883 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
   5884 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
   5885 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
   5886 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
   5887 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
   5888 	gint res;
   5889 	cm_return_val_if_fail(sv, -1);
   5890 
   5891 	if (sv->col_state[sv->col_pos[S_COL_TO]].visible) {
   5892 		str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
   5893 		str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
   5894 	} else {
   5895 		str1 = msginfo1->to;
   5896 		str2 = msginfo2->to;
   5897 	}
   5898 
   5899 	if (!str1)
   5900 		return str2 != NULL;
   5901 
   5902 	if (!str2)
   5903  		return -1;
   5904 
   5905 	res = g_utf8_collate(str1, str2);
   5906 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
   5907 }
   5908 
   5909 static gint summary_cmp_by_simplified_subject
   5910 	(GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2)
   5911 {
   5912 	const FolderItemPrefs *prefs;
   5913 	const gchar *str1, *str2;
   5914 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
   5915 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
   5916 	const MsgInfo *msginfo1 = r1->data;
   5917 	const MsgInfo *msginfo2 = r2->data;
   5918 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
   5919 	gint res;
   5920 
   5921 	cm_return_val_if_fail(sv, -1);
   5922 	cm_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1);
   5923 
   5924 	if (sv->col_state[sv->col_pos[S_COL_SUBJECT]].visible) {
   5925 		str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text;
   5926 		str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text;
   5927 	} else {
   5928 		str1 = msginfo1->subject;
   5929 		str2 = msginfo2->subject;
   5930 	}
   5931 
   5932 	if (!str1)
   5933 		return str2 != NULL;
   5934 
   5935 	if (!str2)
   5936 		return -1;
   5937 
   5938 	prefs = msginfo1->folder->prefs;
   5939 	if (!prefs)
   5940 		prefs = msginfo2->folder->prefs;
   5941 	if (!prefs)
   5942 		return -1;
   5943 
   5944 	res = subject_compare_for_sort(str1, str2);
   5945 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
   5946 }
   5947 
   5948 static gint summary_cmp_by_score(GtkCMCList *clist,
   5949 				 gconstpointer ptr1, gconstpointer ptr2)
   5950 {
   5951 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
   5952 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
   5953 	int diff;
   5954 
   5955 	/* if score are equal, sort by date */
   5956 
   5957 	diff = msginfo1->score - msginfo2->score;
   5958 	if (diff != 0)
   5959 		return diff;
   5960 	else
   5961 		return summary_cmp_by_date(clist, ptr1, ptr2);
   5962 }
   5963 
   5964 static void summary_ignore_thread_func_mark_unread(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   5965 {
   5966 	MsgInfo *msginfo;
   5967 
   5968 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   5969 	cm_return_if_fail(msginfo);
   5970 
   5971 	summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
   5972 	summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
   5973 
   5974 	debug_print("Message %d is marked as ignore thread\n", msginfo->msgnum);
   5975 }
   5976 
   5977 static void summary_ignore_thread_func_set_row(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   5978 {
   5979 	SummaryView *summaryview = (SummaryView *) data;
   5980 	MsgInfo *msginfo;
   5981 
   5982 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   5983 	cm_return_if_fail(msginfo);
   5984 
   5985 	summary_set_row_marks(summaryview, row);
   5986 	debug_print("Message %d update in row view\n", msginfo->msgnum);
   5987 }
   5988 
   5989 void summary_ignore_thread(SummaryView *summaryview)
   5990 {
   5991 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   5992 	GList *cur;
   5993 	gboolean froze = FALSE;
   5994 
   5995 	START_LONG_OPERATION(summaryview, FALSE);
   5996 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   5997 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   5998 					GTK_CMCTREE_FUNC(summary_ignore_thread_func_mark_unread),
   5999 					summaryview);
   6000 
   6001 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   6002 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   6003 					GTK_CMCTREE_FUNC(summary_ignore_thread_func_set_row),
   6004 					summaryview);
   6005 
   6006 	END_LONG_OPERATION(summaryview);
   6007 
   6008 	summary_status_show(summaryview);
   6009 }
   6010 
   6011 static void summary_unignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   6012 {
   6013 	SummaryView *summaryview = (SummaryView *) data;
   6014 	MsgInfo *msginfo;
   6015 
   6016 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   6017 	cm_return_if_fail(msginfo);
   6018 
   6019 	summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0);
   6020 
   6021 	summary_set_row_marks(summaryview, row);
   6022 	debug_print("Message %d is marked as unignore thread\n",
   6023 	    msginfo->msgnum);
   6024 }
   6025 
   6026 void summary_unignore_thread(SummaryView *summaryview)
   6027 {
   6028 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   6029 	GList *cur;
   6030 	gboolean froze = FALSE;
   6031 
   6032 	START_LONG_OPERATION(summaryview, FALSE);
   6033 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   6034 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   6035 					GTK_CMCTREE_FUNC(summary_unignore_thread_func),
   6036 					summaryview);
   6037 
   6038 	END_LONG_OPERATION(summaryview);
   6039 
   6040 	summary_status_show(summaryview);
   6041 }
   6042 
   6043 static void summary_check_ignore_thread_func
   6044 		(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   6045 {
   6046 	MsgInfo *msginfo;
   6047 	gint *found_ignore = (gint *) data;
   6048 
   6049 	if (*found_ignore) return;
   6050 	else {
   6051 		msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   6052 		*found_ignore = msginfo && MSG_IS_IGNORE_THREAD(msginfo->flags);
   6053 	}
   6054 }
   6055 
   6056 void summary_toggle_ignore_thread(SummaryView *summaryview)
   6057 {
   6058 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   6059 	GList *cur;
   6060 	gint found_ignore = 0;
   6061 
   6062 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   6063 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   6064 					GTK_CMCTREE_FUNC(summary_check_ignore_thread_func),
   6065 					&found_ignore);
   6066 
   6067 	if (found_ignore)
   6068 		summary_unignore_thread(summaryview);
   6069 	else
   6070 		summary_ignore_thread(summaryview);
   6071 }
   6072 
   6073 static void summary_watch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   6074 {
   6075 	SummaryView *summaryview = (SummaryView *) data;
   6076 	MsgInfo *msginfo;
   6077 
   6078 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   6079 	cm_return_if_fail(msginfo);
   6080 
   6081 	summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0);
   6082 
   6083 	summary_set_row_marks(summaryview, row);
   6084 	debug_print("Message %d is marked as watch thread\n",
   6085 	    msginfo->msgnum);
   6086 }
   6087 
   6088 void summary_watch_thread(SummaryView *summaryview)
   6089 {
   6090 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   6091 	GList *cur;
   6092 	gboolean froze = FALSE;
   6093 
   6094 	START_LONG_OPERATION(summaryview, FALSE);
   6095 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   6096 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   6097 					GTK_CMCTREE_FUNC(summary_watch_thread_func),
   6098 					summaryview);
   6099 
   6100 	END_LONG_OPERATION(summaryview);
   6101 
   6102 	summary_status_show(summaryview);
   6103 }
   6104 
   6105 static void summary_unwatch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   6106 {
   6107 	SummaryView *summaryview = (SummaryView *) data;
   6108 	MsgInfo *msginfo;
   6109 
   6110 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   6111 	cm_return_if_fail(msginfo);
   6112 
   6113 	summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
   6114 
   6115 	summary_set_row_marks(summaryview, row);
   6116 	debug_print("Message %d is marked as unwatch thread\n",
   6117 	    msginfo->msgnum);
   6118 }
   6119 
   6120 void summary_unwatch_thread(SummaryView *summaryview)
   6121 {
   6122 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   6123 	GList *cur;
   6124 	gboolean froze = FALSE;
   6125 
   6126 	START_LONG_OPERATION(summaryview, FALSE);
   6127 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   6128 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   6129 					GTK_CMCTREE_FUNC(summary_unwatch_thread_func),
   6130 					summaryview);
   6131 
   6132 	END_LONG_OPERATION(summaryview);
   6133 
   6134 	summary_status_show(summaryview);
   6135 }
   6136 
   6137 static void summary_check_watch_thread_func
   6138 		(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
   6139 {
   6140 	MsgInfo *msginfo;
   6141 	gint *found_watch = (gint *) data;
   6142 
   6143 	if (*found_watch) return;
   6144 	else {
   6145 		msginfo = gtk_cmctree_node_get_row_data(ctree, row);
   6146 		*found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags);
   6147 	}
   6148 }
   6149 
   6150 void summary_toggle_watch_thread(SummaryView *summaryview)
   6151 {
   6152 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
   6153 	GList *cur;
   6154 	gint found_watch = 0;
   6155 
   6156 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
   6157 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
   6158 					GTK_CMCTREE_FUNC(summary_check_watch_thread_func),
   6159 					&found_watch);
   6160 
   6161 	if (found_watch)
   6162 		summary_unwatch_thread(summaryview);
   6163 	else
   6164 		summary_watch_thread(summaryview);
   6165 }
   6166 
   6167 
   6168 void summary_toggle_show_read_messages(SummaryView *summaryview)
   6169 {
   6170 	FolderItemUpdateData source;
   6171 	if (summaryview->folder_item->hide_read_msgs)
   6172  		summaryview->folder_item->hide_read_msgs = 0;
   6173  	else
   6174  		summaryview->folder_item->hide_read_msgs = 1;
   6175 
   6176 	source.item = summaryview->folder_item;
   6177 	source.update_flags = F_ITEM_UPDATE_NAME;
   6178 	source.msg = NULL;
   6179 	hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
   6180  	summary_show(summaryview, summaryview->folder_item, FALSE);
   6181 }
   6182 
   6183 void summary_toggle_show_del_messages(SummaryView *summaryview)
   6184 {
   6185 	FolderItemUpdateData source;
   6186 	if (summaryview->folder_item->hide_del_msgs)
   6187  		summaryview->folder_item->hide_del_msgs = 0;
   6188  	else
   6189  		summaryview->folder_item->hide_del_msgs = 1;
   6190 
   6191 	source.item = summaryview->folder_item;
   6192 	source.update_flags = F_ITEM_UPDATE_NAME;
   6193 	source.msg = NULL;
   6194 	hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
   6195  	summary_show(summaryview, summaryview->folder_item, FALSE);
   6196 }
   6197 
   6198 void summary_toggle_show_read_threads(SummaryView *summaryview)
   6199 {
   6200 	FolderItemUpdateData source;
   6201 	if (summaryview->folder_item->hide_read_threads)
   6202  		summaryview->folder_item->hide_read_threads = 0;
   6203  	else
   6204  		summaryview->folder_item->hide_read_threads = 1;
   6205 
   6206 	source.item = summaryview->folder_item;
   6207 	source.update_flags = F_ITEM_UPDATE_NAME;
   6208 	source.msg = NULL;
   6209 	hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
   6210  	summary_show(summaryview, summaryview->folder_item, FALSE);
   6211 }
   6212 
   6213 static void summary_set_hide_menu (SummaryView *summaryview,
   6214 				   const gchar *menu_item,
   6215 				   guint action)
   6216 {
   6217  	GtkWidget *widget;
   6218 
   6219  	widget = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, menu_item);
   6220 	cm_return_if_fail(widget != NULL);
   6221 
   6222  	g_object_set_data(G_OBJECT(widget), "dont_toggle",
   6223  			  GINT_TO_POINTER(1));
   6224  	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action);
   6225  	g_object_set_data(G_OBJECT(widget), "dont_toggle",
   6226  			  GINT_TO_POINTER(0));
   6227 }
   6228 
   6229 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
   6230 {
   6231 	stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
   6232 	stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
   6233 	stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
   6234 	stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
   6235 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
   6236 	stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
   6237 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
   6238 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
   6239 	stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
   6240 	stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
   6241 	stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
   6242 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
   6243 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
   6244 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
   6245 	stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
   6246 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
   6247 	stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
   6248 	stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
   6249 	stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
   6250 
   6251 	summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
   6252 
   6253 	folderview_unselect(summaryview->folderview);
   6254 	folderview_select(summaryview->folderview, summaryview->folder_item);
   6255 	summary_set_column_titles(summaryview);
   6256 }
   6257 
   6258 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp)
   6259 {
   6260 	int err;
   6261 	gchar buf[BUFFSIZE];
   6262 	regex_t *preg = NULL;
   6263 
   6264 	preg = g_new0(regex_t, 1);
   6265 
   6266 	err = regcomp(preg, simplify_subject_regexp, REG_EXTENDED);
   6267 	if (err) {
   6268 		regerror(err, preg, buf, BUFFSIZE);
   6269 		alertpanel_error("Regular expression error:\n%s", buf);
   6270 		regfree(preg);
   6271 		g_free(preg);
   6272 		preg = NULL;
   6273 	}
   6274 
   6275 	return preg;
   6276 }
   6277 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item)
   6278 {
   6279 	FolderSortKey sort_key;
   6280 	FolderSortType sort_type;
   6281 	cm_return_if_fail(summaryview != NULL);
   6282 	cm_return_if_fail(item != NULL);
   6283 
   6284 	/* Subject simplification */
   6285 	if(summaryview->simplify_subject_preg) {
   6286 		regfree(summaryview->simplify_subject_preg);
   6287 		g_free(summaryview->simplify_subject_preg);
   6288 		summaryview->simplify_subject_preg = NULL;
   6289 	}
   6290 	if(item->prefs && item->prefs->simplify_subject_regexp &&
   6291 	   item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject)
   6292 		summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp);
   6293 
   6294 	/* Sorting */
   6295 	sort_key = item->sort_key;
   6296 	sort_type = item->sort_type;
   6297 
   6298 	folder_get_sort_type(item->folder, &sort_key, &sort_type);
   6299 
   6300 	summaryview->sort_key = sort_key;
   6301 	summaryview->sort_type = sort_type;
   6302 
   6303 	/* Threading */
   6304 	summaryview->threaded = item->threaded;
   6305 	summaryview->thread_collapsed = item->thread_collapsed;
   6306 
   6307 	/* Scoring */
   6308 }
   6309 
   6310 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item)
   6311 {
   6312 	/* Sorting */
   6313 	item->sort_key = summaryview->sort_key;
   6314 	item->sort_type = summaryview->sort_type;
   6315 
   6316 	/* Threading */
   6317 	item->threaded = summaryview->threaded;
   6318 	item->thread_collapsed = summaryview->thread_collapsed;
   6319 }
   6320 
   6321 static gboolean summary_update_msg(gpointer source, gpointer data)
   6322 {
   6323 	MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source;
   6324 	SummaryView *summaryview = (SummaryView *)data;
   6325 	GtkCMCTreeNode *node;
   6326 
   6327 	cm_return_val_if_fail(msginfo_update != NULL, TRUE);
   6328 	cm_return_val_if_fail(summaryview != NULL, FALSE);
   6329 
   6330 	if (msginfo_update->msginfo->folder != summaryview->folder_item)
   6331 		return FALSE;
   6332 
   6333 	if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) {
   6334 		node = gtk_cmctree_find_by_row_data(
   6335 				GTK_CMCTREE(summaryview->ctree), NULL,
   6336 				msginfo_update->msginfo);
   6337 
   6338 		if (node)
   6339 			summary_set_row_marks(summaryview, node);
   6340 	}
   6341 
   6342 	return FALSE;
   6343 }
   6344 
   6345 void summary_update_unread(SummaryView *summaryview, FolderItem *removed_item)
   6346 {
   6347 	static gboolean tips_initialized = FALSE;
   6348 
   6349 	if (!tips_initialized)
   6350 		return;
   6351 	summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
   6352 	CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox, NULL);
   6353 	tips_initialized = FALSE;
   6354 }
   6355 
   6356 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data)
   6357 {
   6358 	FolderItemUpdateData *hookdata = (FolderItemUpdateData *)source;
   6359 	SummaryView *summaryview = (SummaryView *)data;
   6360 
   6361 	cm_return_val_if_fail(hookdata != NULL, FALSE);
   6362 	cm_return_val_if_fail(hookdata->item != NULL, FALSE);
   6363 	cm_return_val_if_fail(summaryview != NULL, FALSE);
   6364 
   6365 	if (hookdata->item == summaryview->folder_item &&
   6366 	    hookdata->update_flags & F_ITEM_UPDATE_NAME) {
   6367 		gchar *name = folder_item_get_name(hookdata->item);
   6368 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
   6369 		g_free(name);
   6370 	}
   6371 	summary_update_unread(summaryview, NULL);
   6372 
   6373 	return FALSE;
   6374 }
   6375 
   6376 static gboolean summary_update_folder_hook(gpointer source, gpointer data)
   6377 {
   6378 	FolderUpdateData *hookdata;
   6379 	SummaryView *summaryview = (SummaryView *)data;
   6380 	hookdata = source;
   6381 	if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
   6382 		summary_update_unread(summaryview, hookdata->item);
   6383 	} else
   6384 		summary_update_unread(summaryview, NULL);
   6385 
   6386 	return FALSE;
   6387 }
   6388 
   6389 static void summary_reedit_cb(GtkAction *gaction, gpointer data)
   6390 {
   6391 	SummaryView *summaryview = (SummaryView *)data;
   6392 	summary_reedit(summaryview);
   6393 }
   6394 
   6395 #define DO_ACTION(name, act)	{ if (!strcmp(a_name, name)) action = act; }
   6396 static void summary_reply_cb(GtkAction *gaction, gpointer data)
   6397 {
   6398 	SummaryView *summaryview = (SummaryView *)data;
   6399 	GSList *msginfo_list = NULL;
   6400 	gint action = COMPOSE_REPLY;
   6401 	const gchar *a_name = gtk_action_get_name(gaction);
   6402 
   6403 	DO_ACTION("SummaryViewPopup/Reply", COMPOSE_REPLY);
   6404 	DO_ACTION("SummaryViewPopup/ReplyTo/All", COMPOSE_REPLY_TO_ALL);
   6405 	DO_ACTION("SummaryViewPopup/ReplyTo/Sender", COMPOSE_REPLY_TO_SENDER);
   6406 	DO_ACTION("SummaryViewPopup/ReplyTo/List", COMPOSE_REPLY_TO_LIST);
   6407 	DO_ACTION("SummaryViewPopup/Forward", COMPOSE_FORWARD_INLINE);
   6408 	DO_ACTION("SummaryViewPopup/ForwardAtt", COMPOSE_FORWARD_AS_ATTACH);
   6409 	DO_ACTION("SummaryViewPopup/Redirect", COMPOSE_REDIRECT);
   6410 
   6411 	msginfo_list = summary_get_selection(summaryview);
   6412 	cm_return_if_fail(msginfo_list != NULL);
   6413 	compose_reply_from_messageview(NULL, msginfo_list, action);
   6414 	g_slist_free(msginfo_list);
   6415 }
   6416 
   6417 gboolean summary_is_opened_message_selected(SummaryView *summaryview)
   6418 {
   6419 	GList *sel = NULL;
   6420 
   6421 	cm_return_val_if_fail(summaryview != NULL, FALSE);
   6422 
   6423 	sel = GTK_CMCLIST(summaryview->ctree)->selection;
   6424 
   6425 	if (summaryview->displayed == NULL || sel == NULL) {
   6426 		return FALSE;
   6427 	}
   6428 
   6429 	for ( ; sel != NULL; sel = sel->next) {
   6430 		if (summaryview->displayed == GTK_CMCTREE_NODE(sel->data)) {
   6431 			return TRUE;
   6432 		}
   6433 	}
   6434 	return FALSE;
   6435 }
   6436 
   6437 gboolean summary_has_opened_message(SummaryView *summaryview)
   6438 {
   6439 	cm_return_val_if_fail(summaryview != NULL, FALSE);
   6440 
   6441 	return (summaryview->displayed != NULL);
   6442 }
   6443 
   6444 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data)
   6445 {
   6446 	SummaryView *summaryview = (SummaryView *)data;
   6447 	gboolean sorting_lock = prefs_common_get_prefs()->summary_col_lock;
   6448 
   6449 	if (summaryview->header_menu_lock)
   6450 		return;
   6451 
   6452 	debug_print("%slocking summaryview columns\n",
   6453 			sorting_lock ? "un" : "");
   6454 	prefs_common_get_prefs()->summary_col_lock = !sorting_lock;
   6455 }
   6456 
   6457 static void summary_header_set_displayed_columns_cb(GtkAction *gaction,
   6458 		gpointer data)
   6459 {
   6460 	SummaryView *summaryview = (SummaryView *)data;
   6461 
   6462 	if (summaryview->header_menu_lock)
   6463 		return;
   6464 
   6465 	prefs_summary_column_open();
   6466 }