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