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