fa34635ede7bf2711abc46d48e9edbed05d700ab
[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                 gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0.0);
1106
1107                 if (summaryview->displayed == node)
1108                         summaryview->displayed = NULL;
1109                 summary_display_msg(summaryview, node, FALSE);
1110         } else {
1111                 AlertValue val;
1112
1113                 val = alertpanel(_("No unread message"),
1114                                  _("No unread message found. Go to next folder?"),
1115                                  _("Yes"), _("No"), NULL);
1116                 if (val == G_ALERTDEFAULT) {
1117                         if (gtk_signal_n_emissions_by_name
1118                                 (GTK_OBJECT(ctree), "key_press_event") > 0)
1119                                         gtk_signal_emit_stop_by_name
1120                                                 (GTK_OBJECT(ctree),
1121                                                  "key_press_event");
1122                         folderview_select_next_unread(summaryview->folderview);
1123                 }
1124         }
1125 }
1126
1127 void summary_select_next_marked(SummaryView *summaryview)
1128 {
1129         GtkCTreeNode *node;
1130         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1131
1132         node = summary_find_next_marked_msg(summaryview,
1133                                             summaryview->selected);
1134
1135         if (!node) {
1136                 AlertValue val;
1137
1138                 val = alertpanel(_("No more marked messages"),
1139                                  _("No marked message found. "
1140                                    "Search from the beginning?"),
1141                                  _("Yes"), _("No"), NULL);
1142                 if (val != G_ALERTDEFAULT) return;
1143                 node = summary_find_next_marked_msg(summaryview,
1144                                                     NULL);
1145         }
1146         if (!node) {
1147                 alertpanel_notice(_("No marked messages."));
1148         } else {
1149                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1150                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1151                 if (summaryview->displayed == node)
1152                         summaryview->displayed = NULL;
1153                 summary_display_msg(summaryview, node, FALSE);
1154         }
1155 }
1156
1157 void summary_select_prev_marked(SummaryView *summaryview)
1158 {
1159         GtkCTreeNode *node;
1160         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1161
1162         node = summary_find_prev_marked_msg(summaryview,
1163                                             summaryview->selected);
1164
1165         if (!node) {
1166                 AlertValue val;
1167
1168                 val = alertpanel(_("No more marked messages"),
1169                                  _("No marked message found. "
1170                                    "Search from the end?"),
1171                                  _("Yes"), _("No"), NULL);
1172                 if (val != G_ALERTDEFAULT) return;
1173                 node = summary_find_prev_marked_msg(summaryview,
1174                                                     NULL);
1175         }
1176         if (!node) {
1177                 alertpanel_notice(_("No marked messages."));
1178         } else {
1179                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1180                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1181                 if (summaryview->displayed == node)
1182                         summaryview->displayed = NULL;
1183                 summary_display_msg(summaryview, node, FALSE);
1184         }
1185 }
1186
1187 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1188 {
1189         GtkCTreeNode *node;
1190         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1191
1192         node = summary_find_msg_by_msgnum(summaryview, msgnum);
1193
1194         if (node) {
1195                 GTK_EVENTS_FLUSH();
1196                 gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
1197                 gtk_widget_grab_focus(GTK_WIDGET(ctree));
1198                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1199                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1200                 if (summaryview->msg_is_toggled_on) {
1201                         if (summaryview->displayed == node)
1202                                 summaryview->displayed = NULL;
1203                         summary_display_msg(summaryview, node, FALSE);
1204                 }
1205         }
1206 }
1207
1208 guint summary_get_current_msgnum(SummaryView *summaryview)
1209 {
1210         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1211         MsgInfo *msginfo;
1212
1213         if (!summaryview->selected)
1214                 return 0;
1215         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
1216         return msginfo->msgnum;
1217 }
1218
1219 static GtkCTreeNode *summary_find_next_unread_msg(SummaryView *summaryview,
1220                                                   GtkCTreeNode *current_node)
1221 {
1222         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1223         GtkCTreeNode *node;
1224         MsgInfo *msginfo;
1225
1226         if (current_node)
1227                 node = current_node;
1228                 /*node = GTK_CTREE_NODE_NEXT(current_node);*/
1229         else
1230                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1231
1232         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1233                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1234                 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) break;
1235         }
1236
1237         return node;
1238 }
1239
1240 static GtkCTreeNode *summary_find_next_marked_msg(SummaryView *summaryview,
1241                                                   GtkCTreeNode *current_node)
1242 {
1243         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1244         GtkCTreeNode *node;
1245         MsgInfo *msginfo;
1246
1247         if (current_node)
1248                 node = GTK_CTREE_NODE_NEXT(current_node);
1249         else
1250                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1251
1252         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1253                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1254                 if (MSG_IS_MARKED(msginfo->flags)) break;
1255         }
1256
1257         return node;
1258 }
1259
1260 static GtkCTreeNode *summary_find_prev_marked_msg(SummaryView *summaryview,
1261                                                   GtkCTreeNode *current_node)
1262 {
1263         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1264         GtkCTreeNode *node;
1265         MsgInfo *msginfo;
1266
1267         if (current_node)
1268                 node = GTK_CTREE_NODE_PREV(current_node);
1269         else
1270                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
1271
1272         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1273                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1274                 if (MSG_IS_MARKED(msginfo->flags)) break;
1275         }
1276
1277         return node;
1278 }
1279
1280 static GtkCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
1281                                                 guint msgnum)
1282 {
1283         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1284         GtkCTreeNode *node;
1285         MsgInfo *msginfo;
1286
1287         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1288
1289         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1290                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1291                 if (msginfo->msgnum == msgnum) break;
1292         }
1293
1294         return node;
1295 }
1296
1297 #if 0
1298 static GtkCTreeNode *summary_find_prev_unread_msg(SummaryView *summaryview,
1299                                                   GtkCTreeNode *current_node)
1300 {
1301         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1302         GtkCTreeNode *node;
1303         MsgInfo *msginfo;
1304
1305         if (current_node)
1306                 node = current_node;
1307                 /*node = GTK_CTREE_NODE_PREV(current_node);*/
1308         else
1309                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
1310
1311         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1312                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1313                 if (MSG_IS_UNREAD(msginfo->flags)) break;
1314         }
1315
1316         return node;
1317 }
1318 #endif
1319
1320 static guint attract_hash_func(gconstpointer key)
1321 {
1322         gchar *str;
1323         gchar *p;
1324         guint h;
1325
1326         Xstrdup_a(str, (const gchar *)key, return 0);
1327         trim_subject(str);
1328
1329         p = str;
1330         h = *p;
1331
1332         if (h) {
1333                 for (p += 1; *p != '\0'; p++)
1334                         h = (h << 5) - h + *p;
1335         }
1336
1337         return h;
1338 }
1339
1340 static gint attract_compare_func(gconstpointer a, gconstpointer b)
1341 {
1342         return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1343 }
1344
1345 void summary_attract_by_subject(SummaryView *summaryview)
1346 {
1347         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1348         GtkCList *clist = GTK_CLIST(ctree);
1349         GtkCTreeNode *src_node;
1350         GtkCTreeNode *dst_node, *sibling;
1351         GtkCTreeNode *tmp;
1352         MsgInfo *src_msginfo, *dst_msginfo;
1353         GHashTable *subject_table;
1354
1355         debug_print(_("Attracting messages by subject..."));
1356         STATUSBAR_PUSH(summaryview->mainwin,
1357                        _("Attracting messages by subject..."));
1358
1359         main_window_cursor_wait(summaryview->mainwin);
1360         gtk_clist_freeze(clist);
1361
1362         subject_table = g_hash_table_new(attract_hash_func,
1363                                          attract_compare_func);
1364
1365         for (src_node = GTK_CTREE_NODE(clist->row_list);
1366              src_node != NULL;
1367              src_node = tmp) {
1368                 tmp = GTK_CTREE_ROW(src_node)->sibling;
1369                 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
1370                 if (!src_msginfo) continue;
1371                 if (!src_msginfo->subject) continue;
1372
1373                 /* find attracting node */
1374                 dst_node = g_hash_table_lookup(subject_table,
1375                                                src_msginfo->subject);
1376
1377                 if (dst_node) {
1378                         dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
1379
1380                         /* if the time difference is more than 20 days,
1381                            don't attract */
1382                         if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
1383                             > 60 * 60 * 24 * 20)
1384                                 continue;
1385
1386                         sibling = GTK_CTREE_ROW(dst_node)->sibling;
1387                         if (src_node != sibling)
1388                                 gtk_ctree_move(ctree, src_node, NULL, sibling);
1389                 }
1390
1391                 g_hash_table_insert(subject_table,
1392                                     src_msginfo->subject, src_node);
1393         }
1394
1395         g_hash_table_destroy(subject_table);
1396
1397         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1398
1399         gtk_clist_thaw(clist);
1400
1401         debug_print(_("done.\n"));
1402         STATUSBAR_POP(summaryview->mainwin);
1403
1404         main_window_cursor_normal(summaryview->mainwin);
1405 }
1406
1407 static void summary_free_msginfo_func(GtkCTree *ctree, GtkCTreeNode *node,
1408                                       gpointer data)
1409 {
1410         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1411
1412         if (msginfo)
1413                 procmsg_msginfo_free(msginfo);
1414 }
1415
1416 static void summary_set_marks_func(GtkCTree *ctree, GtkCTreeNode *node,
1417                                    gpointer data)
1418 {
1419         SummaryView *summaryview = data;
1420         MsgInfo *msginfo;
1421
1422         msginfo = gtk_ctree_node_get_row_data(ctree, node);
1423
1424         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1425                 summaryview->newmsgs++;
1426         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1427                 summaryview->unread++;
1428         if (MSG_IS_DELETED(msginfo->flags))
1429                 summaryview->deleted++;
1430
1431         summaryview->messages++;
1432         summaryview->total_size += msginfo->size;
1433
1434         summary_set_row_marks(summaryview, node);
1435 }
1436
1437 static void summary_update_status(SummaryView *summaryview)
1438 {
1439         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1440         GtkCTreeNode *node;
1441         MsgInfo *msginfo;
1442
1443         summaryview->newmsgs = summaryview->unread =
1444         summaryview->messages = summaryview->total_size =
1445         summaryview->deleted = summaryview->moved = summaryview->copied = 0;
1446
1447         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1448              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
1449                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1450
1451                 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1452                         summaryview->newmsgs++;
1453                 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1454                         summaryview->unread++;
1455                 if (MSG_IS_DELETED(msginfo->flags))
1456                         summaryview->deleted++;
1457                 if (MSG_IS_MOVE(msginfo->flags))
1458                         summaryview->moved++;
1459                 if (MSG_IS_COPY(msginfo->flags))
1460                         summaryview->copied++;
1461                 summaryview->messages++;
1462                 summaryview->total_size += msginfo->size;
1463         }
1464 }
1465
1466 static void summary_status_show(SummaryView *summaryview)
1467 {
1468         gchar *str;
1469         gchar *del, *mv, *cp;
1470         gchar *sel;
1471         gchar *spc;
1472         GList *rowlist, *cur;
1473         guint n_selected = 0;
1474         off_t sel_size = 0;
1475         MsgInfo *msginfo;
1476
1477         if (!summaryview->folder_item) {
1478                 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
1479                 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
1480                 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
1481                 return;
1482         }
1483
1484         rowlist = GTK_CLIST(summaryview->ctree)->selection;
1485         for (cur = rowlist; cur != NULL; cur = cur->next) {
1486                 msginfo = gtk_ctree_node_get_row_data
1487                         (GTK_CTREE(summaryview->ctree),
1488                          GTK_CTREE_NODE(cur->data));
1489                 sel_size += msginfo->size;
1490                 n_selected++;
1491         }
1492
1493         gtk_label_set(GTK_LABEL(summaryview->statlabel_folder),
1494                       summaryview->folder_item &&
1495                       summaryview->folder_item->folder->type == F_NEWS
1496                       ? g_basename(summaryview->folder_item->path)
1497                       : summaryview->folder_item->path);
1498
1499         if (summaryview->deleted)
1500                 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
1501         else
1502                 del = g_strdup("");
1503         if (summaryview->moved)
1504                 mv = g_strdup_printf(_("%s%d moved"),
1505                                      summaryview->deleted ? _(", ") : "",
1506                                      summaryview->moved);
1507         else
1508                 mv = g_strdup("");
1509         if (summaryview->copied)
1510                 cp = g_strdup_printf(_("%s%d copied"),
1511                                      summaryview->deleted ||
1512                                      summaryview->moved ? _(", ") : "",
1513                                      summaryview->copied);
1514         else
1515                 cp = g_strdup("");
1516
1517         if (summaryview->deleted || summaryview->moved || summaryview->copied)
1518                 spc = "    ";
1519         else
1520                 spc = "";
1521
1522         if (n_selected)
1523                 sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
1524         else
1525                 sel = g_strdup("");
1526         str = g_strconcat(n_selected ? itos(n_selected) : "",
1527                           n_selected ? _(" item(s) selected") : "",
1528                           sel, spc, del, mv, cp, NULL);
1529         gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str);
1530         g_free(str);
1531         g_free(sel);
1532         g_free(del);
1533         g_free(mv);
1534         g_free(cp);
1535
1536         if (summaryview->folder_item &&
1537             summaryview->folder_item->folder->type == F_MH) {
1538                 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
1539                                       summaryview->newmsgs,
1540                                       summaryview->unread,
1541                                       summaryview->messages,
1542                                       to_human_readable(summaryview->total_size));
1543         } else {
1544                 str = g_strdup_printf(_("%d new, %d unread, %d total"),
1545                                       summaryview->newmsgs,
1546                                       summaryview->unread,
1547                                       summaryview->messages);
1548         }
1549         gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str);
1550         g_free(str);
1551
1552         folderview_update_msg_num(summaryview->folderview,
1553                                   summaryview->folderview->opened,
1554                                   summaryview->newmsgs,
1555                                   summaryview->unread,
1556                                   summaryview->messages);
1557 }
1558
1559 void summary_sort(SummaryView *summaryview, SummarySortType type)
1560 {
1561         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1562         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1563         GtkCListCompareFunc cmp_func;
1564
1565         if (!summaryview->folder_item)
1566                 return;
1567
1568         switch (type) {
1569         case SORT_BY_NUMBER:
1570                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_num;
1571                 break;
1572         case SORT_BY_SIZE:
1573                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_size;
1574                 break;
1575         case SORT_BY_DATE:
1576                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_date;
1577                 break;
1578         case SORT_BY_FROM:
1579                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_from;
1580                 break;
1581         case SORT_BY_SUBJECT:
1582                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
1583                 break;
1584         case SORT_BY_SCORE:
1585                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_score;
1586                 break;
1587         case SORT_BY_LABEL:
1588                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_label;
1589                 break;
1590         default:
1591                 return;
1592         }
1593
1594         debug_print(_("Sorting summary..."));
1595         STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
1596
1597         main_window_cursor_wait(summaryview->mainwin);
1598
1599         gtk_clist_set_compare_func(clist, cmp_func);
1600
1601         /* toggle sort type if the same column is selected */
1602         if (summaryview->sort_mode == type)
1603                 summaryview->sort_type =
1604                         summaryview->sort_type == GTK_SORT_ASCENDING
1605                         ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
1606         else
1607                 summaryview->sort_type = GTK_SORT_ASCENDING;
1608         gtk_clist_set_sort_type(clist, summaryview->sort_type);
1609         summaryview->sort_mode = type;
1610
1611         gtk_ctree_sort_node(ctree, NULL);
1612
1613         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1614         /*gtkut_ctree_set_focus_row(ctree, summaryview->selected);*/
1615
1616         prefs_folder_item_set_config(summaryview->folder_item,
1617                                      summaryview->sort_type,
1618                                      summaryview->sort_mode);
1619         prefs_folder_item_save_config(summaryview->folder_item);
1620
1621         debug_print(_("done.\n"));
1622         STATUSBAR_POP(summaryview->mainwin);
1623
1624         main_window_cursor_normal(summaryview->mainwin);
1625 }
1626
1627 static GtkCTreeNode * subject_table_lookup(GHashTable *subject_table,
1628                                            gchar * subject)
1629 {
1630         if (g_strncasecmp(subject, "Re: ", 4) == 0)
1631                 return g_hash_table_lookup(subject_table, subject + 4);
1632         else
1633                 return g_hash_table_lookup(subject_table, subject);
1634 }
1635
1636 static void subject_table_insert(GHashTable *subject_table, gchar * subject,
1637                                  GtkCTreeNode * node)
1638 {
1639         if (g_strncasecmp(subject, "Re: ", 4) == 0)
1640                 g_hash_table_insert(subject_table, subject + 4, node);
1641         else
1642                 g_hash_table_insert(subject_table, subject, node);
1643 }
1644
1645 static void summary_set_ctree_from_list(SummaryView *summaryview,
1646                                         GSList *mlist)
1647 {
1648         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1649         MsgInfo *msginfo;
1650         MsgInfo *parentinfo;
1651         MsgInfo *cur_msginfo;
1652         GtkCTreeNode *node, *parent;
1653         gchar *text[N_SUMMARY_COLS];
1654         GHashTable *msgid_table;
1655         GHashTable *subject_table;
1656         GSList * cur;
1657         GtkCTreeNode *cur_parent;
1658
1659         if (!mlist) return;
1660
1661         debug_print(_("\tSetting summary from message data..."));
1662         STATUSBAR_PUSH(summaryview->mainwin,
1663                        _("Setting summary from message data..."));
1664
1665         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
1666         summaryview->msgid_table = msgid_table;
1667         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1668         summaryview->subject_table = subject_table;
1669
1670         if (prefs_common.use_addr_book)
1671                 start_address_completion();
1672         
1673         main_window_set_thread_option(summaryview->mainwin);
1674
1675         for (cur = mlist ; cur != NULL; cur = cur->next) {
1676                 msginfo = (MsgInfo *)cur->data;
1677                 msginfo->threadscore = msginfo->score;
1678         }
1679
1680         if (global_scoring || summaryview->folder_item->prefs->scoring) {
1681                 summaryview->important_score = prefs_common.important_score;
1682                 if (summaryview->folder_item->prefs->important_score >
1683                     summaryview->important_score)
1684                         summaryview->important_score =
1685                                 summaryview->folder_item->prefs->important_score;
1686         }
1687         
1688         /*      if (prefs_common.enable_thread) { */
1689         if (summaryview->folder_item->prefs->enable_thread) {
1690                 for (; mlist != NULL; mlist = mlist->next) {
1691                         msginfo = (MsgInfo *)mlist->data;
1692                         parent = NULL;
1693
1694                         summary_set_header(text, msginfo);
1695
1696                         /* search parent node for threading */
1697                         if (msginfo->inreplyto && *msginfo->inreplyto) {
1698                                 parent = g_hash_table_lookup
1699                                         (msgid_table, msginfo->inreplyto);
1700                         }
1701                         if (parent == NULL && msginfo->subject) {
1702                                 parent = subject_table_lookup
1703                                         (subject_table, msginfo->subject);
1704                         }
1705                         if(parent) {
1706                                 parentinfo = gtk_ctree_node_get_row_data(ctree, parent);
1707                                 if(parentinfo && MSG_IS_IGNORE_THREAD(parentinfo->flags)) {
1708 /*
1709                                         if (MSG_IS_NEW(msginfo->flags))
1710                                                 summaryview->newmsgs--;
1711                                         if (MSG_IS_UNREAD(msginfo->flags))
1712                                                 summaryview->unread--;
1713 */
1714                                         MSG_SET_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
1715                                 }
1716                         }
1717
1718                         node = gtk_ctree_insert_node
1719                                 (ctree, parent, NULL, text, 2,
1720                                  NULL, NULL, NULL, NULL, FALSE, TRUE);
1721                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
1722
1723                         summary_set_marks_func(ctree, node, summaryview);
1724                         
1725                         if ( MSG_GET_LABEL(msginfo->flags) )
1726                           summary_set_label_color(ctree, node, (msginfo->flags & MSG_LABEL));
1727
1728                         /* preserve previous node if the message is
1729                            duplicated */
1730                         if (msginfo->msgid && *msginfo->msgid &&
1731                             g_hash_table_lookup(msgid_table, msginfo->msgid)
1732                             == NULL)
1733                                 g_hash_table_insert(msgid_table,
1734                                                     msginfo->msgid, node);
1735                         if (msginfo->subject &&
1736                             subject_table_lookup(subject_table,
1737                                                  msginfo->subject) == NULL) {
1738                                 subject_table_insert(subject_table,
1739                                                      msginfo->subject, node);
1740                         }
1741
1742                         cur_parent = parent;
1743                         cur_msginfo = msginfo;
1744                         while (cur_parent != NULL) {
1745                                 parentinfo = gtk_ctree_node_get_row_data(ctree, cur_parent);
1746
1747                                 if (!parentinfo)
1748                                         break;
1749                                 
1750                                 if (parentinfo->threadscore <
1751                                     cur_msginfo->threadscore) {
1752                                         gchar * s;
1753                                         parentinfo->threadscore =
1754                                                 cur_msginfo->threadscore;
1755 #if 0
1756                                         s = itos(parentinfo->threadscore);
1757                                         gtk_ctree_node_set_text(ctree, cur_parent, S_COL_SCORE, s);
1758 #endif
1759                                 }
1760                                 else break;
1761                                 
1762                                 cur_msginfo = parentinfo;
1763                                 if (cur_msginfo->inreplyto &&
1764                                     *cur_msginfo->inreplyto) {
1765                                         cur_parent = g_hash_table_lookup(msgid_table, cur_msginfo->inreplyto);
1766                                 }
1767                                 if (cur_parent == NULL && cur_msginfo->subject) {
1768                                         cur_parent = subject_table_lookup(subject_table, cur_msginfo->subject);
1769                                 }
1770                         }
1771                 }
1772
1773                 /* complete the thread */
1774                 summary_thread_build(summaryview);
1775         } else {
1776                 for (; mlist != NULL; mlist = mlist->next) {
1777                         msginfo = (MsgInfo *)mlist->data;
1778
1779                         summary_set_header(text, msginfo);
1780
1781                         node = gtk_ctree_insert_node
1782                                 (ctree, NULL, NULL, text, 2,
1783                                  NULL, NULL, NULL, NULL, FALSE, TRUE);
1784                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
1785                         summary_set_marks_func(ctree, node, summaryview);
1786
1787                         if ( MSG_GET_LABEL(msginfo->flags) )
1788                           summary_set_label_color(ctree, node, (msginfo->flags & MSG_LABEL));
1789
1790                         if (msginfo->msgid && *msginfo->msgid &&
1791                             g_hash_table_lookup(msgid_table, msginfo->msgid)
1792                             == NULL)
1793                                 g_hash_table_insert(msgid_table,
1794                                                     msginfo->msgid, node);
1795
1796                         if (msginfo->subject &&
1797                             subject_table_lookup(subject_table,
1798                                                  msginfo->subject) == NULL)
1799                                 subject_table_insert(subject_table,
1800                                                      msginfo->subject, node);
1801                 }
1802         }
1803
1804         if (prefs_common.enable_hscrollbar) {
1805                 gint optimal_width;
1806
1807                 optimal_width = gtk_clist_optimal_column_width
1808                         (GTK_CLIST(ctree), S_COL_SUBJECT);
1809                 gtk_clist_set_column_width(GTK_CLIST(ctree), S_COL_SUBJECT,
1810                                            optimal_width);
1811         }
1812
1813         if (prefs_common.use_addr_book)
1814                 end_address_completion();
1815
1816         debug_print(_("done.\n"));
1817         STATUSBAR_POP(summaryview->mainwin);
1818         if (debug_mode) {
1819                 debug_print("\tmsgid hash table size = %d\n",
1820                             g_hash_table_size(msgid_table));
1821                 debug_print("\tsubject hash table size = %d\n",
1822                             g_hash_table_size(subject_table));
1823         }
1824 }
1825
1826 struct wcachefp
1827 {
1828         FILE *cache_fp;
1829         FILE *mark_fp;
1830 };
1831
1832 gint summary_write_cache(SummaryView *summaryview)
1833 {
1834         struct wcachefp fps;
1835         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1836         gint ver = CACHE_VERSION;
1837         gchar *buf;
1838         gchar *cachefile, *markfile;
1839         GSList * cur;
1840
1841         if (!summaryview->folder_item || !summaryview->folder_item->path)
1842                 return -1;
1843
1844         if (summaryview->folder_item->folder->update_mark != NULL)
1845                 summaryview->folder_item->folder->update_mark(summaryview->folder_item->folder, summaryview->folder_item);
1846
1847         cachefile = folder_item_get_cache_file(summaryview->folder_item);
1848         g_return_val_if_fail(cachefile != NULL, -1);
1849         if ((fps.cache_fp = fopen(cachefile, "w")) == NULL) {
1850                 FILE_OP_ERROR(cachefile, "fopen");
1851                 g_free(cachefile);
1852                 return -1;
1853         }
1854         if (change_file_mode_rw(fps.cache_fp, cachefile) < 0)
1855                 FILE_OP_ERROR(cachefile, "chmod");
1856         g_free(cachefile);
1857
1858         markfile = folder_item_get_mark_file(summaryview->folder_item);
1859         if ((fps.mark_fp = fopen(markfile, "w")) == NULL) {
1860                 FILE_OP_ERROR(markfile, "fopen");
1861                 fclose(fps.cache_fp);
1862                 g_free(markfile);
1863                 return -1;
1864         }
1865         if (change_file_mode_rw(fps.mark_fp, markfile) < 0)
1866                 FILE_OP_ERROR(markfile, "chmod");
1867         g_free(markfile);
1868
1869         buf = g_strdup_printf(_("Writing summary cache (%s)..."),
1870                               summaryview->folder_item->path);
1871         debug_print(buf);
1872         STATUSBAR_PUSH(summaryview->mainwin, buf);
1873         g_free(buf);
1874
1875         WRITE_CACHE_DATA_INT(ver, fps.cache_fp);
1876         ver = MARK_VERSION;
1877         WRITE_CACHE_DATA_INT(ver, fps.mark_fp);
1878
1879         gtk_ctree_pre_recursive(ctree, NULL, summary_write_cache_func, &fps);
1880
1881         for(cur = summaryview->killed_messages ; cur != NULL ;
1882             cur = g_slist_next(cur)) {
1883                 MsgInfo *msginfo = (MsgInfo *) cur->data;
1884
1885                 procmsg_write_cache(msginfo, fps.cache_fp);
1886                 procmsg_write_flags(msginfo, fps.mark_fp);
1887         }
1888
1889         fclose(fps.cache_fp);
1890         fclose(fps.mark_fp);
1891
1892         debug_print(_("done.\n"));
1893         STATUSBAR_POP(summaryview->mainwin);
1894
1895         return 0;
1896 }
1897
1898 static void summary_write_cache_func(GtkCTree *ctree, GtkCTreeNode *node,
1899                                      gpointer data)
1900 {
1901         struct wcachefp *fps = data;
1902         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1903
1904         if (msginfo == NULL) return;
1905
1906         procmsg_write_cache(msginfo, fps->cache_fp);
1907         procmsg_write_flags(msginfo, fps->mark_fp);
1908 }
1909
1910 static void summary_set_header(gchar *text[], MsgInfo *msginfo)
1911 {
1912         static gchar date_modified[80];
1913         static gchar *to = NULL;
1914         static gchar *from_name = NULL;
1915         static gchar col_number[11];
1916         static gchar col_score[11];
1917
1918         text[S_COL_MARK]   = NULL;
1919         text[S_COL_UNREAD] = NULL;
1920         text[S_COL_MIME]   = NULL;
1921         text[S_COL_NUMBER] = itos_buf(col_number, msginfo->msgnum);
1922         text[S_COL_SIZE]   = to_human_readable(msginfo->size);
1923 #if 0
1924         text[S_COL_SCORE]  = itos_buf(col_score, msginfo->threadscore);
1925 #else
1926         text[S_COL_SCORE]  = itos_buf(col_score, msginfo->score);
1927 #endif
1928
1929         if (msginfo->date_t) {
1930                 procheader_date_get_localtime(date_modified,
1931                                               sizeof(date_modified),
1932                                               msginfo->date_t);
1933                 text[S_COL_DATE] = date_modified;
1934         } else if (msginfo->date)
1935                 text[S_COL_DATE] = msginfo->date;
1936         else
1937                 text[S_COL_DATE] = _("(No Date)");
1938
1939         text[S_COL_FROM] = msginfo->fromname ? msginfo->fromname :
1940                 _("(No From)");
1941         if (prefs_common.swap_from && msginfo->from && msginfo->to &&
1942             !MSG_IS_NEWS(msginfo->flags)) {
1943                 gchar *from;
1944
1945                 Xalloca(from, strlen(msginfo->from) + 1, return);
1946                 strcpy(from, msginfo->from);
1947                 extract_address(from);
1948                 if (account_find_mail_from_address(from)) {
1949                         g_free(to);
1950                         to = g_strconcat("-->", msginfo->to, NULL);
1951                         text[S_COL_FROM] = to;
1952                 }
1953         }
1954
1955         if ((text[S_COL_FROM] != to) && prefs_common.use_addr_book &&
1956             msginfo->from) {
1957                 gint count;
1958                 gchar *from;
1959   
1960                 Xalloca(from, strlen(msginfo->from) + 1, return);
1961                 strcpy(from, msginfo->from);
1962                 extract_address(from);
1963                 count = complete_address(from);
1964                 if (count > 1) {
1965                         g_free(from_name);
1966                         from = get_complete_address(1);
1967                         from_name = procheader_get_fromname(from);
1968                         g_free(from);
1969                         text[S_COL_FROM] = from_name;
1970                 }
1971         }
1972
1973         text[S_COL_SUBJECT] = msginfo->subject ? msginfo->subject :
1974                 _("(No Subject)");
1975 }
1976
1977 #define CHANGE_FLAGS(msginfo) \
1978 { \
1979 if (msginfo->folder->folder->change_flags != NULL) \
1980 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
1981                                       msginfo->folder, \
1982                                       msginfo); \
1983 }
1984
1985 static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row,
1986                                 gboolean new_window)
1987 {
1988         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1989         MsgInfo *msginfo;
1990         gchar *filename;
1991         static gboolean lock = FALSE;
1992
1993         if (!new_window && summaryview->displayed == row) return;
1994         g_return_if_fail(row != NULL);
1995
1996         if (lock) return;
1997         lock = TRUE;
1998
1999         STATUSBAR_POP(summaryview->mainwin);
2000
2001         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2002
2003         filename = procmsg_get_message_file(msginfo);
2004         if (!filename) {
2005                 lock = FALSE;
2006                 return;
2007         }
2008         g_free(filename);
2009
2010         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2011                 summaryview->newmsgs--;
2012         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2013                 summaryview->unread--;
2014         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2015                 MSG_UNSET_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2016
2017                 CHANGE_FLAGS(msginfo);
2018
2019                 summary_set_row_marks(summaryview, row);
2020                 gtk_clist_thaw(GTK_CLIST(ctree));
2021                 summary_status_show(summaryview);
2022         }
2023
2024         if (new_window) {
2025                 MessageView *msgview;
2026
2027                 msgview = messageview_create_with_new_window();
2028                 messageview_show(msgview, msginfo);
2029         } else {
2030                 MessageView *msgview;
2031
2032                 msgview = summaryview->messageview;
2033
2034                 summaryview->displayed = row;
2035                 if (!summaryview->msg_is_toggled_on)
2036                         summary_toggle_view(summaryview);
2037                 messageview_show(msgview, msginfo);
2038                 if (msgview->type == MVIEW_TEXT ||
2039                     (msgview->type == MVIEW_MIME &&
2040                      GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL))
2041                         gtk_widget_grab_focus(summaryview->ctree);
2042                 GTK_EVENTS_FLUSH();
2043                 gtkut_ctree_node_move_if_on_the_edge(ctree, row);
2044         }
2045
2046         if (GTK_WIDGET_VISIBLE(summaryview->headerwin->window))
2047                 header_window_show(summaryview->headerwin, msginfo);
2048
2049         lock = FALSE;
2050 }
2051
2052 void summary_redisplay_msg(SummaryView *summaryview)
2053 {
2054         GtkCTreeNode *node;
2055
2056         if (summaryview->displayed) {
2057                 node = summaryview->displayed;
2058                 summaryview->displayed = NULL;
2059                 summary_display_msg(summaryview, node, FALSE);
2060         }
2061 }
2062
2063 void summary_open_msg(SummaryView *summaryview)
2064 {
2065         if (!summaryview->selected) return;
2066
2067         summary_display_msg(summaryview, summaryview->selected, TRUE);
2068 }
2069
2070 void summary_view_source(SummaryView * summaryview)
2071 {
2072         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2073         MsgInfo *msginfo;
2074         SourceWindow *srcwin;
2075
2076         if (!summaryview->selected) return;
2077
2078         srcwin = source_window_create();
2079         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2080         source_window_show_msg(srcwin, msginfo);
2081         source_window_show(srcwin);
2082 }
2083
2084 void summary_reedit(SummaryView *summaryview)
2085 {
2086         MsgInfo *msginfo;
2087
2088         if (!summaryview->selected) return;
2089         if (!summaryview->folder_item ||
2090             (summaryview->folder_item->stype != F_DRAFT &&
2091              summaryview->folder_item->stype != F_OUTBOX &&
2092              summaryview->folder_item->stype != F_QUEUE)) return;
2093
2094         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2095                                               summaryview->selected);
2096         if (!msginfo) return;
2097
2098         compose_reedit(msginfo);
2099 }
2100
2101 void summary_step(SummaryView *summaryview, GtkScrollType type)
2102 {
2103         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2104
2105         gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
2106                                 type, 0.0);
2107
2108         if (summaryview->msg_is_toggled_on)
2109                 summary_display_msg(summaryview, summaryview->selected, FALSE);
2110 }
2111
2112 static void summary_toggle_view(SummaryView *summaryview)
2113 {
2114         MainWindow *mainwin = summaryview->mainwin;
2115         union CompositeWin *cwin = &mainwin->win;
2116         GtkWidget *vpaned = NULL;
2117         GtkWidget *container = NULL;
2118
2119         switch (mainwin->type) {
2120         case SEPARATE_NONE:
2121                 vpaned = cwin->sep_none.vpaned;
2122                 container = cwin->sep_none.hpaned;
2123                 break;
2124         case SEPARATE_FOLDER:
2125                 vpaned = cwin->sep_folder.vpaned;
2126                 container = mainwin->vbox_body;
2127                 break;
2128         case SEPARATE_MESSAGE:
2129         case SEPARATE_BOTH:
2130                 return;
2131         }
2132
2133         if (vpaned->parent != NULL) {
2134                 summaryview->msg_is_toggled_on = FALSE;
2135                 summaryview->displayed = NULL;
2136                 gtk_widget_ref(vpaned);
2137                 gtk_container_remove(GTK_CONTAINER(container), vpaned);
2138                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), container);
2139                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2140                               GTK_ARROW_UP, GTK_SHADOW_OUT);
2141         } else {
2142                 summaryview->msg_is_toggled_on = TRUE;
2143                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), vpaned);
2144                 gtk_container_add(GTK_CONTAINER(container), vpaned);
2145                 gtk_widget_unref(vpaned);
2146                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2147                               GTK_ARROW_DOWN, GTK_SHADOW_OUT);
2148         }
2149
2150         gtk_widget_grab_focus(summaryview->ctree);
2151 }
2152
2153 static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2154 {
2155         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2156         MsgInfo *msginfo;
2157         MsgFlags flags;
2158
2159         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2160         if (!msginfo) return;
2161
2162         flags = msginfo->flags;
2163
2164         gtk_ctree_node_set_foreground(ctree, row, &summaryview->color_normal);
2165
2166         /* set new/unread column */
2167         if (MSG_IS_IGNORE_THREAD(flags)) {
2168                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2169                                           ignorethreadxpm, ignorethreadxpmmask);
2170         } else if (MSG_IS_NEW(flags)) {
2171                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2172                                           newxpm, newxpmmask);
2173         } else if (MSG_IS_UNREAD(flags)) {
2174                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2175                                           unreadxpm, unreadxpmmask);
2176         } else if (MSG_IS_REPLIED(flags)) {
2177                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2178                                           repliedxpm, repliedxpmmask);
2179         } else if (MSG_IS_FORWARDED(flags)) {
2180                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2181                                           forwardedxpm, forwardedxpmmask);
2182         } else {
2183                 gtk_ctree_node_set_text(ctree, row, S_COL_UNREAD, NULL);
2184         }
2185
2186         /* set mark column */
2187         if (MSG_IS_DELETED(flags)) {
2188                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_MARK,
2189                                           deletedxpm, deletedxpmmask);
2190                 gtk_ctree_node_set_foreground(ctree, row,
2191                                               &summaryview->color_dim);
2192         } else if (MSG_IS_MARKED(flags)) {
2193                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_MARK,
2194                                           markxpm, markxpmmask);
2195         } else if (MSG_IS_MOVE(flags)) {
2196                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "o");
2197                 gtk_ctree_node_set_foreground(ctree, row,
2198                                               &summaryview->color_marked);
2199         } else if (MSG_IS_COPY(flags)) {
2200                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "O");
2201                 gtk_ctree_node_set_foreground(ctree, row,
2202                                               &summaryview->color_marked);
2203         }
2204         else if ((global_scoring ||
2205                   summaryview->folder_item->prefs->scoring) &&
2206                  (msginfo->score >= summaryview->important_score) &&
2207                  ((msginfo->flags &
2208                    (MSG_MARKED | MSG_MOVE | MSG_COPY)) == 0)) {
2209                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "!");
2210                 gtk_ctree_node_set_foreground(ctree, row,
2211                                               &summaryview->color_important);
2212         } else {
2213                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, NULL);
2214         }
2215
2216         if (MSG_IS_MIME(flags)) {
2217                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_MIME,
2218                                           clipxpm, clipxpmmask);
2219         } else {
2220                 gtk_ctree_node_set_text(ctree, row, S_COL_MIME, NULL);
2221         }
2222 }
2223
2224 void summary_set_marks_selected(SummaryView *summaryview)
2225 {
2226         summary_set_row_marks(summaryview, summaryview->selected);
2227 }
2228
2229 static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
2230 {
2231         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2232         MsgInfo *msginfo;
2233
2234         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2235         msginfo->to_folder = NULL;
2236         if (MSG_IS_DELETED(msginfo->flags))
2237                 summaryview->deleted--;
2238         if (MSG_IS_MOVE(msginfo->flags))
2239                 summaryview->moved--;
2240         if (MSG_IS_COPY(msginfo->flags))
2241                 summaryview->copied--;
2242         MSG_UNSET_FLAGS(msginfo->flags, MSG_DELETED | MSG_MOVE | MSG_COPY);
2243         MSG_SET_FLAGS(msginfo->flags, MSG_MARKED);
2244
2245         CHANGE_FLAGS(msginfo);
2246
2247         summary_set_row_marks(summaryview, row);
2248         debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2249 }
2250
2251 void summary_mark(SummaryView *summaryview)
2252 {
2253         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2254         GList *cur;
2255
2256         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2257                 summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
2258
2259         /* summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); */
2260         summary_status_show(summaryview);
2261 }
2262
2263 static void summary_mark_row_as_read(SummaryView *summaryview,
2264                                      GtkCTreeNode *row)
2265 {
2266         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2267         MsgInfo *msginfo;
2268
2269         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2270         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2271                 summaryview->newmsgs--;
2272         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2273                 summaryview->unread--;
2274         if (MSG_IS_NEW(msginfo->flags) ||
2275             MSG_IS_UNREAD(msginfo->flags)) {
2276                 MSG_UNSET_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2277
2278                 CHANGE_FLAGS(msginfo);
2279                 
2280                 summary_set_row_marks(summaryview, row);
2281                 debug_print(_("Message %d is marked as read\n"),
2282                             msginfo->msgnum);
2283         }
2284 }
2285
2286 void summary_mark_as_read(SummaryView *summaryview)
2287 {
2288         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2289         GList *cur;
2290
2291         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2292                 summary_mark_row_as_read(summaryview,
2293                                          GTK_CTREE_NODE(cur->data));
2294
2295         summary_status_show(summaryview);
2296 }
2297
2298 static void summary_mark_row_as_unread(SummaryView *summaryview,
2299                                        GtkCTreeNode *row)
2300 {
2301         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2302         MsgInfo *msginfo;
2303
2304         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2305         if (MSG_IS_DELETED(msginfo->flags)) {
2306                 msginfo->to_folder = NULL;
2307                 MSG_UNSET_FLAGS(msginfo->flags, MSG_DELETED);
2308                 summaryview->deleted--;
2309         }
2310         MSG_UNSET_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
2311         if (!MSG_IS_UNREAD(msginfo->flags)) {
2312                 MSG_SET_FLAGS(msginfo->flags, MSG_UNREAD);
2313                 gtk_ctree_node_set_pixmap(ctree, row, S_COL_UNREAD,
2314                                           unreadxpm, unreadxpmmask);
2315                 summaryview->unread++;
2316                 debug_print(_("Message %d is marked as unread\n"),
2317                             msginfo->msgnum);
2318         }
2319
2320         CHANGE_FLAGS(msginfo);
2321
2322         summary_set_row_marks(summaryview, row);
2323 }
2324
2325 void summary_mark_as_unread(SummaryView *summaryview)
2326 {
2327         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2328         GList *cur;
2329
2330         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2331                 summary_mark_row_as_unread(summaryview,
2332                                            GTK_CTREE_NODE(cur->data));
2333
2334         summary_status_show(summaryview);
2335 }
2336
2337 static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
2338 {
2339         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2340         MsgInfo *msginfo;
2341
2342         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2343
2344         if (MSG_IS_DELETED(msginfo->flags)) return;
2345
2346         msginfo->to_folder = NULL;
2347         if (MSG_IS_MOVE(msginfo->flags))
2348                 summaryview->moved--;
2349         if (MSG_IS_COPY(msginfo->flags))
2350                 summaryview->copied--;
2351         MSG_UNSET_FLAGS(msginfo->flags,
2352                         MSG_MARKED |
2353                         MSG_MOVE |
2354                         MSG_COPY);
2355         MSG_SET_FLAGS(msginfo->flags, MSG_DELETED);
2356
2357         CHANGE_FLAGS(msginfo);
2358
2359         summaryview->deleted++;
2360
2361         if (!prefs_common.immediate_exec)
2362                 summary_set_row_marks(summaryview, row);
2363
2364         debug_print(_("Message %s/%d is set to delete\n"),
2365                     msginfo->folder->path, msginfo->msgnum);
2366 }
2367
2368 void summary_delete(SummaryView *summaryview)
2369 {
2370         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2371         GList *cur;
2372
2373         if (!summaryview->folder_item ||
2374             summaryview->folder_item->folder->type == F_NEWS) return;
2375
2376         /* if current folder is trash, don't delete */
2377         if (summaryview->folder_item->stype == F_TRASH) {
2378                 alertpanel_notice(_("Current folder is Trash."));
2379                 return;
2380         }
2381
2382         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2383                 summary_delete_row(summaryview, GTK_CTREE_NODE(cur->data));
2384
2385         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2386
2387         if (prefs_common.immediate_exec)
2388                 summary_execute(summaryview);
2389         else
2390                 summary_status_show(summaryview);
2391 }
2392
2393 void summary_delete_duplicated(SummaryView *summaryview)
2394 {
2395         if (!summaryview->folder_item ||
2396             summaryview->folder_item->folder->type == F_NEWS) return;
2397         if (summaryview->folder_item->stype == F_TRASH) return;
2398
2399         main_window_cursor_wait(summaryview->mainwin);
2400         debug_print(_("Deleting duplicated messages..."));
2401         STATUSBAR_PUSH(summaryview->mainwin,
2402                        _("Deleting duplicated messages..."));
2403
2404         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2405                                 GTK_CTREE_FUNC(summary_delete_duplicated_func),
2406                                 summaryview);
2407
2408         if (prefs_common.immediate_exec)
2409                 summary_execute(summaryview);
2410         else
2411                 summary_status_show(summaryview);
2412
2413         debug_print(_("done.\n"));
2414         STATUSBAR_POP(summaryview->mainwin);
2415         main_window_cursor_normal(summaryview->mainwin);
2416 }
2417
2418 static void summary_delete_duplicated_func(GtkCTree *ctree, GtkCTreeNode *node,
2419                                            SummaryView *summaryview)
2420 {
2421         GtkCTreeNode *found;
2422         MsgInfo *msginfo = GTK_CTREE_ROW(node)->row.data;
2423
2424         if (!msginfo->msgid || !*msginfo->msgid) return;
2425
2426         found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
2427
2428         if (found && found != node)
2429                 summary_delete_row(summaryview, node);
2430 }
2431
2432 static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
2433 {
2434         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2435         MsgInfo *msginfo;
2436
2437         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2438         msginfo->to_folder = NULL;
2439         if (MSG_IS_DELETED(msginfo->flags))
2440                 summaryview->deleted--;
2441         if (MSG_IS_MOVE(msginfo->flags))
2442                 summaryview->moved--;
2443         if (MSG_IS_COPY(msginfo->flags))
2444                 summaryview->copied--;
2445         MSG_UNSET_FLAGS(msginfo->flags,
2446                         MSG_MARKED |
2447                         MSG_DELETED |
2448                         MSG_MOVE |
2449                         MSG_COPY);
2450
2451         CHANGE_FLAGS(msginfo);
2452
2453         summary_set_row_marks(summaryview, row);
2454
2455         debug_print(_("Message %s/%d is unmarked\n"),
2456                     msginfo->folder->path, msginfo->msgnum);
2457 }
2458
2459 void summary_unmark(SummaryView *summaryview)
2460 {
2461         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2462         GList *cur;
2463
2464         for (cur = GTK_CLIST(ctree)->selection; cur != NULL;
2465              cur = cur->next)
2466                 summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
2467
2468         summary_status_show(summaryview);
2469 }
2470
2471 static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2472                                 FolderItem *to_folder)
2473 {
2474         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2475         MsgInfo *msginfo;
2476
2477         g_return_if_fail(to_folder != NULL);
2478
2479         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2480         msginfo->to_folder = to_folder;
2481         if (MSG_IS_DELETED(msginfo->flags))
2482                 summaryview->deleted--;
2483         MSG_UNSET_FLAGS(msginfo->flags,
2484                         MSG_MARKED | MSG_DELETED | MSG_COPY);
2485         if (!MSG_IS_MOVE(msginfo->flags)) {
2486                 MSG_SET_FLAGS(msginfo->flags, MSG_MOVE);
2487                 summaryview->moved++;
2488         }
2489
2490         if (!prefs_common.immediate_exec)
2491                 summary_set_row_marks(summaryview, row);
2492
2493         debug_print(_("Message %d is set to move to %s\n"),
2494                     msginfo->msgnum, to_folder->path);
2495 }
2496
2497 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2498 {
2499         GList *cur;
2500
2501         if (!to_folder) return;
2502         if (!summaryview->folder_item ||
2503             summaryview->folder_item->folder->type == F_NEWS) return;
2504         if (summaryview->folder_item == to_folder) {
2505                 alertpanel_notice(_("Destination is same as current folder."));
2506                 return;
2507         }
2508
2509         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2510              cur != NULL; cur = cur->next)
2511                 summary_move_row_to
2512                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2513
2514         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2515
2516         if (prefs_common.immediate_exec)
2517                 summary_execute(summaryview);
2518         else
2519                 summary_status_show(summaryview);
2520 }
2521
2522 void summary_move_to(SummaryView *summaryview)
2523 {
2524         FolderItem *to_folder;
2525
2526         if (!summaryview->folder_item ||
2527             summaryview->folder_item->folder->type == F_NEWS) return;
2528
2529         to_folder = foldersel_folder_sel(NULL, NULL);
2530         summary_move_selected_to(summaryview, to_folder);
2531 }
2532
2533 static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2534                                 FolderItem *to_folder)
2535 {
2536         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2537         MsgInfo *msginfo;
2538
2539         g_return_if_fail(to_folder != NULL);
2540
2541         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2542         msginfo->to_folder = to_folder;
2543         if (MSG_IS_DELETED(msginfo->flags))
2544                 summaryview->deleted--;
2545         MSG_UNSET_FLAGS(msginfo->flags,
2546                         MSG_MARKED | MSG_DELETED | MSG_MOVE);
2547         if (!MSG_IS_COPY(msginfo->flags)) {
2548                 MSG_SET_FLAGS(msginfo->flags, MSG_COPY);
2549                 summaryview->copied++;
2550         }
2551
2552         if (!prefs_common.immediate_exec)
2553                 summary_set_row_marks(summaryview, row);
2554
2555         debug_print(_("Message %d is set to copy to %s\n"),
2556                     msginfo->msgnum, to_folder->path);
2557 }
2558
2559 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2560 {
2561         GList *cur;
2562
2563         if (!to_folder) return;
2564         if (!summaryview->folder_item ||
2565             summaryview->folder_item->folder->type == F_NEWS) return;
2566         if (summaryview->folder_item == to_folder) {
2567                 alertpanel_notice
2568                         (_("Destination to copy is same as current folder."));
2569                 return;
2570         }
2571
2572         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2573              cur != NULL; cur = cur->next)
2574                 summary_copy_row_to
2575                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2576
2577         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2578
2579         if (prefs_common.immediate_exec)
2580                 summary_execute(summaryview);
2581         else
2582                 summary_status_show(summaryview);
2583 }
2584
2585 void summary_copy_to(SummaryView *summaryview)
2586 {
2587         FolderItem *to_folder;
2588
2589         if (!summaryview->folder_item ||
2590             summaryview->folder_item->folder->type == F_NEWS) return;
2591
2592         to_folder = foldersel_folder_sel(NULL, NULL);
2593         summary_copy_selected_to(summaryview, to_folder);
2594 }
2595
2596 void summary_select_all(SummaryView *summaryview)
2597 {
2598         if (summaryview->messages >= 500) {
2599                 STATUSBAR_PUSH(summaryview->mainwin,
2600                                _("Selecting all messages..."));
2601                 main_window_cursor_wait(summaryview->mainwin);
2602         }
2603
2604         gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
2605
2606         if (summaryview->messages >= 500) {
2607                 STATUSBAR_POP(summaryview->mainwin);
2608                 main_window_cursor_normal(summaryview->mainwin);
2609         }
2610 }
2611
2612 void summary_unselect_all(SummaryView *summaryview)
2613 {
2614         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
2615 }
2616
2617 void summary_save_as(SummaryView *summaryview)
2618 {
2619         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2620         MsgInfo *msginfo;
2621         gchar *src, *dest;
2622
2623         if (!summaryview->selected) return;
2624         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2625         if (!msginfo) return;
2626
2627         dest = filesel_select_file(_("Save as"), NULL);
2628         if (!dest) return;
2629         if (is_file_exist(dest)) {
2630                 AlertValue aval;
2631
2632                 aval = alertpanel(_("Overwrite"),
2633                                   _("Overwrite existing file?"),
2634                                   _("OK"), _("Cancel"), NULL);
2635                 if (G_ALERTDEFAULT != aval) return;
2636         }
2637
2638         src = procmsg_get_message_file(msginfo);
2639         copy_file(src, dest);
2640         g_free(src);
2641 }
2642
2643 void summary_print(SummaryView *summaryview)
2644 {
2645         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2646         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2647         MsgInfo *msginfo;
2648         GList *cur;
2649         gchar *cmdline;
2650         gchar *p;
2651
2652         if (clist->selection == NULL) return;
2653
2654         cmdline = input_dialog(_("Print"),
2655                                _("Enter the print command line:\n"
2656                                  "(`%s' will be replaced with file name)"),
2657                                prefs_common.print_cmd);
2658         if (!cmdline) return;
2659         if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
2660             strchr(p + 2, '%')) {
2661                 alertpanel_error(_("Print command line is invalid:\n`%s'"),
2662                                  cmdline);
2663                 g_free(cmdline);
2664                 return;
2665         }
2666
2667         for (cur = clist->selection; cur != NULL; cur = cur->next) {
2668                 msginfo = gtk_ctree_node_get_row_data
2669                         (ctree, GTK_CTREE_NODE(cur->data));
2670                 if (msginfo) procmsg_print_message(msginfo, cmdline);
2671         }
2672         
2673         g_free(cmdline);
2674 }
2675
2676 void summary_execute(SummaryView *summaryview)
2677 {
2678         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2679         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2680         GtkCTreeNode *node, *next;
2681
2682         if (!summaryview->folder_item ||
2683             summaryview->folder_item->folder->type == F_NEWS) return;
2684
2685         gtk_clist_freeze(clist);
2686
2687         if (summaryview->folder_item->prefs->enable_thread)
2688                 /*      if (prefs_common.enable_thread) */
2689                 summary_unthread_for_exec(summaryview);
2690
2691         summary_execute_move(summaryview);
2692         summary_execute_copy(summaryview);
2693         summary_execute_delete(summaryview);
2694
2695         node = GTK_CTREE_NODE(clist->row_list);
2696         while (node != NULL) {
2697                 next = GTK_CTREE_NODE_NEXT(node);
2698                 if (gtk_ctree_node_get_row_data(ctree, node) == NULL) {
2699                         if (node == summaryview->displayed) {
2700                                 messageview_clear(summaryview->messageview);
2701                                 summaryview->displayed = NULL;
2702                         }
2703                         if (GTK_CTREE_ROW(node)->children != NULL)
2704                                 g_warning("summary_execute(): children != NULL\n");
2705                         else
2706                                 gtk_ctree_remove_node(ctree, node);
2707                 }
2708                 node = next;
2709         }
2710
2711         if (summaryview->folder_item->prefs->enable_thread)
2712                 /*      if (prefs_common.enable_thread) */
2713                 summary_thread_build(summaryview);
2714
2715         summaryview->selected = clist->selection ?
2716                 GTK_CTREE_NODE(clist->selection->data) : NULL;
2717
2718         if (!GTK_CLIST(summaryview->ctree)->row_list) {
2719                 menu_set_insensitive_all
2720                         (GTK_MENU_SHELL(summaryview->popupmenu));
2721                 gtk_widget_grab_focus(summaryview->folderview->ctree);
2722         } else
2723                 gtk_widget_grab_focus(summaryview->ctree);
2724
2725         summary_update_status(summaryview);
2726         summary_status_show(summaryview);
2727
2728         summary_write_cache(summaryview);
2729
2730         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
2731
2732         gtk_clist_thaw(clist);
2733 }
2734
2735 static void summary_execute_move(SummaryView *summaryview)
2736 {
2737         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2738         GSList *cur;
2739
2740         summaryview->folder_table = g_hash_table_new(NULL, NULL);
2741
2742         /* search moving messages and execute */
2743         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
2744                                 summaryview);
2745
2746         if (summaryview->mlist) {
2747                 procmsg_move_messages(summaryview->mlist);
2748
2749                 folder_item_scan_foreach(summaryview->folder_table);
2750                 folderview_update_item_foreach(summaryview->folder_table);
2751
2752                 for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
2753                         procmsg_msginfo_free((MsgInfo *)cur->data);
2754                 g_slist_free(summaryview->mlist);
2755                 summaryview->mlist = NULL;
2756         }
2757
2758         g_hash_table_destroy(summaryview->folder_table);
2759         summaryview->folder_table = NULL;
2760 }
2761
2762 static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
2763                                       gpointer data)
2764 {
2765         SummaryView *summaryview = data;
2766         MsgInfo *msginfo;
2767
2768         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2769
2770         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
2771                 g_hash_table_insert(summaryview->folder_table,
2772                                     msginfo->to_folder, GINT_TO_POINTER(1));
2773
2774                 summaryview->mlist =
2775                         g_slist_append(summaryview->mlist, msginfo);
2776                 gtk_ctree_node_set_row_data(ctree, node, NULL);
2777
2778                 if (msginfo->msgid && *msginfo->msgid &&
2779                     node == g_hash_table_lookup(summaryview->msgid_table,
2780                                                 msginfo->msgid))
2781                         g_hash_table_remove(summaryview->msgid_table,
2782                                             msginfo->msgid);
2783         }
2784 }
2785
2786 static void summary_execute_copy(SummaryView *summaryview)
2787 {
2788         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2789
2790         summaryview->folder_table = g_hash_table_new(NULL, NULL);
2791
2792         /* search copying messages and execute */
2793         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
2794                                 summaryview);
2795
2796         if (summaryview->mlist) {
2797                 procmsg_copy_messages(summaryview->mlist);
2798
2799                 folder_item_scan_foreach(summaryview->folder_table);
2800                 folderview_update_item_foreach(summaryview->folder_table);
2801
2802                 g_slist_free(summaryview->mlist);
2803                 summaryview->mlist = NULL;
2804         }
2805
2806         g_hash_table_destroy(summaryview->folder_table);
2807         summaryview->folder_table = NULL;
2808 }
2809
2810 static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
2811                                       gpointer data)
2812 {
2813         SummaryView *summaryview = data;
2814         MsgInfo *msginfo;
2815
2816         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2817
2818         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
2819                 g_hash_table_insert(summaryview->folder_table,
2820                                     msginfo->to_folder, GINT_TO_POINTER(1));
2821
2822                 summaryview->mlist =
2823                         g_slist_append(summaryview->mlist, msginfo);
2824
2825                 MSG_UNSET_FLAGS(msginfo->flags, MSG_COPY);
2826                 
2827                 summary_set_row_marks(summaryview, node);
2828         }
2829 }
2830
2831 static void summary_execute_delete(SummaryView *summaryview)
2832 {
2833         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2834         FolderItem *trash;
2835         GSList *cur;
2836
2837         trash = summaryview->folder_item->folder->trash;
2838         if (summaryview->folder_item->folder->type == F_MH) {
2839                 g_return_if_fail(trash != NULL);
2840         }
2841         if (summaryview->folder_item == trash) return;
2842
2843         /* search deleting messages and execute */
2844         gtk_ctree_pre_recursive
2845                 (ctree, NULL, summary_execute_delete_func, summaryview);
2846
2847         if (!summaryview->mlist) return;
2848
2849         for(cur = summaryview->mlist ; cur != NULL ; cur = cur->next) {
2850                 MsgInfo * msginfo = cur->data;
2851                 MSG_UNSET_FLAGS(msginfo->flags, MSG_DELETED);
2852         }
2853
2854         folder_item_move_msgs_with_dest(trash, summaryview->mlist);
2855
2856         for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
2857                 procmsg_msginfo_free((MsgInfo *)cur->data);
2858
2859         g_slist_free(summaryview->mlist);
2860         summaryview->mlist = NULL;
2861
2862         folder_item_scan(trash);
2863         folderview_update_item(trash, FALSE);
2864 }
2865
2866 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
2867                                         gpointer data)
2868 {
2869         SummaryView *summaryview = data;
2870         MsgInfo *msginfo;
2871
2872         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2873
2874         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
2875                 summaryview->mlist =
2876                         g_slist_append(summaryview->mlist, msginfo);
2877                 gtk_ctree_node_set_row_data(ctree, node, NULL);
2878
2879                 if (msginfo->msgid && *msginfo->msgid &&
2880                     node == g_hash_table_lookup(summaryview->msgid_table,
2881                                                 msginfo->msgid))
2882                         g_hash_table_remove(summaryview->msgid_table,
2883                                             msginfo->msgid);
2884
2885                 if (msginfo->subject && 
2886                     node == subject_table_lookup(summaryview->subject_table, 
2887                                                 msginfo->subject)) {
2888                         gchar *s = msginfo->subject + (g_strncasecmp(msginfo->subject, "Re: ", 4) == 0 ? 4 : 0);
2889                         g_hash_table_remove(summaryview->subject_table, s);
2890                 }                       
2891         }
2892 }
2893
2894 /* thread functions */
2895
2896 void summary_thread_build(SummaryView *summaryview)
2897 {
2898         debug_print(_("Building threads..."));
2899         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
2900         main_window_cursor_wait(summaryview->mainwin);
2901
2902         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
2903
2904         gtk_ctree_pre_recursive_to_depth
2905                 (GTK_CTREE(summaryview->ctree), NULL, 1,
2906                  GTK_CTREE_FUNC(summary_thread_func),
2907                  summaryview);
2908
2909         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
2910
2911         debug_print(_("done.\n"));
2912         STATUSBAR_POP(summaryview->mainwin);
2913         main_window_cursor_normal(summaryview->mainwin);
2914 }
2915
2916 void summary_unthread(SummaryView *summaryview)
2917 {
2918         GtkCTreeNode *node;
2919         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2920
2921         debug_print(_("Unthreading..."));
2922         STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
2923         main_window_cursor_wait(summaryview->mainwin);
2924
2925         gtk_clist_freeze(GTK_CLIST(ctree));
2926
2927         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2928              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
2929                 summary_unthread_func(ctree, node, NULL);
2930         }
2931
2932         gtk_clist_thaw(GTK_CLIST(ctree));
2933
2934         debug_print(_("done.\n"));
2935         STATUSBAR_POP(summaryview->mainwin);
2936         main_window_cursor_normal(summaryview->mainwin);
2937 }
2938
2939 static void summary_unthread_for_exec(SummaryView *summaryview)
2940 {
2941         GtkCTreeNode *node;
2942         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2943
2944         debug_print(_("Unthreading for execution..."));
2945
2946         gtk_clist_freeze(GTK_CLIST(ctree));
2947
2948         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2949              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
2950                 summary_unthread_for_exec_func(ctree, node, NULL);
2951         }
2952
2953         gtk_clist_thaw(GTK_CLIST(ctree));
2954
2955         debug_print(_("done.\n"));
2956 }
2957
2958 static void summary_thread_func(GtkCTree *ctree, GtkCTreeNode *node,
2959                                 gpointer data)
2960 {
2961         MsgInfo *msginfo;
2962         GtkCTreeNode *parent = NULL;
2963         
2964         SummaryView * summaryview = (SummaryView *) data;
2965         GHashTable *msgid_table = summaryview->msgid_table;
2966         GHashTable *subject_table = summaryview->subject_table;
2967
2968         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2969
2970         if (!msginfo) return;
2971
2972         if(msginfo->inreplyto) {
2973             parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
2974         }
2975         if (parent == NULL && msginfo->subject) {
2976                 parent = subject_table_lookup(subject_table, msginfo->subject);
2977         }
2978
2979         if (parent && parent != node) {
2980                 gtk_ctree_move(ctree, node, parent, NULL);
2981                 gtk_ctree_expand(ctree, parent);
2982         }
2983 }
2984
2985 static void summary_unthread_func(GtkCTree *ctree, GtkCTreeNode *node,
2986                                   gpointer data)
2987 {
2988         GtkCTreeNode *child;
2989         GtkCTreeNode *sibling;
2990
2991         child = GTK_CTREE_ROW(node)->children;
2992         sibling = GTK_CTREE_ROW(node)->sibling;
2993
2994         while (child != NULL) {
2995                 GtkCTreeNode *next_child;
2996
2997                 next_child = GTK_CTREE_ROW(child)->sibling;
2998                 gtk_ctree_move(ctree, child, NULL, sibling);
2999                 child = next_child;
3000         }
3001 }
3002
3003 static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
3004                                            gpointer data)
3005 {
3006         MsgInfo *msginfo;
3007         GtkCTreeNode *top_parent;
3008         GtkCTreeNode *child;
3009         GtkCTreeNode *sibling;
3010
3011         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3012
3013         if (!msginfo ||
3014             (!MSG_IS_MOVE(msginfo->flags) &&
3015              !MSG_IS_DELETED(msginfo->flags)))
3016                 return;
3017         child = GTK_CTREE_ROW(node)->children;
3018         if (!child) return;
3019
3020         for (top_parent = node;
3021              GTK_CTREE_ROW(top_parent)->parent != NULL;
3022              top_parent = GTK_CTREE_ROW(top_parent)->parent)
3023                 ;
3024         sibling = GTK_CTREE_ROW(top_parent)->sibling;
3025
3026         while (child != NULL) {
3027                 GtkCTreeNode *next_child;
3028
3029                 next_child = GTK_CTREE_ROW(child)->sibling;
3030                 gtk_ctree_move(ctree, child, NULL, sibling);
3031                 child = next_child;
3032         }
3033 }
3034
3035 void summary_filter(SummaryView *summaryview)
3036 {
3037         if (!prefs_common.fltlist) return;
3038
3039         debug_print(_("filtering..."));
3040         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
3041         main_window_cursor_wait(summaryview->mainwin);
3042
3043         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
3044
3045         if (prefs_filtering == NULL) {
3046                 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3047                                         GTK_CTREE_FUNC(summary_filter_func),
3048                                         summaryview);
3049                 
3050                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3051
3052                 if (prefs_common.immediate_exec)
3053                         summary_execute(summaryview);
3054                 else
3055                         summary_status_show(summaryview);
3056         }
3057         else {
3058                 summaryview->folder_table = g_hash_table_new(NULL, NULL);
3059
3060                 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3061                                         GTK_CTREE_FUNC(summary_filter_func),
3062                                         summaryview);
3063
3064                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3065
3066                 folder_item_scan_foreach(summaryview->folder_table);
3067                 folderview_update_item_foreach(summaryview->folder_table);
3068
3069                 g_hash_table_destroy(summaryview->folder_table);
3070                 summaryview->folder_table = NULL;
3071
3072                 summary_show(summaryview, summaryview->folder_item, FALSE);
3073         }
3074
3075         debug_print(_("done.\n"));
3076         STATUSBAR_POP(summaryview->mainwin);
3077         main_window_cursor_normal(summaryview->mainwin);
3078 }
3079
3080 static void summary_filter_func(GtkCTree *ctree, GtkCTreeNode *node,
3081                                 gpointer data)
3082 {
3083         MsgInfo *msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3084         SummaryView *summaryview = data;
3085         gchar *file;
3086         FolderItem *dest;
3087
3088         if (prefs_filtering == NULL) {
3089                 /* old filtering */
3090                 file = procmsg_get_message_file_path(msginfo);
3091                 dest = filter_get_dest_folder(prefs_common.fltlist, file);
3092                 g_free(file);
3093
3094                 if (dest && strcmp2(dest->path, FILTER_NOT_RECEIVE) != 0 &&
3095                     summaryview->folder_item != dest)
3096                         summary_move_row_to(summaryview, node, dest);
3097         }
3098         else
3099                 filter_msginfo_move_or_delete(prefs_filtering, msginfo,
3100                                               summaryview->folder_table);
3101 }
3102
3103 /* callback functions */
3104
3105 static void summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
3106                                    SummaryView *summaryview)
3107 {
3108         if (!event)
3109                 return;
3110
3111         if (!summaryview->msg_is_toggled_on && summaryview->selected)
3112                 summary_display_msg(summaryview, summaryview->selected, FALSE);
3113         else
3114                 summary_toggle_view(summaryview);
3115 }
3116
3117 static void summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
3118                                    SummaryView *summaryview)
3119 {
3120         if (!event) return;
3121
3122         if (event->button == 3) {
3123                 /* right clicked */
3124                 summary_add_sender_to_cb(summaryview, 0, 0);    
3125                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
3126                                NULL, NULL, event->button, event->time);
3127         } else if (event->button == 2) {
3128                 summaryview->display_msg = TRUE;
3129         } else if (event->button == 1) {
3130                 if (!prefs_common.emulate_emacs &&
3131                     summaryview->msg_is_toggled_on)
3132                         summaryview->display_msg = TRUE;
3133         }
3134 }
3135
3136 static void summary_button_released(GtkWidget *ctree, GdkEventButton *event,
3137                                     SummaryView *summaryview)
3138 {
3139 }
3140
3141 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
3142 {
3143         summary_key_pressed(summaryview->ctree, event, summaryview);
3144 }
3145
3146 #define BREAK_ON_MODIFIER_KEY() \
3147         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
3148
3149 #define KEY_PRESS_EVENT_STOP() \
3150         if (gtk_signal_n_emissions_by_name \
3151                 (GTK_OBJECT(ctree), "key_press_event") > 0) { \
3152                 gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree), \
3153                                              "key_press_event"); \
3154         }
3155
3156 static void summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
3157                                 SummaryView *summaryview)
3158 {
3159         GtkCTree *ctree = GTK_CTREE(widget);
3160         GtkCTreeNode *node;
3161         FolderItem *to_folder;
3162
3163         if (!event) return;
3164
3165         switch (event->keyval) {
3166         case GDK_g:             /* Go */
3167         case GDK_G:
3168                 BREAK_ON_MODIFIER_KEY();
3169                 KEY_PRESS_EVENT_STOP();
3170                 to_folder = foldersel_folder_sel(NULL, NULL);
3171                 if (to_folder) {
3172                         debug_print(_("Go to %s\n"), to_folder->path);
3173                         folderview_select(summaryview->folderview, to_folder);
3174                 }
3175                 return;
3176         case GDK_w:             /* Write new message */
3177                 BREAK_ON_MODIFIER_KEY();
3178                 if (summaryview->folder_item) {
3179                         PrefsAccount *ac;
3180                         ac = summaryview->folder_item->folder->account;
3181                         if (ac && ac->protocol == A_NNTP)
3182                                 compose_new_with_recipient
3183                                         (ac, summaryview->folder_item->path);
3184                         else
3185                                 compose_new(ac);
3186                 } else
3187                         compose_new(NULL);
3188                 return;
3189         case GDK_D:             /* Empty trash */
3190                 BREAK_ON_MODIFIER_KEY();
3191                 KEY_PRESS_EVENT_STOP();
3192                 main_window_empty_trash(summaryview->mainwin, TRUE);
3193                 return;
3194         case GDK_Q:             /* Quit */
3195                 BREAK_ON_MODIFIER_KEY();
3196
3197                 if (prefs_common.confirm_on_exit) {
3198                         if (alertpanel(_("Exit"), _("Exit this program?"),
3199                                        _("OK"), _("Cancel"), NULL)
3200                                        == G_ALERTDEFAULT) {
3201                                 manage_window_focus_in
3202                                         (summaryview->mainwin->window,
3203                                          NULL, NULL);
3204                                 app_will_exit(NULL, summaryview->mainwin);
3205                         }
3206                 }
3207                 return;
3208         case GDK_Left:          /* Move focus */
3209         case GDK_Escape:
3210                 gtk_widget_grab_focus(summaryview->folderview->ctree);
3211                 return;
3212         default:
3213                 break;
3214         }
3215
3216         if (!summaryview->selected) {
3217                 node = gtk_ctree_node_nth(ctree, 0);
3218                 if (node)
3219                         gtk_ctree_select(ctree, node);
3220                 else
3221                         return;
3222         }
3223
3224         switch (event->keyval) {
3225         case GDK_space:         /* Page down or go to the next */
3226                 if (summaryview->displayed != summaryview->selected) {
3227                         summary_display_msg(summaryview,
3228                                             summaryview->selected, FALSE);
3229                         break;
3230                 }
3231                 if (!textview_scroll_page(summaryview->messageview->textview,
3232                                           FALSE))
3233                         summary_select_next_unread(summaryview);
3234                 break;
3235         case GDK_n:             /* Next */
3236         case GDK_N:
3237                 BREAK_ON_MODIFIER_KEY();
3238                 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3239                 break;
3240         case GDK_BackSpace:     /* Page up */
3241         case GDK_Delete:
3242                 textview_scroll_page(summaryview->messageview->textview, TRUE);
3243                 break;
3244         case GDK_p:             /* Prev */
3245         case GDK_P:
3246                 BREAK_ON_MODIFIER_KEY();
3247                 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
3248                 break;
3249         case GDK_v:             /* Toggle summary mode / message mode */
3250         case GDK_V:
3251                 BREAK_ON_MODIFIER_KEY();
3252
3253                 if (!summaryview->msg_is_toggled_on && summaryview->selected)
3254                         summary_display_msg(summaryview,
3255                                             summaryview->selected, FALSE);
3256                 else
3257                         summary_toggle_view(summaryview);
3258                 break;
3259         case GDK_Return:        /* Scroll up/down one line */
3260                 if (summaryview->displayed != summaryview->selected) {
3261                         summary_display_msg(summaryview,
3262                                             summaryview->selected, FALSE);
3263                         break;
3264                 }
3265                 textview_scroll_one_line(summaryview->messageview->textview,
3266                                          (event->state & GDK_MOD1_MASK) != 0);
3267                 break;
3268         case GDK_asterisk:      /* Mark */
3269                 summary_mark(summaryview);
3270                 break;
3271         case GDK_exclam:        /* Mark as unread */
3272                 summary_mark_as_unread(summaryview);
3273                 break;
3274         case GDK_d:             /* Delete */
3275                 BREAK_ON_MODIFIER_KEY();
3276                 summary_delete(summaryview);
3277                 break;
3278         case GDK_u:             /* Unmark */
3279         case GDK_U:
3280                 BREAK_ON_MODIFIER_KEY();
3281                 summary_unmark(summaryview);
3282                 break;
3283         case GDK_o:             /* Move */
3284                 BREAK_ON_MODIFIER_KEY();
3285                 summary_move_to(summaryview);
3286                 break;
3287         case GDK_O:             /* Copy */
3288                 BREAK_ON_MODIFIER_KEY();
3289                 summary_copy_to(summaryview);
3290                 break;
3291         case GDK_x:             /* Execute */
3292         case GDK_X:
3293                 BREAK_ON_MODIFIER_KEY();
3294                 KEY_PRESS_EVENT_STOP();
3295                 summary_execute(summaryview);
3296                 break;
3297         case GDK_a:             /* Reply to the message */
3298                 BREAK_ON_MODIFIER_KEY();
3299                 summary_reply_cb(summaryview,
3300                                  COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE, NULL);
3301                 break;
3302         case GDK_A:             /* Reply to the message with quotation */
3303                 BREAK_ON_MODIFIER_KEY();
3304                 summary_reply_cb(summaryview,
3305                                  COMPOSE_REPLY_TO_ALL_WITH_QUOTE, NULL);
3306                 break;
3307         case GDK_f:             /* Forward the message */
3308                 BREAK_ON_MODIFIER_KEY();
3309                 summary_reply_cb(summaryview, COMPOSE_FORWARD, NULL);
3310                 break;
3311         case GDK_F:
3312                 BREAK_ON_MODIFIER_KEY();
3313                 summary_reply_cb(summaryview, COMPOSE_FORWARD_AS_ATTACH, NULL);
3314                 break;
3315         case GDK_y:             /* Save the message */
3316                 BREAK_ON_MODIFIER_KEY();
3317                 summary_save_as(summaryview);
3318                 break;
3319         default:
3320                 break;
3321         }
3322 }
3323
3324 #undef BREAK_ON_MODIFIER_KEY
3325 #undef KEY_PRESS_EVENT_STOP
3326
3327 static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
3328 {
3329         if (summaryview->folder_item->stype == F_DRAFT)
3330                 summary_reedit(summaryview);
3331         else
3332                 summary_open_msg(summaryview);
3333
3334         summaryview->display_msg = FALSE;
3335 }
3336
3337 static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
3338                              gint column, SummaryView *summaryview)
3339 {
3340         MsgInfo *msginfo;
3341
3342         summary_status_show(summaryview);
3343         summary_set_menu_sensitive(summaryview);
3344
3345         if (GTK_CLIST(ctree)->selection &&
3346              GTK_CLIST(ctree)->selection->next) {
3347                 summaryview->display_msg = FALSE;
3348                 return;
3349         }
3350
3351         summaryview->selected = row;
3352
3353         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3354
3355         switch (column) {
3356         case S_COL_MARK:
3357                 if (MSG_IS_MARKED(msginfo->flags)) {
3358                         MSG_UNSET_FLAGS(msginfo->flags, MSG_MARKED);
3359
3360                         CHANGE_FLAGS(msginfo);
3361                         
3362                         summary_set_row_marks(summaryview, row);
3363                 } else
3364                         summary_mark_row(summaryview, row);
3365                 break;
3366         case S_COL_UNREAD:
3367                 if (MSG_IS_UNREAD(msginfo->flags)) {
3368                         summary_mark_row_as_read(summaryview, row);
3369                         summary_status_show(summaryview);
3370                 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
3371                          !MSG_IS_FORWARDED(msginfo->flags)) {
3372                         summary_mark_row_as_unread(summaryview, row);
3373                         summary_status_show(summaryview);
3374                 }
3375                 break;
3376         default:
3377                 break;
3378         }
3379
3380         if (summaryview->display_msg)
3381                 summary_display_msg(summaryview, row, FALSE);
3382