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