revert
[claws.git] / src / summaryview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkscrolledwindow.h>
25 #include <gtk/gtkwidget.h>
26 #include <gtk/gtkpixmap.h>
27 #include <gtk/gtkctree.h>
28 #include <gtk/gtkcontainer.h>
29 #include <gtk/gtksignal.h>
30 #include <gtk/gtktext.h>
31 #include <gtk/gtkmenu.h>
32 #include <gtk/gtkmenuitem.h>
33 #include <gtk/gtkitemfactory.h>
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkwindow.h>
37 #include <gtk/gtkstyle.h>
38 #include <gtk/gtkarrow.h>
39 #include <gtk/gtkeventbox.h>
40 #include <gtk/gtkstatusbar.h>
41 #include <gtk/gtkmenuitem.h>
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <unistd.h>
48
49 #include "intl.h"
50 #include "main.h"
51 #include "menu.h"
52 #include "mainwindow.h"
53 #include "folderview.h"
54 #include "summaryview.h"
55 #include "messageview.h"
56 #include "foldersel.h"
57 #include "procmsg.h"
58 #include "procheader.h"
59 #include "headerwindow.h"
60 #include "sourcewindow.h"
61 #include "prefs_common.h"
62 #include "account.h"
63 #include "compose.h"
64 #include "utils.h"
65 #include "gtkutils.h"
66 #include "filesel.h"
67 #include "manage_window.h"
68 #include "alertpanel.h"
69 #include "inputdialog.h"
70 #include "statusbar.h"
71 #include "filter.h"
72 #include "folder.h"
73 #include "addressbook.h"
74 #include "addr_compl.h"
75 #include "scoring.h"
76 #include "prefs_folder_item.h"
77 #include "filtering.h"
78
79 #include "pixmaps/dir-open.xpm"
80 #include "pixmaps/mark.xpm"
81 #include "pixmaps/deleted.xpm"
82 #include "pixmaps/new.xpm"
83 #include "pixmaps/unread.xpm"
84 #include "pixmaps/replied.xpm"
85 #include "pixmaps/forwarded.xpm"
86 #include "pixmaps/clip.xpm"
87 #include "pixmaps/ignorethread.xpm"
88
89 #define STATUSBAR_PUSH(mainwin, str) \
90 { \
91         gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
92                            mainwin->summaryview_cid, str); \
93         gtkut_widget_wait_for_draw(mainwin->hbox_stat); \
94 }
95
96 #define STATUSBAR_POP(mainwin) \
97 { \
98         gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
99                           mainwin->summaryview_cid); \
100 }
101
102 #define SUMMARY_COL_MARK_WIDTH          10
103 #define SUMMARY_COL_UNREAD_WIDTH        13
104 #define SUMMARY_COL_MIME_WIDTH          10
105
106 static GdkFont *smallfont;
107
108 static GdkPixmap *folderxpm;
109 static GdkBitmap *folderxpmmask;
110
111 static GdkPixmap *markxpm;
112 static GdkBitmap *markxpmmask;
113 static GdkPixmap *deletedxpm;
114 static GdkBitmap *deletedxpmmask;
115
116 static GdkPixmap *newxpm;
117 static GdkBitmap *newxpmmask;
118 static GdkPixmap *unreadxpm;
119 static GdkBitmap *unreadxpmmask;
120 static GdkPixmap *repliedxpm;
121 static GdkBitmap *repliedxpmmask;
122 static GdkPixmap *forwardedxpm;
123 static GdkBitmap *forwardedxpmmask;
124 static GdkPixmap *ignorethreadxpm;
125 static GdkBitmap *ignorethreadxpmmask;
126
127 static GdkPixmap *clipxpm;
128 static GdkBitmap *clipxpmmask;
129
130 static void summary_free_msginfo_func   (GtkCTree               *ctree,
131                                          GtkCTreeNode           *node,
132                                          gpointer                data);
133 static void summary_set_marks_func      (GtkCTree               *ctree,
134                                          GtkCTreeNode           *node,
135                                          gpointer                data);
136 static void summary_write_cache_func    (GtkCTree               *ctree,
137                                          GtkCTreeNode           *node,
138                                          gpointer                data);
139
140 static void summary_set_menu_sensitive  (SummaryView            *summaryview);
141
142 static GtkCTreeNode *summary_find_next_unread_msg
143                                         (SummaryView            *summaryview,
144                                          GtkCTreeNode           *current_node);
145 static GtkCTreeNode *summary_find_next_marked_msg
146                                         (SummaryView            *summaryview,
147                                          GtkCTreeNode           *current_node);
148 static GtkCTreeNode *summary_find_prev_marked_msg
149                                         (SummaryView            *summaryview,
150                                          GtkCTreeNode           *current_node);
151 static GtkCTreeNode *summary_find_msg_by_msgnum
152                                         (SummaryView            *summaryview,
153                                          guint                   msgnum);
154 #if 0
155 static GtkCTreeNode *summary_find_prev_unread_msg
156                                         (SummaryView            *summaryview,
157                                          GtkCTreeNode           *current_node);
158 #endif
159
160 static void summary_update_status       (SummaryView            *summaryview);
161
162 /* display functions */
163 static void summary_status_show         (SummaryView            *summaryview);
164 static void summary_set_ctree_from_list (SummaryView            *summaryview,
165                                          GSList                 *mlist);
166 static void summary_set_header          (gchar                  *text[],
167                                          MsgInfo                *msginfo);
168 static void summary_display_msg         (SummaryView            *summaryview,
169                                          GtkCTreeNode           *row,
170                                          gboolean                new_window);
171 static void summary_toggle_view         (SummaryView            *summaryview);
172 static void summary_set_row_marks       (SummaryView            *summaryview,
173                                          GtkCTreeNode           *row);
174
175 /* message handling */
176 static void summary_mark_row            (SummaryView            *summaryview,
177                                          GtkCTreeNode           *row);
178 static void summary_mark_row_as_read    (SummaryView            *summaryview,
179                                          GtkCTreeNode           *row);
180 static void summary_mark_row_as_unread  (SummaryView            *summaryview,
181                                          GtkCTreeNode           *row);
182 static void summary_delete_row          (SummaryView            *summaryview,
183                                          GtkCTreeNode           *row);
184 static void summary_unmark_row          (SummaryView            *summaryview,
185                                          GtkCTreeNode           *row);
186 static void summary_move_row_to         (SummaryView            *summaryview,
187                                          GtkCTreeNode           *row,
188                                          FolderItem             *to_folder);
189 static void summary_copy_row_to         (SummaryView            *summaryview,
190                                          GtkCTreeNode           *row,
191                                          FolderItem             *to_folder);
192
193 static void summary_delete_duplicated_func
194                                         (GtkCTree               *ctree,
195                                          GtkCTreeNode           *node,
196                                          SummaryView            *summaryview);
197
198 static void summary_execute_move        (SummaryView            *summaryview);
199 static void summary_execute_move_func   (GtkCTree               *ctree,
200                                          GtkCTreeNode           *node,
201                                          gpointer                data);
202 static void summary_execute_copy        (SummaryView            *summaryview);
203 static void summary_execute_copy_func   (GtkCTree               *ctree,
204                                          GtkCTreeNode           *node,
205                                          gpointer                data);
206 static void summary_execute_delete      (SummaryView            *summaryview);
207 static void summary_execute_delete_func (GtkCTree               *ctree,
208                                          GtkCTreeNode           *node,
209                                          gpointer                data);
210 static void summary_ignore_thread(SummaryView *summaryview);
211 static void summary_unignore_thread(SummaryView *summaryview);
212
213 /* thread functions */
214 static void summary_thread_func                 (GtkCTree       *ctree,
215                                                  GtkCTreeNode   *node,
216                                                  gpointer        data);
217 static void summary_unthread_func               (GtkCTree       *ctree,
218                                                  GtkCTreeNode   *node,
219                                                  gpointer        data);
220 static void summary_unthread_for_exec           (SummaryView    *summaryview);
221 static void summary_unthread_for_exec_func      (GtkCTree       *ctree,
222                                                  GtkCTreeNode   *node,
223                                                  gpointer        data);
224
225 static void summary_filter_func         (GtkCTree               *ctree,
226                                          GtkCTreeNode           *node,
227                                          gpointer                data);
228
229 /* callback functions */
230 static void summary_toggle_pressed      (GtkWidget              *eventbox,
231                                          GdkEventButton         *event,
232                                          SummaryView            *summaryview);
233 static void summary_button_pressed      (GtkWidget              *ctree,
234                                          GdkEventButton         *event,
235                                          SummaryView            *summaryview);
236 static void summary_button_released     (GtkWidget              *ctree,
237                                          GdkEventButton         *event,
238                                          SummaryView            *summaryview);
239 static void summary_key_pressed         (GtkWidget              *ctree,
240                                          GdkEventKey            *event,
241                                          SummaryView            *summaryview);
242 static void summary_open_row            (GtkSCTree              *sctree,
243                                          SummaryView            *summaryview);
244 static void summary_selected            (GtkCTree               *ctree,
245                                          GtkCTreeNode           *row,
246                                          gint                    column,
247                                          SummaryView            *summaryview);
248 static void summary_col_resized         (GtkCList               *clist,
249                                          gint                    column,
250                                          gint                    width,
251                                          SummaryView            *summaryview);
252 static void summary_reply_cb            (SummaryView            *summaryview,
253                                          guint                   action,
254                                          GtkWidget              *widget);
255 static void summary_show_all_header_cb  (SummaryView            *summaryview,
256                                          guint                   action,
257                                          GtkWidget              *widget);
258 static void summary_add_sender_to_cb (SummaryView                       *summaryview,
259                                          guint                   action,
260                                          GtkWidget              *widget);
261
262 static void summary_num_clicked         (GtkWidget              *button,
263                                          SummaryView            *summaryview);
264 static void summary_score_clicked       (GtkWidget *button,
265                                          SummaryView *summaryview);
266 static void summary_size_clicked        (GtkWidget              *button,
267                                          SummaryView            *summaryview);
268 static void summary_date_clicked        (GtkWidget              *button,
269                                          SummaryView            *summaryview);
270 static void summary_from_clicked        (GtkWidget              *button,
271                                          SummaryView            *summaryview);
272 static void summary_subject_clicked     (GtkWidget              *button,
273                                          SummaryView            *summaryview);
274 static void summary_mark_clicked        (GtkWidget              *button,
275                                          SummaryView            *summaryview);
276
277 static void summary_start_drag          (GtkWidget        *widget, 
278                                          int button,
279                                          GdkEvent *event,
280                                          SummaryView      *summaryview);
281 static void summary_drag_data_get       (GtkWidget        *widget,
282                                          GdkDragContext   *drag_context,
283                                          GtkSelectionData *selection_data,
284                                          guint             info,
285                                          guint             time,
286                                          SummaryView      *summaryview);
287
288 /* custom compare functions for sorting */
289
290 static gint summary_cmp_by_num          (GtkCList               *clist,
291                                          gconstpointer           ptr1,
292                                          gconstpointer           ptr2);
293 static gint summary_cmp_by_size         (GtkCList               *clist,
294                                          gconstpointer           ptr1,
295                                          gconstpointer           ptr2);
296 static gint summary_cmp_by_date         (GtkCList               *clist,
297                                          gconstpointer           ptr1,
298                                          gconstpointer           ptr2);
299 static gint summary_cmp_by_from         (GtkCList               *clist,
300                                          gconstpointer           ptr1,
301                                          gconstpointer           ptr2);
302 static gint summary_cmp_by_subject      (GtkCList               *clist,
303                                          gconstpointer           ptr1,
304                                          gconstpointer           ptr2);
305 static gint summary_cmp_by_score        (GtkCList               *clist,
306                                          gconstpointer           ptr1,
307                                          gconstpointer           ptr2);
308 static gint summary_cmp_by_label        (GtkCList               *clist,
309                                          gconstpointer           ptr1,
310                                          gconstpointer           ptr2);
311
312 GtkTargetEntry summary_drag_types[1] =
313 {
314         {"text/plain", GTK_TARGET_SAME_APP, TARGET_DUMMY}
315 };
316
317 static GtkItemFactoryEntry summary_popup_entries[] =
318 {
319         {N_("/M_ove..."),               NULL, summary_move_to,  0, NULL},
320         {N_("/_Copy..."),               NULL, summary_copy_to,  0, NULL},
321         {N_("/_Delete"),                NULL, summary_delete,   0, NULL},
322         {N_("/E_xecute"),               NULL, summary_execute,  0, NULL},
323         {N_("/_Mark"),                  NULL, NULL,             0, "<Branch>"},
324         {N_("/_Mark/_Mark"),            NULL, summary_mark,     0, NULL},
325         {N_("/_Mark/_Unmark"),          NULL, summary_unmark,   0, NULL},
326         {N_("/_Mark/---"),              NULL, NULL,             0, "<Separator>"},
327         {N_("/_Mark/Mark as unr_ead"),  NULL, summary_mark_as_unread, 0, NULL},
328         {N_("/_Mark/Mark as rea_d"),    NULL, summary_mark_as_read, 0, NULL},
329         {N_("/_Mark/Ignore thread"),    NULL, summary_ignore_thread, 0, NULL},
330         {N_("/_Mark/Unignore thread"),  NULL, summary_unignore_thread, 0, NULL},
331
332         {N_("/_Label"),                 NULL, NULL, 0, "<Branch>"},
333         {N_("/_Label/None"),            NULL, summary_set_label, MSG_LABEL_NONE, NULL},
334         {N_("/_Label/---"),             NULL, NULL,             0, "<Separator>"},
335         {N_("/_Label/Orange"),          NULL, summary_set_label, MSG_LABEL_ORANGE, NULL},
336         {N_("/_Label/Red"),             NULL, summary_set_label, MSG_LABEL_RED, NULL},
337         {N_("/_Label/Pink"),            NULL, summary_set_label, MSG_LABEL_PINK, NULL},
338         {N_("/_Label/SkyBlue"),         NULL, summary_set_label, MSG_LABEL_SKYBLUE, NULL},
339         {N_("/_Label/Blue"),            NULL, summary_set_label, MSG_LABEL_BLUE, NULL},
340         {N_("/_Label/Green"),           NULL, summary_set_label, MSG_LABEL_GREEN, NULL},
341         {N_("/_Label/Brown"),           NULL, summary_set_label, MSG_LABEL_BROWN, NULL},
342
343         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
344         {N_("/_Reply"),                 NULL, summary_reply_cb, COMPOSE_REPLY, NULL},
345         {N_("/Repl_y to sender"),       NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
346         {N_("/Follow-up and reply to"), NULL, summary_reply_cb, COMPOSE_FOLLOWUP_AND_REPLY_TO, NULL},
347         {N_("/Reply to a_ll"),          NULL, summary_reply_cb, COMPOSE_REPLY_TO_ALL, NULL},
348         {N_("/_Forward"),               NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
349         {N_("/Forward as a_ttachment"),
350                                         NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
351         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
352         {N_("/Open in new _window"),    NULL, summary_open_msg, 0, NULL},
353         {N_("/View so_urce"),           NULL, summary_view_source, 0, NULL},
354         {N_("/Show all _header"),       NULL, summary_show_all_header_cb, 0, NULL},
355         {N_("/Re-_edit"),               NULL, summary_reedit,   0, NULL},
356         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
357         {N_("/_Save as..."),            NULL, summary_save_as,  0, NULL},
358         {N_("/_Print..."),              NULL, summary_print,    0, NULL},
359         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
360         {N_("/Select _all"),            NULL, summary_select_all, 0, NULL}
361 };
362
363 void summary_set_label_color(GtkCTree *ctree, GtkCTreeNode *node,
364                              guint labelcolor)
365 {
366   GdkColor color;
367   GtkStyle *style, *prev_style, *ctree_style;
368   MsgInfo *msginfo;
369   
370   ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
371   
372   prev_style = gtk_ctree_node_get_row_style(ctree, node);
373   
374   if (!prev_style)
375     prev_style = ctree_style;
376   
377   style = gtk_style_copy(prev_style);
378   
379   switch (labelcolor) {
380   case MSG_LABEL_ORANGE:
381     color.red = 0xffff;
382     color.green = (0x99<<8);
383     color.blue = 0x0;
384     break;
385   case MSG_LABEL_RED:
386     color.red = 0xffff;
387     color.green = color.blue = 0x0;
388     break;
389   case MSG_LABEL_PINK:
390     color.red = 0xffff;
391     color.green = (0x66<<8);
392     color.blue = 0xffff;
393     break;
394   case MSG_LABEL_SKYBLUE:
395     color.red = 0x0;
396     color.green = (0xcc<<8);
397     color.blue = 0xffff;
398     break;
399   case MSG_LABEL_BLUE:
400     color.red = 0x0;
401     color.green = 0x0;
402     color.blue = 0xffff;
403     break;
404   case MSG_LABEL_GREEN:
405     color.red = 0x0;
406     color.green = (0x99<<8);
407     color.blue = 0x0;
408     break;
409   case MSG_LABEL_BROWN:
410     color.red = (0x66<<8);
411     color.green = (0x33<<8);
412     color.blue = (0x33<<8);
413     break;
414   case MSG_LABEL_NONE:
415   default:
416     labelcolor = MSG_LABEL_NONE;
417     color.red = ctree_style->fg[GTK_STATE_NORMAL].red;
418     color.green = ctree_style->fg[GTK_STATE_NORMAL].green;
419     color.blue = ctree_style->fg[GTK_STATE_NORMAL].blue;
420     style->fg[GTK_STATE_NORMAL] = color;
421
422     color.red = ctree_style->fg[GTK_STATE_SELECTED].red;
423     color.green = ctree_style->fg[GTK_STATE_SELECTED].green;
424     color.blue = ctree_style->fg[GTK_STATE_SELECTED].blue;
425     style->fg[GTK_STATE_SELECTED] = color;
426     gtk_ctree_node_set_row_style(ctree, node, style);
427     break;
428   }
429
430   msginfo = gtk_ctree_node_get_row_data(ctree, node);
431
432   MSG_UNSET_FLAGS(msginfo->flags, MSG_LABEL);
433   MSG_SET_FLAGS(msginfo->flags, labelcolor);
434
435   if ( style ) {
436     style->fg[GTK_STATE_NORMAL] = color;
437     style->fg[GTK_STATE_SELECTED] = color;
438     gtk_ctree_node_set_row_style(ctree, node, style);
439   }
440 }
441
442 void summary_set_label(SummaryView *summaryview, guint labelcolor, GtkWidget *widget)
443 {
444   GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
445   GtkCList *clist = GTK_CLIST(summaryview->ctree);
446   GList *cur;
447   
448   for (cur = clist->selection; cur != NULL; cur = cur->next)
449     summary_set_label_color(ctree, GTK_CTREE_NODE(cur->data), labelcolor);
450 }
451
452 SummaryView *summary_create(void)
453 {
454         SummaryView *summaryview;
455         gchar *titles[N_SUMMARY_COLS] = {_("M"), _("U")};
456         GtkWidget *vbox;
457         GtkWidget *scrolledwin;
458         GtkWidget *ctree;
459         GtkWidget *hbox;
460         GtkWidget *statlabel_folder;
461         GtkWidget *statlabel_select;
462         GtkWidget *statlabel_msgs;
463         GtkWidget *toggle_eventbox;
464         GtkWidget *toggle_arrow;
465         GtkWidget *popupmenu;
466         GtkItemFactory *popupfactory;
467         gint n_entries;
468         gint i;
469
470         debug_print(_("Creating summary view...\n"));
471         summaryview = g_new0(SummaryView, 1);
472
473         vbox = gtk_vbox_new(FALSE, 2);
474
475         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
476         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
477                                        GTK_POLICY_AUTOMATIC,
478                                        GTK_POLICY_ALWAYS);
479         gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
480         gtk_widget_set_usize(vbox,
481                              prefs_common.summaryview_width,
482                              prefs_common.summaryview_height);
483
484         if (prefs_common.trans_hdr) {
485                 titles[S_COL_NUMBER]  = _("No.");
486                 titles[S_COL_DATE]    = _("Date");
487                 titles[S_COL_FROM]    = _("From");
488                 titles[S_COL_SUBJECT] = _("Subject");
489         } else {
490                 titles[S_COL_NUMBER]  = "No.";
491                 titles[S_COL_DATE]    = "Date";
492                 titles[S_COL_FROM]    = "From";
493                 titles[S_COL_SUBJECT] = "Subject";
494         }
495         titles[S_COL_SIZE]  = _("Size");
496         titles[S_COL_SCORE] = _("Score");
497
498         ctree = gtk_sctree_new_with_titles(N_SUMMARY_COLS, S_COL_SUBJECT, titles);
499         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
500                                             GTK_CLIST(ctree)->hadjustment);
501         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
502                                             GTK_CLIST(ctree)->vadjustment);
503         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
504         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_EXTENDED);
505         gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_MARK,
506                                            GTK_JUSTIFY_CENTER);
507         gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_UNREAD,
508                                            GTK_JUSTIFY_CENTER);
509         gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_MIME,
510                                            GTK_JUSTIFY_CENTER);
511         gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_NUMBER,
512                                            GTK_JUSTIFY_RIGHT);
513         gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_SCORE,
514                                            GTK_JUSTIFY_RIGHT);
515         gtk_clist_set_column_justification(GTK_CLIST(ctree), S_COL_SIZE,
516                                            GTK_JUSTIFY_RIGHT);
517         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_MARK,
518                                    SUMMARY_COL_MARK_WIDTH);
519         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_UNREAD,
520                                    SUMMARY_COL_UNREAD_WIDTH);
521         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_MIME,
522                                    SUMMARY_COL_MIME_WIDTH);
523         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_NUMBER,
524                                    prefs_common.summary_col_number);
525         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SCORE,
526                                    prefs_common.summary_col_score);
527         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SIZE,
528                                    prefs_common.summary_col_size);
529         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_DATE,
530                                    prefs_common.summary_col_date);
531         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_FROM,
532                                    prefs_common.summary_col_from);
533         gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SUBJECT,
534                                    prefs_common.summary_col_subject);
535         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
536         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
537                                      GTK_CTREE_EXPANDER_SQUARE);
538 #if 0
539         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
540         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
541                                      GTK_CTREE_EXPANDER_TRIANGLE);
542 #endif
543         gtk_ctree_set_indent(GTK_CTREE(ctree), 18);
544         gtk_object_set_user_data(GTK_OBJECT(ctree), summaryview);
545
546         /* don't let title buttons take key focus */
547         for (i = 0; i < N_SUMMARY_COLS; i++)
548                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
549                                        GTK_CAN_FOCUS);
550
551         /* connect signal to the buttons for sorting */
552         gtk_signal_connect
553                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_NUMBER].button),
554                  "clicked",
555                  GTK_SIGNAL_FUNC(summary_num_clicked),
556                  summaryview);
557         gtk_signal_connect
558                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_SCORE].button),
559                  "clicked",
560                  GTK_SIGNAL_FUNC(summary_score_clicked),
561                  summaryview);
562         gtk_signal_connect
563                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_SIZE].button),
564                  "clicked",
565                  GTK_SIGNAL_FUNC(summary_size_clicked),
566                  summaryview);
567         gtk_signal_connect
568                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_DATE].button),
569                  "clicked",
570                  GTK_SIGNAL_FUNC(summary_date_clicked),
571                  summaryview);
572         gtk_signal_connect
573                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_FROM].button),
574                  "clicked",
575                  GTK_SIGNAL_FUNC(summary_from_clicked),
576                  summaryview);
577         gtk_signal_connect
578                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_SUBJECT].button),
579                  "clicked",
580                  GTK_SIGNAL_FUNC(summary_subject_clicked),
581                  summaryview);
582         gtk_signal_connect
583                 (GTK_OBJECT(GTK_CLIST(ctree)->column[S_COL_MARK].button),
584                  "clicked",
585                  GTK_SIGNAL_FUNC(summary_mark_clicked),
586                  summaryview);
587
588         /* create status label */
589         hbox = gtk_hbox_new(FALSE, 0);
590         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
591
592         statlabel_folder = gtk_label_new("");
593         gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2);
594         statlabel_select = gtk_label_new("");
595         gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 16);
596
597         /* toggle view button */
598         toggle_eventbox = gtk_event_box_new();
599         gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
600         toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
601         gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
602
603         statlabel_msgs = gtk_label_new("");
604         gtk_box_pack_end(GTK_BOX(hbox), statlabel_msgs, FALSE, FALSE, 4);
605
606         /* create popup menu */
607         n_entries = sizeof(summary_popup_entries) /
608                 sizeof(summary_popup_entries[0]);
609         popupmenu = menu_create_items(summary_popup_entries, n_entries,
610                                       "<SummaryView>", &popupfactory,
611                                       summaryview);
612
613         /* connect signals */
614         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
615                            GTK_SIGNAL_FUNC(summary_selected), summaryview);
616         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
617                            GTK_SIGNAL_FUNC(summary_button_pressed),
618                            summaryview);
619         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
620                            GTK_SIGNAL_FUNC(summary_button_released),
621                            summaryview);
622         gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
623                            GTK_SIGNAL_FUNC(summary_key_pressed), summaryview);
624         gtk_signal_connect(GTK_OBJECT(ctree), "resize_column",
625                            GTK_SIGNAL_FUNC(summary_col_resized), summaryview);
626         gtk_signal_connect(GTK_OBJECT(ctree), "open_row",
627                            GTK_SIGNAL_FUNC(summary_open_row), summaryview);
628         gtk_signal_connect(GTK_OBJECT(toggle_eventbox), "button_press_event",
629                            GTK_SIGNAL_FUNC(summary_toggle_pressed),
630                            summaryview);
631
632         summaryview->vbox = vbox;
633         summaryview->scrolledwin = scrolledwin;
634         summaryview->ctree = ctree;
635         summaryview->hbox = hbox;
636         summaryview->statlabel_folder = statlabel_folder;
637         summaryview->statlabel_select = statlabel_select;
638         summaryview->statlabel_msgs = statlabel_msgs;
639         summaryview->toggle_eventbox = toggle_eventbox;
640         summaryview->toggle_arrow = toggle_arrow;
641         summaryview->popupmenu = popupmenu;
642         summaryview->popupfactory = popupfactory;
643         summaryview->msg_is_toggled_on = TRUE;
644         summaryview->sort_mode = SORT_BY_NONE;
645         summaryview->sort_type = GTK_SORT_ASCENDING;
646
647         summary_change_display_item(summaryview);
648
649         gtk_widget_show_all(vbox);
650
651         return summaryview;
652 }
653
654 void summary_init(SummaryView *summaryview)
655 {
656         GtkStyle *style;
657         GtkWidget *pixmap;
658
659         PIXMAP_CREATE(summaryview->ctree, markxpm, markxpmmask, mark_xpm);
660         PIXMAP_CREATE(summaryview->ctree, deletedxpm, deletedxpmmask,
661                       deleted_xpm);
662         PIXMAP_CREATE(summaryview->ctree, newxpm, newxpmmask, new_xpm);
663         PIXMAP_CREATE(summaryview->ctree, unreadxpm, unreadxpmmask, unread_xpm);
664         PIXMAP_CREATE(summaryview->ctree, repliedxpm, repliedxpmmask,
665                       replied_xpm);
666         PIXMAP_CREATE(summaryview->ctree, forwardedxpm, forwardedxpmmask,
667                       forwarded_xpm);
668         PIXMAP_CREATE(summaryview->ctree, ignorethreadxpm, ignorethreadxpmmask,
669                       ignorethread_xpm);
670         PIXMAP_CREATE(summaryview->ctree, clipxpm, clipxpmmask, clip_xpm);
671         PIXMAP_CREATE(summaryview->hbox, folderxpm, folderxpmmask,
672                       DIRECTORY_OPEN_XPM);
673
674         pixmap = gtk_pixmap_new(clipxpm, clipxpmmask);
675         gtk_clist_set_column_widget(GTK_CLIST(summaryview->ctree),
676                                     S_COL_MIME, pixmap);
677         gtk_widget_show(pixmap);
678
679         if (!smallfont)
680                 smallfont = gdk_fontset_load(SMALL_FONT);
681
682         style = gtk_style_copy(gtk_widget_get_style
683                                 (summaryview->statlabel_folder));
684         if (smallfont) style->font = smallfont;
685         gtk_widget_set_style(summaryview->statlabel_folder, style);
686         gtk_widget_set_style(summaryview->statlabel_select, style);
687         gtk_widget_set_style(summaryview->statlabel_msgs, style);
688
689         pixmap = gtk_pixmap_new(folderxpm, folderxpmmask);
690         gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
691         gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
692         gtk_widget_show(pixmap);
693
694         summary_clear_list(summaryview);
695         summary_set_menu_sensitive(summaryview);
696 }
697
698 GtkCTreeNode * summary_find_next_important_score(SummaryView *summaryview,
699                                                  GtkCTreeNode *current_node)
700 {
701         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
702         GtkCTreeNode *node;
703         MsgInfo *msginfo;
704         gint best_score = MIN_SCORE;
705         GtkCTreeNode *best_node = NULL;
706
707         if (current_node)
708                 /*node = current_node;*/
709                 node = GTK_CTREE_NODE_NEXT(current_node);
710         else
711                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
712
713         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
714                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
715                 if (msginfo->score >= summaryview->important_score)
716                         break;
717                 if (msginfo->score > best_score) {
718                         best_score = msginfo->score;
719                         best_node = node;
720                 }
721         }
722
723         if (node != NULL)
724                 return node;
725         else
726                 return best_node;
727 }
728
729 GtkCTreeNode * summary_find_prev_important_score(SummaryView *summaryview,
730                                                  GtkCTreeNode *current_node)
731 {
732         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
733         GtkCTreeNode *node;
734         MsgInfo *msginfo;
735         gint best_score = MIN_SCORE;
736         GtkCTreeNode *best_node = NULL;
737
738         if (current_node)
739                 /*node = current_node;*/
740                 node = GTK_CTREE_NODE_PREV(current_node);
741         else
742                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
743
744         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
745                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
746                 if (msginfo->score >= summaryview->important_score)
747                         break;
748                 if (msginfo->score > best_score) {
749                         best_score = msginfo->score;
750                         best_node = node;
751                 }
752         }
753
754         if (node != NULL)
755                 return node;
756         else
757                 return best_node;
758 }
759
760 gboolean summary_show(SummaryView *summaryview, FolderItem *item,
761                       gboolean update_cache)
762 {
763         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
764         GtkCTreeNode *node;
765         GSList *mlist = NULL;
766         gchar *buf;
767         gboolean is_refresh;
768         guint prev_msgnum = 0;
769         GtkCTreeNode *selected_node = summaryview->folderview->selected;
770         GSList *cur;
771         gint sort_mode;
772         gint sort_type;
773
774         STATUSBAR_POP(summaryview->mainwin);
775
776         is_refresh = (!prefs_common.open_inbox_on_inc &&
777                       item == summaryview->folder_item) ? TRUE : FALSE;
778         if (is_refresh) {
779                 prev_msgnum = summary_get_current_msgnum(summaryview);
780                 if (prev_msgnum < 1)
781                         is_refresh = FALSE;
782         }
783
784         /* process the marks if any */
785         if (summaryview->moved > 0 || summaryview->copied > 0) {
786                 AlertValue val;
787
788                 val = alertpanel(_("Process mark"),
789                                  _("Some marks are left. Process it?"),
790                                  _("Yes"), _("No"), _("Cancel"));
791                 if (G_ALERTDEFAULT == val)
792                         summary_execute(summaryview);
793                 else if (G_ALERTALTERNATE == val)
794                         summary_write_cache(summaryview);
795                 else
796                         return FALSE;
797         } else
798                 summary_write_cache(summaryview);
799
800         summaryview->folderview->opened = selected_node;
801
802         gtk_clist_freeze(GTK_CLIST(ctree));
803
804         summary_clear_list(summaryview);
805         summary_set_menu_sensitive(summaryview);
806         messageview_clear(summaryview->messageview);
807
808         buf = NULL;
809         if (!item || !item->path || !item->parent || item->no_select ||
810             (item->folder->type == F_MH &&
811              ((buf = folder_item_get_path(item)) == NULL ||
812               change_dir(buf) < 0))) {
813                 g_free(buf);
814                 debug_print(_("empty folder\n\n"));
815                 summary_clear_all(summaryview);
816                 summaryview->folder_item = item;
817                 gtk_clist_thaw(GTK_CLIST(ctree));
818                 return TRUE;
819         }
820         g_free(buf);
821
822         summaryview->folder_item = item;
823
824         gtk_signal_disconnect_by_data(GTK_OBJECT(ctree), summaryview);
825
826         buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
827         debug_print("%s\n", buf);
828         STATUSBAR_PUSH(summaryview->mainwin, buf);
829         g_free(buf);
830
831         main_window_cursor_wait(summaryview->mainwin);
832
833         mlist = item->folder->get_msg_list(item->folder, item, !update_cache);
834
835         for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
836                 MsgInfo * msginfo = (MsgInfo *) cur->data;
837
838                 msginfo->score = score_message(global_scoring, msginfo);
839                 if (msginfo->score != MAX_SCORE &&
840                     msginfo->score != MIN_SCORE) {
841                         msginfo->score += score_message(item->prefs->scoring,
842                                                         msginfo);
843                 }
844         }
845
846         summaryview->killed_messages = NULL;
847         if ((global_scoring || item->prefs->scoring) &&
848             (item->folder->type == F_NEWS)) {
849                 GSList *not_killed;
850                 gint kill_score;
851
852                 not_killed = NULL;
853                 kill_score = prefs_common.kill_score;
854                 if (item->prefs->kill_score > kill_score)
855                         kill_score = item->prefs->kill_score;
856                 for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
857                         MsgInfo * msginfo = (MsgInfo *) cur->data;
858
859                         if (MSG_IS_NEWS(msginfo->flags) &&
860                             (msginfo->score <= kill_score))
861                                 summaryview->killed_messages = g_slist_append(summaryview->killed_messages, msginfo);
862                         else
863                                 not_killed = g_slist_append(not_killed,
864                                                             msginfo);
865                 }
866                 g_slist_free(mlist);
867                 mlist = not_killed;
868         }
869
870         STATUSBAR_POP(summaryview->mainwin);
871
872         /* set ctree and hash table from the msginfo list
873            creating thread, and count the number of messages */
874         summary_set_ctree_from_list(summaryview, mlist);
875
876         g_slist_free(mlist);
877
878         summary_write_cache(summaryview);
879
880         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
881                            GTK_SIGNAL_FUNC(summary_selected), summaryview);
882         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
883                            GTK_SIGNAL_FUNC(summary_button_pressed),
884                            summaryview);
885         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
886                            GTK_SIGNAL_FUNC(summary_button_released),
887                            summaryview);
888         gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
889                            GTK_SIGNAL_FUNC(summary_key_pressed), summaryview);
890         gtk_signal_connect(GTK_OBJECT(ctree), "resize_column",
891                            GTK_SIGNAL_FUNC(summary_col_resized), summaryview);
892         gtk_signal_connect(GTK_OBJECT(ctree), "open_row",
893                            GTK_SIGNAL_FUNC(summary_open_row), summaryview);
894
895         /*connect drag and drop signal*/
896         gtk_signal_connect(GTK_OBJECT (ctree),"start_drag",
897                            GTK_SIGNAL_FUNC (summary_start_drag),
898                            summaryview);
899         gtk_signal_connect(GTK_OBJECT (ctree),"drag_data_get",
900                            GTK_SIGNAL_FUNC (summary_drag_data_get),
901                            summaryview);
902
903         gtk_clist_thaw(GTK_CLIST(ctree));
904
905         /* sort before */
906         sort_mode = prefs_folder_item_get_sort_mode(item);
907         sort_type = prefs_folder_item_get_sort_type(item);
908
909         if (sort_mode != SORT_BY_NONE) {
910                 summaryview->sort_mode = sort_mode;
911                 if (sort_type == GTK_SORT_DESCENDING)
912                         summaryview->sort_type = GTK_SORT_ASCENDING;
913                 else
914                         summaryview->sort_type = GTK_SORT_DESCENDING;
915
916                 summary_sort(summaryview, sort_mode);
917         }
918
919         if (is_refresh) {
920                 summary_select_by_msgnum(summaryview, prev_msgnum);
921         } else {
922                 /* select first unread message */
923                 if (sort_mode == SORT_BY_SCORE)
924                         node = summary_find_next_important_score(summaryview,
925                                                                  NULL);
926                 else
927                         node = summary_find_next_unread_msg(summaryview, NULL);
928
929                 if (node == NULL && GTK_CLIST(ctree)->row_list != NULL)
930                         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
931                 if (node) {
932                         GTK_EVENTS_FLUSH();
933                         gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
934                         gtk_widget_grab_focus(GTK_WIDGET(ctree));
935                         if (prefs_common.open_unread_on_enter)
936                                 summaryview->display_msg = TRUE;
937                         gtk_sctree_select(GTK_SCTREE(ctree), node);
938                 }
939         }
940
941         summary_status_show(summaryview);
942
943         summary_set_menu_sensitive(summaryview);
944
945         main_window_set_toolbar_sensitive
946                 (summaryview->mainwin, summaryview->selected ? TRUE : FALSE);
947
948         debug_print("\n");
949         STATUSBAR_PUSH(summaryview->mainwin, _("done."));
950
951         main_window_cursor_normal(summaryview->mainwin);
952
953         return TRUE;
954 }
955
956 void summary_clear_list(SummaryView *summaryview)
957 {
958         GtkCList *clist = GTK_CLIST(summaryview->ctree);
959         gint optimal_width;
960         GSList * cur;
961
962         gtk_clist_freeze(clist);
963
964         for(cur = summaryview->killed_messages ; cur != NULL ; 
965             cur = g_slist_next(cur)) {
966                 MsgInfo * msginfo = (MsgInfo *) cur->data;
967
968                 procmsg_msginfo_free(msginfo);
969         }
970         g_slist_free(summaryview->killed_messages);
971         summaryview->killed_messages = NULL;
972
973         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree),
974                                 NULL, summary_free_msginfo_func, NULL);
975
976         summaryview->folder_item = NULL;
977
978         summaryview->display_msg = FALSE;
979
980         summaryview->selected  = NULL;
981         summaryview->displayed = NULL;
982         summaryview->newmsgs   = summaryview->unread     = 0;
983         summaryview->messages  = summaryview->total_size = 0;
984         summaryview->deleted   = summaryview->moved      = 0;
985         summaryview->copied    = 0;
986         if (summaryview->msgid_table) {
987                 g_hash_table_destroy(summaryview->msgid_table);
988                 summaryview->msgid_table = NULL;
989         }
990         if (summaryview->subject_table) {
991                 g_hash_table_destroy(summaryview->subject_table);
992                 summaryview->subject_table = NULL;
993         }
994         summaryview->mlist = NULL;
995         if (summaryview->folder_table) {
996                 g_hash_table_destroy(summaryview->folder_table);
997                 summaryview->folder_table = NULL;
998         }
999         summaryview->sort_mode = SORT_BY_NONE;
1000         summaryview->sort_type = GTK_SORT_ASCENDING;
1001
1002         gtk_clist_clear(clist);
1003         optimal_width = gtk_clist_optimal_column_width(clist, S_COL_SUBJECT);
1004         gtk_clist_set_column_width(clist, S_COL_SUBJECT, optimal_width);
1005
1006         gtk_clist_thaw(clist);
1007 }
1008
1009 void summary_clear_all(SummaryView *summaryview)
1010 {
1011         summary_clear_list(summaryview);
1012         summary_set_menu_sensitive(summaryview);
1013         main_window_set_toolbar_sensitive(summaryview->mainwin, FALSE);
1014         summary_status_show(summaryview);
1015 }
1016
1017 static void summary_set_menu_sensitive(SummaryView *summaryview)
1018 {
1019         GtkItemFactory *ifactory = summaryview->popupfactory;
1020         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1021         gboolean sens;
1022         SummarySelection selection;
1023
1024         if (!clist->row_list)
1025                 selection = SUMMARY_NONE;
1026         else if (!clist->selection)
1027                 selection = SUMMARY_SELECTED_NONE;
1028         else if (!clist->selection->next)
1029                 selection = SUMMARY_SELECTED_SINGLE;
1030         else
1031                 selection = SUMMARY_SELECTED_MULTIPLE;
1032
1033         main_window_set_menu_sensitive(summaryview->mainwin, selection);
1034
1035         if (selection == SUMMARY_NONE) {
1036                 GtkWidget *submenu;
1037
1038                 submenu = gtk_item_factory_get_widget
1039                         (summaryview->popupfactory, "/Mark");
1040                 menu_set_insensitive_all(GTK_MENU_SHELL(submenu));
1041                 menu_set_insensitive_all
1042                         (GTK_MENU_SHELL(summaryview->popupmenu));
1043                 return;
1044         }
1045
1046         if (summaryview->folder_item->folder->type != F_NEWS) {
1047                 if (summaryview->folder_item->stype != F_TRASH)
1048                         menu_set_sensitive(ifactory, "/Delete", TRUE);
1049                 menu_set_sensitive(ifactory, "/Move...", TRUE);
1050                 menu_set_sensitive(ifactory, "/Copy...", TRUE);
1051         }
1052         menu_set_sensitive(ifactory, "/Execute", TRUE);
1053
1054         sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1055         menu_set_sensitive(ifactory, "/Reply",                    sens);
1056         menu_set_sensitive(ifactory, "/Reply to sender",          sens);
1057         menu_set_sensitive(ifactory, "/Reply to all",             sens);
1058         menu_set_sensitive(ifactory, "/Forward",                  TRUE);
1059         menu_set_sensitive(ifactory, "/Forward as attachment",    TRUE);
1060         
1061         menu_set_sensitive(ifactory, "/Open in new window", sens);
1062         menu_set_sensitive(ifactory, "/View source", sens);
1063         menu_set_sensitive(ifactory, "/Show all header", sens);
1064         if ((summaryview->folder_item->stype == F_DRAFT) ||
1065             (summaryview->folder_item->stype == F_OUTBOX) ||
1066             (summaryview->folder_item->stype == F_QUEUE))
1067                 menu_set_sensitive(ifactory, "/Re-edit", sens);
1068
1069         menu_set_sensitive(ifactory, "/Save as...", sens);
1070         menu_set_sensitive(ifactory, "/Print...",   TRUE);
1071
1072         menu_set_sensitive(ifactory, "/Mark", TRUE);
1073
1074         menu_set_sensitive(ifactory, "/Mark/Mark",   TRUE);
1075         menu_set_sensitive(ifactory, "/Mark/Unmark", TRUE);
1076
1077         menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
1078         menu_set_sensitive(ifactory, "/Mark/Mark as read",   TRUE);
1079         menu_set_sensitive(ifactory, "/Mark/Ignore thread",   TRUE);
1080         menu_set_sensitive(ifactory, "/Mark/Unignore thread", TRUE);
1081
1082         menu_set_sensitive(ifactory, "/Label", TRUE);
1083
1084         menu_set_sensitive(ifactory, "/Select all", TRUE);
1085
1086         if (summaryview->folder_item->folder->account)
1087                 sens = summaryview->folder_item->folder->account->protocol
1088                         == A_NNTP;
1089         else
1090                 sens = FALSE;
1091         menu_set_sensitive(ifactory, "/Follow-up and reply to", sens);
1092 }
1093
1094 void summary_select_next_unread(SummaryView *summaryview)
1095 {
1096         GtkCTreeNode *node;
1097         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1098
1099         node = summary_find_next_unread_msg(summaryview,
1100                                             summaryview->selected);
1101
1102         if (node) {
1103                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1104                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1105
1106                 /* BUGFIX: select next unread message 
1107                  * REVERT: causes incorrect folder stats
1108                  * gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0.0); 
1109                  */
1110
1111                 if (summaryview->displayed == node)
1112                         summaryview->displayed = NULL;
1113                 summary_display_msg(summaryview, node, FALSE);
1114         } else {
1115                 AlertValue val;
1116
1117                 val = alertpanel(_("No unread message"),
1118                                  _("No unread message found. Go to next folder?"),
1119                                  _("Yes"), _("No"), NULL);
1120                 if (val == G_ALERTDEFAULT) {
1121                         if (gtk_signal_n_emissions_by_name
1122                                 (GTK_OBJECT(ctree), "key_press_event") > 0)
1123                                         gtk_signal_emit_stop_by_name
1124                                                 (GTK_OBJECT(ctree),
1125                                                  "key_press_event");
1126                         folderview_select_next_unread(summaryview->folderview);
1127                 }
1128         }
1129 }
1130
1131 void summary_select_next_marked(SummaryView *summaryview)
1132 {
1133         GtkCTreeNode *node;
1134         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1135
1136         node = summary_find_next_marked_msg(summaryview,
1137                                             summaryview->selected);
1138
1139         if (!node) {
1140                 AlertValue val;
1141
1142                 val = alertpanel(_("No more marked messages"),
1143                                  _("No marked message found. "
1144                                    "Search from the beginning?"),
1145                                  _("Yes"), _("No"), NULL);
1146                 if (val != G_ALERTDEFAULT) return;
1147                 node = summary_find_next_marked_msg(summaryview,
1148                                                     NULL);
1149         }
1150         if (!node) {
1151                 alertpanel_notice(_("No marked messages."));
1152         } else {
1153                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1154                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1155                 if (summaryview->displayed == node)
1156                         summaryview->displayed = NULL;
1157                 summary_display_msg(summaryview, node, FALSE);
1158         }
1159 }
1160
1161 void summary_select_prev_marked(SummaryView *summaryview)
1162 {
1163         GtkCTreeNode *node;
1164         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1165
1166         node = summary_find_prev_marked_msg(summaryview,
1167                                             summaryview->selected);
1168
1169         if (!node) {
1170                 AlertValue val;
1171
1172                 val = alertpanel(_("No more marked messages"),
1173                                  _("No marked message found. "
1174                                    "Search from the end?"),
1175                                  _("Yes"), _("No"), NULL);
1176                 if (val != G_ALERTDEFAULT) return;
1177                 node = summary_find_prev_marked_msg(summaryview,
1178                                                     NULL);
1179         }
1180         if (!node) {
1181                 alertpanel_notice(_("No marked messages."));
1182         } else {
1183                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1184                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1185                 if (summaryview->displayed == node)
1186                         summaryview->displayed = NULL;
1187                 summary_display_msg(summaryview, node, FALSE);
1188         }
1189 }
1190
1191 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1192 {
1193         GtkCTreeNode *node;
1194         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1195
1196         node = summary_find_msg_by_msgnum(summaryview, msgnum);
1197
1198         if (node) {
1199                 GTK_EVENTS_FLUSH();
1200                 gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
1201                 gtk_widget_grab_focus(GTK_WIDGET(ctree));
1202                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1203                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1204                 if (summaryview->msg_is_toggled_on) {
1205                         if (summaryview->displayed == node)
1206                                 summaryview->displayed = NULL;
1207                         summary_display_msg(summaryview, node, FALSE);
1208                 }
1209         }
1210 }
1211
1212 guint summary_get_current_msgnum(SummaryView *summaryview)
1213 {
1214         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1215         MsgInfo *msginfo;
1216
1217         if (!summaryview->selected)
1218                 return 0;
1219         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
1220         return msginfo->msgnum;
1221 }
1222
1223 static GtkCTreeNode *summary_find_next_unread_msg(SummaryView *summaryview,
1224                                                   GtkCTreeNode *current_node)
1225 {
1226         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1227         GtkCTreeNode *node;
1228         MsgInfo *msginfo;
1229
1230         if (current_node)
1231                 node = current_node;
1232                 /*node = GTK_CTREE_NODE_NEXT(current_node);*/
1233         else
1234                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1235
1236         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1237                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1238                 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) break;
1239         }
1240
1241         return node;
1242 }
1243
1244 static GtkCTreeNode *summary_find_next_marked_msg(SummaryView *summaryview,
1245                                                   GtkCTreeNode *current_node)
1246 {
1247         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1248         GtkCTreeNode *node;
1249         MsgInfo *msginfo;
1250
1251         if (current_node)
1252                 node = GTK_CTREE_NODE_NEXT(current_node);
1253         else
1254                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1255
1256         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1257                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1258                 if (MSG_IS_MARKED(msginfo->flags)) break;
1259         }
1260
1261         return node;
1262 }
1263
1264 static GtkCTreeNode *summary_find_prev_marked_msg(SummaryView *summaryview,
1265                                                   GtkCTreeNode *current_node)
1266 {
1267         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1268         GtkCTreeNode *node;
1269         MsgInfo *msginfo;
1270
1271         if (current_node)
1272                 node = GTK_CTREE_NODE_PREV(current_node);
1273         else
1274                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
1275
1276         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1277                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1278                 if (MSG_IS_MARKED(msginfo->flags)) break;
1279         }
1280
1281         return node;
1282 }
1283
1284 static GtkCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
1285                                                 guint msgnum)
1286 {
1287         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1288         GtkCTreeNode *node;
1289         MsgInfo *msginfo;
1290
1291         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1292
1293         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1294                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1295                 if (msginfo->msgnum == msgnum) break;
1296         }
1297
1298         return node;
1299 }
1300
1301 #if 0
1302 static GtkCTreeNode *summary_find_prev_unread_msg(SummaryView *summaryview,
1303                                                   GtkCTreeNode *current_node)
1304 {
1305         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1306         GtkCTreeNode *node;
1307         MsgInfo *msginfo;
1308
1309         if (current_node)
1310                 node = current_node;
1311                 /*node = GTK_CTREE_NODE_PREV(current_node);*/
1312         else
1313                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
1314
1315         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1316                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1317                 if (MSG_IS_UNREAD(msginfo->flags)) break;
1318         }
1319
1320         return node;
1321 }
1322 #endif
1323
1324 static guint attract_hash_func(gconstpointer key)
1325 {
1326         gchar *str;
1327         gchar *p;
1328         guint h;
1329
1330         Xstrdup_a(str, (const gchar *)key, return 0);
1331         trim_subject(str);
1332
1333         p = str;
1334         h = *p;
1335
1336         if (h) {
1337                 for (p += 1; *p != '\0'; p++)
1338                         h = (h << 5) - h + *p;
1339         }
1340
1341         return h;
1342 }
1343
1344 static gint attract_compare_func(gconstpointer a, gconstpointer b)
1345 {
1346         return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1347 }
1348
1349 void summary_attract_by_subject(SummaryView *summaryview)
1350 {
1351         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1352         GtkCList *clist = GTK_CLIST(ctree);
1353         GtkCTreeNode *src_node;
1354         GtkCTreeNode *dst_node, *sibling;
1355         GtkCTreeNode *tmp;
1356         MsgInfo *src_msginfo, *dst_msginfo;
1357         GHashTable *subject_table;
1358
1359         debug_print(_("Attracting messages by subject..."));
1360         STATUSBAR_PUSH(summaryview->mainwin,
1361                        _("Attracting messages by subject..."));
1362
1363         main_window_cursor_wait(summaryview->mainwin);
1364         gtk_clist_freeze(clist);
1365
1366         subject_table = g_hash_table_new(attract_hash_func,
1367                                          attract_compare_func);
1368
1369         for (src_node = GTK_CTREE_NODE(clist->row_list);
1370              src_node != NULL;
1371              src_node = tmp) {
1372                 tmp = GTK_CTREE_ROW(src_node)->sibling;
1373                 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
1374                 if (!src_msginfo) continue;
1375                 if (!src_msginfo->subject) continue;
1376
1377                 /* find attracting node */
1378                 dst_node = g_hash_table_lookup(subject_table,
1379                                                src_msginfo->subject);
1380
1381                 if (dst_node) {
1382                         dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
1383
1384                         /* if the time difference is more than 20 days,
1385                            don't attract */
1386                         if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
1387                             > 60 * 60 * 24 * 20)
1388                                 continue;
1389
1390                         sibling = GTK_CTREE_ROW(dst_node)->sibling;
1391                         if (src_node != sibling)
1392                                 gtk_ctree_move(ctree, src_node, NULL, sibling);
1393                 }
1394
1395                 g_hash_table_insert(subject_table,
1396                                     src_msginfo->subject, src_node);
1397         }
1398
1399         g_hash_table_destroy(subject_table);
1400
1401         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1402
1403         gtk_clist_thaw(clist);
1404
1405         debug_print(_("done.\n"));
1406         STATUSBAR_POP(summaryview->mainwin);
1407
1408         main_window_cursor_normal(summaryview->mainwin);
1409 }
1410
1411 static void summary_free_msginfo_func(GtkCTree *ctree, GtkCTreeNode *node,
1412                                       gpointer data)
1413 {
1414         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1415
1416         if (msginfo)
1417                 procmsg_msginfo_free(msginfo);
1418 }
1419
1420 static void summary_set_marks_func(GtkCTree *ctree, GtkCTreeNode *node,
1421                                    gpointer data)
1422 {
1423         SummaryView *summaryview = data;
1424         MsgInfo *msginfo;
1425
1426         msginfo = gtk_ctree_node_get_row_data(ctree, node);
1427
1428         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1429                 summaryview->newmsgs++;
1430         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1431                 summaryview->unread++;
1432         if (MSG_IS_DELETED(msginfo->flags))
1433                 summaryview->deleted++;
1434
1435         summaryview->messages++;
1436         summaryview->total_size += msginfo->size;
1437
1438         summary_set_row_marks(summaryview, node);
1439 }
1440
1441 static void summary_update_status(SummaryView *summaryview)
1442 {
1443         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1444         GtkCTreeNode *node;
1445         MsgInfo *msginfo;
1446
1447         summaryview->newmsgs = summaryview->unread =
1448         summaryview->messages = summaryview->total_size =
1449         summaryview->deleted = summaryview->moved = summaryview->copied = 0;
1450
1451         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1452              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1453                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1454
1455                 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1456                         summaryview->newmsgs++;
1457                 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1458                         summaryview->unread++;
1459                 if (MSG_IS_DELETED(msginfo->flags))
1460                         summaryview->deleted++;
1461                 if (MSG_IS_MOVE(msginfo->flags))
1462                         summaryview->moved++;
1463                 if (MSG_IS_COPY(msginfo->flags))
1464                         summaryview->copied++;
1465                 summaryview->messages++;
1466                 summaryview->total_size += msginfo->size;
1467         }
1468 }
1469
1470 static void summary_status_show(SummaryView *summaryview)
1471 {
1472         gchar *str;
1473         gchar *del, *mv, *cp;
1474         gchar *sel;
1475         gchar *spc;
1476         GList *rowlist, *cur;
1477         guint n_selected = 0;
1478         off_t sel_size = 0;
1479         MsgInfo *msginfo;
1480
1481         if (!summaryview->folder_item) {
1482                 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
1483                 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
1484                 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
1485                 return;
1486         }
1487
1488         rowlist = GTK_CLIST(summaryview->ctree)->selection;
1489         for (cur = rowlist; cur != NULL; cur = cur->next) {
1490                 msginfo = gtk_ctree_node_get_row_data
1491                         (GTK_CTREE(summaryview->ctree),
1492                          GTK_CTREE_NODE(cur->data));
1493                 sel_size += msginfo->size;
1494                 n_selected++;
1495         }
1496
1497         gtk_label_set(GTK_LABEL(summaryview->statlabel_folder),
1498                       summaryview->folder_item &&
1499                       summaryview->folder_item->folder->type == F_NEWS
1500                       ? g_basename(summaryview->folder_item->path)
1501                       : summaryview->folder_item->path);
1502
1503         if (summaryview->deleted)
1504                 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
1505         else
1506                 del = g_strdup("");
1507         if (summaryview->moved)
1508                 mv = g_strdup_printf(_("%s%d moved"),
1509                                      summaryview->deleted ? _(", ") : "",
1510                                      summaryview->moved);
1511         else
1512                 mv = g_strdup("");
1513         if (summaryview->copied)
1514                 cp = g_strdup_printf(_("%s%d copied"),
1515                                      summaryview->deleted ||
1516                                      summaryview->moved ? _(", ") : "",
1517                                      summaryview->copied);
1518         else
1519                 cp = g_strdup("");
1520
1521         if (summaryview->deleted || summaryview->moved || summaryview->copied)
1522                 spc = "    ";
1523         else
1524                 spc = "";
1525
1526         if (n_selected)
1527                 sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
1528         else
1529                 sel = g_strdup("");
1530         str = g_strconcat(n_selected ? itos(n_selected) : "",
1531                           n_selected ? _(" item(s) selected") : "",
1532                           sel, spc, del, mv, cp, NULL);
1533         gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str);
1534         g_free(str);
1535         g_free(sel);
1536         g_free(del);
1537         g_free(mv);
1538         g_free(cp);
1539
1540         if (summaryview->folder_item &&
1541             summaryview->folder_item->folder->type == F_MH) {
1542                 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
1543                                       summaryview->newmsgs,
1544                                       summaryview->unread,
1545                                       summaryview->messages,
1546                                       to_human_readable(summaryview->total_size));
1547         } else {
1548                 str = g_strdup_printf(_("%d new, %d unread, %d total"),
1549                                       summaryview->newmsgs,
1550                                       summaryview->unread,
1551                                       summaryview->messages);
1552         }
1553         gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str);
1554         g_free(str);
1555
1556         folderview_update_msg_num(summaryview->folderview,
1557                                   summaryview->folderview->opened,
1558                                   summaryview->newmsgs,
1559                                   summaryview->unread,
1560                                   summaryview->messages);
1561 }
1562
1563 void summary_sort(SummaryView *summaryview, SummarySortType type)
1564 {
1565         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1566         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1567         GtkCListCompareFunc cmp_func;
1568
1569         if (!summaryview->folder_item)
1570                 return;
1571
1572         switch (type) {
1573         case SORT_BY_NUMBER:
1574                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_num;
1575                 break;
1576         case SORT_BY_SIZE:
1577                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_size;
1578                 break;
1579         case SORT_BY_DATE:
1580                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_date;
1581                 break;
1582         case SORT_BY_FROM:
1583                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_from;
1584                 break;
1585         case SORT_BY_SUBJECT:
1586                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
1587                 break;
1588         case SORT_BY_SCORE:
1589                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_score;
1590                 break;
1591         case SORT_BY_LABEL:
1592                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_label;
1593                 break;
1594         default:
1595                 return;
1596         }
1597
1598         debug_print(_("Sorting summary..."));
1599         STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
1600
1601         main_window_cursor_wait(summaryview->mainwin);
1602
1603         gtk_clist_set_compare_func(clist, cmp_func);
1604
1605         /* toggle sort type if the same column is selected */
1606         if (summaryview->sort_mode == type)
1607                 summaryview->sort_type =
1608                         summaryview->sort_type == GTK_SORT_ASCENDING
1609                         ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
1610         else
1611                 summaryview->sort_type = GTK_SORT_ASCENDING;
1612         gtk_clist_set_sort_type(clist, summaryview->sort_type);
1613         summaryview->sort_mode = type;
1614
1615         gtk_ctree_sort_node(ctree, NULL);
1616
1617         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1618         /*gtkut_ctree_set_focus_row(ctree, summaryview->selected);*/
1619
1620         prefs_folder_item_set_config(summaryview->folder_item,
1621                                      summaryview->sort_type,
1622                                      summaryview->sort_mode);
1623         prefs_folder_item_save_config(summaryview->folder_item);
1624
1625         debug_print(_("done.\n"));
1626         STATUSBAR_POP(summaryview->mainwin);
1627
1628         main_window_cursor_normal(summaryview->mainwin);
1629 }
1630
1631 static GtkCTreeNode * subject_table_lookup(GHashTable *subject_table,
1632                                            gchar * subject)
1633 {
1634         if (g_strncasecmp(subject, "Re: ", 4) == 0)
1635                 return g_hash_table_lookup(subject_table, subject + 4);
1636         else
1637                 return g_hash_table_lookup(subject_table, subject);
1638 }
1639
1640 static void subject_table_insert(GHashTable *subject_table, gchar * subject,
1641                                  GtkCTreeNode * node)
1642 {
1643         if (g_strncasecmp(subject, "Re: ", 4) == 0)
1644                 g_hash_table_insert(subject_table, subject + 4, node);
1645         else
1646                 g_hash_table_insert(subject_table, subject, node);
1647 }
1648
1649 static void summary_set_ctree_from_list(SummaryView *summaryview,
1650                                         GSList *mlist)
1651 {
1652         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1653         MsgInfo *msginfo;
1654         MsgInfo *parentinfo;
1655         MsgInfo *cur_msginfo;
1656         GtkCTreeNode *node, *parent;
1657         gchar *text[N_SUMMARY_COLS];
1658         GHashTable *msgid_table;
1659         GHashTable *subject_table;
1660         GSList * cur;
1661         GtkCTreeNode *cur_parent;
1662
1663         if (!mlist) return;
1664
1665         debug_print(_("\tSetting summary from message data..."));
1666         STATUSBAR_PUSH(summaryview->mainwin,
1667                        _("Setting summary from message data..."));
1668
1669         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
1670         summaryview->msgid_table = msgid_table;
1671         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1672         summaryview->subject_table = subject_table;
1673
1674         if (prefs_common.use_addr_book)
1675                 start_address_completion();
1676         
1677         main_window_set_thread_option(summaryview->mainwin);
1678
1679         for (cur = mlist ; cur != NULL; cur = cur->next) {
1680                 msginfo = (MsgInfo *)cur->data;
1681                 msginfo->threadscore = msginfo->score;
1682         }
1683
1684         if (global_scoring || summaryview->folder_item->prefs->scoring) {
1685                 summaryview->important_score = prefs_common.important_score;
1686                 if (summaryview->folder_item->prefs->important_score >
1687                     summaryview->important_score)
1688                         summaryview->important_score =
1689                                 summaryview->folder_item->prefs->important_score;
1690         }
1691         
1692         /*      if (prefs_common.enable_thread) { */
1693         if (summaryview->folder_item->prefs->enable_thread) {
1694                 for (; mlist != NULL; mlist = mlist->next) {
1695                         msginfo = (MsgInfo *)mlist->data;
1696                         parent = NULL;
1697
1698                         summary_set_header(text, msginfo);
1699
1700                         /* search parent node for threading */
1701                         if (msginfo->inreplyto && *msginfo->inreplyto) {
1702                                 parent = g_hash_table_lookup
1703                                         (msgid_table, msginfo->inreplyto);
1704                         }
1705                         if (parent == NULL && msginfo->subject &&
1706                             g_strncasecmp(msginfo->subject, "Re: ", 4) == 0) {
1707                                 parent = subject_table_lookup
1708                                         (subject_table, msginfo->subject);
1709                         }
1710                         if(parent) {
1711                                 parentinfo = gtk_ctree_node_get_row_data(ctree, parent);
1712                                 if(parentinfo && MSG_IS_IGNORE_THREAD(parentinfo->flags)) {
1713 /*
1714                                         if (MSG_IS_NEW(msginfo->flags))
1715                                                 summaryview->newmsgs--;
1716                                         if (MSG_IS_UNREAD(msginfo->flags))
1717                                                 summaryview->unread--;
1718 */
1719                                         MSG_SET_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
1720                                 }
1721                         }
1722
1723                         node = gtk_ctree_insert_node
1724                                 (ctree, parent, NULL, text, 2,
1725                                  NULL, NULL, NULL, NULL, FALSE, TRUE);
1726                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
1727
1728                         summary_set_marks_func(ctree, node, summaryview);
1729                         
1730                         if ( MSG_GET_LABEL(msginfo->flags) )
1731                           summary_set_label_color(ctree, node, (msginfo->flags & MSG_LABEL));
1732
1733                         /* preserve previous node if the message is
1734                            duplicated */
1735                         if (msginfo->msgid && *msginfo->msgid &&
1736                             g_hash_table_lookup(msgid_table, msginfo->msgid)
1737                             == NULL)
1738                                 g_hash_table_insert(msgid_table,
1739                                                     msginfo->msgid, node);
1740                         if (msginfo->subject &&
1741                             subject_table_lookup(subject_table,
1742                                                  msginfo->subject) == NULL) {
1743                                 subject_table_insert(subject_table,
1744                                                      msginfo->subject, node);
1745                         }
1746
1747                         cur_parent = parent;
1748                         cur_msginfo = msginfo;
1749                         while (cur_parent != NULL) {
1750                                 parentinfo = gtk_ctree_node_get_row_data(ctree, cur_parent);
1751
1752                                 if (!parentinfo)
1753                                         break;
1754                                 
1755                                 if (parentinfo->threadscore <
1756                                     cur_msginfo->threadscore) {
1757                                         gchar * s;
1758                                         parentinfo->threadscore =
1759                                                 cur_msginfo->threadscore;
1760 #if 0
1761                                         s = itos(parentinfo->threadscore);
1762                                         gtk_ctree_node_set_text(ctree, cur_parent, S_COL_SCORE, s);
1763 #endif
1764                                 }
1765                                 else break;
1766                                 
1767                                 cur_msginfo = parentinfo;
1768                                 if (cur_msginfo->inreplyto &&
1769                                     *cur_msginfo->inreplyto) {
1770                                         cur_parent = g_hash_table_lookup(msgid_table, cur_msginfo->inreplyto);
1771                                 }
1772                                 if (cur_parent == NULL &&
1773                                     cur_msginfo->subject &&
1774                                     g_strncasecmp(cur_msginfo->subject,
1775                                                   "Re: ", 4) == 0) {
1776                                         cur_parent = subject_table_lookup(subject_table, cur_msginfo->subject);
1777                                 }
1778                         }
1779                 }
1780
1781                 /* complete the thread */
1782                 summary_thread_build(summaryview);
1783         } else {
1784                 for (; mlist != NULL; mlist = mlist->next) {
1785                         msginfo = (MsgInfo *)mlist->data;
1786
1787                         summary_set_header(text, msginfo);
1788
1789                         node = gtk_ctree_insert_node
1790                                 (ctree, NULL, NULL, text, 2,
1791                                  NULL, NULL, NULL, NULL, FALSE, TRUE);
1792                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
1793                         summary_set_marks_func(ctree, node, summaryview);
1794
1795                         if ( MSG_GET_LABEL(msginfo->flags) )
1796                           summary_set_label_color(ctree, node, (msginfo->flags & MSG_LABEL));
1797
1798                         if (msginfo->msgid && *msginfo->msgid &&
1799                             g_hash_table_lookup(msgid_table, msginfo->msgid)
1800                             == NULL)
1801                                 g_hash_table_insert(msgid_table,
1802                                                     msginfo->msgid, node);
1803
1804                         if (msginfo->subject &&
1805                             subject_table_lookup(subject_table,
1806                                                  msginfo->subject) == NULL)
1807                                 subject_table_insert(subject_table,
1808                                                      msginfo->subject, node);
1809                 }
1810         }
1811
1812         if (prefs_common.enable_hscrollbar) {
1813                 gint optimal_width;
1814
1815                 optimal_width = gtk_clist_optimal_column_width
1816                         (GTK_CLIST(ctree), S_COL_SUBJECT);
1817                 gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SUBJECT,
1818                                            optimal_width);
1819         }
1820
1821         if (prefs_common.use_addr_book)
1822                 end_address_completion();
1823
1824         debug_print(_("done.\n"));
1825         STATUSBAR_POP(summaryview->mainwin);
1826         if (debug_mode) {
1827                 debug_print("\tmsgid hash table size = %d\n",
1828                             g_hash_table_size(msgid_table));
1829                 debug_print("\tsubject hash table size = %d\n",
1830                             g_hash_table_size(subject_table));
1831         }
1832 }
1833
1834 struct wcachefp
1835 {
1836         FILE *cache_fp;
1837         FILE *mark_fp;
1838 };
1839
1840 gint summary_write_cache(SummaryView *summaryview)
1841 {
1842         struct wcachefp fps;
1843         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1844         gint ver = CACHE_VERSION;
1845         gchar *buf;
1846         gchar *cachefile, *markfile;
1847         GSList * cur;
1848
1849         if (!summaryview->folder_item || !summaryview->folder_item->path)
1850                 return -1;
1851
1852         if (summaryview->folder_item->folder->update_mark != NULL)
1853                 summaryview->folder_item->folder->update_mark(summaryview->folder_item->folder, summaryview->folder_item);
1854
1855         cachefile = folder_item_get_cache_file(summaryview->folder_item);
1856         g_return_val_if_fail(cachefile != NULL, -1);
1857         if ((fps.cache_fp = fopen(cachefile, "w")) == NULL) {
1858                 FILE_OP_ERROR(cachefile, "fopen");
1859                 g_free(cachefile);
1860                 return -1;
1861         }
1862         if (change_file_mode_rw(fps.cache_fp, cachefile) < 0)
1863                 FILE_OP_ERROR(cachefile, "chmod");
1864         g_free(cachefile);
1865
1866         markfile = folder_item_get_mark_file(summaryview->folder_item);
1867         if ((fps.mark_fp = fopen(markfile, "w")) == NULL) {
1868                 FILE_OP_ERROR(markfile, "fopen");
1869                 fclose(fps.cache_fp);
1870                 g_free(markfile);
1871                 return -1;
1872         }
1873         if (change_file_mode_rw(fps.mark_fp, markfile) < 0)
1874                 FILE_OP_ERROR(markfile, "chmod");
1875         g_free(markfile);
1876
1877         buf = g_strdup_printf(_("Writing summary cache (%s)..."),
1878                               summaryview->folder_item->path);
1879         debug_print(buf);
1880         STATUSBAR_PUSH(summaryview->mainwin, buf);
1881         g_free(buf);
1882
1883         WRITE_CACHE_DATA_INT(ver, fps.cache_fp);
1884         ver = MARK_VERSION;
1885         WRITE_CACHE_DATA_INT(ver, fps.mark_fp);
1886
1887         gtk_ctree_pre_recursive(ctree, NULL, summary_write_cache_func, &fps);
1888
1889         for(cur = summaryview->killed_messages ; cur != NULL ;
1890             cur = g_slist_next(cur)) {
1891                 MsgInfo *msginfo = (MsgInfo *) cur->data;
1892
1893                 procmsg_write_cache(msginfo, fps.cache_fp);
1894                 procmsg_write_flags(msginfo, fps.mark_fp);
1895         }
1896
1897         fclose(fps.cache_fp);
1898         fclose(fps.mark_fp);
1899
1900         debug_print(_("done.\n"));
1901         STATUSBAR_POP(summaryview->mainwin);
1902
1903         return 0;
1904 }
1905
1906 static void summary_write_cache_func(GtkCTree *ctree, GtkCTreeNode *node,
1907                                      gpointer data)
1908 {
1909         struct wcachefp *fps = data;
1910         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1911
1912         if (msginfo == NULL) return;
1913
1914         procmsg_write_cache(msginfo, fps->cache_fp);
1915         procmsg_write_flags(msginfo, fps->mark_fp);
1916 }
1917
1918 static void summary_set_header(gchar *text[], MsgInfo *msginfo)
1919 {
1920         static gchar date_modified[80];
1921         static gchar *to = NULL;
1922         static gchar *from_name = NULL;
1923         static gchar col_number[11];
1924         static gchar col_score[11];
1925
1926         text[S_COL_MARK]   = NULL;
1927         text[S_COL_UNREAD] = NULL;
1928         text[S_COL_MIME]   = NULL;
1929         text[S_COL_NUMBER] = itos_buf(col_number, msginfo->msgnum);
1930         text[S_COL_SIZE]   = to_human_readable(msginfo->size);
1931 #if 0
1932         text[S_COL_SCORE]  = itos_buf(col_score, msginfo->threadscore);
1933 #else
1934         text[S_COL_SCORE]  = itos_buf(col_score, msginfo->score);
1935 #endif
1936
1937         if (msginfo->date_t) {
1938                 procheader_date_get_localtime(date_modified,
1939                                               sizeof(date_modified),
1940                                               msginfo->date_t);
1941                 text[S_COL_DATE] = date_modified;
1942         } else if (msginfo->date)
1943                 text[S_COL_DATE] = msginfo->date;
1944         else
1945                 text[S_COL_DATE] = _("(No Date)");
1946
1947         text[S_COL_FROM] = msginfo->fromname ? msginfo->fromname :
1948                 _("(No From)");
1949         if (prefs_common.swap_from && msginfo->from && msginfo->to &&
1950             !MSG_IS_NEWS(msginfo->flags)) {
1951                 gchar *from;
1952
1953                 Xalloca(from, strlen(msginfo->from) + 1, return);
1954                 strcpy(from, msginfo->from);
1955                 extract_address(from);
1956                 if (account_find_mail_from_address(from)) {
1957                         g_free(to);
1958                         to = g_strconcat("-->", msginfo->to, NULL);
1959                         text[S_COL_FROM] = to;
1960                 }
1961         }
1962
1963         if ((text[S_COL_FROM] != to) && prefs_common.use_addr_book &&
1964             msginfo->from) {
1965                 gint count;
1966                 gchar *from;
1967   
1968                 Xalloca(from, strlen(msginfo->from) + 1, return);
1969                 strcpy(from, msginfo->from);
1970                 extract_address(from);
1971                 count = complete_address(from);
1972                 if (count > 1) {
1973                         g_free(from_name);
1974                         from = get_complete_address(1);
1975                         from_name = procheader_get_fromname(from);
1976                         g_free(from);
1977                         text[S_COL_FROM] = from_name;
1978                 }
1979         }
1980
1981         text[S_COL_SUBJECT] = msginfo->subject ? msginfo->subject :
1982                 _("(No Subject)");
1983 }
1984
1985 #define CHANGE_FLAGS(msginfo) \
1986 { \
1987 if (msginfo->folder->folder->change_flags != NULL) \
1988 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
1989                                       msginfo->folder, \
1990                                       msginfo); \
1991 }
1992
1993 static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row,
1994                                 gboolean new_window)
1995 {
1996         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1997         MsgInfo *msginfo;
1998         gchar *filename;
1999         static gboolean lock = FALSE;
2000
2001         if (!new_window && summaryview->displayed == row) return;
2002         g_return_if_fail(row != NULL);
2003
2004         if (lock) return;
2005         lock = TRUE;
2006
2007         STATUSBAR_POP(summaryview->mainwin);
2008
2009         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2010
2011         filename = procmsg_get_message_file(msginfo);
2012         if (!filename) {
2013                 lock = FALSE;
2014                 return;
2015         }
2016         g_free(filename);
2017
2018         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2019                 summaryview->newmsgs--;
2020         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2021                 summaryview->unread--;
2022         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2023                 MSG_UNSET_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2024
2025                 CHANGE_FLAGS(msginfo);
2026
2027                 summary_set_row_marks(summaryview, row);
2028                 gtk_clist_thaw(GTK_CLIST(ctree));
2029                 summary_status_show(summaryview);
2030         }
2031
2032         if (new_window) {
2033                 MessageView *msgview;
2034
2035                 msgview = messageview_create_with_new_window();
2036                 messageview_show(msgview, msginfo);
2037         } else {
2038                 MessageView *msgview;
2039
2040                 msgview = summaryview->messageview;
2041
2042                 summaryview->displayed = row;
2043                 if (!summaryview->msg_is_toggled_on)
2044                         summary_toggle_view(summaryview);
2045                 messageview_show(msgview, msginfo);
2046                 if (msgview->type == MVIEW_TEXT ||
2047                     (msgview->type == MVIEW_MIME &&
2048                      GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL))
2049                         gtk_widget_grab_focus(summaryview->ctree);
2050                 GTK_EVENTS_FLUSH();
2051                 gtkut_ctree_node_move_if_on_the_edge(ctree, row);
2052         }
2053
2054         if (GTK_WIDGET_VISIBLE(summaryview->headerwin->window))
2055                 header_window_show(summaryview->headerwin, msginfo);
2056
2057         lock = FALSE;
2058 }
2059
2060 void summary_redisplay_msg(SummaryView *summaryview)
2061 {
2062         GtkCTreeNode *node;
2063
2064         if (summaryview->displayed) {
2065                 node = summaryview->displayed;
2066                 summaryview->displayed = NULL;
2067                 summary_display_msg(summaryview, node, FALSE);
2068         }
2069 }
2070
2071 void summary_open_msg(SummaryView *summaryview)
2072 {
2073         if (!summaryview->selected) return;
2074
2075         summary_display_msg(summaryview, summaryview->selected, TRUE);
2076 }
2077
2078 void summary_view_source(SummaryView * summaryview)
2079 {
2080         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2081         MsgInfo *msginfo;
2082         SourceWindow *srcwin;
2083
2084         if (!summaryview->selected) return;
2085
2086         srcwin = source_window_create();
2087         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2088         source_window_show_msg(srcwin, msginfo);
2089         source_window_show(srcwin);
2090 }
2091
2092 void summary_reedit(SummaryView *summaryview)
2093 {
2094         MsgInfo *msginfo;
2095
2096         if (!summaryview->selected) return;
2097         if (!summaryview->folder_item ||
2098             (summaryview->folder_item->stype != F_DRAFT &&
2099              summaryview->folder_item->stype != F_OUTBOX &&
2100              summaryview->folder_item->stype != F_QUEUE)) return;
2101
2102         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2103                                               summaryview->selected);
2104         if (!msginfo) return;
2105
2106         compose_reedit(msginfo);
2107 }
2108
2109 void summary_step(SummaryView *summaryview, GtkScrollType type)
2110 {
2111         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2112
2113         gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
2114                                 type, 0.0);
2115
2116         if (summaryview->msg_is_toggled_on)
2117                 summary_display_msg(summaryview, summaryview->selected, FALSE);
2118 }
2119
2120 static void summary_toggle_view(SummaryView *summaryview)
2121 {
2122         MainWindow *mainwin = summaryview->mainwin;
2123         union CompositeWin *cwin = &mainwin->win;
2124         GtkWidget *vpaned = NULL;
2125         GtkWidget *container = NULL;
2126
2127         switch (mainwin->type) {
2128         case SEPARATE_NONE:
2129                 vpaned = cwin->sep_none.vpaned;
2130                 container = cwin->sep_none.hpaned;
2131                 break;
2132         case SEPARATE_FOLDER:
2133                 vpaned = cwin->sep_folder.vpaned;
2134                 container = mainwin->vbox_body;
2135                 break;
2136         case SEPARATE_MESSAGE:
2137         case SEPARATE_BOTH:
2138                 return;
2139         }
2140
2141         if (vpaned->parent != NULL) {
2142                 summaryview->msg_is_toggled_on = FALSE;
2143                 summaryview->displayed = NULL;
2144                 gtk_widget_ref(vpaned);
2145                 gtk_container_remove(GTK_CONTAINER(container), vpaned);
2146                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), container);
2147                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2148                               GTK_ARROW_UP, GTK_SHADOW_OUT);
2149         } else {
2150                 summaryview->msg_is_toggled_on = TRUE;
2151                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), vpaned);
2152                 gtk_container_add(GTK_CONTAINER(container), vpaned);
2153                 gtk_widget_unref(vpaned);
2154                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2155                               GTK_ARROW_DOWN, GTK_SHADOW_OUT);
2156         }
2157
2158         gtk_widget_grab_focus(summaryview->ctree);
2159 }
2160
2161 static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2162 {
2163         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2164         MsgInfo *msginfo;
2165         MsgFlags flags;
2166
2167         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2168         if (!msginfo) return;
2169
2170         flags = msginfo->flags;
2171
2172         gtk_ctree_node_set_foreground(ctree, row, NULL);
2173
2174         /* set new/unread column */
2175         if (MSG_IS_IGNORE_THREAD(flags)) {
2176                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2177                                           ignorethreadxpm, ignorethreadxpmmask);
2178         } else if (MSG_IS_NEW(flags)) {
2179                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2180                                           newxpm, newxpmmask);
2181         } else if (MSG_IS_UNREAD(flags)) {
2182                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2183                                           unreadxpm, unreadxpmmask);
2184         } else if (MSG_IS_REPLIED(flags)) {
2185                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2186                                           repliedxpm, repliedxpmmask);
2187         } else if (MSG_IS_FORWARDED(flags)) {
2188                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2189                                           forwardedxpm, forwardedxpmmask);
2190         } else {
2191                 gtk_ctree_node_set_text(ctree, row, S_COL_UNREAD, NULL);
2192         }
2193
2194         /* set mark column */
2195         if (MSG_IS_DELETED(flags)) {
2196                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_MARK,
2197                                           deletedxpm, deletedxpmmask);
2198                 gtk_ctree_node_set_foreground(ctree, row,
2199                                               &summaryview->color_dim);
2200         } else if (MSG_IS_MARKED(flags)) {
2201                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_MARK,
2202                                           markxpm, markxpmmask);
2203         } else if (MSG_IS_MOVE(flags)) {
2204                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "o");
2205                 gtk_ctree_node_set_foreground(ctree, row,
2206                                               &summaryview->color_marked);
2207         } else if (MSG_IS_COPY(flags)) {
2208                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "O");
2209                 gtk_ctree_node_set_foreground(ctree, row,
2210                                               &summaryview->color_marked);
2211         }
2212         else if ((global_scoring ||
2213                   summaryview->folder_item->prefs->scoring) &&
2214                  (msginfo->score >= summaryview->important_score) &&
2215                  ((msginfo->flags &
2216                    (MSG_MARKED | MSG_MOVE | MSG_COPY)) == 0)) {
2217                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "!");
2218                 gtk_ctree_node_set_foreground(ctree, row,
2219                                               &summaryview->color_important);
2220         } else {
2221                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, NULL);
2222         }
2223
2224         if (MSG_IS_MIME(flags)) {
2225                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_MIME,
2226                                           clipxpm, clipxpmmask);
2227         } else {
2228                 gtk_ctree_node_set_text(ctree, row, S_COL_MIME, NULL);
2229         }
2230 }
2231
2232 void summary_set_marks_selected(SummaryView *summaryview)
2233 {
2234         summary_set_row_marks(summaryview, summaryview->selected);
2235 }
2236
2237 static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
2238 {
2239         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2240         MsgInfo *msginfo;
2241
2242         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2243         msginfo->to_folder = NULL;
2244         if (MSG_IS_DELETED(msginfo->flags))
2245                 summaryview->deleted--;
2246         if (MSG_IS_MOVE(msginfo->flags))
2247                 summaryview->moved--;
2248         if (MSG_IS_COPY(msginfo->flags))
2249                 summaryview->copied--;
2250         MSG_UNSET_FLAGS(msginfo->flags, MSG_DELETED | MSG_MOVE | MSG_COPY);
2251         MSG_SET_FLAGS(msginfo->flags, MSG_MARKED);
2252
2253         CHANGE_FLAGS(msginfo);
2254
2255         summary_set_row_marks(summaryview, row);
2256         debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2257 }
2258
2259 void summary_mark(SummaryView *summaryview)
2260 {
2261         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2262         GList *cur;
2263
2264         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2265                 summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
2266
2267         /* summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); */
2268         summary_status_show(summaryview);
2269 }
2270
2271 static void summary_mark_row_as_read(SummaryView *summaryview,
2272                                      GtkCTreeNode *row)
2273 {
2274         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2275         MsgInfo *msginfo;
2276
2277         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2278         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2279                 summaryview->newmsgs--;
2280         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2281                 summaryview->unread--;
2282         if (MSG_IS_NEW(msginfo->flags) ||
2283             MSG_IS_UNREAD(msginfo->flags)) {
2284                 MSG_UNSET_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2285
2286                 CHANGE_FLAGS(msginfo);
2287                 
2288                 summary_set_row_marks(summaryview, row);
2289                 debug_print(_("Message %d is marked as read\n"),
2290                             msginfo->msgnum);
2291         }
2292 }
2293
2294 void summary_mark_as_read(SummaryView *summaryview)
2295 {
2296         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2297         GList *cur;
2298
2299         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2300                 summary_mark_row_as_read(summaryview,
2301                                          GTK_CTREE_NODE(cur->data));
2302
2303         summary_status_show(summaryview);
2304 }
2305
2306 static void summary_mark_row_as_unread(SummaryView *summaryview,
2307                                        GtkCTreeNode *row)
2308 {
2309         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2310         MsgInfo *msginfo;
2311
2312         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2313         if (MSG_IS_DELETED(msginfo->flags)) {
2314                 msginfo->to_folder = NULL;
2315                 MSG_UNSET_FLAGS(msginfo->flags, MSG_DELETED);
2316                 summaryview->deleted--;
2317         }
2318         MSG_UNSET_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
2319         if (!MSG_IS_UNREAD(msginfo->flags)) {
2320                 MSG_SET_FLAGS(msginfo->flags, MSG_UNREAD);
2321                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2322                                           unreadxpm, unreadxpmmask);
2323                 summaryview->unread++;
2324                 debug_print(_("Message %d is marked as unread\n"),
2325                             msginfo->msgnum);
2326         }
2327
2328         CHANGE_FLAGS(msginfo);
2329
2330         summary_set_row_marks(summaryview, row);
2331 }
2332
2333 void summary_mark_as_unread(SummaryView *summaryview)
2334 {
2335         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2336         GList *cur;
2337
2338         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2339                 summary_mark_row_as_unread(summaryview,
2340                                            GTK_CTREE_NODE(cur->data));
2341
2342         summary_status_show(summaryview);
2343 }
2344
2345 static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
2346 {
2347         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2348         MsgInfo *msginfo;
2349
2350         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2351
2352         if (MSG_IS_DELETED(msginfo->flags)) return;
2353
2354         msginfo->to_folder = NULL;
2355         if (MSG_IS_MOVE(msginfo->flags))
2356                 summaryview->moved--;
2357         if (MSG_IS_COPY(msginfo->flags))
2358                 summaryview->copied--;
2359         MSG_UNSET_FLAGS(msginfo->flags,
2360                         MSG_MARKED |
2361                         MSG_MOVE |
2362                         MSG_COPY);
2363         MSG_SET_FLAGS(msginfo->flags, MSG_DELETED);
2364
2365         CHANGE_FLAGS(msginfo);
2366
2367         summaryview->deleted++;
2368
2369         if (!prefs_common.immediate_exec)
2370                 summary_set_row_marks(summaryview, row);
2371
2372         debug_print(_("Message %s/%d is set to delete\n"),
2373                     msginfo->folder->path, msginfo->msgnum);
2374 }
2375
2376 void summary_delete(SummaryView *summaryview)
2377 {
2378         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2379         GList *cur;
2380
2381         if (!summaryview->folder_item ||
2382             summaryview->folder_item->folder->type == F_NEWS) return;
2383
2384         /* if current folder is trash, don't delete */
2385         if (summaryview->folder_item->stype == F_TRASH) {
2386                 alertpanel_notice(_("Current folder is Trash."));
2387                 return;
2388         }
2389
2390         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2391                 summary_delete_row(summaryview, GTK_CTREE_NODE(cur->data));
2392
2393         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2394
2395         if (prefs_common.immediate_exec)
2396                 summary_execute(summaryview);
2397         else
2398                 summary_status_show(summaryview);
2399 }
2400
2401 void summary_delete_duplicated(SummaryView *summaryview)
2402 {
2403         if (!summaryview->folder_item ||
2404             summaryview->folder_item->folder->type == F_NEWS) return;
2405         if (summaryview->folder_item->stype == F_TRASH) return;
2406
2407         main_window_cursor_wait(summaryview->mainwin);
2408         debug_print(_("Deleting duplicated messages..."));
2409         STATUSBAR_PUSH(summaryview->mainwin,
2410                        _("Deleting duplicated messages..."));
2411
2412         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2413                                 GTK_CTREE_FUNC(summary_delete_duplicated_func),
2414                                 summaryview);
2415
2416         if (prefs_common.immediate_exec)
2417                 summary_execute(summaryview);
2418         else
2419                 summary_status_show(summaryview);
2420
2421         debug_print(_("done.\n"));
2422         STATUSBAR_POP(summaryview->mainwin);
2423         main_window_cursor_normal(summaryview->mainwin);
2424 }
2425
2426 static void summary_delete_duplicated_func(GtkCTree *ctree, GtkCTreeNode *node,
2427                                            SummaryView *summaryview)
2428 {
2429         GtkCTreeNode *found;
2430         MsgInfo *msginfo = GTK_CTREE_ROW(node)->row.data;
2431
2432         if (!msginfo->msgid || !*msginfo->msgid) return;
2433
2434         found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
2435
2436         if (found && found != node)
2437                 summary_delete_row(summaryview, node);
2438 }
2439
2440 static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
2441 {
2442         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2443         MsgInfo *msginfo;
2444
2445         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2446         msginfo->to_folder = NULL;
2447         if (MSG_IS_DELETED(msginfo->flags))
2448                 summaryview->deleted--;
2449         if (MSG_IS_MOVE(msginfo->flags))
2450                 summaryview->moved--;
2451         if (MSG_IS_COPY(msginfo->flags))
2452                 summaryview->copied--;
2453         MSG_UNSET_FLAGS(msginfo->flags,
2454                         MSG_MARKED |
2455                         MSG_DELETED |
2456                         MSG_MOVE |
2457                         MSG_COPY);
2458
2459         CHANGE_FLAGS(msginfo);
2460
2461         summary_set_row_marks(summaryview, row);
2462
2463         debug_print(_("Message %s/%d is unmarked\n"),
2464                     msginfo->folder->path, msginfo->msgnum);
2465 }
2466
2467 void summary_unmark(SummaryView *summaryview)
2468 {
2469         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2470         GList *cur;
2471
2472         for (cur = GTK_CLIST(ctree)->selection; cur != NULL;
2473              cur = cur->next)
2474                 summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
2475
2476         summary_status_show(summaryview);
2477 }
2478
2479 static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2480                                 FolderItem *to_folder)
2481 {
2482         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2483         MsgInfo *msginfo;
2484
2485         g_return_if_fail(to_folder != NULL);
2486
2487         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2488         msginfo->to_folder = to_folder;
2489         if (MSG_IS_DELETED(msginfo->flags))
2490                 summaryview->deleted--;
2491         MSG_UNSET_FLAGS(msginfo->flags,
2492                         MSG_MARKED | MSG_DELETED | MSG_COPY);
2493         if (!MSG_IS_MOVE(msginfo->flags)) {
2494                 MSG_SET_FLAGS(msginfo->flags, MSG_MOVE);
2495                 summaryview->moved++;
2496         }
2497
2498         if (!prefs_common.immediate_exec)
2499                 summary_set_row_marks(summaryview, row);
2500
2501         debug_print(_("Message %d is set to move to %s\n"),
2502                     msginfo->msgnum, to_folder->path);
2503 }
2504
2505 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2506 {
2507         GList *cur;
2508
2509         if (!to_folder) return;
2510         if (!summaryview->folder_item ||
2511             summaryview->folder_item->folder->type == F_NEWS) return;
2512         if (summaryview->folder_item == to_folder) {
2513                 alertpanel_notice(_("Destination is same as current folder."));
2514                 return;
2515         }
2516
2517         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2518              cur != NULL; cur = cur->next)
2519                 summary_move_row_to
2520                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2521
2522         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2523
2524         if (prefs_common.immediate_exec)
2525                 summary_execute(summaryview);
2526         else
2527                 summary_status_show(summaryview);
2528 }
2529
2530 void summary_move_to(SummaryView *summaryview)
2531 {
2532         FolderItem *to_folder;
2533
2534         if (!summaryview->folder_item ||
2535             summaryview->folder_item->folder->type == F_NEWS) return;
2536
2537         to_folder = foldersel_folder_sel(NULL, NULL);
2538         summary_move_selected_to(summaryview, to_folder);
2539 }
2540
2541 static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2542                                 FolderItem *to_folder)
2543 {
2544         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2545         MsgInfo *msginfo;
2546
2547         g_return_if_fail(to_folder != NULL);
2548
2549         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2550         msginfo->to_folder = to_folder;
2551         if (MSG_IS_DELETED(msginfo->flags))
2552                 summaryview->deleted--;
2553         MSG_UNSET_FLAGS(msginfo->flags,
2554                         MSG_MARKED | MSG_DELETED | MSG_MOVE);
2555         if (!MSG_IS_COPY(msginfo->flags)) {
2556                 MSG_SET_FLAGS(msginfo->flags, MSG_COPY);
2557                 summaryview->copied++;
2558         }
2559
2560         if (!prefs_common.immediate_exec)
2561                 summary_set_row_marks(summaryview, row);
2562
2563         debug_print(_("Message %d is set to copy to %s\n"),
2564                     msginfo->msgnum, to_folder->path);
2565 }
2566
2567 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2568 {
2569         GList *cur;
2570
2571         if (!to_folder) return;
2572         if (!summaryview->folder_item ||
2573             summaryview->folder_item->folder->type == F_NEWS) return;
2574         if (summaryview->folder_item == to_folder) {
2575                 alertpanel_notice
2576                         (_("Destination to copy is same as current folder."));
2577                 return;
2578         }
2579
2580         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2581              cur != NULL; cur = cur->next)
2582                 summary_copy_row_to
2583                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2584
2585         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2586
2587         if (prefs_common.immediate_exec)
2588                 summary_execute(summaryview);
2589         else
2590                 summary_status_show(summaryview);
2591 }
2592
2593 void summary_copy_to(SummaryView *summaryview)
2594 {
2595         FolderItem *to_folder;
2596
2597         if (!summaryview->folder_item ||
2598             summaryview->folder_item->folder->type == F_NEWS) return;
2599
2600         to_folder = foldersel_folder_sel(NULL, NULL);
2601         summary_copy_selected_to(summaryview, to_folder);
2602 }
2603
2604 void summary_select_all(SummaryView *summaryview)
2605 {
2606         if (summaryview->messages >= 500) {
2607                 STATUSBAR_PUSH(summaryview->mainwin,
2608                                _("Selecting all messages..."));
2609                 main_window_cursor_wait(summaryview->mainwin);
2610         }
2611
2612         gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
2613
2614         if (summaryview->messages >= 500) {
2615                 STATUSBAR_POP(summaryview->mainwin);
2616                 main_window_cursor_normal(summaryview->mainwin);
2617         }
2618 }
2619
2620 void summary_unselect_all(SummaryView *summaryview)
2621 {
2622         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
2623 }
2624
2625 void summary_save_as(SummaryView *summaryview)
2626 {
2627         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2628         MsgInfo *msginfo;
2629         gchar *src, *dest;
2630
2631         if (!summaryview->selected) return;
2632         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2633         if (!msginfo) return;
2634
2635         dest = filesel_select_file(_("Save as"), NULL);
2636         if (!dest) return;
2637         if (is_file_exist(dest)) {
2638                 AlertValue aval;
2639
2640                 aval = alertpanel(_("Overwrite"),
2641                                   _("Overwrite existing file?"),
2642                                   _("OK"), _("Cancel"), NULL);
2643                 if (G_ALERTDEFAULT != aval) return;
2644         }
2645
2646         src = procmsg_get_message_file(msginfo);
2647         copy_file(src, dest);
2648         g_free(src);
2649 }
2650
2651 void summary_print(SummaryView *summaryview)
2652 {
2653         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2654         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2655         MsgInfo *msginfo;
2656         GList *cur;
2657         gchar *cmdline;
2658         gchar *p;
2659
2660         if (clist->selection == NULL) return;
2661
2662         cmdline = input_dialog(_("Print"),
2663                                _("Enter the print command line:\n"
2664                                  "(`%s' will be replaced with file name)"),
2665                                prefs_common.print_cmd);
2666         if (!cmdline) return;
2667         if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
2668             strchr(p + 2, '%')) {
2669                 alertpanel_error(_("Print command line is invalid:\n`%s'"),
2670                                  cmdline);
2671                 g_free(cmdline);
2672                 return;
2673         }
2674
2675         for (cur = clist->selection; cur != NULL; cur = cur->next) {
2676                 msginfo = gtk_ctree_node_get_row_data
2677                         (ctree, GTK_CTREE_NODE(cur->data));
2678                 if (msginfo) procmsg_print_message(msginfo, cmdline);
2679         }
2680         
2681         g_free(cmdline);
2682 }
2683
2684 void summary_execute(SummaryView *summaryview)
2685 {
2686         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2687         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2688         GtkCTreeNode *node, *next;
2689
2690         if (!summaryview->folder_item ||
2691             summaryview->folder_item->folder->type == F_NEWS) return;
2692
2693         gtk_clist_freeze(clist);
2694
2695         if (summaryview->folder_item->prefs->enable_thread)
2696                 /*      if (prefs_common.enable_thread) */
2697                 summary_unthread_for_exec(summaryview);
2698
2699         summary_execute_move(summaryview);
2700         summary_execute_copy(summaryview);
2701         summary_execute_delete(summaryview);
2702
2703         node = GTK_CTREE_NODE(clist->row_list);
2704         while (node != NULL) {
2705                 next = GTK_CTREE_NODE_NEXT(node);
2706                 if (gtk_ctree_node_get_row_data(ctree, node) == NULL) {
2707                         if (node == summaryview->displayed) {
2708                                 messageview_clear(summaryview->messageview);
2709                                 summaryview->displayed = NULL;
2710                         }
2711                         if (GTK_CTREE_ROW(node)->children != NULL)
2712                                 g_warning("summary_execute(): children != NULL\n");
2713                         else
2714                                 gtk_ctree_remove_node(ctree, node);
2715                 }
2716                 node = next;
2717         }
2718
2719         if (summaryview->folder_item->prefs->enable_thread)
2720                 /*      if (prefs_common.enable_thread) */
2721                 summary_thread_build(summaryview);
2722
2723         summaryview->selected = clist->selection ?
2724                 GTK_CTREE_NODE(clist->selection->data) : NULL;
2725
2726         if (!GTK_CLIST(summaryview->ctree)->row_list) {
2727                 menu_set_insensitive_all
2728                         (GTK_MENU_SHELL(summaryview->popupmenu));
2729                 gtk_widget_grab_focus(summaryview->folderview->ctree);
2730         } else
2731                 gtk_widget_grab_focus(summaryview->ctree);
2732
2733         summary_update_status(summaryview);
2734         summary_status_show(summaryview);
2735
2736         summary_write_cache(summaryview);
2737
2738         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
2739
2740         gtk_clist_thaw(clist);
2741 }
2742
2743 static void summary_execute_move(SummaryView *summaryview)
2744 {
2745         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2746         GSList *cur;
2747
2748         summaryview->folder_table = g_hash_table_new(NULL, NULL);
2749
2750         /* search moving messages and execute */
2751         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
2752                                 summaryview);
2753
2754         if (summaryview->mlist) {
2755                 procmsg_move_messages(summaryview->mlist);
2756
2757                 folder_item_scan_foreach(summaryview->folder_table);
2758                 folderview_update_item_foreach(summaryview->folder_table);
2759
2760                 for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
2761                         procmsg_msginfo_free((MsgInfo *)cur->data);
2762                 g_slist_free(summaryview->mlist);
2763                 summaryview->mlist = NULL;
2764         }
2765
2766         g_hash_table_destroy(summaryview->folder_table);
2767         summaryview->folder_table = NULL;
2768 }
2769
2770 static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
2771                                       gpointer data)
2772 {
2773         SummaryView *summaryview = data;
2774         MsgInfo *msginfo;
2775
2776         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2777
2778         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
2779                 g_hash_table_insert(summaryview->folder_table,
2780                                     msginfo->to_folder, GINT_TO_POINTER(1));
2781
2782                 summaryview->mlist =
2783                         g_slist_append(summaryview->mlist, msginfo);
2784                 gtk_ctree_node_set_row_data(ctree, node, NULL);
2785
2786                 if (msginfo->msgid && *msginfo->msgid &&
2787                     node == g_hash_table_lookup(summaryview->msgid_table,
2788                                                 msginfo->msgid))
2789                         g_hash_table_remove(summaryview->msgid_table,
2790                                             msginfo->msgid);
2791         }
2792 }
2793
2794 static void summary_execute_copy(SummaryView *summaryview)
2795 {
2796         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2797
2798         summaryview->folder_table = g_hash_table_new(NULL, NULL);
2799
2800         /* search copying messages and execute */
2801         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
2802                                 summaryview);
2803
2804         if (summaryview->mlist) {
2805                 procmsg_copy_messages(summaryview->mlist);
2806
2807                 folder_item_scan_foreach(summaryview->folder_table);
2808                 folderview_update_item_foreach(summaryview->folder_table);
2809
2810                 g_slist_free(summaryview->mlist);
2811                 summaryview->mlist = NULL;
2812         }
2813
2814         g_hash_table_destroy(summaryview->folder_table);
2815         summaryview->folder_table = NULL;
2816 }
2817
2818 static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
2819                                       gpointer data)
2820 {
2821         SummaryView *summaryview = data;
2822         MsgInfo *msginfo;
2823
2824         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2825
2826         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
2827                 g_hash_table_insert(summaryview->folder_table,
2828                                     msginfo->to_folder, GINT_TO_POINTER(1));
2829
2830                 summaryview->mlist =
2831                         g_slist_append(summaryview->mlist, msginfo);
2832
2833                 MSG_UNSET_FLAGS(msginfo->flags, MSG_COPY);
2834                 
2835                 summary_set_row_marks(summaryview, node);
2836         }
2837 }
2838
2839 static void summary_execute_delete(SummaryView *summaryview)
2840 {
2841         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2842         FolderItem *trash;
2843         GSList *cur;
2844
2845         trash = summaryview->folder_item->folder->trash;
2846         if (summaryview->folder_item->folder->type == F_MH) {
2847                 g_return_if_fail(trash != NULL);
2848         }
2849         if (summaryview->folder_item == trash) return;
2850
2851         /* search deleting messages and execute */
2852         gtk_ctree_pre_recursive
2853                 (ctree, NULL, summary_execute_delete_func, summaryview);
2854
2855         if (!summaryview->mlist) return;
2856
2857         for(cur = summaryview->mlist ; cur != NULL ; cur = cur->next) {
2858                 MsgInfo * msginfo = cur->data;
2859                 MSG_UNSET_FLAGS(msginfo->flags, MSG_DELETED);
2860         }
2861
2862         folder_item_move_msgs_with_dest(trash, summaryview->mlist);
2863
2864         for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
2865                 procmsg_msginfo_free((MsgInfo *)cur->data);
2866
2867         g_slist_free(summaryview->mlist);
2868         summaryview->mlist = NULL;
2869
2870         folder_item_scan(trash);
2871         folderview_update_item(trash, FALSE);
2872 }
2873
2874 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
2875                                         gpointer data)
2876 {
2877         SummaryView *summaryview = data;
2878         MsgInfo *msginfo;
2879
2880         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2881
2882         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
2883                 summaryview->mlist =
2884                         g_slist_append(summaryview->mlist, msginfo);
2885                 gtk_ctree_node_set_row_data(ctree, node, NULL);
2886
2887                 if (msginfo->msgid && *msginfo->msgid &&
2888                     node == g_hash_table_lookup(summaryview->msgid_table,
2889                                                 msginfo->msgid))
2890                         g_hash_table_remove(summaryview->msgid_table,
2891                                             msginfo->msgid);
2892
2893                 if (msginfo->subject && 
2894                     node == subject_table_lookup(summaryview->subject_table, 
2895                                                 msginfo->subject)) {
2896                         gchar *s = msginfo->subject + (g_strncasecmp(msginfo->subject, "Re: ", 4) == 0 ? 4 : 0);
2897                         g_hash_table_remove(summaryview->subject_table, s);
2898                 }                       
2899         }
2900 }
2901
2902 /* thread functions */
2903
2904 void summary_thread_build(SummaryView *summaryview)
2905 {
2906         debug_print(_("Building threads..."));
2907         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
2908         main_window_cursor_wait(summaryview->mainwin);
2909
2910         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
2911
2912         gtk_ctree_pre_recursive_to_depth
2913                 (GTK_CTREE(summaryview->ctree), NULL, 1,
2914                  GTK_CTREE_FUNC(summary_thread_func),
2915                  summaryview);
2916
2917         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
2918
2919         debug_print(_("done.\n"));
2920         STATUSBAR_POP(summaryview->mainwin);
2921         main_window_cursor_normal(summaryview->mainwin);
2922 }
2923
2924 void summary_unthread(SummaryView *summaryview)
2925 {
2926         GtkCTreeNode *node;
2927         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2928
2929         debug_print(_("Unthreading..."));
2930         STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
2931         main_window_cursor_wait(summaryview->mainwin);
2932
2933         gtk_clist_freeze(GTK_CLIST(ctree));
2934
2935         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2936              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
2937                 summary_unthread_func(ctree, node, NULL);
2938         }
2939
2940         gtk_clist_thaw(GTK_CLIST(ctree));
2941
2942         debug_print(_("done.\n"));
2943         STATUSBAR_POP(summaryview->mainwin);
2944         main_window_cursor_normal(summaryview->mainwin);
2945 }
2946
2947 static void summary_unthread_for_exec(SummaryView *summaryview)
2948 {
2949         GtkCTreeNode *node;
2950         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2951
2952         debug_print(_("Unthreading for execution..."));
2953
2954         gtk_clist_freeze(GTK_CLIST(ctree));
2955
2956         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2957              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
2958                 summary_unthread_for_exec_func(ctree, node, NULL);
2959         }
2960
2961         gtk_clist_thaw(GTK_CLIST(ctree));
2962
2963         debug_print(_("done.\n"));
2964 }
2965
2966 static void summary_thread_func(GtkCTree *ctree, GtkCTreeNode *node,
2967                                 gpointer data)
2968 {
2969         MsgInfo *msginfo;
2970         GtkCTreeNode *parent = NULL;
2971         
2972         SummaryView * summaryview = (SummaryView *) data;
2973         GHashTable *msgid_table = summaryview->msgid_table;
2974         GHashTable *subject_table = summaryview->subject_table;
2975
2976         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2977
2978         if (!msginfo) return;
2979
2980         if(msginfo->inreplyto) {
2981             parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
2982         }
2983         if (parent == NULL && msginfo->subject &&
2984             g_strncasecmp(msginfo->subject, "Re: ", 4) == 0) {
2985                 parent = subject_table_lookup(subject_table, msginfo->subject);
2986         }
2987
2988         if (parent && parent != node) {
2989                 gtk_ctree_move(ctree, node, parent, NULL);
2990                 gtk_ctree_expand(ctree, parent);
2991         }
2992 }
2993
2994 static void summary_unthread_func(GtkCTree *ctree, GtkCTreeNode *node,
2995                                   gpointer data)
2996 {
2997         GtkCTreeNode *child;
2998         GtkCTreeNode *sibling;
2999
3000         child = GTK_CTREE_ROW(node)->children;
3001         sibling = GTK_CTREE_ROW(node)->sibling;
3002
3003         while (child != NULL) {
3004                 GtkCTreeNode *next_child;
3005
3006                 next_child = GTK_CTREE_ROW(child)->sibling;
3007                 gtk_ctree_move(ctree, child, NULL, sibling);
3008                 child = next_child;
3009         }
3010 }
3011
3012 static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
3013                                            gpointer data)
3014 {
3015         MsgInfo *msginfo;
3016         GtkCTreeNode *top_parent;
3017         GtkCTreeNode *child;
3018         GtkCTreeNode *sibling;
3019
3020         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3021
3022         if (!msginfo ||
3023             (!MSG_IS_MOVE(msginfo->flags) &&
3024              !MSG_IS_DELETED(msginfo->flags)))
3025                 return;
3026         child = GTK_CTREE_ROW(node)->children;
3027         if (!child) return;
3028
3029         for (top_parent = node;
3030              GTK_CTREE_ROW(top_parent)->parent != NULL;
3031              top_parent = GTK_CTREE_ROW(top_parent)->parent)
3032                 ;
3033         sibling = GTK_CTREE_ROW(top_parent)->sibling;
3034
3035         while (child != NULL) {
3036                 GtkCTreeNode *next_child;
3037
3038                 next_child = GTK_CTREE_ROW(child)->sibling;
3039                 gtk_ctree_move(ctree, child, NULL, sibling);
3040                 child = next_child;
3041         }
3042 }
3043
3044 void summary_filter(SummaryView *summaryview)
3045 {
3046         if (!prefs_common.fltlist) return;
3047
3048         debug_print(_("filtering..."));
3049         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
3050         main_window_cursor_wait(summaryview->mainwin);
3051
3052         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
3053
3054         if (prefs_filtering == NULL) {
3055                 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3056                                         GTK_CTREE_FUNC(summary_filter_func),
3057                                         summaryview);
3058                 
3059                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3060
3061                 if (prefs_common.immediate_exec)
3062                         summary_execute(summaryview);
3063                 else
3064                         summary_status_show(summaryview);
3065         }
3066         else {
3067                 summaryview->folder_table = g_hash_table_new(NULL, NULL);
3068
3069                 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3070                                         GTK_CTREE_FUNC(summary_filter_func),
3071                                         summaryview);
3072
3073                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3074
3075                 folder_item_scan_foreach(summaryview->folder_table);
3076                 folderview_update_item_foreach(summaryview->folder_table);
3077
3078                 g_hash_table_destroy(summaryview->folder_table);
3079                 summaryview->folder_table = NULL;
3080
3081                 summary_show(summaryview, summaryview->folder_item, FALSE);
3082         }
3083
3084         debug_print(_("done.\n"));
3085         STATUSBAR_POP(summaryview->mainwin);
3086         main_window_cursor_normal(summaryview->mainwin);
3087 }
3088
3089 static void summary_filter_func(GtkCTree *ctree, GtkCTreeNode *node,
3090                                 gpointer data)
3091 {
3092         MsgInfo *msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3093         SummaryView *summaryview = data;
3094         gchar *file;
3095         FolderItem *dest;
3096
3097         if (prefs_filtering == NULL) {
3098                 /* old filtering */
3099                 file = procmsg_get_message_file_path(msginfo);
3100                 dest = filter_get_dest_folder(prefs_common.fltlist, file);
3101                 g_free(file);
3102
3103                 if (dest && strcmp2(dest->path, FILTER_NOT_RECEIVE) != 0 &&
3104                     summaryview->folder_item != dest)
3105                         summary_move_row_to(summaryview, node, dest);
3106         }
3107         else
3108                 filter_msginfo_move_or_delete(prefs_filtering, msginfo,
3109                                               summaryview->folder_table);
3110 }
3111
3112 /* callback functions */
3113
3114 static void summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
3115                                    SummaryView *summaryview)
3116 {
3117         if (!event)
3118                 return;
3119
3120         if (!summaryview->msg_is_toggled_on && summaryview->selected)
3121                 summary_display_msg(summaryview, summaryview->selected, FALSE);
3122         else
3123                 summary_toggle_view(summaryview);
3124 }
3125
3126 static void summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
3127                                    SummaryView *summaryview)
3128 {
3129         if (!event) return;
3130
3131         if (event->button == 3) {
3132                 /* right clicked */
3133                 summary_add_sender_to_cb(summaryview, 0, 0);    
3134                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
3135                                NULL, NULL, event->button, event->time);
3136         } else if (event->button == 2) {
3137                 summaryview->display_msg = TRUE;
3138         } else if (event->button == 1) {
3139                 if (!prefs_common.emulate_emacs &&
3140                     summaryview->msg_is_toggled_on)
3141                         summaryview->display_msg = TRUE;
3142         }
3143 }
3144
3145 static void summary_button_released(GtkWidget *ctree, GdkEventButton *event,
3146                                     SummaryView *summaryview)
3147 {
3148 }
3149
3150 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
3151 {
3152         summary_key_pressed(summaryview->ctree, event, summaryview);
3153 }
3154
3155 #define BREAK_ON_MODIFIER_KEY() \
3156         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
3157
3158 #define KEY_PRESS_EVENT_STOP() \
3159         if (gtk_signal_n_emissions_by_name \
3160                 (GTK_OBJECT(ctree), "key_press_event") > 0) { \
3161                 gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree), \
3162                                              "key_press_event"); \
3163         }
3164
3165 static void summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
3166                                 SummaryView *summaryview)
3167 {
3168         GtkCTree *ctree = GTK_CTREE(widget);
3169         GtkCTreeNode *node;
3170         FolderItem *to_folder;
3171
3172         if (!event) return;
3173
3174         switch (event->keyval) {
3175         case GDK_g:             /* Go */
3176         case GDK_G:
3177                 BREAK_ON_MODIFIER_KEY();
3178                 KEY_PRESS_EVENT_STOP();
3179                 to_folder = foldersel_folder_sel(NULL, NULL);
3180                 if (to_folder) {
3181                         debug_print(_("Go to %s\n"), to_folder->path);
3182                         folderview_select(summaryview->folderview, to_folder);
3183                 }
3184                 return;
3185         case GDK_w:             /* Write new message */
3186                 BREAK_ON_MODIFIER_KEY();
3187                 if (summaryview->folder_item) {
3188                         PrefsAccount *ac;
3189                         ac = summaryview->folder_item->folder->account;
3190                         if (ac && ac->protocol == A_NNTP)
3191                                 compose_new_with_recipient
3192                                         (ac, summaryview->folder_item->path);
3193                         else
3194                                 compose_new(ac);
3195                 } else
3196                         compose_new(NULL);
3197                 return;
3198         case GDK_D:             /* Empty trash */
3199                 BREAK_ON_MODIFIER_KEY();
3200                 KEY_PRESS_EVENT_STOP();
3201                 main_window_empty_trash(summaryview->mainwin, TRUE);
3202                 return;
3203         case GDK_Q:             /* Quit */
3204                 BREAK_ON_MODIFIER_KEY();
3205
3206                 if (prefs_common.confirm_on_exit) {
3207                         if (alertpanel(_("Exit"), _("Exit this program?"),
3208                                        _("OK"), _("Cancel"), NULL)
3209                                        == G_ALERTDEFAULT) {
3210                                 manage_window_focus_in
3211                                         (summaryview->mainwin->window,
3212                                          NULL, NULL);
3213                                 app_will_exit(NULL, summaryview->mainwin);
3214                         }
3215                 }
3216                 return;
3217         case GDK_Left:          /* Move focus */
3218         case GDK_Escape:
3219                 gtk_widget_grab_focus(summaryview->folderview->ctree);
3220                 return;
3221         default:
3222                 break;
3223         }
3224
3225         if (!summaryview->selected) {
3226                 node = gtk_ctree_node_nth(ctree, 0);
3227                 if (node)
3228                         gtk_ctree_select(ctree, node);
3229                 else
3230                         return;
3231         }
3232
3233         switch (event->keyval) {
3234         case GDK_space:         /* Page down or go to the next */
3235                 if (summaryview->displayed != summaryview->selected) {
3236                         summary_display_msg(summaryview,
3237                                             summaryview->selected, FALSE);
3238                         break;
3239                 }
3240                 if (!textview_scroll_page(summaryview->messageview->textview,
3241                                           FALSE))
3242                         summary_select_next_unread(summaryview);
3243                 break;
3244         case GDK_n:             /* Next */
3245         case GDK_N:
3246                 BREAK_ON_MODIFIER_KEY();
3247                 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3248                 break;
3249         case GDK_BackSpace:     /* Page up */
3250         case GDK_Delete:
3251                 textview_scroll_page(summaryview->messageview->textview, TRUE);
3252                 break;
3253         case GDK_p:             /* Prev */
3254         case GDK_P:
3255                 BREAK_ON_MODIFIER_KEY();
3256                 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
3257                 break;
3258         case GDK_v:             /* Toggle summary mode / message mode */
3259         case GDK_V:
3260                 BREAK_ON_MODIFIER_KEY();
3261
3262                 if (!summaryview->msg_is_toggled_on && summaryview->selected)
3263                         summary_display_msg(summaryview,
3264                                             summaryview->selected, FALSE);
3265                 else
3266                         summary_toggle_view(summaryview);
3267                 break;
3268         case GDK_Return:        /* Scroll up/down one line */
3269                 if (summaryview->displayed != summaryview->selected) {
3270                         summary_display_msg(summaryview,
3271                                             summaryview->selected, FALSE);
3272                         break;
3273                 }
3274                 textview_scroll_one_line(summaryview->messageview->textview,
3275                                          (event->state & GDK_MOD1_MASK) != 0);
3276                 break;
3277         case GDK_asterisk:      /* Mark */
3278                 summary_mark(summaryview);
3279                 break;
3280         case GDK_exclam:        /* Mark as unread */
3281                 summary_mark_as_unread(summaryview);
3282                 break;
3283         case GDK_d:             /* Delete */
3284                 BREAK_ON_MODIFIER_KEY();
3285                 summary_delete(summaryview);
3286                 break;
3287         case GDK_u:             /* Unmark */
3288         case GDK_U:
3289                 BREAK_ON_MODIFIER_KEY();
3290                 summary_unmark(summaryview);
3291                 break;
3292         case GDK_o:             /* Move */
3293                 BREAK_ON_MODIFIER_KEY();
3294                 summary_move_to(summaryview);
3295                 break;
3296         case GDK_O:             /* Copy */
3297                 BREAK_ON_MODIFIER_KEY();
3298                 summary_copy_to(summaryview);
3299                 break;
3300         case GDK_x:             /* Execute */
3301         case GDK_X:
3302                 BREAK_ON_MODIFIER_KEY();
3303                 KEY_PRESS_EVENT_STOP();
3304                 summary_execute(summaryview);
3305                 break;
3306         case GDK_a:             /* Reply to the message */
3307                 BREAK_ON_MODIFIER_KEY();
3308                 summary_reply_cb(summaryview,
3309                                  COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE, NULL);
3310                 break;
3311         case GDK_A:             /* Reply to the message with quotation */
3312                 BREAK_ON_MODIFIER_KEY();
3313                 summary_reply_cb(summaryview,
3314                                  COMPOSE_REPLY_TO_ALL_WITH_QUOTE, NULL);
3315                 break;
3316         case GDK_f:             /* Forward the message */
3317                 BREAK_ON_MODIFIER_KEY();
3318                 summary_reply_cb(summaryview, COMPOSE_FORWARD, NULL);
3319                 break;
3320         case GDK_F:
3321                 BREAK_ON_MODIFIER_KEY();
3322                 summary_reply_cb(summaryview, COMPOSE_FORWARD_AS_ATTACH, NULL);
3323                 break;
3324         case GDK_y:             /* Save the message */
3325                 BREAK_ON_MODIFIER_KEY();
3326                 summary_save_as(summaryview);
3327                 break;
3328         default:
3329                 break;
3330         }
3331 }
3332
3333 #undef BREAK_ON_MODIFIER_KEY
3334 #undef KEY_PRESS_EVENT_STOP
3335
3336 static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
3337 {
3338         if (summaryview->folder_item->stype == F_DRAFT)
3339                 summary_reedit(summaryview);
3340         else
3341                 summary_open_msg(summaryview);
3342
3343         summaryview->display_msg = FALSE;
3344 }
3345
3346 static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
3347                              gint column, SummaryView *summaryview)
3348 {
3349         MsgInfo *msginfo;
3350
3351         summary_status_show(summaryview);
3352         summary_set_menu_sensitive(summaryview);
3353
3354         if (GTK_CLIST(ctree)->selection &&
3355              GTK_CLIST(ctree)->selection->next) {
3356                 summaryview->display_msg = FALSE;
3357                 return;
3358         }
3359
3360         summaryview->selected = row;
3361
3362         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3363
3364         switch (column) {
3365         case S_COL_MARK:
3366                 if (MSG_IS_MARKED(msginfo->flags)) {
3367                         MSG_UNSET_FLAGS(msginfo->flags, MSG_MARKED);
3368
3369                         CHANGE_FLAGS(msginfo);
3370                         
3371                         summary_set_row_marks(summaryview, row);
3372                 } else
3373                         summary_mark_row(summaryview, row);
3374                 break;
3375         case S_COL_UNREAD:
3376                 if (MSG_IS_UNREAD(msginfo->flags)) {
3377                         summary_mark_row_as_read(summaryview, row);