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