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