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