sync with sylpheed 0.4.65cvs10
[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         g_return_if_fail(trash != NULL);
2215         if (summaryview->folder_item == trash) return;
2216
2217         /* search deleting messages and execute */
2218         gtk_ctree_pre_recursive
2219                 (ctree, NULL, summary_execute_delete_func, summaryview);
2220
2221         if (!summaryview->mlist) return;
2222
2223         folder_item_move_msgs_with_dest(trash, summaryview->mlist);
2224
2225         for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
2226                 procmsg_msginfo_free((MsgInfo *)cur->data);
2227
2228         g_slist_free(summaryview->mlist);
2229         summaryview->mlist = NULL;
2230
2231         folder_item_scan(trash);
2232         folderview_update_item(trash, FALSE);
2233 }
2234
2235 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
2236                                         gpointer data)
2237 {
2238         SummaryView *summaryview = data;
2239         MsgInfo *msginfo;
2240
2241         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2242
2243         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
2244                 summaryview->mlist =
2245                         g_slist_append(summaryview->mlist, msginfo);
2246                 gtk_ctree_node_set_row_data(ctree, node, NULL);
2247
2248                 if (msginfo->msgid && *msginfo->msgid &&
2249                     node == g_hash_table_lookup(summaryview->msgid_table,
2250                                                 msginfo->msgid))
2251                         g_hash_table_remove(summaryview->msgid_table,
2252                                             msginfo->msgid);
2253         }
2254 }
2255
2256 /* thread functions */
2257
2258 void summary_thread_build(SummaryView *summaryview)
2259 {
2260         debug_print(_("Building threads..."));
2261         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
2262         main_window_cursor_wait(summaryview->mainwin);
2263
2264         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
2265
2266         gtk_ctree_pre_recursive_to_depth
2267                 (GTK_CTREE(summaryview->ctree), NULL, 1,
2268                  GTK_CTREE_FUNC(summary_thread_func),
2269                  summaryview->msgid_table);
2270
2271         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
2272
2273         debug_print(_("done.\n"));
2274         STATUSBAR_POP(summaryview->mainwin);
2275         main_window_cursor_normal(summaryview->mainwin);
2276 }
2277
2278 void summary_unthread(SummaryView *summaryview)
2279 {
2280         GtkCTreeNode *node;
2281         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2282
2283         debug_print(_("Unthreading..."));
2284         STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
2285         main_window_cursor_wait(summaryview->mainwin);
2286
2287         gtk_clist_freeze(GTK_CLIST(ctree));
2288
2289         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2290              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
2291                 summary_unthread_func(ctree, node, NULL);
2292         }
2293
2294         gtk_clist_thaw(GTK_CLIST(ctree));
2295
2296         debug_print(_("done.\n"));
2297         STATUSBAR_POP(summaryview->mainwin);
2298         main_window_cursor_normal(summaryview->mainwin);
2299 }
2300
2301 static void summary_unthread_for_exec(SummaryView *summaryview)
2302 {
2303         GtkCTreeNode *node;
2304         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2305
2306         debug_print(_("Unthreading for execution..."));
2307
2308         gtk_clist_freeze(GTK_CLIST(ctree));
2309
2310         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2311              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
2312                 summary_unthread_for_exec_func(ctree, node, NULL);
2313         }
2314
2315         gtk_clist_thaw(GTK_CLIST(ctree));
2316
2317         debug_print(_("done.\n"));
2318 }
2319
2320 static void summary_thread_func(GtkCTree *ctree, GtkCTreeNode *node,
2321                                 gpointer data)
2322 {
2323         MsgInfo *msginfo;
2324         GtkCTreeNode *parent;
2325         GHashTable *msgid_table = (GHashTable *)data;
2326
2327         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2328
2329         if (!msginfo || !msginfo->inreplyto) return;
2330
2331         parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto);
2332
2333         if (parent && parent != node) {
2334                 gtk_ctree_move(ctree, node, parent, NULL);
2335                 gtk_ctree_expand(ctree, parent);
2336         }
2337 }
2338
2339 static void summary_unthread_func(GtkCTree *ctree, GtkCTreeNode *node,
2340                                   gpointer data)
2341 {
2342         GtkCTreeNode *child;
2343         GtkCTreeNode *sibling;
2344
2345         child = GTK_CTREE_ROW(node)->children;
2346         sibling = GTK_CTREE_ROW(node)->sibling;
2347
2348         while (child != NULL) {
2349                 GtkCTreeNode *next_child;
2350
2351                 next_child = GTK_CTREE_ROW(child)->sibling;
2352                 gtk_ctree_move(ctree, child, NULL, sibling);
2353                 child = next_child;
2354         }
2355 }
2356
2357 static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
2358                                            gpointer data)
2359 {
2360         MsgInfo *msginfo;
2361         GtkCTreeNode *top_parent;
2362         GtkCTreeNode *child;
2363         GtkCTreeNode *sibling;
2364
2365         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2366
2367         if (!msginfo ||
2368             (!MSG_IS_MOVE(msginfo->flags) &&
2369              !MSG_IS_DELETED(msginfo->flags)))
2370                 return;
2371         child = GTK_CTREE_ROW(node)->children;
2372         if (!child) return;
2373
2374         for (top_parent = node;
2375              GTK_CTREE_ROW(top_parent)->parent != NULL;
2376              top_parent = GTK_CTREE_ROW(top_parent)->parent)
2377                 ;
2378         sibling = GTK_CTREE_ROW(top_parent)->sibling;
2379
2380         while (child != NULL) {
2381                 GtkCTreeNode *next_child;
2382
2383                 next_child = GTK_CTREE_ROW(child)->sibling;
2384                 gtk_ctree_move(ctree, child, NULL, sibling);
2385                 child = next_child;
2386         }
2387 }
2388
2389 void summary_filter(SummaryView *summaryview)
2390 {
2391         if (!prefs_common.fltlist) return;
2392
2393         debug_print(_("filtering..."));
2394         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
2395         main_window_cursor_wait(summaryview->mainwin);
2396
2397         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
2398
2399         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2400                                 GTK_CTREE_FUNC(summary_filter_func),
2401                                 summaryview);
2402
2403         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
2404
2405         if (prefs_common.immediate_exec)
2406                 summary_execute(summaryview);
2407         else
2408                 summary_status_show(summaryview);
2409
2410         debug_print(_("done.\n"));
2411         STATUSBAR_POP(summaryview->mainwin);
2412         main_window_cursor_normal(summaryview->mainwin);
2413 }
2414
2415 static void summary_filter_func(GtkCTree *ctree, GtkCTreeNode *node,
2416                                 gpointer data)
2417 {
2418         MsgInfo *msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2419         SummaryView *summaryview = data;
2420         gchar *file;
2421         FolderItem *dest;
2422
2423         file = procmsg_get_message_file_path(msginfo);
2424         dest = filter_get_dest_folder(prefs_common.fltlist, file);
2425         g_free(file);
2426
2427         if (dest && strcmp2(dest->path, FILTER_NOT_RECEIVE) != 0 &&
2428             summaryview->folder_item != dest)
2429                 summary_move_row_to(summaryview, node, dest);
2430 }
2431
2432 /* callback functions */
2433
2434 static void summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
2435                                    SummaryView *summaryview)
2436 {
2437         if (!event)
2438                 return;
2439
2440         if (!summaryview->msg_is_toggled_on && summaryview->selected)
2441                 summary_display_msg(summaryview, summaryview->selected, FALSE);
2442         else
2443                 summary_toggle_view(summaryview);
2444 }
2445
2446 static void summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2447                                    SummaryView *summaryview)
2448 {
2449         if (!event) return;
2450
2451         if (event->button == 3) {
2452                 /* right clicked */
2453                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
2454                                NULL, NULL, event->button, event->time);
2455         } else if (event->button == 2) {
2456                 summaryview->display_msg = TRUE;
2457         } else if (event->button == 1) {
2458                 if (!prefs_common.emulate_emacs &&
2459                     summaryview->msg_is_toggled_on)
2460                         summaryview->display_msg = TRUE;
2461         }
2462 }
2463
2464 static void summary_button_released(GtkWidget *ctree, GdkEventButton *event,
2465                                     SummaryView *summaryview)
2466 {
2467 }
2468
2469 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
2470 {
2471         summary_key_pressed(summaryview->ctree, event, summaryview);
2472 }
2473
2474 #define BREAK_ON_MODIFIER_KEY() \
2475         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2476
2477 static void summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
2478                                 SummaryView *summaryview)
2479 {
2480         GtkCTree *ctree = GTK_CTREE(widget);
2481         GtkCTreeNode *node;
2482         FolderItem *to_folder;
2483
2484         if (!event) return;
2485
2486         switch (event->keyval) {
2487         case GDK_g:             /* Go */
2488         case GDK_G:
2489                 BREAK_ON_MODIFIER_KEY();
2490
2491                 if (gtk_signal_n_emissions_by_name
2492                         (GTK_OBJECT(ctree), "key_press_event") > 0)
2493                         gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree),
2494                                                      "key_press_event");
2495
2496                 to_folder = foldersel_folder_sel(NULL);
2497                 if (to_folder) {
2498                         debug_print(_("Go to %s\n"), to_folder->path);
2499                         folderview_select(summaryview->folderview, to_folder);
2500                 }
2501                 return;
2502         case GDK_w:             /* Write new message */
2503                 BREAK_ON_MODIFIER_KEY();
2504                 if (summaryview->folder_item)
2505                         compose_new(summaryview->folder_item->folder->account);
2506                 else
2507                         compose_new(NULL);
2508                 return;
2509         case GDK_D:             /* Empty trash */
2510                 BREAK_ON_MODIFIER_KEY();
2511                 if (gtk_signal_n_emissions_by_name
2512                         (GTK_OBJECT(ctree), "key_press_event") > 0)
2513                         gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree),
2514                                                      "key_press_event");
2515                 main_window_empty_trash(summaryview->mainwin, TRUE);
2516                 return;
2517         case GDK_Q:             /* Quit */
2518                 BREAK_ON_MODIFIER_KEY();
2519
2520                 if (prefs_common.confirm_on_exit) {
2521                         if (alertpanel(_("Exit"), _("Exit this program?"),
2522                                        _("OK"), _("Cancel"), NULL)
2523                                        == G_ALERTDEFAULT) {
2524                                 manage_window_focus_in
2525                                         (summaryview->mainwin->window,
2526                                          NULL, NULL);
2527                                 app_will_exit(NULL, summaryview->mainwin);
2528                         }
2529                 }
2530                 return;
2531         case GDK_Left:          /* Move focus */
2532         case GDK_Escape:
2533                 gtk_widget_grab_focus(summaryview->folderview->ctree);
2534                 return;
2535         default:
2536         }
2537
2538         if (!summaryview->selected) {
2539                 node = gtk_ctree_node_nth(ctree, 0);
2540                 if (node)
2541                         gtk_ctree_select(ctree, node);
2542                 else
2543                         return;
2544         }
2545
2546         switch (event->keyval) {
2547         case GDK_space:         /* Page down or go to the next */
2548                 if (summaryview->displayed != summaryview->selected) {
2549                         summary_display_msg(summaryview,
2550                                             summaryview->selected, FALSE);
2551                         break;
2552                 }
2553                 if (!textview_scroll_page(summaryview->messageview->textview,
2554                                           FALSE))
2555                         summary_select_next_unread(summaryview);
2556                 break;
2557         case GDK_n:             /* Next */
2558         case GDK_N:
2559                 BREAK_ON_MODIFIER_KEY();
2560                 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2561                 break;
2562         case GDK_BackSpace:     /* Page up */
2563         case GDK_Delete:
2564                 textview_scroll_page(summaryview->messageview->textview, TRUE);
2565                 break;
2566         case GDK_p:             /* Prev */
2567         case GDK_P:
2568                 BREAK_ON_MODIFIER_KEY();
2569                 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
2570                 break;
2571         case GDK_v:             /* Toggle summary mode / message mode */
2572         case GDK_V:
2573                 BREAK_ON_MODIFIER_KEY();
2574
2575                 if (!summaryview->msg_is_toggled_on && summaryview->selected)
2576                         summary_display_msg(summaryview,
2577                                             summaryview->selected, FALSE);
2578                 else
2579                         summary_toggle_view(summaryview);
2580                 break;
2581         case GDK_Return:        /* Scroll up/down one line */
2582                 if (summaryview->displayed != summaryview->selected) {
2583                         summary_display_msg(summaryview,
2584                                             summaryview->selected, FALSE);
2585                         break;
2586                 }
2587                 textview_scroll_one_line(summaryview->messageview->textview,
2588                                          (event->state & GDK_MOD1_MASK) != 0);
2589                 break;
2590         case GDK_asterisk:      /* Mark */
2591                 summary_mark(summaryview);
2592                 break;
2593         case GDK_exclam:        /* Mark as unread */
2594                 summary_mark_as_unread(summaryview);
2595                 break;
2596         case GDK_d:             /* Delete */
2597                 BREAK_ON_MODIFIER_KEY();
2598                 summary_delete(summaryview);
2599                 break;
2600         case GDK_u:             /* Unmark */
2601         case GDK_U:
2602                 BREAK_ON_MODIFIER_KEY();
2603                 summary_unmark(summaryview);
2604                 break;
2605         case GDK_o:             /* Move */
2606                 BREAK_ON_MODIFIER_KEY();
2607                 summary_move_to(summaryview);
2608                 break;
2609         case GDK_O:             /* Copy */
2610                 BREAK_ON_MODIFIER_KEY();
2611                 summary_copy_to(summaryview);
2612                 break;
2613         case GDK_x:             /* Execute */
2614         case GDK_X:
2615                 BREAK_ON_MODIFIER_KEY();
2616                 summary_execute(summaryview);
2617                 break;
2618         case GDK_a:             /* Reply to the message */
2619                 BREAK_ON_MODIFIER_KEY();
2620                 summary_reply_cb(summaryview,
2621                                  COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE, NULL);
2622                 break;
2623         case GDK_A:             /* Reply to the message with quotation */
2624                 BREAK_ON_MODIFIER_KEY();
2625                 summary_reply_cb(summaryview,
2626                                  COMPOSE_REPLY_TO_ALL_WITH_QUOTE, NULL);
2627                 break;
2628         case GDK_f:             /* Forward the message */
2629                 BREAK_ON_MODIFIER_KEY();
2630                 summary_reply_cb(summaryview, COMPOSE_FORWARD, NULL);
2631                 break;
2632         case GDK_F:
2633                 BREAK_ON_MODIFIER_KEY();
2634                 summary_reply_cb(summaryview, COMPOSE_FORWARD_AS_ATTACH, NULL);
2635                 break;
2636         case GDK_y:             /* Save the message */
2637                 BREAK_ON_MODIFIER_KEY();
2638                 summary_save_as(summaryview);
2639                 break;
2640         default:
2641         }
2642 }
2643
2644 static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
2645 {
2646         if (summaryview->folder_item->stype == F_DRAFT)
2647                 summary_reedit(summaryview);
2648         else
2649                 summary_open_msg(summaryview);
2650
2651         summaryview->display_msg = FALSE;
2652 }
2653
2654 static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
2655                              gint column, SummaryView *summaryview)
2656 {
2657         MsgInfo *msginfo;
2658
2659         summary_status_show(summaryview);
2660         summary_set_menu_sensitive(summaryview);
2661
2662         if (GTK_CLIST(ctree)->selection &&
2663              GTK_CLIST(ctree)->selection->next) {
2664                 summaryview->display_msg = FALSE;
2665                 return;
2666         }
2667
2668         summaryview->selected = row;
2669
2670         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2671
2672         switch (column) {
2673         case S_COL_MARK:
2674                 if (MSG_IS_MARKED(msginfo->flags)) {
2675                         MSG_UNSET_FLAGS(msginfo->flags, MSG_MARKED);
2676                         summary_set_row_marks(summaryview, row);
2677                 } else
2678                         summary_mark_row(summaryview, row);
2679                 break;
2680         case S_COL_UNREAD:
2681                 if (MSG_IS_UNREAD(msginfo->flags)) {
2682                         summary_mark_row_as_read(summaryview, row);
2683                         summary_status_show(summaryview);
2684                 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
2685                          !MSG_IS_FORWARDED(msginfo->flags)) {
2686                         summary_mark_row_as_unread(summaryview, row);
2687                         summary_status_show(summaryview);
2688                 }
2689                 break;
2690         default:
2691         }
2692
2693         if (summaryview->display_msg)
2694                 summary_display_msg(summaryview, row, FALSE);
2695
2696         summaryview->display_msg = FALSE;
2697 }
2698
2699 static void summary_col_resized(GtkCList *clist, gint column, gint width,
2700                                 SummaryView *summaryview)
2701 {
2702         switch (column) {
2703         case S_COL_MARK:
2704                 prefs_common.summary_col_mark = width;
2705                 break;
2706         case S_COL_UNREAD:
2707                 prefs_common.summary_col_unread = width;
2708                 break;
2709         case S_COL_MIME:
2710                 prefs_common.summary_col_mime = width;
2711                 break;
2712         case S_COL_NUMBER:
2713                 prefs_common.summary_col_number = width;
2714                 break;
2715         case S_COL_SIZE:
2716                 prefs_common.summary_col_size = width;
2717                 break;
2718         case S_COL_DATE:
2719                 prefs_common.summary_col_date = width;
2720                 break;
2721         case S_COL_FROM:
2722                 prefs_common.summary_col_from = width;
2723                 break;
2724         case S_COL_SUBJECT:
2725                 prefs_common.summary_col_subject = width;
2726                 break;
2727         default:
2728         }
2729 }
2730
2731 static void summary_reply_cb(SummaryView *summaryview, guint action,
2732                              GtkWidget *widget)
2733 {
2734         MsgInfo *msginfo;
2735
2736         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2737                                               summaryview->selected);
2738         if (!msginfo) return;
2739
2740         switch ((ComposeReplyMode)action) {
2741         case COMPOSE_REPLY:
2742                 compose_reply(msginfo, prefs_common.reply_with_quote, FALSE);
2743                 break;
2744         case COMPOSE_REPLY_WITH_QUOTE:
2745                 compose_reply(msginfo, TRUE, FALSE);
2746                 break;
2747         case COMPOSE_REPLY_WITHOUT_QUOTE:
2748                 compose_reply(msginfo, FALSE, FALSE);
2749                 break;
2750         case COMPOSE_REPLY_TO_ALL:
2751                 compose_reply(msginfo, prefs_common.reply_with_quote, TRUE);
2752                 break;
2753         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
2754                 compose_reply(msginfo, TRUE, TRUE);
2755                 break;
2756         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
2757                 compose_reply(msginfo, FALSE, TRUE);
2758                 break;
2759         case COMPOSE_FORWARD:
2760                 compose_forward(msginfo, FALSE);
2761                 break;
2762         case COMPOSE_FORWARD_AS_ATTACH:
2763                 compose_forward(msginfo, TRUE);
2764                 break;
2765         default:
2766                 compose_reply(msginfo, prefs_common.reply_with_quote, FALSE);
2767         }
2768
2769         summary_set_marks_selected(summaryview);
2770 }
2771
2772 static void summary_show_all_header_cb(SummaryView *summaryview,
2773                                        guint action, GtkWidget *widget)
2774 {
2775         header_window_show_cb(summaryview->mainwin, action, widget);
2776 }
2777
2778 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
2779 {
2780         summary_sort(summaryview, SORT_BY_NUMBER);
2781 }
2782
2783 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
2784 {
2785         summary_sort(summaryview, SORT_BY_SIZE);
2786 }
2787
2788 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
2789 {
2790         summary_sort(summaryview, SORT_BY_DATE);
2791 }
2792
2793 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
2794 {
2795         summary_sort(summaryview, SORT_BY_FROM);
2796 }
2797
2798 static void summary_subject_clicked(GtkWidget *button,
2799                                     SummaryView *summaryview)
2800 {
2801         summary_sort(summaryview, SORT_BY_SUBJECT);
2802 }
2803
2804 void summary_change_display_item(SummaryView *summaryview)
2805 {
2806         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2807
2808         gtk_clist_set_column_visibility(clist, S_COL_MARK, prefs_common.show_mark);
2809         gtk_clist_set_column_visibility(clist, S_COL_UNREAD, prefs_common.show_unread);
2810         gtk_clist_set_column_visibility(clist, S_COL_MIME, prefs_common.show_mime);
2811         gtk_clist_set_column_visibility(clist, S_COL_NUMBER, prefs_common.show_number);
2812         gtk_clist_set_column_visibility(clist, S_COL_SIZE, prefs_common.show_size);
2813         gtk_clist_set_column_visibility(clist, S_COL_DATE, prefs_common.show_date);
2814         gtk_clist_set_column_visibility(clist, S_COL_FROM, prefs_common.show_from);
2815         gtk_clist_set_column_visibility(clist, S_COL_SUBJECT, prefs_common.show_subject);
2816 }
2817
2818 static void summary_start_drag (GtkWidget   *widget,
2819                                 gint         button,
2820                                 GdkEvent    *event,
2821                                 SummaryView *summaryview)
2822 {
2823         GtkTargetList *list;
2824         GdkDragContext *context;
2825
2826         g_return_if_fail(summaryview != NULL);
2827         g_return_if_fail(summaryview->folder_item != NULL);
2828         g_return_if_fail(summaryview->folder_item->folder != NULL);
2829         if (summaryview->folder_item->folder->type == F_NEWS ||
2830             summaryview->selected == NULL)
2831                 return;
2832
2833         list = gtk_target_list_new(summary_drag_types, 1);
2834
2835         context = gtk_drag_begin(widget, list,
2836                                  GDK_ACTION_MOVE, button, event);
2837         gtk_drag_set_icon_default(context);
2838 }
2839
2840 static void summary_drag_data_get(GtkWidget        *widget,
2841                                   GdkDragContext   *drag_context,
2842                                   GtkSelectionData *selection_data,
2843                                   guint             info,
2844                                   guint             time,
2845                                   SummaryView      *summaryview)
2846 {
2847         if (info == TARGET_MAIL_URI_LIST) {
2848                 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2849                 GList *cur;
2850                 MsgInfo *msginfo;
2851                 gchar *mail_list = NULL, *tmp1, *tmp2;
2852
2853                 for (cur = GTK_CLIST(ctree)->selection;
2854                      cur != NULL; cur = cur->next) {
2855                         msginfo = gtk_ctree_node_get_row_data
2856                                 (ctree, GTK_CTREE_NODE(cur->data));
2857                         tmp2 = procmsg_get_message_file_path(msginfo);
2858                         if (!tmp2) continue;
2859                         tmp1 = g_strconcat("file:/", tmp2, NULL);
2860                         g_free(tmp2);
2861
2862                         if (!mail_list) {
2863                                 mail_list = tmp1;
2864                         } else {
2865                                 tmp2 = g_strconcat(mail_list, tmp1, NULL);
2866                                 g_free(mail_list);
2867                                 g_free(tmp1);
2868                                 mail_list = tmp2;
2869                         }
2870                 }
2871
2872                 if (mail_list != NULL) {
2873                         gtk_selection_data_set(selection_data,
2874                                                selection_data->target, 8,
2875                                                mail_list, strlen(mail_list));
2876                         g_free(mail_list);
2877                 } 
2878         } else if (info == TARGET_DUMMY) {
2879                 if (GTK_CLIST(summaryview->ctree)->selection)
2880                         gtk_selection_data_set(selection_data,
2881                                                selection_data->target, 8,
2882                                                "Dummy", 6);
2883         }
2884 }
2885
2886
2887 /* custom compare functions for sorting */
2888
2889 static gint summary_cmp_by_num(GtkCList *clist,
2890                                gconstpointer ptr1, gconstpointer ptr2)
2891 {
2892         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
2893         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
2894
2895         return msginfo1->msgnum - msginfo2->msgnum;
2896 }
2897
2898 static gint summary_cmp_by_size(GtkCList *clist,
2899                                 gconstpointer ptr1, gconstpointer ptr2)
2900 {
2901         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
2902         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
2903
2904         return msginfo1->size - msginfo2->size;
2905 }
2906
2907 static gint summary_cmp_by_date(GtkCList *clist,
2908                                gconstpointer ptr1, gconstpointer ptr2)
2909 {
2910         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
2911         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
2912
2913         return msginfo1->date_t - msginfo2->date_t;
2914 }
2915
2916 static gint summary_cmp_by_from(GtkCList *clist,
2917                                gconstpointer ptr1, gconstpointer ptr2)
2918 {
2919         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
2920         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
2921
2922         if (!msginfo1->fromname)
2923                 return (msginfo2->fromname != NULL);
2924         if (!msginfo2->fromname)
2925                 return -1;
2926
2927         return strcasecmp(msginfo1->fromname, msginfo2->fromname);
2928 }
2929
2930 static gint summary_cmp_by_subject(GtkCList *clist,
2931                                gconstpointer ptr1, gconstpointer ptr2)
2932 {
2933         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
2934         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
2935
2936         if (!msginfo1->subject)
2937                 return (msginfo2->subject != NULL);
2938         if (!msginfo2->subject)
2939                 return -1;
2940
2941         return strcasecmp(msginfo1->subject, msginfo2->subject);
2942 }