d5539e45b3a7c3bb69e4f97a9cffd2f94cb585a8
[claws.git] / src / summaryview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkscrolledwindow.h>
25 #include <gtk/gtkwidget.h>
26 #include <gtk/gtkpixmap.h>
27 #include <gtk/gtkctree.h>
28 #include <gtk/gtkcontainer.h>
29 #include <gtk/gtksignal.h>
30 #include <gtk/gtktext.h>
31 #include <gtk/gtkmenu.h>
32 #include <gtk/gtkmenuitem.h>
33 #include <gtk/gtkitemfactory.h>
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkwindow.h>
37 #include <gtk/gtkstyle.h>
38 #include <gtk/gtkarrow.h>
39 #include <gtk/gtkeventbox.h>
40 #include <gtk/gtkstatusbar.h>
41 #include <gtk/gtkmenuitem.h>
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <unistd.h>
48 #include <sys/stat.h>
49
50 #include "intl.h"
51 #include "main.h"
52 #include "menu.h"
53 #include "mainwindow.h"
54 #include "folderview.h"
55 #include "summaryview.h"
56 #include "messageview.h"
57 #include "foldersel.h"
58 #include "procmsg.h"
59 #include "procheader.h"
60 #include "headerwindow.h"
61 #include "sourcewindow.h"
62 #include "prefs_common.h"
63 #include "prefs_summary_column.h"
64 #include "account.h"
65 #include "compose.h"
66 #include "utils.h"
67 #include "gtkutils.h"
68 #include "filesel.h"
69 #include "manage_window.h"
70 #include "alertpanel.h"
71 #include "inputdialog.h"
72 #include "statusbar.h"
73 #include "filter.h"
74 #include "folder.h"
75 #include "colorlabel.h"
76 #include "inc.h"
77 #include "addressbook.h"
78 #include "addr_compl.h"
79 #include "scoring.h"
80 #include "prefs_folder_item.h"
81 #include "filtering.h"
82
83 #include "pixmaps/dir-open.xpm"
84 #include "pixmaps/mark.xpm"
85 #include "pixmaps/deleted.xpm"
86 #include "pixmaps/new.xpm"
87 #include "pixmaps/unread.xpm"
88 #include "pixmaps/replied.xpm"
89 #include "pixmaps/forwarded.xpm"
90 #include "pixmaps/clip.xpm"
91 #include "pixmaps/ignorethread.xpm"
92
93 #define STATUSBAR_PUSH(mainwin, str) \
94 { \
95         gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
96                            mainwin->summaryview_cid, str); \
97         gtkut_widget_wait_for_draw(mainwin->hbox_stat); \
98 }
99
100 #define STATUSBAR_POP(mainwin) \
101 { \
102         gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
103                           mainwin->summaryview_cid); \
104 }
105
106 #define SUMMARY_COL_MARK_WIDTH          10
107 #define SUMMARY_COL_UNREAD_WIDTH        13
108 #define SUMMARY_COL_MIME_WIDTH          10
109
110 static GdkFont *boldfont;
111 static GdkFont *smallfont;
112
113 static GtkStyle *bold_style;
114 static GtkStyle *bold_marked_style;
115 static GtkStyle *bold_deleted_style;
116 static GtkStyle *small_style;
117 static GtkStyle *small_marked_style;
118 static GtkStyle *small_deleted_style;
119
120 static GdkPixmap *folderxpm;
121 static GdkBitmap *folderxpmmask;
122
123 static GdkPixmap *markxpm;
124 static GdkBitmap *markxpmmask;
125 static GdkPixmap *deletedxpm;
126 static GdkBitmap *deletedxpmmask;
127
128 static GdkPixmap *newxpm;
129 static GdkBitmap *newxpmmask;
130 static GdkPixmap *unreadxpm;
131 static GdkBitmap *unreadxpmmask;
132 static GdkPixmap *repliedxpm;
133 static GdkBitmap *repliedxpmmask;
134 static GdkPixmap *forwardedxpm;
135 static GdkBitmap *forwardedxpmmask;
136 static GdkPixmap *ignorethreadxpm;
137 static GdkBitmap *ignorethreadxpmmask;
138
139 static GdkPixmap *clipxpm;
140 static GdkBitmap *clipxpmmask;
141
142 static void summary_free_msginfo_func   (GtkCTree               *ctree,
143                                          GtkCTreeNode           *node,
144                                          gpointer                data);
145 static void summary_set_marks_func      (GtkCTree               *ctree,
146                                          GtkCTreeNode           *node,
147                                          gpointer                data);
148 static void summary_write_cache_func    (GtkCTree               *ctree,
149                                          GtkCTreeNode           *node,
150                                          gpointer                data);
151
152 static void summary_set_menu_sensitive  (SummaryView            *summaryview);
153
154 static guint summary_get_msgnum         (SummaryView            *summaryview,
155                                          GtkCTreeNode           *node);
156
157 static GtkCTreeNode *summary_find_prev_msg
158                                         (SummaryView            *summaryview,
159                                          GtkCTreeNode           *current_node);
160 static GtkCTreeNode *summary_find_next_msg
161                                         (SummaryView            *summaryview,
162                                          GtkCTreeNode           *current_node);
163 static GtkCTreeNode *summary_find_prev_unread_msg
164                                         (SummaryView            *summaryview,
165                                          GtkCTreeNode           *current_node);
166 static GtkCTreeNode *summary_find_next_unread_msg
167                                         (SummaryView            *summaryview,
168                                          GtkCTreeNode           *current_node);
169 static GtkCTreeNode *summary_find_prev_marked_msg
170                                         (SummaryView            *summaryview,
171                                          GtkCTreeNode           *current_node);
172 static GtkCTreeNode *summary_find_next_marked_msg
173                                         (SummaryView            *summaryview,
174                                          GtkCTreeNode           *current_node);
175 static GtkCTreeNode *summary_find_prev_labeled_msg
176                                         (SummaryView            *summaryview,
177                                          GtkCTreeNode           *current_node);
178 static GtkCTreeNode *summary_find_next_labeled_msg
179                                         (SummaryView            *summaryview,
180                                          GtkCTreeNode           *current_node);
181 static GtkCTreeNode *summary_find_msg_by_msgnum
182                                         (SummaryView            *summaryview,
183                                          guint                   msgnum);
184
185 static void summary_update_status       (SummaryView            *summaryview);
186
187 /* display functions */
188 static void summary_status_show         (SummaryView            *summaryview);
189 static void summary_set_column_titles   (SummaryView            *summaryview);
190 static void summary_set_ctree_from_list (SummaryView            *summaryview,
191                                          GSList                 *mlist);
192 static void summary_set_header          (SummaryView            *summaryview,
193                                          gchar                  *text[],
194                                          MsgInfo                *msginfo);
195 static void summary_display_msg         (SummaryView            *summaryview,
196                                          GtkCTreeNode           *row,
197                                          gboolean                new_window);
198 static void summary_toggle_view         (SummaryView            *summaryview);
199 static void summary_set_row_marks       (SummaryView            *summaryview,
200                                          GtkCTreeNode           *row);
201
202 /* message handling */
203 static void summary_mark_row            (SummaryView            *summaryview,
204                                          GtkCTreeNode           *row);
205 static void summary_mark_row_as_read    (SummaryView            *summaryview,
206                                          GtkCTreeNode           *row);
207 static void summary_mark_row_as_unread  (SummaryView            *summaryview,
208                                          GtkCTreeNode           *row);
209 static void summary_delete_row          (SummaryView            *summaryview,
210                                          GtkCTreeNode           *row);
211 static void summary_unmark_row          (SummaryView            *summaryview,
212                                          GtkCTreeNode           *row);
213 static void summary_move_row_to         (SummaryView            *summaryview,
214                                          GtkCTreeNode           *row,
215                                          FolderItem             *to_folder);
216 static void summary_copy_row_to         (SummaryView            *summaryview,
217                                          GtkCTreeNode           *row,
218                                          FolderItem             *to_folder);
219
220 static void summary_delete_duplicated_func
221                                         (GtkCTree               *ctree,
222                                          GtkCTreeNode           *node,
223                                          SummaryView            *summaryview);
224
225 static void summary_execute_move        (SummaryView            *summaryview);
226 static void summary_execute_move_func   (GtkCTree               *ctree,
227                                          GtkCTreeNode           *node,
228                                          gpointer                data);
229 static void summary_execute_copy        (SummaryView            *summaryview);
230 static void summary_execute_copy_func   (GtkCTree               *ctree,
231                                          GtkCTreeNode           *node,
232                                          gpointer                data);
233 static void summary_execute_delete      (SummaryView            *summaryview);
234 static void summary_execute_delete_func (GtkCTree               *ctree,
235                                          GtkCTreeNode           *node,
236                                          gpointer                data);
237
238 static void summary_thread_init         (SummaryView            *summaryview);
239 static void summary_ignore_thread       (SummaryView  *summaryview);
240 static void summary_unignore_thread     (SummaryView *summaryview);
241
242 static void summary_unthread_for_exec           (SummaryView    *summaryview);
243 static void summary_unthread_for_exec_func      (GtkCTree       *ctree,
244                                                  GtkCTreeNode   *node,
245                                                  gpointer        data);
246
247 static void summary_filter_func         (GtkCTree               *ctree,
248                                          GtkCTreeNode           *node,
249                                          gpointer                data);
250
251 static void summary_colorlabel_menu_item_activate_cb
252                                           (GtkWidget    *widget,
253                                            gpointer      data);
254 static void summary_colorlabel_menu_item_activate_item_cb
255                                           (GtkMenuItem  *label_menu_item,
256                                            gpointer      data);
257 static void summary_colorlabel_menu_create(SummaryView  *summaryview);
258
259 static GtkWidget *summary_ctree_create  (SummaryView    *summaryview);
260
261 /* callback functions */
262 static void summary_toggle_pressed      (GtkWidget              *eventbox,
263                                          GdkEventButton         *event,
264                                          SummaryView            *summaryview);
265 static void summary_button_pressed      (GtkWidget              *ctree,
266                                          GdkEventButton         *event,
267                                          SummaryView            *summaryview);
268 static void summary_button_released     (GtkWidget              *ctree,
269                                          GdkEventButton         *event,
270                                          SummaryView            *summaryview);
271 static void summary_key_pressed         (GtkWidget              *ctree,
272                                          GdkEventKey            *event,
273                                          SummaryView            *summaryview);
274 static void summary_open_row            (GtkSCTree              *sctree,
275                                          SummaryView            *summaryview);
276 static void summary_tree_expanded       (GtkCTree               *ctree,
277                                          GtkCTreeNode           *node,
278                                          SummaryView            *summaryview);
279 static void summary_tree_collapsed      (GtkCTree               *ctree,
280                                          GtkCTreeNode           *node,
281                                          SummaryView            *summaryview);
282 static void summary_selected            (GtkCTree               *ctree,
283                                          GtkCTreeNode           *row,
284                                          gint                    column,
285                                          SummaryView            *summaryview);
286 static void summary_col_resized         (GtkCList               *clist,
287                                          gint                    column,
288                                          gint                    width,
289                                          SummaryView            *summaryview);
290 static void summary_reply_cb            (SummaryView            *summaryview,
291                                          guint                   action,
292                                          GtkWidget              *widget);
293 static void summary_execute_cb          (SummaryView            *summaryview,
294                                          guint                   action,
295                                          GtkWidget              *widget);
296 static void summary_show_all_header_cb  (SummaryView            *summaryview,
297                                          guint                   action,
298                                          GtkWidget              *widget);
299
300 static void summary_add_address_cb      (SummaryView            *summaryview,
301                                          guint                   action,
302                                          GtkWidget              *widget);
303
304 static void summary_mark_clicked        (GtkWidget              *button,
305                                          SummaryView            *summaryview);
306 static void summary_unread_clicked      (GtkWidget              *button,
307                                          SummaryView            *summaryview);
308 static void summary_mime_clicked        (GtkWidget              *button,
309                                          SummaryView            *summaryview);
310 static void summary_num_clicked         (GtkWidget              *button,
311                                          SummaryView            *summaryview);
312 static void summary_score_clicked       (GtkWidget *button,
313                                          SummaryView *summaryview);
314 static void summary_size_clicked        (GtkWidget              *button,
315                                          SummaryView            *summaryview);
316 static void summary_date_clicked        (GtkWidget              *button,
317                                          SummaryView            *summaryview);
318 static void summary_from_clicked        (GtkWidget              *button,
319                                          SummaryView            *summaryview);
320 static void summary_subject_clicked     (GtkWidget              *button,
321                                          SummaryView            *summaryview);
322 static void summary_score_clicked       (GtkWidget              *button,
323                                          SummaryView            *summaryview);
324
325 static void summary_start_drag          (GtkWidget        *widget, 
326                                          int button,
327                                          GdkEvent *event,
328                                          SummaryView      *summaryview);
329 static void summary_drag_data_get       (GtkWidget        *widget,
330                                          GdkDragContext   *drag_context,
331                                          GtkSelectionData *selection_data,
332                                          guint             info,
333                                          guint             time,
334                                          SummaryView      *summaryview);
335
336 /* custom compare functions for sorting */
337
338 static gint summary_cmp_by_mark         (GtkCList               *clist,
339                                          gconstpointer           ptr1,
340                                          gconstpointer           ptr2);
341 static gint summary_cmp_by_unread       (GtkCList               *clist,
342                                          gconstpointer           ptr1,
343                                          gconstpointer           ptr2);
344 static gint summary_cmp_by_mime         (GtkCList               *clist,
345                                          gconstpointer           ptr1,
346                                          gconstpointer           ptr2);
347 static gint summary_cmp_by_num          (GtkCList               *clist,
348                                          gconstpointer           ptr1,
349                                          gconstpointer           ptr2);
350 static gint summary_cmp_by_size         (GtkCList               *clist,
351                                          gconstpointer           ptr1,
352                                          gconstpointer           ptr2);
353 static gint summary_cmp_by_date         (GtkCList               *clist,
354                                          gconstpointer           ptr1,
355                                          gconstpointer           ptr2);
356 static gint summary_cmp_by_from         (GtkCList               *clist,
357                                          gconstpointer           ptr1,
358                                          gconstpointer           ptr2);
359 static gint summary_cmp_by_subject      (GtkCList               *clist,
360                                          gconstpointer           ptr1,
361                                          gconstpointer           ptr2);
362 static gint summary_cmp_by_score        (GtkCList               *clist,
363                                          gconstpointer           ptr1,
364                                          gconstpointer           ptr2);
365 static gint summary_cmp_by_label        (GtkCList               *clist,
366                                          gconstpointer           ptr1,
367                                          gconstpointer           ptr2);
368
369 #if MARK_ALL_READ
370 static void summary_mark_all_read (SummaryView *summaryview);                                    
371 #endif
372
373 GtkTargetEntry summary_drag_types[1] =
374 {
375         {"text/plain", GTK_TARGET_SAME_APP, TARGET_DUMMY}
376 };
377
378 static GtkItemFactoryEntry summary_popup_entries[] =
379 {
380         {N_("/_Reply"),                 NULL, summary_reply_cb, COMPOSE_REPLY, NULL},
381         {N_("/Repl_y to sender"),       NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
382         {N_("/Follow-up and reply to"), NULL, summary_reply_cb, COMPOSE_FOLLOWUP_AND_REPLY_TO, NULL},
383         {N_("/Reply to a_ll"),          NULL, summary_reply_cb, COMPOSE_REPLY_TO_ALL, NULL},
384         {N_("/_Forward"),               NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
385         {N_("/Forward as a_ttachment"),
386                                         NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
387         {N_("/Bounce"),                 NULL, summary_reply_cb, COMPOSE_BOUNCE, NULL},
388         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
389         {N_("/Re-_edit"),               NULL, summary_reedit,   0, NULL},
390         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
391         {N_("/M_ove..."),               NULL, summary_move_to,  0, NULL},
392         {N_("/_Copy..."),               NULL, summary_copy_to,  0, NULL},
393         {N_("/_Delete"),                NULL, summary_delete,   0, NULL},
394         {N_("/E_xecute"),               NULL, summary_execute_cb,       0, NULL},
395         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
396         {N_("/_Mark"),                  NULL, NULL,             0, "<Branch>"},
397         {N_("/_Mark/_Mark"),            NULL, summary_mark,     0, NULL},
398         {N_("/_Mark/_Unmark"),          NULL, summary_unmark,   0, NULL},
399         {N_("/_Mark/---"),              NULL, NULL,             0, "<Separator>"},
400         {N_("/_Mark/Mark as unr_ead"),  NULL, summary_mark_as_unread, 0, NULL},
401         {N_("/_Mark/Mark as rea_d"),    NULL, summary_mark_as_read, 0, NULL},
402         {N_("/_Mark/Mark all read"),    NULL, summary_mark_all_read, 0, NULL},
403         {N_("/_Mark/Ignore thread"),    NULL, summary_ignore_thread, 0, NULL},
404         {N_("/_Mark/Unignore thread"),  NULL, summary_unignore_thread, 0, NULL},
405         {N_("/Color la_bel"),           NULL, NULL,             0, NULL},
406
407         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
408         {N_("/Add sender to address boo_k"),
409                                         NULL, summary_add_address_cb, 0, NULL},
410         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
411         {N_("/_View"),                  NULL, NULL,             0, "<Branch>"},
412         {N_("/_View/Open in new _window"),
413                                         NULL, summary_open_msg, 0, NULL},
414         {N_("/_View/_Source"),          NULL, summary_view_source, 0, NULL},
415         {N_("/_View/All _header"),      NULL, summary_show_all_header_cb, 0, NULL},
416         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
417         {N_("/_Save as..."),            NULL, summary_save_as,  0, NULL},
418         {N_("/_Print..."),              NULL, summary_print,    0, NULL},
419         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
420         {N_("/Select _all"),            NULL, summary_select_all, 0, NULL}
421 };
422
423 static const gchar *const col_label[N_SUMMARY_COLS] = {
424         N_("M"),        /* S_COL_MARK    */
425         N_("U"),        /* S_COL_UNREAD  */
426         "",             /* S_COL_MIME    */
427         N_("Subject"),  /* S_COL_SUBJECT */
428         N_("From"),     /* S_COL_FROM    */
429         N_("Date"),     /* S_COL_DATE    */
430         N_("Size"),     /* S_COL_SIZE    */
431         N_("No."),      /* S_COL_NUMBER  */
432         N_("Score")     /* S_COL_SCORE   */
433 };
434
435 SummaryView *summary_create(void)
436 {
437         SummaryView *summaryview;
438         GtkWidget *vbox;
439         GtkWidget *scrolledwin;
440         GtkWidget *ctree;
441         GtkWidget *hbox;
442         GtkWidget *statlabel_folder;
443         GtkWidget *statlabel_select;
444         GtkWidget *statlabel_msgs;
445         GtkWidget *toggle_eventbox;
446         GtkWidget *toggle_arrow;
447         GtkWidget *popupmenu;
448         GtkItemFactory *popupfactory;
449         gint n_entries;
450
451         debug_print(_("Creating summary view...\n"));
452         summaryview = g_new0(SummaryView, 1);
453
454         vbox = gtk_vbox_new(FALSE, 2);
455
456         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
457         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
458                                        GTK_POLICY_AUTOMATIC,
459                                        GTK_POLICY_ALWAYS);
460         gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
461         gtk_widget_set_usize(vbox,
462                              prefs_common.summaryview_width,
463                              prefs_common.summaryview_height);
464
465         ctree = summary_ctree_create(summaryview);
466
467         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
468                                             GTK_CLIST(ctree)->hadjustment);
469         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
470                                             GTK_CLIST(ctree)->vadjustment);
471         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
472
473         /* create status label */
474         hbox = gtk_hbox_new(FALSE, 0);
475         gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
476
477         statlabel_folder = gtk_label_new("");
478         gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2);
479         statlabel_select = gtk_label_new("");
480         gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 16);
481
482         /* toggle view button */
483         toggle_eventbox = gtk_event_box_new();
484         gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
485         toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
486         gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
487         gtk_signal_connect(GTK_OBJECT(toggle_eventbox), "button_press_event",
488                            GTK_SIGNAL_FUNC(summary_toggle_pressed),
489                            summaryview);
490
491         statlabel_msgs = gtk_label_new("");
492         gtk_box_pack_end(GTK_BOX(hbox), statlabel_msgs, FALSE, FALSE, 4);
493
494         /* create popup menu */
495         n_entries = sizeof(summary_popup_entries) /
496                 sizeof(summary_popup_entries[0]);
497         popupmenu = menu_create_items(summary_popup_entries, n_entries,
498                                       "<SummaryView>", &popupfactory,
499                                       summaryview);
500
501         summaryview->vbox = vbox;
502         summaryview->scrolledwin = scrolledwin;
503         summaryview->ctree = ctree;
504         summaryview->hbox = hbox;
505         summaryview->statlabel_folder = statlabel_folder;
506         summaryview->statlabel_select = statlabel_select;
507         summaryview->statlabel_msgs = statlabel_msgs;
508         summaryview->toggle_eventbox = toggle_eventbox;
509         summaryview->toggle_arrow = toggle_arrow;
510         summaryview->popupmenu = popupmenu;
511         summaryview->popupfactory = popupfactory;
512         summaryview->msg_is_toggled_on = TRUE;
513         summaryview->lock_count = 0;
514         summaryview->sort_mode = SORT_BY_NONE;
515         summaryview->sort_type = GTK_SORT_ASCENDING;
516
517         gtk_widget_show_all(vbox);
518
519         return summaryview;
520 }
521
522 void summary_init(SummaryView *summaryview)
523 {
524         GtkStyle *style;
525         GtkWidget *pixmap;
526
527         PIXMAP_CREATE(summaryview->ctree, markxpm, markxpmmask, mark_xpm);
528         PIXMAP_CREATE(summaryview->ctree, deletedxpm, deletedxpmmask,
529                       deleted_xpm);
530         PIXMAP_CREATE(summaryview->ctree, newxpm, newxpmmask, new_xpm);
531         PIXMAP_CREATE(summaryview->ctree, unreadxpm, unreadxpmmask, unread_xpm);
532         PIXMAP_CREATE(summaryview->ctree, repliedxpm, repliedxpmmask,
533                       replied_xpm);
534         PIXMAP_CREATE(summaryview->ctree, forwardedxpm, forwardedxpmmask,
535                       forwarded_xpm);
536         PIXMAP_CREATE(summaryview->ctree, ignorethreadxpm, ignorethreadxpmmask,
537                       ignorethread_xpm);
538         PIXMAP_CREATE(summaryview->ctree, clipxpm, clipxpmmask, clip_xpm);
539         PIXMAP_CREATE(summaryview->hbox, folderxpm, folderxpmmask,
540                       dir_open_xpm);
541
542         if (!small_style) {
543                 small_style = gtk_style_copy
544                         (gtk_widget_get_style(summaryview->ctree));
545                 if (!smallfont)
546                         smallfont = gdk_fontset_load(SMALL_FONT);
547                 small_style->font = smallfont;
548                 small_marked_style = gtk_style_copy(small_style);
549                 small_marked_style->fg[GTK_STATE_NORMAL] =
550                         summaryview->color_marked;
551                 small_deleted_style = gtk_style_copy(small_style);
552                 small_deleted_style->fg[GTK_STATE_NORMAL] =
553                         summaryview->color_dim;
554         }
555         if (!bold_style) {
556                 bold_style = gtk_style_copy
557                         (gtk_widget_get_style(summaryview->ctree));
558                 if (!boldfont)
559                         boldfont = gdk_fontset_load(BOLD_FONT);
560                 bold_style->font = boldfont;
561                 bold_marked_style = gtk_style_copy(bold_style);
562                 bold_marked_style->fg[GTK_STATE_NORMAL] =
563                         summaryview->color_marked;
564                 bold_deleted_style = gtk_style_copy(bold_style);
565                 bold_deleted_style->fg[GTK_STATE_NORMAL] =
566                         summaryview->color_dim;
567         }
568
569         style = gtk_style_copy(gtk_widget_get_style
570                                 (summaryview->statlabel_folder));
571
572         gtk_widget_set_style(summaryview->statlabel_folder, style);
573         gtk_widget_set_style(summaryview->statlabel_select, style);
574         gtk_widget_set_style(summaryview->statlabel_msgs, style);
575
576         pixmap = gtk_pixmap_new(folderxpm, folderxpmmask);
577         gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
578         gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
579         gtk_widget_show(pixmap);
580
581         summary_clear_list(summaryview);
582         summary_set_column_titles(summaryview);
583         summary_colorlabel_menu_create(summaryview);
584         summary_set_menu_sensitive(summaryview);
585
586 }
587
588 GtkCTreeNode * summary_find_next_important_score(SummaryView *summaryview,
589                                                  GtkCTreeNode *current_node)
590 {
591         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
592         GtkCTreeNode *node;
593         MsgInfo *msginfo;
594         gint best_score = MIN_SCORE;
595         GtkCTreeNode *best_node = NULL;
596
597         if (current_node)
598                 /*node = current_node;*/
599                 node = GTK_CTREE_NODE_NEXT(current_node);
600         else
601                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
602
603         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
604                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
605                 if (msginfo->score >= summaryview->important_score)
606                         break;
607                 if (msginfo->score > best_score) {
608                         best_score = msginfo->score;
609                         best_node = node;
610                 }
611         }
612
613         if (node != NULL)
614                 return node;
615         else
616                 return best_node;
617 }
618
619 GtkCTreeNode * summary_find_prev_important_score(SummaryView *summaryview,
620                                                  GtkCTreeNode *current_node)
621 {
622         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
623         GtkCTreeNode *node;
624         MsgInfo *msginfo;
625         gint best_score = MIN_SCORE;
626         GtkCTreeNode *best_node = NULL;
627
628         if (current_node)
629                 /*node = current_node;*/
630                 node = GTK_CTREE_NODE_PREV(current_node);
631         else
632                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
633
634         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
635                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
636                 if (msginfo->score >= summaryview->important_score)
637                         break;
638                 if (msginfo->score > best_score) {
639                         best_score = msginfo->score;
640                         best_node = node;
641                 }
642         }
643
644         if (node != NULL)
645                 return node;
646         else
647                 return best_node;
648 }
649
650 gboolean summary_show(SummaryView *summaryview, FolderItem *item,
651                       gboolean update_cache)
652 {
653         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
654         GtkCTreeNode *node;
655         GSList *mlist = NULL;
656         gchar *buf;
657         gboolean is_refresh;
658         guint selected_msgnum = 0;
659         guint displayed_msgnum = 0;
660         GtkCTreeNode *selected_node = summaryview->folderview->selected;
661         GSList *cur;
662         gint sort_mode;
663         gint sort_type;
664         static gboolean locked = FALSE;
665
666         if (summary_is_locked(summaryview)) return FALSE;
667
668         inc_lock();
669         summary_lock(summaryview);
670
671         STATUSBAR_POP(summaryview->mainwin);
672
673         is_refresh = (!prefs_common.open_inbox_on_inc &&
674                       item == summaryview->folder_item &&
675                       update_cache == FALSE) ? TRUE : FALSE;
676         if (is_refresh) {
677                 selected_msgnum = summary_get_msgnum(summaryview,
678                                                      summaryview->selected);
679                 displayed_msgnum = summary_get_msgnum(summaryview,
680                                                       summaryview->displayed);
681         }
682
683         /* process the marks if any */
684         if (summaryview->mainwin->lock_count == 0 &&
685             (summaryview->moved > 0 || summaryview->copied > 0)) {
686                 AlertValue val;
687
688                 val = alertpanel(_("Process mark"),
689                                  _("Some marks are left. Process it?"),
690                                  _("Yes"), _("No"), _("Cancel"));
691                 if (G_ALERTDEFAULT == val)
692                         summary_execute(summaryview);
693                 else if (G_ALERTALTERNATE == val)
694                         summary_write_cache(summaryview);
695                 else {
696                         summary_unlock(summaryview);
697                         inc_unlock();
698                         return FALSE;
699                 }
700                 folder_update_op_count();
701         }
702         else if (!summaryview->filtering_happened) {
703                 summary_write_cache(summaryview);
704         }
705
706         summaryview->filtering_happened = FALSE;
707
708         summaryview->folderview->opened = selected_node;
709
710         gtk_clist_freeze(GTK_CLIST(ctree));
711
712         summary_clear_list(summaryview);
713         summary_set_column_titles(summaryview);
714         summary_set_menu_sensitive(summaryview);
715         if (!is_refresh)
716                 messageview_clear(summaryview->messageview);
717
718         buf = NULL;
719         if (!item || !item->path || !item->parent || item->no_select ||
720             (item->folder->type == F_MH &&
721              ((buf = folder_item_get_path(item)) == NULL ||
722               change_dir(buf) < 0))) {
723                 g_free(buf);
724                 debug_print(_("empty folder\n\n"));
725                 if (is_refresh)
726                         messageview_clear(summaryview->messageview);
727                 summary_clear_all(summaryview);
728                 summaryview->folder_item = item;
729                 gtk_clist_thaw(GTK_CLIST(ctree));
730                 summary_unlock(summaryview);
731                 inc_unlock();
732                 return TRUE;
733         }
734         g_free(buf);
735
736         summaryview->folder_item = item;
737
738         gtk_signal_handler_block_by_data(GTK_OBJECT(ctree), summaryview);
739
740         buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
741         debug_print("%s\n", buf);
742         STATUSBAR_PUSH(summaryview->mainwin, buf);
743         g_free(buf);
744
745         main_window_cursor_wait(summaryview->mainwin);
746
747         mlist = item->folder->get_msg_list(item->folder, item, !update_cache);
748
749         for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
750                 MsgInfo * msginfo = (MsgInfo *) cur->data;
751
752                 msginfo->score = score_message(global_scoring, msginfo);
753                 if (msginfo->score != MAX_SCORE &&
754                     msginfo->score != MIN_SCORE) {
755                         msginfo->score += score_message(item->prefs->scoring,
756                                                         msginfo);
757                 }
758         }
759
760         summaryview->killed_messages = NULL;
761         if ((global_scoring || item->prefs->scoring) &&
762             (item->folder->type == F_NEWS)) {
763                 GSList *not_killed;
764                 gint kill_score;
765
766                 not_killed = NULL;
767                 kill_score = prefs_common.kill_score;
768                 if (item->prefs->kill_score > kill_score)
769                         kill_score = item->prefs->kill_score;
770                 for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
771                         MsgInfo * msginfo = (MsgInfo *) cur->data;
772
773                         if (MSG_IS_NEWS(msginfo->flags) &&
774                             (msginfo->score <= kill_score))
775                                 summaryview->killed_messages = g_slist_append(summaryview->killed_messages, msginfo);
776                         else
777                                 not_killed = g_slist_append(not_killed,
778                                                             msginfo);
779                 }
780                 g_slist_free(mlist);
781                 mlist = not_killed;
782         }
783
784         STATUSBAR_POP(summaryview->mainwin);
785
786         /* set ctree and hash table from the msginfo list
787            creating thread, and count the number of messages */
788         summary_set_ctree_from_list(summaryview, mlist);
789
790         g_slist_free(mlist);
791
792         summary_write_cache(summaryview);
793
794         gtk_signal_handler_unblock_by_data(GTK_OBJECT(ctree), summaryview);
795
796         gtk_clist_thaw(GTK_CLIST(ctree));
797
798         /* sort before */
799         sort_mode = prefs_folder_item_get_sort_mode(item);
800         sort_type = prefs_folder_item_get_sort_type(item);
801
802         if (sort_mode != SORT_BY_NONE) {
803                 summaryview->sort_mode = sort_mode;
804                 if (sort_type == GTK_SORT_DESCENDING)
805                         summaryview->sort_type = GTK_SORT_ASCENDING;
806                 else
807                         summaryview->sort_type = GTK_SORT_DESCENDING;
808
809                 summary_sort(summaryview, sort_mode);
810         }
811
812         if (is_refresh) {
813                 summaryview->displayed =
814                         summary_find_msg_by_msgnum(summaryview,
815                                                    displayed_msgnum);
816                 if (!summaryview->displayed)
817                         messageview_clear(summaryview->messageview);
818                 summary_select_by_msgnum(summaryview, selected_msgnum);
819                 if (!summaryview->selected) {
820                         /* no selected message - select first unread
821                            message, but do not display it */
822                         node = summary_find_next_unread_msg(summaryview, NULL);
823                         if (node == NULL && GTK_CLIST(ctree)->row_list != NULL)
824                                 node = gtk_ctree_node_nth
825                                         (ctree, GTK_CLIST(ctree)->rows - 1);
826                         summary_select_node(summaryview, node, FALSE);
827                 }
828         } else {
829                 /* select first unread message */
830                 if (sort_mode == SORT_BY_SCORE)
831                         node = summary_find_next_important_score(summaryview,
832                                                                  NULL);
833                 else
834                         node = summary_find_next_unread_msg(summaryview, NULL);
835
836                 if (node == NULL && GTK_CLIST(ctree)->row_list != NULL) {
837                         /* Get the last visible node on screen */
838                         /* FIXME: huh, what happens if node is null? that allowed?? */
839                         node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
840                 }       
841                 if (prefs_common.open_unread_on_enter) {
842                         summary_unlock(summaryview);
843                         summary_select_node(summaryview, node, TRUE);
844                         summary_lock(summaryview);
845                 } else
846                         summary_select_node(summaryview, node, FALSE);
847         }
848
849         summary_status_show(summaryview);
850         summary_set_menu_sensitive(summaryview);
851         main_window_set_toolbar_sensitive(summaryview->mainwin);
852
853         debug_print("\n");
854         STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
855
856         main_window_cursor_normal(summaryview->mainwin);
857         summary_unlock(summaryview);
858         inc_unlock();
859
860         return TRUE;
861 }
862
863 void summary_clear_list(SummaryView *summaryview)
864 {
865         GtkCList *clist = GTK_CLIST(summaryview->ctree);
866         gint optimal_width;
867         GSList * cur;
868
869         gtk_clist_freeze(clist);
870
871         for(cur = summaryview->killed_messages ; cur != NULL ; 
872             cur = g_slist_next(cur)) {
873                 MsgInfo * msginfo = (MsgInfo *) cur->data;
874
875                 procmsg_msginfo_free(msginfo);
876         }
877         g_slist_free(summaryview->killed_messages);
878         summaryview->killed_messages = NULL;
879
880         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree),
881                                 NULL, summary_free_msginfo_func, NULL);
882
883         summaryview->folder_item = NULL;
884
885         summaryview->display_msg = FALSE;
886
887         summaryview->selected  = NULL;
888         summaryview->displayed = NULL;
889         summaryview->newmsgs   = summaryview->unread     = 0;
890         summaryview->messages  = summaryview->total_size = 0;
891         summaryview->deleted   = summaryview->moved      = 0;
892         summaryview->copied    = 0;
893         if (summaryview->msgid_table) {
894                 g_hash_table_destroy(summaryview->msgid_table);
895                 summaryview->msgid_table = NULL;
896         }
897         if (summaryview->subject_table) {
898                 g_hash_table_destroy(summaryview->subject_table);
899                 summaryview->subject_table = NULL;
900         }
901         summaryview->mlist = NULL;
902         if (summaryview->folder_table) {
903                 g_hash_table_destroy(summaryview->folder_table);
904                 summaryview->folder_table = NULL;
905         }
906         summaryview->sort_mode = SORT_BY_NONE;
907         summaryview->sort_type = GTK_SORT_ASCENDING;
908
909         gtk_clist_clear(clist);
910         if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
911                 optimal_width = gtk_clist_optimal_column_width
912                         (clist, summaryview->col_pos[S_COL_SUBJECT]);
913                 gtk_clist_set_column_width
914                         (clist, summaryview->col_pos[S_COL_SUBJECT],
915                          optimal_width);
916         }
917
918         gtk_clist_thaw(clist);
919 }
920
921 void summary_clear_all(SummaryView *summaryview)
922 {
923         summary_clear_list(summaryview);
924         summary_set_menu_sensitive(summaryview);
925         main_window_set_toolbar_sensitive(summaryview->mainwin);
926         summary_status_show(summaryview);
927 }
928
929 void summary_lock(SummaryView *summaryview)
930 {
931         summaryview->lock_count++;
932 }
933
934 void summary_unlock(SummaryView *summaryview)
935 {
936         if (summaryview->lock_count)
937                 summaryview->lock_count--;
938 }
939
940 gboolean summary_is_locked(SummaryView *summaryview)
941 {
942         return summaryview->lock_count > 0;
943 }
944
945 SummarySelection summary_get_selection_type(SummaryView *summaryview)
946 {
947         GtkCList *clist = GTK_CLIST(summaryview->ctree);
948         SummarySelection selection;
949
950         if (!clist->row_list)
951                 selection = SUMMARY_NONE;
952         else if (!clist->selection)
953                 selection = SUMMARY_SELECTED_NONE;
954         else if (!clist->selection->next)
955                 selection = SUMMARY_SELECTED_SINGLE;
956         else
957                 selection = SUMMARY_SELECTED_MULTIPLE;
958
959         return selection;
960 }
961
962 static void summary_set_menu_sensitive(SummaryView *summaryview)
963 {
964         GtkItemFactory *ifactory = summaryview->popupfactory;
965         SummarySelection selection;
966         gboolean sens;
967
968         selection = summary_get_selection_type(summaryview);
969         main_window_set_menu_sensitive(summaryview->mainwin);
970
971         if (selection == SUMMARY_NONE) {
972                 GtkWidget *submenu;
973
974                 submenu = gtk_item_factory_get_widget
975                         (summaryview->popupfactory, "/Mark");
976                 menu_set_insensitive_all(GTK_MENU_SHELL(submenu));
977                 menu_set_insensitive_all
978                         (GTK_MENU_SHELL(summaryview->popupmenu));
979                 return;
980         }
981
982         if (summaryview->folder_item->folder->type != F_NEWS) {
983                 menu_set_sensitive(ifactory, "/Move...", TRUE);
984                 menu_set_sensitive(ifactory, "/Copy...", TRUE);
985                 menu_set_sensitive(ifactory, "/Delete", TRUE);
986         }
987
988         menu_set_sensitive(ifactory, "/Execute", TRUE);
989
990         menu_set_sensitive(ifactory, "/Mark", TRUE);
991         menu_set_sensitive(ifactory, "/Mark/Mark",   TRUE);
992         menu_set_sensitive(ifactory, "/Mark/Unmark", TRUE);
993
994         menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
995         menu_set_sensitive(ifactory, "/Mark/Mark as read",   TRUE);
996         menu_set_sensitive(ifactory, "/Mark/Mark all read", TRUE);
997         menu_set_sensitive(ifactory, "/Mark/Ignore thread",   TRUE);
998         menu_set_sensitive(ifactory, "/Mark/Unignore thread", TRUE);
999
1000         menu_set_sensitive(ifactory, "/Color label", TRUE);
1001
1002         sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1003         menu_set_sensitive(ifactory, "/Reply",                    sens);
1004         menu_set_sensitive(ifactory, "/Reply to sender",          sens);
1005         menu_set_sensitive(ifactory, "/Reply to all",             sens);
1006         menu_set_sensitive(ifactory, "/Forward",                  TRUE);
1007         menu_set_sensitive(ifactory, "/Forward as attachment",    TRUE);
1008         menu_set_sensitive(ifactory, "/Bounce",                   TRUE);
1009
1010         menu_set_sensitive(ifactory, "/Add sender to address book", sens);
1011
1012         menu_set_sensitive(ifactory, "/View", sens);
1013         menu_set_sensitive(ifactory, "/View/Open in new window", sens);
1014         menu_set_sensitive(ifactory, "/View/Source", sens);
1015         menu_set_sensitive(ifactory, "/View/All header", sens);
1016         if (summaryview->folder_item->stype == F_OUTBOX ||
1017             summaryview->folder_item->stype == F_DRAFT  ||
1018             summaryview->folder_item->stype == F_QUEUE)
1019                 menu_set_sensitive(ifactory, "/Re-edit", sens);
1020
1021         menu_set_sensitive(ifactory, "/Save as...", sens);
1022         menu_set_sensitive(ifactory, "/Print...",   TRUE);
1023
1024         menu_set_sensitive(ifactory, "/Select all", TRUE);
1025
1026         if (summaryview->folder_item->folder->account)
1027                 sens = summaryview->folder_item->folder->account->protocol
1028                         == A_NNTP;
1029         else
1030                 sens = FALSE;
1031         menu_set_sensitive(ifactory, "/Follow-up and reply to", sens);
1032 }
1033
1034 void summary_select_prev_unread(SummaryView *summaryview)
1035 {
1036         GtkCTreeNode *node;
1037         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1038
1039         node = summary_find_prev_unread_msg(summaryview, summaryview->selected);
1040
1041         if (!node) {
1042                 AlertValue val;
1043
1044                 switch (prefs_common.next_unread_msg_dialog) {
1045                         case NEXTUNREADMSGDIALOG_ALWAYS:
1046                                 val = alertpanel(_("No more unread messages"),
1047                                                  _("No unread message found. "
1048                                                    "Search from the end?"),
1049                                                  _("Yes"), _("No"), NULL);
1050                                 break;
1051                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1052                                 val = G_ALERTDEFAULT;
1053                                 break;
1054                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1055                                 val = !G_ALERTDEFAULT;
1056                                 break;
1057                         default:
1058                                 debug_print(
1059                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1060                 }
1061                 if (val != G_ALERTDEFAULT) return;
1062                 node = summary_find_prev_unread_msg(summaryview, NULL);
1063         }
1064
1065         if (!node) {
1066                 alertpanel_notice(_("No unread messages."));
1067         } else {
1068                 gtkut_ctree_expand_parent_all(ctree, node);
1069                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1070                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1071                 if (summaryview->displayed == node)
1072                         summaryview->displayed = NULL;
1073                 summary_display_msg(summaryview, node, FALSE);
1074         }
1075 }
1076
1077 void summary_select_next_unread(SummaryView *summaryview)
1078 {
1079         GtkCTreeNode *node;
1080         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1081
1082         node = summary_find_next_unread_msg(summaryview, summaryview->selected);
1083
1084         if (node) {
1085                 gtkut_ctree_expand_parent_all(ctree, node);
1086                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1087                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1088                 if (summaryview->displayed == node)
1089                         summaryview->displayed = NULL;
1090                 summary_display_msg(summaryview, node, FALSE);
1091         } else {
1092                 AlertValue val;
1093
1094                 switch (prefs_common.next_unread_msg_dialog) {
1095                         case NEXTUNREADMSGDIALOG_ALWAYS:
1096                                 val = alertpanel(_("No more unread messages"),
1097                                                  _("No unread message found. "
1098                                                    "Go to next folder?"),
1099                                                  _("Yes"), _("No"), NULL);
1100                                 break;
1101                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1102                                 val = G_ALERTDEFAULT;
1103                                 break;
1104                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1105                                 val = !G_ALERTDEFAULT;
1106                                 break;
1107                         default:
1108                                 debug_print(
1109                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1110                 }
1111
1112                 if (val == G_ALERTDEFAULT) {
1113                         if (gtk_signal_n_emissions_by_name
1114                                 (GTK_OBJECT(ctree), "key_press_event") > 0)
1115                                         gtk_signal_emit_stop_by_name
1116                                                 (GTK_OBJECT(ctree),
1117                                                  "key_press_event");
1118                         folderview_select_next_unread(summaryview->folderview);
1119                 }
1120         }
1121 }
1122
1123 void summary_select_prev_marked(SummaryView *summaryview)
1124 {
1125         GtkCTreeNode *node;
1126         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1127
1128         node = summary_find_prev_marked_msg(summaryview, summaryview->selected);
1129
1130         if (!node) {
1131                 AlertValue val;
1132
1133                 val = alertpanel(_("No more marked messages"),
1134                                  _("No marked message found. "
1135                                    "Search from the end?"),
1136                                  _("Yes"), _("No"), NULL);
1137                 if (val != G_ALERTDEFAULT) return;
1138                 node = summary_find_prev_marked_msg(summaryview, NULL);
1139         }
1140
1141         if (!node) {
1142                 alertpanel_notice(_("No marked messages."));
1143         } else {
1144                 gtkut_ctree_expand_parent_all(ctree, node);
1145                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1146                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1147                 if (summaryview->displayed == node)
1148                         summaryview->displayed = NULL;
1149                 summary_display_msg(summaryview, node, FALSE);
1150         }
1151 }
1152
1153 void summary_select_next_marked(SummaryView *summaryview)
1154 {
1155         GtkCTreeNode *node;
1156         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1157
1158         node = summary_find_next_marked_msg(summaryview, summaryview->selected);
1159
1160         if (!node) {
1161                 AlertValue val;
1162
1163                 val = alertpanel(_("No more marked messages"),
1164                                  _("No marked message found. "
1165                                    "Search from the beginning?"),
1166                                  _("Yes"), _("No"), NULL);
1167                 if (val != G_ALERTDEFAULT) return;
1168                 node = summary_find_next_marked_msg(summaryview, NULL);
1169         }
1170
1171         if (!node) {
1172                 alertpanel_notice(_("No marked messages."));
1173         } else {
1174                 gtkut_ctree_expand_parent_all(ctree, node);
1175                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1176                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1177                 if (summaryview->displayed == node)
1178                         summaryview->displayed = NULL;
1179                 summary_display_msg(summaryview, node, FALSE);
1180         }
1181 }
1182
1183 void summary_select_prev_labeled(SummaryView *summaryview)
1184 {
1185         GtkCTreeNode *node;
1186         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1187
1188         node = summary_find_prev_labeled_msg(summaryview, summaryview->selected);
1189
1190         if (!node) {
1191                 AlertValue val;
1192
1193                 val = alertpanel(_("No more labeled messages"),
1194                                  _("No labeled message found. "
1195                                    "Search from the end?"),
1196                                  _("Yes"), _("No"), NULL);
1197                 if (val != G_ALERTDEFAULT) return;
1198                 node = summary_find_prev_labeled_msg(summaryview, NULL);
1199         }
1200
1201         if (!node) {
1202                 alertpanel_notice(_("No labeled messages."));
1203         } else {
1204                 gtkut_ctree_expand_parent_all(ctree, node);
1205                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1206                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1207                 if (summaryview->displayed == node)
1208                         summaryview->displayed = NULL;
1209                 summary_display_msg(summaryview, node, FALSE);
1210         }
1211 }
1212
1213 void summary_select_next_labeled(SummaryView *summaryview)
1214 {
1215         GtkCTreeNode *node;
1216         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1217
1218         node = summary_find_next_labeled_msg(summaryview, summaryview->selected);
1219
1220         if (!node) {
1221                 AlertValue val;
1222
1223                 val = alertpanel(_("No more labeled messages"),
1224                                  _("No labeled message found. "
1225                                    "Search from the beginning?"),
1226                                  _("Yes"), _("No"), NULL);
1227                 if (val != G_ALERTDEFAULT) return;
1228                 node = summary_find_next_labeled_msg(summaryview, NULL);
1229         }
1230
1231         if (!node) {
1232                 alertpanel_notice(_("No labeled messages."));
1233         } else {
1234                 gtkut_ctree_expand_parent_all(ctree, node);
1235                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1236                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1237                 if (summaryview->displayed == node)
1238                         summaryview->displayed = NULL;
1239                 summary_display_msg(summaryview, node, FALSE);
1240         }
1241 }
1242
1243 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1244 {
1245         GtkCTreeNode *node;
1246
1247         node = summary_find_msg_by_msgnum(summaryview, msgnum);
1248         summary_select_node(summaryview, node, FALSE);
1249 }
1250
1251 /**
1252  * summary_select_node:
1253  * @summaryview: Summary view.
1254  * @node: Summary tree node.
1255  * @display_msg: TRUE to display the selected message.
1256  *
1257  * Select @node (bringing it into view by scrolling and expanding its
1258  * thread, if necessary) and unselect all others.  If @display_msg is
1259  * TRUE, display the corresponding message in the message view.
1260  **/
1261 void summary_select_node(SummaryView *summaryview, GtkCTreeNode *node,
1262                          gboolean display_msg)
1263 {
1264         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1265
1266         if (node) {
1267                 GTK_EVENTS_FLUSH();
1268                 gtkut_ctree_expand_parent_all(ctree, node);
1269                 gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
1270                 gtk_widget_grab_focus(GTK_WIDGET(ctree));
1271                 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1272                 summaryview->display_msg = display_msg;
1273                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1274         }
1275 }
1276
1277 static guint summary_get_msgnum(SummaryView *summaryview, GtkCTreeNode *node)
1278 {
1279         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1280         MsgInfo *msginfo;
1281
1282         if (!node)
1283                 return 0;
1284         msginfo = gtk_ctree_node_get_row_data(ctree, node);
1285         return msginfo->msgnum;
1286 }
1287
1288 static GtkCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
1289                                            GtkCTreeNode *current_node)
1290 {
1291         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1292         GtkCTreeNode *node;
1293         MsgInfo *msginfo;
1294
1295         if (current_node)
1296                 node = current_node;
1297         else
1298                 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1299
1300         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1301                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1302                 if (!MSG_IS_DELETED(msginfo->flags)) break;
1303         }
1304
1305         return node;
1306 }
1307
1308 static GtkCTreeNode *summary_find_next_msg(SummaryView *summaryview,
1309                                            GtkCTreeNode *current_node)
1310 {
1311         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1312         GtkCTreeNode *node;
1313         MsgInfo *msginfo;
1314
1315         if (current_node)
1316                 node = current_node;
1317         else
1318                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1319
1320         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1321                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1322                 if (!MSG_IS_DELETED(msginfo->flags)) break;
1323         }
1324
1325         return node;
1326 }
1327
1328 static GtkCTreeNode *summary_find_prev_unread_msg(SummaryView *summaryview,
1329                                                   GtkCTreeNode *current_node)
1330 {
1331         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1332         GtkCTreeNode *node;
1333         MsgInfo *msginfo;
1334
1335         if (current_node)
1336                 node = current_node;
1337         else
1338                 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1339
1340         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1341                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1342                 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) break;
1343         }
1344
1345         return node;
1346 }
1347
1348 static GtkCTreeNode *summary_find_next_unread_msg(SummaryView *summaryview,
1349                                                   GtkCTreeNode *current_node)
1350 {
1351         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1352         GtkCTreeNode *node;
1353         MsgInfo *msginfo;
1354
1355         if (current_node)
1356                 node = current_node;
1357         else
1358                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1359
1360         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1361                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1362                 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) break;
1363         }
1364
1365         return node;
1366 }
1367
1368 static GtkCTreeNode *summary_find_prev_marked_msg(SummaryView *summaryview,
1369                                                   GtkCTreeNode *current_node)
1370 {
1371         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1372         GtkCTreeNode *node;
1373         MsgInfo *msginfo;
1374
1375         if (current_node)
1376                 node = GTK_CTREE_NODE_PREV(current_node);
1377         else
1378                 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1379
1380         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1381                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1382                 if (MSG_IS_MARKED(msginfo->flags)) break;
1383         }
1384
1385         return node;
1386 }
1387
1388 static GtkCTreeNode *summary_find_next_marked_msg(SummaryView *summaryview,
1389                                                   GtkCTreeNode *current_node)
1390 {
1391         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1392         GtkCTreeNode *node;
1393         MsgInfo *msginfo;
1394
1395         if (current_node)
1396                 node = gtkut_ctree_node_next(ctree, current_node);
1397         else
1398                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1399
1400         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1401                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1402                 if (MSG_IS_MARKED(msginfo->flags)) break;
1403         }
1404
1405         return node;
1406 }
1407
1408 static GtkCTreeNode *summary_find_prev_labeled_msg(SummaryView *summaryview,
1409                                                    GtkCTreeNode *current_node)
1410 {
1411         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1412         GtkCTreeNode *node;
1413         MsgInfo *msginfo;
1414
1415         if (current_node)
1416                 node = GTK_CTREE_NODE_PREV(current_node);
1417         else
1418                 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1419
1420         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1421                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1422                 if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) > 0) break;
1423         }
1424
1425         return node;
1426 }
1427
1428 static GtkCTreeNode *summary_find_next_labeled_msg(SummaryView *summaryview,
1429                                                    GtkCTreeNode *current_node)
1430 {
1431         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1432         GtkCTreeNode *node;
1433         MsgInfo *msginfo;
1434
1435         if (current_node)
1436                 node = gtkut_ctree_node_next(ctree, current_node);
1437         else
1438                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1439
1440         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1441                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1442                 if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) > 0) break;
1443         }
1444
1445         return node;
1446 }
1447
1448 static GtkCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
1449                                                 guint msgnum)
1450 {
1451         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1452         GtkCTreeNode *node;
1453         MsgInfo *msginfo;
1454
1455         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1456
1457         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1458                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1459                 if (msginfo->msgnum == msgnum) break;
1460         }
1461
1462         return node;
1463 }
1464
1465 static guint attract_hash_func(gconstpointer key)
1466 {
1467         gchar *str;
1468         gchar *p;
1469         guint h;
1470
1471         Xstrdup_a(str, (const gchar *)key, return 0);
1472         trim_subject(str);
1473
1474         p = str;
1475         h = *p;
1476
1477         if (h) {
1478                 for (p += 1; *p != '\0'; p++)
1479                         h = (h << 5) - h + *p;
1480         }
1481
1482         return h;
1483 }
1484
1485 static gint attract_compare_func(gconstpointer a, gconstpointer b)
1486 {
1487         return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1488 }
1489
1490 void summary_attract_by_subject(SummaryView *summaryview)
1491 {
1492         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1493         GtkCList *clist = GTK_CLIST(ctree);
1494         GtkCTreeNode *src_node;
1495         GtkCTreeNode *dst_node, *sibling;
1496         GtkCTreeNode *tmp;
1497         MsgInfo *src_msginfo, *dst_msginfo;
1498         GHashTable *subject_table;
1499
1500         debug_print(_("Attracting messages by subject..."));
1501         STATUSBAR_PUSH(summaryview->mainwin,
1502                        _("Attracting messages by subject..."));
1503
1504         main_window_cursor_wait(summaryview->mainwin);
1505         gtk_clist_freeze(clist);
1506
1507         subject_table = g_hash_table_new(attract_hash_func,
1508                                          attract_compare_func);
1509
1510         for (src_node = GTK_CTREE_NODE(clist->row_list);
1511              src_node != NULL;
1512              src_node = tmp) {
1513                 tmp = GTK_CTREE_ROW(src_node)->sibling;
1514                 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
1515                 if (!src_msginfo) continue;
1516                 if (!src_msginfo->subject) continue;
1517
1518                 /* find attracting node */
1519                 dst_node = g_hash_table_lookup(subject_table,
1520                                                src_msginfo->subject);
1521
1522                 if (dst_node) {
1523                         dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
1524
1525                         /* if the time difference is more than 20 days,
1526                            don't attract */
1527                         if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
1528                             > 60 * 60 * 24 * 20)
1529                                 continue;
1530
1531                         sibling = GTK_CTREE_ROW(dst_node)->sibling;
1532                         if (src_node != sibling)
1533                                 gtk_ctree_move(ctree, src_node, NULL, sibling);
1534                 }
1535
1536                 g_hash_table_insert(subject_table,
1537                                     src_msginfo->subject, src_node);
1538         }
1539
1540         g_hash_table_destroy(subject_table);
1541
1542         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1543
1544         gtk_clist_thaw(clist);
1545
1546         debug_print(_("done.\n"));
1547         STATUSBAR_POP(summaryview->mainwin);
1548
1549         main_window_cursor_normal(summaryview->mainwin);
1550 }
1551
1552 static void summary_free_msginfo_func(GtkCTree *ctree, GtkCTreeNode *node,
1553                                       gpointer data)
1554 {
1555         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1556
1557         if (msginfo)
1558                 procmsg_msginfo_free(msginfo);
1559 }
1560
1561 static void summary_set_marks_func(GtkCTree *ctree, GtkCTreeNode *node,
1562                                    gpointer data)
1563 {
1564         SummaryView *summaryview = data;
1565         MsgInfo *msginfo;
1566
1567         msginfo = gtk_ctree_node_get_row_data(ctree, node);
1568
1569         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1570                 summaryview->newmsgs++;
1571         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1572                 summaryview->unread++;
1573         if (MSG_IS_DELETED(msginfo->flags))
1574                 summaryview->deleted++;
1575
1576         summaryview->messages++;
1577         summaryview->total_size += msginfo->size;
1578
1579         summary_set_row_marks(summaryview, node);
1580 }
1581
1582 static void summary_update_status(SummaryView *summaryview)
1583 {
1584         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1585         GtkCTreeNode *node;
1586         MsgInfo *msginfo;
1587
1588         summaryview->newmsgs = summaryview->unread =
1589         summaryview->messages = summaryview->total_size =
1590         summaryview->deleted = summaryview->moved = summaryview->copied = 0;
1591
1592         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1593              node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1594                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1595
1596                 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1597                         summaryview->newmsgs++;
1598                 if (MSG_IS_UNREAD(msginfo->flags)&& !MSG_IS_IGNORE_THREAD(msginfo->flags))
1599                         summaryview->unread++;
1600                 if (MSG_IS_DELETED(msginfo->flags))
1601                         summaryview->deleted++;
1602                 if (MSG_IS_MOVE(msginfo->flags))
1603                         summaryview->moved++;
1604                 if (MSG_IS_COPY(msginfo->flags))
1605                         summaryview->copied++;
1606                 summaryview->messages++;
1607                 summaryview->total_size += msginfo->size;
1608         }
1609 }
1610
1611 static void summary_status_show(SummaryView *summaryview)
1612 {
1613         gchar *str;
1614         gchar *del, *mv, *cp;
1615         gchar *sel;
1616         gchar *spc;
1617         GList *rowlist, *cur;
1618         guint n_selected = 0;
1619         off_t sel_size = 0;
1620         MsgInfo *msginfo;
1621
1622         if (!summaryview->folder_item) {
1623                 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
1624                 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
1625                 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
1626                 return;
1627         }
1628
1629         rowlist = GTK_CLIST(summaryview->ctree)->selection;
1630         for (cur = rowlist; cur != NULL; cur = cur->next) {
1631                 msginfo = gtk_ctree_node_get_row_data
1632                         (GTK_CTREE(summaryview->ctree),
1633                          GTK_CTREE_NODE(cur->data));
1634                 sel_size += msginfo->size;
1635                 n_selected++;
1636         }
1637
1638         gtk_label_set(GTK_LABEL(summaryview->statlabel_folder),
1639                       summaryview->folder_item &&
1640                       summaryview->folder_item->folder->type == F_NEWS
1641                       ? g_basename(summaryview->folder_item->path)
1642                       : summaryview->folder_item->path);
1643
1644         if (summaryview->deleted)
1645                 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
1646         else
1647                 del = g_strdup("");
1648         if (summaryview->moved)
1649                 mv = g_strdup_printf(_("%s%d moved"),
1650                                      summaryview->deleted ? _(", ") : "",
1651                                      summaryview->moved);
1652         else
1653                 mv = g_strdup("");
1654         if (summaryview->copied)
1655                 cp = g_strdup_printf(_("%s%d copied"),
1656                                      summaryview->deleted ||
1657                                      summaryview->moved ? _(", ") : "",
1658                                      summaryview->copied);
1659         else
1660                 cp = g_strdup("");
1661
1662         if (summaryview->deleted || summaryview->moved || summaryview->copied)
1663                 spc = "    ";
1664         else
1665                 spc = "";
1666
1667         if (n_selected)
1668                 sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
1669         else
1670                 sel = g_strdup("");
1671         str = g_strconcat(n_selected ? itos(n_selected) : "",
1672                           n_selected ? _(" item(s) selected") : "",
1673                           sel, spc, del, mv, cp, NULL);
1674         gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str);
1675         g_free(str);
1676         g_free(sel);
1677         g_free(del);
1678         g_free(mv);
1679         g_free(cp);
1680
1681         if (summaryview->folder_item &&
1682             FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
1683                 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
1684                                       summaryview->newmsgs,
1685                                       summaryview->unread,
1686                                       summaryview->messages,
1687                                       to_human_readable(summaryview->total_size));
1688         } else {
1689                 str = g_strdup_printf(_("%d new, %d unread, %d total"),
1690                                       summaryview->newmsgs,
1691                                       summaryview->unread,
1692                                       summaryview->messages);
1693         }
1694         gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str);
1695         g_free(str);
1696
1697         folderview_update_msg_num(summaryview->folderview,
1698                                   summaryview->folderview->opened,
1699                                   summaryview->newmsgs,
1700                                   summaryview->unread,
1701                                   summaryview->messages);
1702 }
1703
1704 static void summary_set_column_titles(SummaryView *summaryview)
1705 {
1706         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1707         GtkWidget *hbox;
1708         GtkWidget *label;
1709         GtkWidget *arrow;
1710         gint pos;
1711         const gchar *title;
1712         SummaryColumnType type;
1713         gboolean single_char;
1714         GtkJustification justify;
1715
1716         static SummarySortType sort_by[N_SUMMARY_COLS] = {
1717                 SORT_BY_MARK,
1718                 SORT_BY_UNREAD,
1719                 SORT_BY_MIME,
1720                 SORT_BY_SUBJECT,
1721                 SORT_BY_FROM,
1722                 SORT_BY_DATE,
1723                 SORT_BY_SIZE,
1724                 SORT_BY_NUMBER
1725         };
1726
1727         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
1728                 type = summaryview->col_state[pos].type;
1729
1730                 single_char = (type == S_COL_MIME ||
1731                                type == S_COL_MARK || type == S_COL_UNREAD);
1732                 justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
1733                         ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
1734
1735                 switch (type) {
1736                 case S_COL_SUBJECT:
1737                 case S_COL_FROM:
1738                 case S_COL_DATE:
1739                 case S_COL_NUMBER:
1740                         if (prefs_common.trans_hdr)
1741                                 title = gettext(col_label[type]);
1742                         else
1743                                 title = col_label[type];
1744                         break;
1745                 default:
1746                         title = gettext(col_label[type]);
1747                 }
1748
1749                 if (type == S_COL_MIME) {
1750                         label = gtk_pixmap_new(clipxpm, clipxpmmask);
1751                         gtk_widget_show(label);
1752                         gtk_clist_set_column_widget(clist, pos, label);
1753                         continue;
1754                 }
1755                 if (single_char) {
1756                         gtk_clist_set_column_title(clist, pos, title);
1757                         continue;
1758                 }
1759
1760                 hbox = gtk_hbox_new(FALSE, 4);
1761                 label = gtk_label_new(title);
1762                 if (justify == GTK_JUSTIFY_RIGHT)
1763                         gtk_box_pack_end(GTK_BOX(hbox), label,
1764                                          FALSE, FALSE, 0);
1765                 else
1766                         gtk_box_pack_start(GTK_BOX(hbox), label,
1767                                            FALSE, FALSE, 0);
1768
1769                 if (summaryview->sort_mode == sort_by[type]) {
1770                         arrow = gtk_arrow_new
1771                                 (summaryview->sort_type == GTK_SORT_ASCENDING
1772                                  ? GTK_ARROW_DOWN : GTK_ARROW_UP,
1773                                  GTK_SHADOW_IN);
1774                         if (justify == GTK_JUSTIFY_RIGHT)
1775                                 gtk_box_pack_start(GTK_BOX(hbox), arrow,
1776                                                    FALSE, FALSE, 0);
1777                         else
1778                                 gtk_box_pack_end(GTK_BOX(hbox), arrow,
1779                                                  FALSE, FALSE, 0);
1780                 }
1781
1782                 gtk_widget_show_all(hbox);
1783                 gtk_clist_set_column_widget(clist, pos, hbox);
1784         }
1785 }
1786
1787 void summary_sort(SummaryView *summaryview, SummarySortType type)
1788 {
1789         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1790         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1791         GtkCListCompareFunc cmp_func;
1792
1793         if (!summaryview->folder_item)
1794                 return;
1795
1796         switch (type) {
1797         case SORT_BY_MARK:
1798                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_mark;
1799                 break;
1800         case SORT_BY_UNREAD:
1801                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_unread;
1802                 break;
1803         case SORT_BY_MIME:
1804                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_mime;
1805                 break;
1806         case SORT_BY_NUMBER:
1807                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_num;
1808                 break;
1809         case SORT_BY_SIZE:
1810                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_size;
1811                 break;
1812         case SORT_BY_DATE:
1813                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_date;
1814                 break;
1815         case SORT_BY_FROM:
1816                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_from;
1817                 break;
1818         case SORT_BY_SUBJECT:
1819                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
1820                 break;
1821         case SORT_BY_SCORE:
1822                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_score;
1823                 break;
1824         case SORT_BY_LABEL:
1825                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_label;
1826                 break;
1827         default:
1828                 return;
1829         }
1830
1831         debug_print(_("Sorting summary..."));
1832         STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
1833
1834         main_window_cursor_wait(summaryview->mainwin);
1835
1836         gtk_clist_set_compare_func(clist, cmp_func);
1837
1838         /* toggle sort type if the same column is selected */
1839         if (summaryview->sort_mode == type)
1840                 summaryview->sort_type =
1841                         summaryview->sort_type == GTK_SORT_ASCENDING
1842                         ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
1843         else
1844                 summaryview->sort_type = GTK_SORT_ASCENDING;
1845         gtk_clist_set_sort_type(clist, summaryview->sort_type);
1846         summaryview->sort_mode = type;
1847
1848         summary_set_column_titles(summaryview);
1849
1850         gtk_ctree_sort_node(ctree, NULL);
1851
1852         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1853         prefs_folder_item_set_config(summaryview->folder_item,
1854                                      summaryview->sort_type,
1855                                      summaryview->sort_mode);
1856         prefs_folder_item_save_config(summaryview->folder_item);
1857
1858         debug_print(_("done.\n"));
1859         STATUSBAR_POP(summaryview->mainwin);
1860
1861         main_window_cursor_normal(summaryview->mainwin);
1862 }
1863
1864 gboolean summary_insert_gnode_func(GtkCTree *ctree, guint depth, GNode *gnode,
1865                                    GtkCTreeNode *cnode, gpointer data)
1866 {
1867         SummaryView *summaryview = (SummaryView *)data;
1868         MsgInfo *msginfo = (MsgInfo *)gnode->data;
1869         gchar *text[N_SUMMARY_COLS];
1870         gint *col_pos = summaryview->col_pos;
1871         const gchar *msgid = msginfo->msgid;
1872         GHashTable *msgid_table = summaryview->msgid_table;
1873
1874         summary_set_header(summaryview, text, msginfo);
1875
1876         gtk_ctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
1877                                 NULL, NULL, NULL, NULL, FALSE,
1878                                 gnode->parent->parent ? TRUE : FALSE);
1879 #define SET_TEXT(col) \
1880         gtk_ctree_node_set_text(ctree, cnode, col_pos[col], \
1881                                 text[col_pos[col]])
1882
1883         SET_TEXT(S_COL_NUMBER);
1884         SET_TEXT(S_COL_SCORE);
1885         SET_TEXT(S_COL_SIZE);
1886         SET_TEXT(S_COL_DATE);
1887         SET_TEXT(S_COL_FROM);
1888         SET_TEXT(S_COL_SUBJECT);
1889
1890 #undef SET_TEXT
1891
1892         GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
1893         summary_set_marks_func(ctree, cnode, summaryview);
1894
1895         if (msgid)
1896                 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
1897
1898         return TRUE;
1899 }
1900
1901 static void summary_set_ctree_from_list(SummaryView *summaryview,
1902                                         GSList *mlist)
1903 {
1904         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1905         MsgInfo *msginfo;
1906         MsgInfo *parentinfo;
1907         MsgInfo *cur_msginfo;
1908         GtkCTreeNode *node = NULL;
1909         GHashTable *msgid_table;
1910         GHashTable *subject_table;
1911         GSList * cur;
1912         GtkCTreeNode *cur_parent;
1913
1914         if (!mlist) return;
1915
1916         debug_print(_("\tSetting summary from message data..."));
1917         STATUSBAR_PUSH(summaryview->mainwin,
1918                        _("Setting summary from message data..."));
1919         gdk_flush();
1920
1921         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
1922         summaryview->msgid_table = msgid_table;
1923         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1924         summaryview->subject_table = subject_table;
1925
1926         if (prefs_common.use_addr_book)
1927                 start_address_completion();
1928         
1929         for (cur = mlist ; cur != NULL; cur = cur->next) {
1930                 msginfo = (MsgInfo *)cur->data;
1931                 msginfo->threadscore = msginfo->score;
1932         }
1933
1934         if (global_scoring || summaryview->folder_item->prefs->scoring) {
1935                 summaryview->important_score = prefs_common.important_score;
1936                 if (summaryview->folder_item->prefs->important_score >
1937                     summaryview->important_score)
1938                         summaryview->important_score =
1939                                 summaryview->folder_item->prefs->important_score;
1940         }
1941         
1942         if (summaryview->folder_item->threaded) {
1943                 GNode *root, *gnode;
1944
1945                 root = procmsg_get_thread_tree(mlist);
1946
1947                 for (gnode = root->children; gnode != NULL;
1948                      gnode = gnode->next) {
1949                         node = gtk_ctree_insert_gnode
1950                                 (ctree, NULL, node, gnode,
1951                                  summary_insert_gnode_func, summaryview);
1952                 }
1953
1954                 g_node_destroy(root);
1955
1956                 summary_thread_init(summaryview);
1957         } else {
1958                 gchar *text[N_SUMMARY_COLS];
1959
1960                 mlist = g_slist_reverse(mlist);
1961                 for (; mlist != NULL; mlist = mlist->next) {
1962                         msginfo = (MsgInfo *)mlist->data;
1963
1964                         summary_set_header(summaryview, text, msginfo);
1965
1966                         node = gtk_ctree_insert_node
1967                                 (ctree, NULL, node, text, 2,
1968                                  NULL, NULL, NULL, NULL, FALSE, FALSE);
1969                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
1970                         summary_set_marks_func(ctree, node, summaryview);
1971
1972                         if (msginfo->msgid)
1973                                 g_hash_table_insert(msgid_table,
1974                                                     msginfo->msgid, node);
1975
1976                         subject_table_insert(subject_table,
1977                                              msginfo->subject,
1978                                              node);
1979                 }
1980                 mlist = g_slist_reverse(mlist);
1981         }
1982
1983         if (prefs_common.enable_hscrollbar &&
1984             summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1985                 gint optimal_width;
1986
1987                 optimal_width = gtk_clist_optimal_column_width
1988                         (GTK_CLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
1989                 gtk_clist_set_column_width(GTK_CLIST(ctree),
1990                                            summaryview->col_pos[S_COL_SUBJECT],
1991                                            optimal_width);
1992         }
1993
1994         if (prefs_common.use_addr_book)
1995                 end_address_completion();
1996
1997         debug_print(_("done.\n"));
1998         STATUSBAR_POP(summaryview->mainwin);
1999         if (debug_mode) {
2000                 debug_print("\tmsgid hash table size = %d\n",
2001                             g_hash_table_size(msgid_table));
2002                 debug_print("\tsubject hash table size = %d\n",
2003                             g_hash_table_size(subject_table));
2004         }
2005 }
2006
2007 struct wcachefp
2008 {
2009         FILE *cache_fp;
2010         FILE *mark_fp;
2011 };
2012
2013 gint summary_write_cache(SummaryView *summaryview)
2014 {
2015         struct wcachefp fps;
2016         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2017         gint ver = CACHE_VERSION;
2018         gchar *buf;
2019         gchar *cachefile, *markfile;
2020         GSList * cur;
2021         gint filemode = 0;
2022         PrefsFolderItem *prefs;
2023
2024         if (!summaryview->folder_item || !summaryview->folder_item->path)
2025                 return -1;
2026
2027         if (summaryview->folder_item->folder->update_mark != NULL)
2028                 summaryview->folder_item->folder->update_mark(summaryview->folder_item->folder, summaryview->folder_item);
2029
2030         cachefile = folder_item_get_cache_file(summaryview->folder_item);
2031         g_return_val_if_fail(cachefile != NULL, -1);
2032         if ((fps.cache_fp = fopen(cachefile, "w")) == NULL) {
2033                 FILE_OP_ERROR(cachefile, "fopen");
2034                 g_free(cachefile);
2035                 return -1;
2036         }
2037         if (change_file_mode_rw(fps.cache_fp, cachefile) < 0)
2038                 FILE_OP_ERROR(cachefile, "chmod");
2039
2040         prefs = summaryview->folder_item->prefs;
2041         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2042                 /* for cache file */
2043                 filemode = prefs->folder_chmod;
2044                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
2045                 if (filemode & S_IROTH) filemode |= S_IWOTH;
2046 #if HAVE_FCHMOD
2047                 fchmod(fileno(fps.cache_fp), filemode);
2048 #else
2049                 chmod(cachefile, filemode);
2050 #endif
2051         }
2052
2053         g_free(cachefile);
2054
2055         markfile = folder_item_get_mark_file(summaryview->folder_item);
2056         if ((fps.mark_fp = fopen(markfile, "w")) == NULL) {
2057                 FILE_OP_ERROR(markfile, "fopen");
2058                 fclose(fps.cache_fp);
2059                 g_free(markfile);
2060                 return -1;
2061         }
2062         if (change_file_mode_rw(fps.mark_fp, markfile) < 0)
2063                 FILE_OP_ERROR(markfile, "chmod");
2064         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2065 #if HAVE_FCHMOD
2066                 fchmod(fileno(fps.mark_fp), filemode);
2067 #else
2068                 chmod(markfile, filemode);
2069 #endif
2070         }
2071
2072         g_free(markfile);
2073
2074         buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2075                               summaryview->folder_item->path);
2076         debug_print(buf);
2077         STATUSBAR_PUSH(summaryview->mainwin, buf);
2078         g_free(buf);
2079
2080         WRITE_CACHE_DATA_INT(ver, fps.cache_fp);
2081         ver = MARK_VERSION;
2082         WRITE_CACHE_DATA_INT(ver, fps.mark_fp);
2083
2084         gtk_ctree_pre_recursive(ctree, NULL, summary_write_cache_func, &fps);
2085
2086         for(cur = summaryview->killed_messages ; cur != NULL ;
2087             cur = g_slist_next(cur)) {
2088                 MsgInfo *msginfo = (MsgInfo *) cur->data;
2089
2090                 procmsg_write_cache(msginfo, fps.cache_fp);
2091                 procmsg_write_flags(msginfo, fps.mark_fp);
2092         }
2093
2094         fclose(fps.cache_fp);
2095         fclose(fps.mark_fp);
2096
2097         debug_print(_("done.\n"));
2098         STATUSBAR_POP(summaryview->mainwin);
2099
2100         return 0;
2101 }
2102
2103 static void summary_write_cache_func(GtkCTree *ctree, GtkCTreeNode *node,
2104                                      gpointer data)
2105 {
2106         struct wcachefp *fps = data;
2107         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
2108
2109         if (msginfo == NULL) return;
2110
2111         procmsg_write_cache(msginfo, fps->cache_fp);
2112         procmsg_write_flags(msginfo, fps->mark_fp);
2113 }
2114
2115 static void summary_set_header(SummaryView *summaryview, gchar *text[],
2116                                MsgInfo *msginfo)
2117 {
2118         static gchar date_modified[80];
2119         static gchar *to = NULL;
2120         static gchar *from_name = NULL;
2121         static gchar col_number[11];
2122         static gchar col_score[11];
2123         gint *col_pos = summaryview->col_pos;
2124
2125         text[col_pos[S_COL_MARK]]   = NULL;
2126         text[col_pos[S_COL_UNREAD]] = NULL;
2127         text[col_pos[S_COL_MIME]]   = NULL;
2128         text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
2129         text[col_pos[S_COL_SIZE]]   = to_human_readable(msginfo->size);
2130
2131 #if 0
2132         text[col_pos[S_COL_SCORE]]  = itos_buf(col_score, msginfo->threadscore);
2133 #else
2134         text[col_pos[S_COL_SCORE]]  = itos_buf(col_score, msginfo->score);
2135 #endif
2136
2137         if (msginfo->date_t) {
2138                 procheader_date_get_localtime(date_modified,
2139                                               sizeof(date_modified),
2140                                               msginfo->date_t);
2141                 text[col_pos[S_COL_DATE]] = date_modified;
2142         } else if (msginfo->date)
2143                 text[col_pos[S_COL_DATE]] = msginfo->date;
2144         else
2145                 text[col_pos[S_COL_DATE]] = _("(No Date)");
2146
2147         text[col_pos[S_COL_FROM]] = msginfo->fromname ? msginfo->fromname :
2148                 _("(No From)");
2149         if (prefs_common.swap_from && msginfo->from && msginfo->to &&
2150             !MSG_IS_NEWS(msginfo->flags)) {
2151                 gchar *from;
2152
2153                 Xstrdup_a(from, msginfo->from, return);
2154                 extract_address(from);
2155                 if (account_find_from_address(from)) {
2156                         g_free(to);
2157                         to = g_strconcat("-->", msginfo->to, NULL);
2158                         text[col_pos[S_COL_FROM]] = to;
2159                 }
2160         }
2161
2162         if ((text[col_pos[S_COL_FROM]] != to) && prefs_common.use_addr_book &&
2163             msginfo->from) {
2164                 gint count;
2165                 gchar *from;
2166   
2167                 Xstrdup_a(from, msginfo->from, return);
2168                 extract_address(from);
2169                 if (*from) {
2170                         count = complete_address(from);
2171                         if (count > 1) {
2172                                 g_free(from_name);
2173                                 from = get_complete_address(1);
2174                                 from_name = procheader_get_fromname(from);
2175                                 g_free(from);
2176                                 text[col_pos[S_COL_FROM]] = from_name;
2177                         }
2178                 }
2179         }
2180
2181         text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
2182                 _("(No Subject)");
2183 }
2184
2185 #define CHANGE_FLAGS(msginfo) \
2186 { \
2187 if (msginfo->folder->folder->change_flags != NULL) \
2188 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
2189                                       msginfo->folder, \
2190                                       msginfo); \
2191 }
2192
2193 static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row,
2194                                 gboolean new_window)
2195 {
2196         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2197         MsgInfo *msginfo;
2198         gchar *filename;
2199
2200         if (!new_window && summaryview->displayed == row) return;
2201         g_return_if_fail(row != NULL);
2202
2203         if (summary_is_locked(summaryview)) return;
2204         summary_lock(summaryview);
2205
2206         STATUSBAR_POP(summaryview->mainwin);
2207         GTK_EVENTS_FLUSH();
2208
2209         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2210
2211         filename = procmsg_get_message_file(msginfo);
2212         if (!filename) {
2213                 summary_unlock(summaryview);
2214                 return;
2215         }
2216         g_free(filename);
2217
2218         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2219                 summaryview->newmsgs--;
2220         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2221                 summaryview->unread--;
2222
2223         if (new_window) {
2224                 MessageView *msgview;
2225
2226                 msgview = messageview_create_with_new_window();
2227                 messageview_show(msgview, msginfo);
2228         } else {
2229                 MessageView *msgview;
2230
2231                 msgview = summaryview->messageview;
2232
2233                 summaryview->displayed = row;
2234                 if (!summaryview->msg_is_toggled_on)
2235                         summary_toggle_view(summaryview);
2236                 messageview_show(msgview, msginfo);
2237                 if (msgview->type == MVIEW_TEXT ||
2238                     (msgview->type == MVIEW_MIME &&
2239                      GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL))
2240                         gtk_widget_grab_focus(summaryview->ctree);
2241                 GTK_EVENTS_FLUSH();
2242                 gtkut_ctree_node_move_if_on_the_edge(ctree, row);
2243         }
2244
2245         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2246                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2247                 CHANGE_FLAGS(msginfo);
2248                 summary_set_row_marks(summaryview, row);
2249                 gtk_clist_thaw(GTK_CLIST(ctree));
2250                 summary_status_show(summaryview);
2251         }
2252
2253         if (GTK_WIDGET_VISIBLE(summaryview->headerwin->window))
2254                 header_window_show(summaryview->headerwin, msginfo);
2255
2256         summary_unlock(summaryview);
2257 }
2258
2259 void summary_redisplay_msg(SummaryView *summaryview)
2260 {
2261         GtkCTreeNode *node;
2262
2263         if (summaryview->displayed) {
2264                 node = summaryview->displayed;
2265                 summaryview->displayed = NULL;
2266                 summary_display_msg(summaryview, node, FALSE);
2267         }
2268 }
2269
2270 void summary_open_msg(SummaryView *summaryview)
2271 {
2272         if (!summaryview->selected) return;
2273
2274         summary_display_msg(summaryview, summaryview->selected, TRUE);
2275 }
2276
2277 void summary_view_source(SummaryView * summaryview)
2278 {
2279         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2280         MsgInfo *msginfo;
2281         SourceWindow *srcwin;
2282
2283         if (!summaryview->selected) return;
2284
2285         srcwin = source_window_create();
2286         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2287         source_window_show_msg(srcwin, msginfo);
2288         source_window_show(srcwin);
2289 }
2290
2291 void summary_reedit(SummaryView *summaryview)
2292 {
2293         MsgInfo *msginfo;
2294
2295         if (!summaryview->selected) return;
2296         if (!summaryview->folder_item) return;
2297         if (summaryview->folder_item->stype != F_OUTBOX &&
2298             summaryview->folder_item->stype != F_DRAFT  &&
2299             summaryview->folder_item->stype != F_QUEUE) return;
2300
2301         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2302                                               summaryview->selected);
2303         if (!msginfo) return;
2304
2305         compose_reedit(msginfo);
2306 }
2307
2308 void summary_step(SummaryView *summaryview, GtkScrollType type)
2309 {
2310         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2311
2312         if (type == GTK_SCROLL_STEP_FORWARD) {
2313                 GtkCTreeNode *node;
2314                 node = gtkut_ctree_node_next(ctree, summaryview->selected);
2315                 if (node)
2316                         gtkut_ctree_expand_parent_all(ctree, node);
2317         }
2318
2319         gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
2320                                 type, 0.0);
2321         
2322         if (summaryview->msg_is_toggled_on)
2323                 summary_display_msg(summaryview, summaryview->selected, FALSE);
2324 }
2325
2326 static void summary_toggle_view(SummaryView *summaryview)
2327 {
2328         MainWindow *mainwin = summaryview->mainwin;
2329         union CompositeWin *cwin = &mainwin->win;
2330         GtkWidget *vpaned = NULL;
2331         GtkWidget *container = NULL;
2332
2333         switch (mainwin->type) {
2334         case SEPARATE_NONE:
2335                 vpaned = cwin->sep_none.vpaned;
2336                 container = cwin->sep_none.hpaned;
2337                 break;
2338         case SEPARATE_FOLDER:
2339                 vpaned = cwin->sep_folder.vpaned;
2340                 container = mainwin->vbox_body;
2341                 break;
2342         case SEPARATE_MESSAGE:
2343         case SEPARATE_BOTH:
2344                 return;
2345         }
2346
2347         if (vpaned->parent != NULL) {
2348                 summaryview->msg_is_toggled_on = FALSE;
2349                 summaryview->displayed = NULL;
2350                 gtk_widget_ref(vpaned);
2351                 gtk_container_remove(GTK_CONTAINER(container), vpaned);
2352                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), container);
2353                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2354                               GTK_ARROW_UP, GTK_SHADOW_OUT);
2355         } else {
2356                 summaryview->msg_is_toggled_on = TRUE;
2357                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), vpaned);
2358                 gtk_container_add(GTK_CONTAINER(container), vpaned);
2359                 gtk_widget_unref(vpaned);
2360                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2361                               GTK_ARROW_DOWN, GTK_SHADOW_OUT);
2362         }
2363
2364         gtk_widget_grab_focus(summaryview->ctree);
2365 }
2366
2367 static gboolean summary_search_unread_recursive(GtkCTree *ctree,
2368                                                 GtkCTreeNode *node)
2369 {
2370         MsgInfo *msginfo;
2371
2372         if (node) {
2373                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
2374                 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2375                         return TRUE;
2376                 node = GTK_CTREE_ROW(node)->children;
2377         } else
2378                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2379
2380         while (node) {
2381                 if (summary_search_unread_recursive(ctree, node) == TRUE)
2382                         return TRUE;
2383                 node = GTK_CTREE_ROW(node)->sibling;
2384         }
2385
2386         return FALSE;
2387 }
2388
2389 static gboolean summary_have_unread_children(SummaryView *summaryview,
2390                                              GtkCTreeNode *node)
2391 {
2392         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2393
2394         if (!node) return FALSE;
2395
2396         node = GTK_CTREE_ROW(node)->children;
2397
2398         while (node) {
2399                 if (summary_search_unread_recursive(ctree, node) == TRUE)
2400                         return TRUE;
2401                 node = GTK_CTREE_ROW(node)->sibling;
2402         }
2403
2404         return FALSE;
2405 }
2406
2407 static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2408 {
2409         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2410         GtkStyle *style = NULL;
2411         MsgInfo *msginfo;
2412         MsgFlags flags;
2413         gint *col_pos = summaryview->col_pos;
2414
2415         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2416         if (!msginfo) return;
2417
2418         flags = msginfo->flags;
2419
2420         gtk_ctree_node_set_foreground(ctree, row, NULL);
2421
2422         /* set new/unread column */
2423         if (MSG_IS_IGNORE_THREAD(flags)) {
2424                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2425                                           ignorethreadxpm, ignorethreadxpmmask);
2426         } else if (MSG_IS_NEW(flags)) {
2427                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2428                                           newxpm, newxpmmask);
2429         } else if (MSG_IS_UNREAD(flags)) {
2430                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2431                                           unreadxpm, unreadxpmmask);
2432         } else if (MSG_IS_REPLIED(flags)) {
2433                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2434                                           repliedxpm, repliedxpmmask);
2435         } else if (MSG_IS_FORWARDED(flags)) {
2436                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2437                                           forwardedxpm, forwardedxpmmask);
2438         } else {
2439                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_UNREAD],
2440                                         NULL);
2441         }
2442
2443         if (prefs_common.bold_unread &&
2444             ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
2445              (!GTK_CTREE_ROW(row)->expanded &&
2446               GTK_CTREE_ROW(row)->children &&
2447               summary_have_unread_children(summaryview, row))))
2448                 style = bold_style;
2449
2450         /* set mark column */
2451         if (MSG_IS_DELETED(flags)) {
2452                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2453                                           deletedxpm, deletedxpmmask);
2454                 if (style)
2455                         style = bold_deleted_style;
2456                 else {
2457                         style = small_deleted_style;
2458                 }
2459                         gtk_ctree_node_set_foreground
2460                                 (ctree, row, &summaryview->color_dim);
2461         } else if (MSG_IS_MARKED(flags)) {
2462                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2463                                           markxpm, markxpmmask);
2464         } else if (MSG_IS_MOVE(flags)) {
2465                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "o");
2466                 if (style)
2467                         style = bold_marked_style;
2468                 else {
2469                         style = small_marked_style;
2470                 }
2471                         gtk_ctree_node_set_foreground
2472                                 (ctree, row, &summaryview->color_marked);
2473         } else if (MSG_IS_COPY(flags)) {
2474                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "O");
2475                 if (style)
2476                         style = bold_marked_style;
2477                 else {
2478                         style = small_marked_style;
2479                 }
2480                         gtk_ctree_node_set_foreground
2481                                 (ctree, row, &summaryview->color_marked);
2482         }
2483         else if ((global_scoring ||
2484                   summaryview->folder_item->prefs->scoring) &&
2485                  (msginfo->score >= summaryview->important_score) &&
2486                  (MSG_IS_MARKED(msginfo->flags) || MSG_IS_MOVE(msginfo->flags) || MSG_IS_COPY(msginfo->flags))) {
2487                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "!");
2488                 gtk_ctree_node_set_foreground(ctree, row,
2489                                               &summaryview->color_important);
2490         } else {
2491                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], NULL);
2492         }
2493
2494         if (MSG_IS_MIME(flags)) {
2495                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
2496                                           clipxpm, clipxpmmask);
2497         } else {
2498                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MIME], NULL);
2499         }
2500         if (!style)
2501                 style = small_style;
2502
2503         gtk_ctree_node_set_row_style(ctree, row, style);
2504
2505         if (MSG_GET_COLORLABEL(flags))
2506                 summary_set_colorlabel_color(ctree, row,
2507                                              MSG_GET_COLORLABEL_VALUE(flags));
2508 }
2509
2510 void summary_set_marks_selected(SummaryView *summaryview)
2511 {
2512         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2513         GList *cur;
2514
2515         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2516                 summary_set_row_marks(summaryview, GTK_CTREE_NODE(cur->data));
2517 }
2518
2519 static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
2520 {
2521         gboolean changed = FALSE;
2522         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2523         MsgInfo *msginfo;
2524
2525         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2526         if (MSG_IS_DELETED(msginfo->flags))
2527                 summaryview->deleted--;
2528         if (MSG_IS_MOVE(msginfo->flags)) {
2529                 summaryview->moved--;
2530                 changed = TRUE;
2531         }
2532         if (MSG_IS_COPY(msginfo->flags)) {
2533                 summaryview->copied--;
2534                 changed = TRUE;
2535         }
2536         if (changed && !prefs_common.immediate_exec) {
2537                 msginfo->to_folder->op_count--;
2538                 if (msginfo->to_folder->op_count == 0)
2539                         folderview_update_item(msginfo->to_folder, 0);
2540         }
2541         msginfo->to_folder = NULL;
2542         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2543         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2544         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2545         CHANGE_FLAGS(msginfo);
2546         summary_set_row_marks(summaryview, row);
2547         debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2548 }
2549
2550 void summary_mark(SummaryView *summaryview)
2551 {
2552         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2553         GList *cur;
2554
2555         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2556                 summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
2557
2558         /* summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); */
2559         summary_status_show(summaryview);
2560 }
2561
2562 static void summary_mark_row_as_read(SummaryView *summaryview,
2563                                      GtkCTreeNode *row)
2564 {
2565         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2566         MsgInfo *msginfo;
2567
2568         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2569         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2570                 summaryview->newmsgs--;
2571         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2572                 summaryview->unread--;
2573         if (MSG_IS_NEW(msginfo->flags) ||
2574             MSG_IS_UNREAD(msginfo->flags)) {
2575                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2576                 CHANGE_FLAGS(msginfo);
2577                 summary_set_row_marks(summaryview, row);
2578                 debug_print(_("Message %d is marked as read\n"),
2579                             msginfo->msgnum);
2580         }
2581 }
2582
2583 void summary_mark_as_read(SummaryView *summaryview)
2584 {
2585         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2586         GList *cur;
2587
2588         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2589                 summary_mark_row_as_read(summaryview,
2590                                          GTK_CTREE_NODE(cur->data));
2591
2592         summary_status_show(summaryview);
2593 }
2594
2595 void summary_mark_all_read(SummaryView *summaryview)
2596 {
2597         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2598         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2599         GtkCTreeNode *node;
2600
2601         gtk_clist_freeze(clist);
2602         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2603              node = gtkut_ctree_node_next(ctree, node))
2604                 summary_mark_row_as_read(summaryview, node);
2605         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2606              node = gtkut_ctree_node_next(ctree, node)) {
2607                 if (!GTK_CTREE_ROW(node)->expanded)
2608                         summary_set_row_marks(summaryview, node);
2609         }
2610         gtk_clist_thaw(clist);
2611
2612         summary_status_show(summaryview);
2613 }
2614
2615 static void summary_mark_row_as_unread(SummaryView *summaryview,
2616                                        GtkCTreeNode *row)
2617 {
2618         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2619         MsgInfo *msginfo;
2620
2621         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2622         if (MSG_IS_DELETED(msginfo->flags)) {
2623                 msginfo->to_folder = NULL;
2624                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2625                 summaryview->deleted--;
2626         }
2627         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
2628         if (!MSG_IS_UNREAD(msginfo->flags)) {
2629                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
2630                 summaryview->unread++;
2631                 debug_print(_("Message %d is marked as unread\n"),
2632                             msginfo->msgnum);
2633         }
2634
2635         CHANGE_FLAGS(msginfo);
2636
2637         summary_set_row_marks(summaryview, row);
2638 }
2639
2640 void summary_mark_as_unread(SummaryView *summaryview)
2641 {
2642         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2643         GList *cur;
2644
2645         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2646                 summary_mark_row_as_unread(summaryview,
2647                                            GTK_CTREE_NODE(cur->data));
2648
2649         summary_status_show(summaryview);
2650 }
2651
2652 static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
2653 {
2654         gboolean changed = FALSE;
2655         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2656         MsgInfo *msginfo;
2657
2658         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2659
2660         if (MSG_IS_DELETED(msginfo->flags)) return;
2661
2662         if (MSG_IS_MOVE(msginfo->flags)) {
2663                 summaryview->moved--;
2664                 changed = TRUE;
2665         }
2666         if (MSG_IS_COPY(msginfo->flags)) {
2667                 summaryview->copied--;
2668                 changed = TRUE;
2669         }
2670         if (changed && !prefs_common.immediate_exec) {
2671                 msginfo->to_folder->op_count--;
2672                 if (msginfo->to_folder->op_count == 0)
2673                         folderview_update_item(msginfo->to_folder, 0);
2674         }
2675         msginfo->to_folder = NULL;
2676         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2677         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2678         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2679         CHANGE_FLAGS(msginfo);
2680         summaryview->deleted++;
2681
2682         if (!prefs_common.immediate_exec && 
2683             summaryview->folder_item->stype != F_TRASH)
2684                 summary_set_row_marks(summaryview, row);
2685
2686         debug_print(_("Message %s/%d is set to delete\n"),
2687                     msginfo->folder->path, msginfo->msgnum);
2688 }
2689
2690 void summary_delete(SummaryView *summaryview)
2691 {
2692         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2693         FolderItem *item = summaryview->folder_item;
2694         GList *cur;
2695         GtkCTreeNode *sel_last = NULL;
2696         GtkCTreeNode *node;
2697
2698         if (!item || item->folder->type == F_NEWS) return;
2699
2700         if (summary_is_locked(summaryview)) return;
2701
2702         /* if current folder is trash, ask for confirmation */
2703         if (item->stype == F_TRASH) {
2704                 AlertValue aval;
2705
2706                 aval = alertpanel(_("Delete message(s)"),
2707                                   _("Do you really want to delete message(s) from the trash?"),
2708                                   _("Yes"), _("No"), NULL);
2709                 if (aval != G_ALERTDEFAULT) return;
2710         }
2711
2712         /* next code sets current row focus right. We need to find a row
2713          * that is not deleted. */
2714         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
2715                 sel_last = GTK_CTREE_NODE(cur->data);
2716                 summary_delete_row(summaryview, sel_last);
2717         }
2718
2719         node = summary_find_next_msg(summaryview, sel_last);
2720         if (!node)
2721                 node = summary_find_prev_msg(summaryview, sel_last);
2722
2723         if (node) {
2724                 if (sel_last && node == gtkut_ctree_node_next(ctree, sel_last))
2725                         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2726                 else if (sel_last && node == GTK_CTREE_NODE_PREV(sel_last))
2727                         summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
2728                 else
2729                         summary_select_node(summaryview, node,
2730                                             summaryview->msg_is_toggled_on);
2731         }
2732
2733         if (prefs_common.immediate_exec || item->stype == F_TRASH)
2734                 summary_execute(summaryview);
2735         else
2736                 summary_status_show(summaryview);
2737 }
2738
2739 void summary_delete_duplicated(SummaryView *summaryview)
2740 {
2741         if (!summaryview->folder_item ||
2742             summaryview->folder_item->folder->type == F_NEWS) return;
2743         if (summaryview->folder_item->stype == F_TRASH) return;
2744
2745         main_window_cursor_wait(summaryview->mainwin);
2746         debug_print(_("Deleting duplicated messages..."));
2747         STATUSBAR_PUSH(summaryview->mainwin,
2748                        _("Deleting duplicated messages..."));
2749
2750         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2751                                 GTK_CTREE_FUNC(summary_delete_duplicated_func),
2752                                 summaryview);
2753
2754         if (prefs_common.immediate_exec)
2755                 summary_execute(summaryview);
2756         else
2757                 summary_status_show(summaryview);
2758
2759         debug_print(_("done.\n"));
2760         STATUSBAR_POP(summaryview->mainwin);
2761         main_window_cursor_normal(summaryview->mainwin);
2762 }
2763
2764 static void summary_delete_duplicated_func(GtkCTree *ctree, GtkCTreeNode *node,
2765                                            SummaryView *summaryview)
2766 {
2767         GtkCTreeNode *found;
2768         MsgInfo *msginfo = GTK_CTREE_ROW(node)->row.data;
2769
2770         if (!msginfo->msgid || !*msginfo->msgid) return;
2771
2772         found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
2773
2774         if (found && found != node)
2775                 summary_delete_row(summaryview, node);
2776 }
2777
2778 static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
2779 {
2780         gboolean changed = FALSE;
2781         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2782         MsgInfo *msginfo;
2783
2784         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2785         if (MSG_IS_DELETED(msginfo->flags))
2786                 summaryview->deleted--;
2787         if (MSG_IS_MOVE(msginfo->flags)) {
2788                 summaryview->moved--;
2789                 changed = TRUE;
2790         }
2791         if (MSG_IS_COPY(msginfo->flags)) {
2792                 summaryview->copied--;
2793                 changed = TRUE;
2794         }
2795         if (changed && !prefs_common.immediate_exec) {
2796                 msginfo->to_folder->op_count--;
2797                 if (msginfo->to_folder->op_count == 0)
2798                         folderview_update_item(msginfo->to_folder, 0);
2799         }
2800         msginfo->to_folder = NULL;
2801         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2802         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2803         CHANGE_FLAGS(msginfo);
2804         summary_set_row_marks(summaryview, row);
2805
2806         debug_print(_("Message %s/%d is unmarked\n"),
2807                     msginfo->folder->path, msginfo->msgnum);
2808 }
2809
2810 void summary_unmark(SummaryView *summaryview)
2811 {
2812         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2813         GList *cur;
2814
2815         for (cur = GTK_CLIST(ctree)->selection; cur != NULL;
2816              cur = cur->next)
2817                 summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
2818
2819         summary_status_show(summaryview);
2820 }
2821
2822 static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2823                                 FolderItem *to_folder)
2824 {
2825         gboolean changed = FALSE;
2826         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2827         MsgInfo *msginfo;
2828
2829         g_return_if_fail(to_folder != NULL);
2830
2831         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2832         if (MSG_IS_MOVE(msginfo->flags)) {
2833                 if (!prefs_common.immediate_exec) {
2834                         msginfo->to_folder->op_count--;
2835                         if (msginfo->to_folder->op_count == 0) {
2836                                 folderview_update_item(msginfo->to_folder, 0);
2837                                 changed = TRUE;
2838                         }
2839                 }
2840         }
2841         msginfo->to_folder = to_folder;
2842         if (MSG_IS_DELETED(msginfo->flags))
2843                 summaryview->deleted--;
2844         if (MSG_IS_COPY(msginfo->flags)) {
2845                 summaryview->copied--;
2846                 if (!prefs_common.immediate_exec)
2847                         msginfo->to_folder->op_count--;
2848         }
2849         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2850         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2851         if (!MSG_IS_MOVE(msginfo->flags)) {
2852                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2853                 summaryview->moved++;
2854                 changed = TRUE;
2855         }
2856         if (!prefs_common.immediate_exec) {
2857                 summary_set_row_marks(summaryview, row);
2858                 if (changed) {
2859                         msginfo->to_folder->op_count++;
2860                         if (msginfo->to_folder->op_count == 1)
2861                                 folderview_update_item(msginfo->to_folder, 0);
2862                 }
2863         }
2864
2865         debug_print(_("Message %d is set to move to %s\n"),
2866                     msginfo->msgnum, to_folder->path);
2867 }
2868
2869 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2870 {
2871         GList *cur;
2872
2873         if (!to_folder) return;
2874         if (!summaryview->folder_item ||
2875             summaryview->folder_item->folder->type == F_NEWS) return;
2876
2877         if (summary_is_locked(summaryview)) return;
2878
2879         if (summaryview->folder_item == to_folder) {
2880                 alertpanel_notice(_("Destination is same as current folder."));
2881                 return;
2882         }
2883
2884         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2885              cur != NULL; cur = cur->next)
2886                 summary_move_row_to
2887                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2888
2889         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2890
2891         if (prefs_common.immediate_exec)
2892                 summary_execute(summaryview);
2893         else {
2894                 summary_status_show(summaryview);
2895
2896                 folderview_update_item(to_folder, 0);
2897         }
2898 }
2899
2900 void summary_move_to(SummaryView *summaryview)
2901 {
2902         FolderItem *to_folder;
2903
2904         if (!summaryview->folder_item ||
2905             summaryview->folder_item->folder->type == F_NEWS) return;
2906
2907         to_folder = foldersel_folder_sel(NULL, NULL);
2908         summary_move_selected_to(summaryview, to_folder);
2909 }
2910
2911 static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2912                                 FolderItem *to_folder)
2913 {
2914         gboolean changed = FALSE;
2915         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2916         MsgInfo *msginfo;
2917
2918         g_return_if_fail(to_folder != NULL);
2919
2920         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2921         if (MSG_IS_COPY(msginfo->flags)) {
2922                 if (!prefs_common.immediate_exec) {
2923                         msginfo->to_folder->op_count--;
2924                         if (msginfo->to_folder->op_count == 0) {
2925                                 folderview_update_item(msginfo->to_folder, 0);
2926                                 changed = TRUE;
2927                         }
2928                 }
2929         }
2930         msginfo->to_folder = to_folder;
2931         if (MSG_IS_DELETED(msginfo->flags))
2932                 summaryview->deleted--;
2933         if (MSG_IS_MOVE(msginfo->flags)) {
2934                 summaryview->moved--;
2935                 if (!prefs_common.immediate_exec)
2936                         msginfo->to_folder->op_count--;
2937         }
2938         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2939         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2940         if (!MSG_IS_COPY(msginfo->flags)) {
2941                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2942                 summaryview->copied++;
2943                 changed = TRUE;
2944         }
2945         if (!prefs_common.immediate_exec) {
2946                 summary_set_row_marks(summaryview, row);
2947                 if (changed) {
2948                         msginfo->to_folder->op_count++;
2949                         if (msginfo->to_folder->op_count == 1)
2950                                 folderview_update_item(msginfo->to_folder, 0);
2951                 }
2952         }
2953
2954         debug_print(_("Message %d is set to copy to %s\n"),
2955                     msginfo->msgnum, to_folder->path);
2956 }
2957
2958 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2959 {
2960         GList *cur;
2961
2962         if (!to_folder) return;
2963         if (!summaryview->folder_item ||
2964             summaryview->folder_item->folder->type == F_NEWS) return;
2965
2966         if (summary_is_locked(summaryview)) return;
2967
2968         if (summaryview->folder_item == to_folder) {
2969                 alertpanel_notice
2970                         (_("Destination to copy is same as current folder."));
2971                 return;
2972         }
2973
2974         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2975              cur != NULL; cur = cur->next)
2976                 summary_copy_row_to
2977                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2978
2979         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2980
2981         if (prefs_common.immediate_exec)
2982                 summary_execute(summaryview);
2983         else {
2984                 summary_status_show(summaryview);
2985
2986                 folderview_update_item(to_folder, 0);
2987         }
2988 }
2989
2990 void summary_copy_to(SummaryView *summaryview)
2991 {
2992         FolderItem *to_folder;
2993
2994         if (!summaryview->folder_item ||
2995             summaryview->folder_item->folder->type == F_NEWS) return;
2996
2997         to_folder = foldersel_folder_sel(NULL, NULL);
2998         summary_copy_selected_to(summaryview, to_folder);
2999 }
3000
3001 void summary_add_address(SummaryView *summaryview)
3002 {
3003         MsgInfo *msginfo;
3004         gchar *from;
3005
3006         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
3007                                               summaryview->selected);
3008         if (!msginfo) return;
3009
3010         Xstrdup_a(from, msginfo->from, return);
3011         eliminate_address_comment(from);
3012         extract_address(from);
3013         addressbook_add_contact(msginfo->fromname, from, NULL);
3014 }
3015
3016 void summary_select_all(SummaryView *summaryview)
3017 {
3018         if (summaryview->messages >= 500) {
3019                 STATUSBAR_PUSH(summaryview->mainwin,
3020                                _("Selecting all messages..."));
3021                 main_window_cursor_wait(summaryview->mainwin);
3022         }
3023
3024         gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
3025
3026         if (summaryview->messages >= 500) {
3027                 STATUSBAR_POP(summaryview->mainwin);
3028                 main_window_cursor_normal(summaryview->mainwin);
3029         }
3030 }
3031
3032 void summary_unselect_all(SummaryView *summaryview)
3033 {
3034         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
3035 }
3036
3037 void summary_save_as(SummaryView *summaryview)
3038 {
3039         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3040         MsgInfo *msginfo;
3041         gchar *src, *dest;
3042
3043         if (!summaryview->selected) return;
3044         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
3045         if (!msginfo) return;
3046
3047         dest = filesel_select_file(_("Save as"), NULL);
3048         if (!dest) return;
3049         if (is_file_exist(dest)) {
3050                 AlertValue aval;
3051
3052                 aval = alertpanel(_("Overwrite"),
3053                                   _("Overwrite existing file?"),
3054                                   _("OK"), _("Cancel"), NULL);
3055                 if (G_ALERTDEFAULT != aval) return;
3056         }
3057
3058         src = procmsg_get_message_file(msginfo);
3059         copy_file(src, dest);
3060         g_free(src);
3061 }
3062
3063 void summary_print(SummaryView *summaryview)
3064 {
3065         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3066         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3067         MsgInfo *msginfo;
3068         GList *cur;
3069         gchar *cmdline;
3070         gchar *p;
3071
3072         if (clist->selection == NULL) return;
3073
3074         cmdline = input_dialog(_("Print"),
3075                                _("Enter the print command line:\n"
3076                                  "(`%s' will be replaced with file name)"),
3077                                prefs_common.print_cmd);
3078         if (!cmdline) return;
3079         if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
3080             strchr(p + 2, '%')) {
3081                 alertpanel_error(_("Print command line is invalid:\n`%s'"),
3082                                  cmdline);
3083                 g_free(cmdline);
3084                 return;
3085         }
3086
3087         for (cur = clist->selection; cur != NULL; cur = cur->next) {
3088                 msginfo = gtk_ctree_node_get_row_data
3089                         (ctree, GTK_CTREE_NODE(cur->data));
3090                 if (msginfo) procmsg_print_message(msginfo, cmdline);
3091         }
3092
3093         g_free(cmdline);
3094 }
3095
3096 gboolean summary_execute(SummaryView *summaryview)
3097 {
3098         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3099         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3100         GtkCTreeNode *node, *next;
3101
3102         if (!summaryview->folder_item ||
3103             summaryview->folder_item->folder->type == F_NEWS) return FALSE;
3104
3105         if (summary_is_locked(summaryview)) return FALSE;
3106         summary_lock(summaryview);
3107
3108         gtk_clist_freeze(clist);
3109
3110         if (summaryview->folder_item->threaded)
3111                 summary_unthread_for_exec(summaryview);
3112
3113         summary_execute_move(summaryview);
3114         summary_execute_copy(summaryview);
3115         summary_execute_delete(summaryview);
3116
3117         node = GTK_CTREE_NODE(clist->row_list);
3118         while (node != NULL) {
3119                 next = gtkut_ctree_node_next(ctree, node);
3120                 if (gtk_ctree_node_get_row_data(ctree, node) == NULL) {
3121                         if (node == summaryview->displayed) {
3122                                 messageview_clear(summaryview->messageview);
3123                                 summaryview->displayed = NULL;
3124                         }
3125                         if (GTK_CTREE_ROW(node)->children != NULL)
3126                                 g_warning("summary_execute(): children != NULL\n");
3127                         else
3128                                 gtk_ctree_remove_node(ctree, node);
3129                 }
3130                 node = next;
3131         }
3132
3133         if (summaryview->folder_item->threaded)
3134                 summary_thread_build(summaryview);
3135
3136         summaryview->selected = clist->selection ?
3137                 GTK_CTREE_NODE(clist->selection->data) : NULL;
3138
3139         if (!GTK_CLIST(summaryview->ctree)->row_list) {
3140                 menu_set_insensitive_all
3141                         (GTK_MENU_SHELL(summaryview->popupmenu));
3142                 gtk_widget_grab_focus(summaryview->folderview->ctree);
3143         } else
3144                 gtk_widget_grab_focus(summaryview->ctree);
3145
3146         summary_update_status(summaryview);
3147         summary_status_show(summaryview);
3148
3149         summary_write_cache(summaryview);
3150
3151         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
3152
3153         gtk_clist_thaw(clist);
3154
3155         summary_unlock(summaryview);
3156         return TRUE;
3157 }
3158
3159 static void summary_execute_move(SummaryView *summaryview)
3160 {
3161         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3162         GSList *cur;
3163
3164         summaryview->folder_table = g_hash_table_new(NULL, NULL);
3165
3166         /* search moving messages and execute */
3167         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
3168                                 summaryview);
3169
3170         if (summaryview->mlist) {
3171                 procmsg_move_messages(summaryview->mlist);
3172
3173                 folder_item_scan_foreach(summaryview->folder_table);
3174                 folderview_update_item_foreach(summaryview->folder_table);
3175
3176                 for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
3177                         procmsg_msginfo_free((MsgInfo *)cur->data);
3178                 g_slist_free(summaryview->mlist);
3179                 summaryview->mlist = NULL;
3180         }
3181
3182         g_hash_table_destroy(summaryview->folder_table);
3183         summaryview->folder_table = NULL;
3184 }
3185
3186 static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
3187                                       gpointer data)
3188 {
3189         SummaryView *summaryview = data;
3190         MsgInfo *msginfo;
3191
3192         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3193
3194         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
3195                 g_hash_table_insert(summaryview->folder_table,
3196                                     msginfo->to_folder, GINT_TO_POINTER(1));
3197
3198                 summaryview->mlist =
3199                         g_slist_append(summaryview->mlist, msginfo);
3200                 gtk_ctree_node_set_row_data(ctree, node, NULL);
3201
3202                 if (msginfo->msgid && *msginfo->msgid &&
3203                     node == g_hash_table_lookup(summaryview->msgid_table,
3204                                                 msginfo->msgid))
3205                         g_hash_table_remove(summaryview->msgid_table,
3206                                             msginfo->msgid);
3207         }
3208 }
3209
3210 static void summary_execute_copy(SummaryView *summaryview)
3211 {
3212         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3213
3214         summaryview->folder_table = g_hash_table_new(NULL, NULL);
3215
3216         /* search copying messages and execute */
3217         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
3218                                 summaryview);
3219
3220         if (summaryview->mlist) {
3221                 procmsg_copy_messages(summaryview->mlist);
3222
3223                 folder_item_scan_foreach(summaryview->folder_table);
3224                 folderview_update_item_foreach(summaryview->folder_table);
3225
3226                 g_slist_free(summaryview->mlist);
3227                 summaryview->mlist = NULL;
3228         }
3229
3230         g_hash_table_destroy(summaryview->folder_table);
3231         summaryview->folder_table = NULL;
3232 }
3233
3234 static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
3235                                       gpointer data)
3236 {
3237         SummaryView *summaryview = data;
3238         MsgInfo *msginfo;
3239
3240         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3241
3242         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
3243                 g_hash_table_insert(summaryview->folder_table,
3244                                     msginfo->to_folder, GINT_TO_POINTER(1));
3245
3246                 summaryview->mlist =
3247                         g_slist_append(summaryview->mlist, msginfo);
3248
3249                 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3250                 summary_set_row_marks(summaryview, node);
3251         }
3252 }
3253
3254 static void summary_execute_delete(SummaryView *summaryview)
3255 {
3256         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3257         FolderItem *trash;
3258         GSList *cur;
3259
3260         trash = summaryview->folder_item->folder->trash;
3261         if (summaryview->folder_item->folder->type == F_MH) {
3262                 g_return_if_fail(trash != NULL);
3263         }
3264
3265         /* search deleting messages and execute */
3266         gtk_ctree_pre_recursive
3267                 (ctree, NULL, summary_execute_delete_func, summaryview);
3268
3269         if (!summaryview->mlist) return;
3270
3271         if (summaryview->folder_item != trash)
3272                 folder_item_move_msgs_with_dest(trash, summaryview->mlist);
3273         else
3274                 folder_item_remove_msgs(trash, summaryview->mlist);
3275
3276         for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
3277                 procmsg_msginfo_free((MsgInfo *)cur->data);
3278
3279         g_slist_free(summaryview->mlist);
3280         summaryview->mlist = NULL;
3281
3282         if (summaryview->folder_item != trash) {
3283                 folder_item_scan(trash);
3284                 folderview_update_item(trash, FALSE);
3285         }
3286 }
3287
3288 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
3289                                         gpointer data)
3290 {
3291         SummaryView *summaryview = data;
3292         MsgInfo *msginfo;
3293
3294         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3295
3296         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
3297                 summaryview->mlist =
3298                         g_slist_append(summaryview->mlist, msginfo);
3299                 gtk_ctree_node_set_row_data(ctree, node, NULL);
3300
3301                 if (msginfo->msgid && *msginfo->msgid &&
3302                     node == g_hash_table_lookup(summaryview->msgid_table,
3303                                                 msginfo->msgid))
3304                         g_hash_table_remove(summaryview->msgid_table,
3305                                             msginfo->msgid);
3306         }
3307 }
3308
3309 /* thread functions */
3310
3311 void summary_thread_build(SummaryView *summaryview)
3312 {
3313         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3314         GtkCTreeNode *node;
3315         GtkCTreeNode *next;
3316         GtkCTreeNode *parent;
3317         MsgInfo *msginfo;
3318
3319         summary_lock(summaryview);
3320
3321         debug_print(_("Building threads..."));
3322         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
3323         main_window_cursor_wait(summaryview->mainwin);
3324
3325         gtk_signal_handler_block_by_func(GTK_OBJECT(ctree),
3326                                          summary_tree_expanded, summaryview);
3327         gtk_clist_freeze(GTK_CLIST(ctree));
3328
3329         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3330         while (node) {
3331                 next = GTK_CTREE_ROW(node)->sibling;
3332
3333                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3334
3335                 parent = NULL;
3336
3337                 /* alfons - claws seems to prefer subject threading before
3338                  * inreplyto threading. we should look more deeply in this,
3339                  * because inreplyto should have precedence... */
3340                 if (msginfo && msginfo->inreplyto) {
3341                         parent = g_hash_table_lookup(summaryview->msgid_table,
3342                                                      msginfo->inreplyto);
3343                 }
3344
3345                 if (parent == NULL) {
3346                         parent = subject_table_lookup
3347                                 (summaryview->subject_table,
3348                                  msginfo->subject);
3349                 }
3350
3351                 if (parent && parent != node) {
3352                         gtk_ctree_move(ctree, node, parent, NULL);
3353                         gtk_ctree_expand(ctree, node);
3354                 }
3355
3356                 node = next;
3357         }
3358
3359         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3360
3361         while (node) {
3362                 next = GTK_CTREE_NODE_NEXT(node);
3363                 if (prefs_common.expand_thread)
3364                         gtk_ctree_expand(ctree, node);
3365                 if (prefs_common.bold_unread &&
3366                     GTK_CTREE_ROW(node)->children)
3367                         summary_set_row_marks(summaryview, node);
3368                 node = next;
3369         }
3370
3371         gtk_clist_thaw(GTK_CLIST(ctree));
3372         gtk_signal_handler_unblock_by_func(GTK_OBJECT(ctree),
3373                                            summary_tree_expanded, summaryview);
3374
3375         debug_print(_("done.\n"));
3376         STATUSBAR_POP(summaryview->mainwin);
3377         main_window_cursor_normal(summaryview->mainwin);
3378
3379         summary_unlock(summaryview);
3380 }
3381
3382 static void summary_thread_init(SummaryView *summaryview)
3383 {
3384         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3385         GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3386         GtkCTreeNode *next;
3387
3388         if (prefs_common.expand_thread) {
3389                 while (node) {
3390                         next = GTK_CTREE_ROW(node)->sibling;
3391                         if (GTK_CTREE_ROW(node)->children)
3392                                 gtk_ctree_expand(ctree, node);
3393                         node = next;
3394                 }
3395         } else if (prefs_common.bold_unread) {
3396                 while (node) {
3397                         next = GTK_CTREE_ROW(node)->sibling;
3398                         if (GTK_CTREE_ROW(node)->children)
3399                                 summary_set_row_marks(summaryview, node);
3400                         node = next;
3401                 }
3402         }
3403 }
3404
3405 void summary_unthread(SummaryView *summaryview)
3406 {
3407         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3408         GtkCTreeNode *node;
3409         GtkCTreeNode *child;
3410         GtkCTreeNode *sibling;
3411         GtkCTreeNode *next_child;
3412
3413         summary_lock(summaryview);
3414
3415         debug_print(_("Unthreading..."));
3416         STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
3417         main_window_cursor_wait(summaryview->mainwin);
3418
3419         gtk_signal_handler_block_by_func(GTK_OBJECT(ctree),
3420                                          summary_tree_collapsed, summaryview);
3421         gtk_clist_freeze(GTK_CLIST(ctree));
3422
3423         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3424              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
3425                 child = GTK_CTREE_ROW(node)->children;
3426                 sibling = GTK_CTREE_ROW(node)->sibling;
3427
3428                 while (child != NULL) {
3429                         next_child = GTK_CTREE_ROW(child)->sibling;
3430                         gtk_ctree_move(ctree, child, NULL, sibling);
3431                         child = next_child;
3432                 }
3433         }
3434
3435         gtk_clist_thaw(GTK_CLIST(ctree));
3436         gtk_signal_handler_unblock_by_func(GTK_OBJECT(ctree),
3437                                            summary_tree_collapsed, summaryview);
3438
3439         debug_print(_("done.\n"));
3440         STATUSBAR_POP(summaryview->mainwin);
3441         main_window_cursor_normal(summaryview->mainwin);
3442
3443         summary_unlock(summaryview);
3444 }
3445
3446 static void summary_unthread_for_exec(SummaryView *summaryview)
3447 {
3448         GtkCTreeNode *node;
3449         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3450
3451         summary_lock(summaryview);
3452
3453         debug_print(_("Unthreading for execution..."));
3454
3455         gtk_clist_freeze(GTK_CLIST(ctree));
3456
3457         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3458              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
3459                 summary_unthread_for_exec_func(ctree, node, NULL);
3460         }
3461
3462         gtk_clist_thaw(GTK_CLIST(ctree));
3463
3464         debug_print(_("done.\n"));
3465
3466         summary_unlock(summaryview);
3467 }
3468
3469 static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
3470                                            gpointer data)
3471 {
3472         MsgInfo *msginfo;
3473         GtkCTreeNode *top_parent;
3474         GtkCTreeNode *child;
3475         GtkCTreeNode *sibling;
3476
3477         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3478
3479         if (!msginfo ||
3480             (!MSG_IS_MOVE(msginfo->flags) &&
3481              !MSG_IS_DELETED(msginfo->flags)))
3482                 return;
3483         child = GTK_CTREE_ROW(node)->children;
3484         if (!child) return;
3485
3486         for (top_parent = node;
3487              GTK_CTREE_ROW(top_parent)->parent != NULL;
3488              top_parent = GTK_CTREE_ROW(top_parent)->parent)
3489                 ;
3490         sibling = GTK_CTREE_ROW(top_parent)->sibling;
3491
3492         while (child != NULL) {
3493                 GtkCTreeNode *next_child;
3494
3495                 next_child = GTK_CTREE_ROW(child)->sibling;
3496                 gtk_ctree_move(ctree, child, NULL, sibling);
3497                 child = next_child;
3498         }
3499 }
3500
3501 void summary_filter(SummaryView *summaryview)
3502 {
3503         if (!prefs_common.fltlist && !global_processing) {
3504                 alertpanel_error(_("No filter rules defined."));
3505                 return;
3506         }
3507
3508         summary_lock(summaryview);
3509
3510         debug_print(_("filtering..."));
3511         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
3512         main_window_cursor_wait(summaryview->mainwin);
3513
3514         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
3515
3516         if (global_processing == NULL) {
3517                 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3518                                         GTK_CTREE_FUNC(summary_filter_func),
3519                                         summaryview);
3520                 
3521                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3522                 
3523                 if (prefs_common.immediate_exec) {
3524                         summary_unlock(summaryview);
3525                         summary_execute(summaryview);
3526                         summary_lock(summaryview);
3527                 } else
3528                         summary_status_show(summaryview);
3529         }
3530         else {
3531                 summaryview->folder_table = g_hash_table_new(NULL, NULL);
3532
3533                 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3534                                         GTK_CTREE_FUNC(summary_filter_func),
3535                                         summaryview);
3536
3537                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3538
3539                 folder_item_scan_foreach(summaryview->folder_table);
3540                 folderview_update_item_foreach(summaryview->folder_table);
3541
3542                 g_hash_table_destroy(summaryview->folder_table);
3543                 summaryview->folder_table = NULL;
3544
3545                 summary_show(summaryview, summaryview->folder_item, FALSE);
3546         }
3547
3548         debug_print(_("done.\n"));
3549         STATUSBAR_POP(summaryview->mainwin);
3550         main_window_cursor_normal(summaryview->mainwin);
3551
3552         summary_unlock(summaryview);
3553 }
3554
3555 static void summary_filter_func(GtkCTree *ctree, GtkCTreeNode *node,
3556                                 gpointer data)
3557 {
3558         MsgInfo *msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3559         SummaryView *summaryview = data;
3560         gchar *file;
3561         FolderItem *dest;
3562
3563         if (global_processing == NULL) {
3564                 /* old filtering */
3565                 file = procmsg_get_message_file_path(msginfo);
3566                 dest = filter_get_dest_folder(prefs_common.fltlist, file);
3567                 g_free(file);
3568
3569                 if (dest && strcmp2(dest->path, FILTER_NOT_RECEIVE) != 0 &&
3570                     summaryview->folder_item != dest)
3571                         summary_move_row_to(summaryview, node, dest);
3572         }
3573         else
3574                 filter_msginfo_move_or_delete(global_processing, msginfo,
3575                                               summaryview->folder_table);
3576 }
3577
3578 /* color label */
3579
3580 #define N_COLOR_LABELS colorlabel_get_color_count()
3581
3582 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
3583                                                      gpointer data)
3584 {
3585         guint color = GPOINTER_TO_UINT(data);
3586         SummaryView *summaryview;
3587
3588         summaryview = gtk_object_get_data(GTK_OBJECT(widget), "summaryview");
3589         g_return_if_fail(summaryview != NULL);
3590
3591         /* "dont_toggle" state set? */
3592         if (gtk_object_get_data(GTK_OBJECT(summaryview->colorlabel_menu),
3593                                 "dont_toggle"))
3594                 return;
3595
3596         summary_set_colorlabel(summaryview, color, NULL);
3597 }
3598
3599 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
3600  * for the messsage; not the color index */
3601 void summary_set_colorlabel_color(GtkCTree *ctree, GtkCTreeNode *node,
3602                                   guint labelcolor)
3603 {
3604         GdkColor color;
3605         GtkStyle *style, *prev_style, *ctree_style;
3606         MsgInfo *msginfo;
3607         gint color_index;
3608
3609         msginfo = gtk_ctree_node_get_row_data(ctree, node);
3610         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_CLABEL_FLAG_MASK);
3611         MSG_SET_COLORLABEL_VALUE(msginfo->flags, labelcolor);
3612
3613         color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
3614         ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
3615         prev_style = gtk_ctree_node_get_row_style(ctree, node);
3616
3617         if (color_index < 0 || color_index >= N_COLOR_LABELS) {
3618                 if (!prev_style) return;
3619                 style = gtk_style_copy(prev_style);
3620                 color = ctree_style->fg[GTK_STATE_NORMAL];
3621                 style->fg[GTK_STATE_NORMAL] = color;
3622                 color = ctree_style->fg[GTK_STATE_SELECTED];
3623                 style->fg[GTK_STATE_SELECTED] = color;
3624         } else {
3625                 if (prev_style)
3626                         style = gtk_style_copy(prev_style);
3627                 else
3628                         style = gtk_style_copy(ctree_style);
3629                 color = colorlabel_get_color(color_index);
3630                 style->fg[GTK_STATE_NORMAL] = color;
3631                 /* get the average of label color and selected fg color
3632                    for visibility */
3633                 style->fg[GTK_STATE_SELECTED].red   = (color.red   + ctree_style->fg[GTK_STATE_SELECTED].red  ) / 2;
3634                 style->fg[GTK_STATE_SELECTED].green = (color.green + ctree_style->fg[GTK_STATE_SELECTED].green) / 2;
3635                 style->fg[GTK_STATE_SELECTED].blue  = (color.blue  + ctree_style->fg[GTK_STATE_SELECTED].blue ) / 2;
3636         }
3637
3638         gtk_ctree_node_set_row_style(ctree, node, style);
3639 }
3640
3641 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
3642                             GtkWidget *widget)
3643 {
3644         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3645         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3646         GList *cur;
3647
3648         for (cur = clist->selection; cur != NULL; cur = cur->next)
3649                 summary_set_colorlabel_color(ctree, GTK_CTREE_NODE(cur->data),
3650                                              labelcolor);
3651 }
3652
3653 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
3654                                                           gpointer data)
3655 {
3656         SummaryView *summaryview;
3657         GtkMenuShell *menu;
3658         GtkCheckMenuItem **items;
3659         gint n;
3660         GList *cur, *sel;
3661
3662         summaryview = (SummaryView *)data;
3663         g_return_if_fail(summaryview != NULL);
3664
3665         sel = GTK_CLIST(summaryview->ctree)->selection;
3666         if (!sel) return;
3667
3668         menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
3669         g_return_if_fail(menu != NULL);
3670
3671         Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
3672
3673         /* NOTE: don't return prematurely because we set the "dont_toggle"
3674          * state for check menu items */
3675         gtk_object_set_data(GTK_OBJECT(menu), "dont_toggle",
3676                             GINT_TO_POINTER(1));
3677
3678         /* clear items. get item pointers. */
3679         for (n = 0, cur = menu->children; cur != NULL; cur = cur->next) {
3680                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
3681                         gtk_check_menu_item_set_state
3682                                 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
3683                         items[n] = GTK_CHECK_MENU_ITEM(cur->data);
3684                         n++;
3685                 }
3686         }
3687
3688         if (n == (N_COLOR_LABELS + 1)) {
3689                 /* iterate all messages and set the state of the appropriate
3690                  * items */
3691                 for (; sel != NULL; sel = sel->next) {
3692                         MsgInfo *msginfo;
3693                         gint clabel;
3694
3695                         msginfo = gtk_ctree_node_get_row_data
3696                                 (GTK_CTREE(summaryview->ctree),
3697                                  GTK_CTREE_NODE(sel->data));
3698                         if (msginfo) {
3699                                 clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
3700                                 if (!items[clabel]->active)
3701                                         gtk_check_menu_item_set_state
3702                                                 (items[clabel], TRUE);
3703                         }
3704                 }
3705         } else
3706                 g_warning("invalid number of color elements (%d)\n", n);
3707
3708         /* reset "dont_toggle" state */
3709         gtk_object_set_data(GTK_OBJECT(menu), "dont_toggle",
3710                             GINT_TO_POINTER(0));
3711 }
3712
3713 static void summary_colorlabel_menu_create(SummaryView *summaryview)
3714 {
3715         GtkWidget *label_menuitem;
3716         GtkWidget *menu;
3717         GtkWidget *item;
3718         gint i;
3719
3720         label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
3721                                                    "/Color label");
3722         gtk_signal_connect(GTK_OBJECT(label_menuitem), "activate",
3723                            GTK_SIGNAL_FUNC(summary_colorlabel_menu_item_activate_item_cb),
3724                            summaryview);
3725         gtk_widget_show(label_menuitem);
3726
3727         menu = gtk_menu_new();
3728
3729         /* create sub items. for the menu item activation callback we pass the
3730          * index of label_colors[] as data parameter. for the None color we
3731          * pass an invalid (high) value. also we attach a data pointer so we
3732          * can always get back the SummaryView pointer. */
3733
3734         item = gtk_check_menu_item_new_with_label(_("None"));
3735         gtk_menu_append(GTK_MENU(menu), item);
3736         gtk_signal_connect(GTK_OBJECT(item), "activate",
3737                            GTK_SIGNAL_FUNC(summary_colorlabel_menu_item_activate_cb),
3738                            GUINT_TO_POINTER(0));
3739         gtk_object_set_data(GTK_OBJECT(item), "summaryview", summaryview);
3740         gtk_widget_show(item);
3741
3742         item = gtk_menu_item_new();
3743         gtk_menu_append(GTK_MENU(menu), item);
3744         gtk_widget_show(item);
3745
3746         /* create pixmap/label menu items */
3747         for (i = 0; i < N_COLOR_LABELS; i++) {
3748                 item = colorlabel_create_check_color_menu_item(i);
3749                 gtk_menu_append(GTK_MENU(menu), item);
3750                 gtk_signal_connect(GTK_OBJECT(item), "activate",
3751                                    GTK_SIGNAL_FUNC(summary_colorlabel_menu_item_activate_cb),
3752                                    GUINT_TO_POINTER(i + 1));
3753                 gtk_object_set_data(GTK_OBJECT(item), "summaryview",
3754                                     summaryview);
3755                 gtk_widget_show(item);
3756         }
3757
3758         gtk_widget_show(menu);
3759         gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
3760         summaryview->colorlabel_menu = menu;
3761 }
3762
3763 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
3764 {
3765         GtkWidget *ctree;
3766         gint *col_pos = summaryview->col_pos;
3767         SummaryColumnState *col_state;
3768         gchar *titles[N_SUMMARY_COLS];
3769         SummaryColumnType type;
3770         gint pos;
3771
3772         memset(titles, 0, sizeof(titles));
3773
3774         col_state = prefs_summary_column_get_config();
3775         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
3776                 summaryview->col_state[pos] = col_state[pos];
3777                 type = col_state[pos].type;
3778                 col_pos[type] = pos;
3779         }
3780         col_state = summaryview->col_state;
3781
3782         ctree = gtk_sctree_new_with_titles
3783                 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
3784
3785         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_EXTENDED);
3786         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MARK],
3787                                            GTK_JUSTIFY_CENTER);
3788         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_UNREAD],
3789                                            GTK_JUSTIFY_CENTER);
3790         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MIME],
3791                                            GTK_JUSTIFY_CENTER);
3792         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
3793                                            GTK_JUSTIFY_RIGHT);
3794         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
3795                                            GTK_JUSTIFY_RIGHT);
3796         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SCORE],
3797                                            GTK_JUSTIFY_RIGHT);
3798         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MARK],
3799                                    SUMMARY_COL_MARK_WIDTH);
3800         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_UNREAD],
3801                                    SUMMARY_COL_UNREAD_WIDTH);
3802         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MIME],
3803                                    SUMMARY_COL_MIME_WIDTH);
3804         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SUBJECT],
3805                                    prefs_common.summary_col_size[S_COL_SUBJECT]);
3806         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_FROM],
3807                                    prefs_common.summary_col_size[S_COL_FROM]);
3808         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_DATE],
3809                                    prefs_common.summary_col_size[S_COL_DATE]);
3810         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
3811                                    prefs_common.summary_col_size[S_COL_SIZE]);
3812         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
3813                                    prefs_common.summary_col_size[S_COL_NUMBER]);
3814         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SCORE],
3815                                    prefs_common.summary_col_size[S_COL_SCORE]);
3816         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
3817         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
3818                                      GTK_CTREE_EXPANDER_SQUARE);
3819 #if 0
3820         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
3821         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
3822                                      GTK_CTREE_EXPANDER_TRIANGLE);
3823 #endif
3824         gtk_ctree_set_indent(GTK_CTREE(ctree), 16);
3825         gtk_object_set_user_data(GTK_OBJECT(ctree), summaryview);
3826
3827         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
3828                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[pos].button,
3829                                        GTK_CAN_FOCUS);
3830                 gtk_clist_set_column_visibility
3831                         (GTK_CLIST(ctree), pos, col_state[pos].visible);
3832         }
3833
3834         /* connect signal to the buttons for sorting */
3835 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
3836         gtk_signal_connect \
3837                 (GTK_OBJECT(GTK_CLIST(ctree)->column[col_pos[col]].button), \
3838                  "clicked", \
3839                  GTK_SIGNAL_FUNC(func), \
3840                  summaryview)
3841
3842         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked);
3843         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_UNREAD , summary_unread_clicked);
3844         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked);
3845         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked);
3846         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked);
3847         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked);
3848         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked);
3849         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
3850         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE,   summary_score_clicked);
3851
3852 #undef CLIST_BUTTON_SIGNAL_CONNECT
3853
3854         gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
3855                            GTK_SIGNAL_FUNC(summary_selected), summaryview);
3856         gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
3857                            GTK_SIGNAL_FUNC(summary_button_pressed),
3858                            summaryview);
3859         gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
3860                            GTK_SIGNAL_FUNC(summary_button_released),
3861                            summaryview);
3862         gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
3863                            GTK_SIGNAL_FUNC(summary_key_pressed), summaryview);
3864         gtk_signal_connect(GTK_OBJECT(ctree), "resize_column",
3865                            GTK_SIGNAL_FUNC(summary_col_resized), summaryview);
3866         gtk_signal_connect(GTK_OBJECT(ctree), "open_row",
3867                            GTK_SIGNAL_FUNC(summary_open_row), summaryview);
3868
3869         gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_expand",
3870                                  GTK_SIGNAL_FUNC(summary_tree_expanded),
3871                                  summaryview);
3872         gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_collapse",
3873                                  GTK_SIGNAL_FUNC(summary_tree_collapsed),
3874                                  summaryview);
3875
3876         gtk_signal_connect(GTK_OBJECT(ctree), "start_drag",
3877                            GTK_SIGNAL_FUNC(summary_start_drag),
3878                            summaryview);
3879         gtk_signal_connect(GTK_OBJECT(ctree), "drag_data_get",
3880                            GTK_SIGNAL_FUNC(summary_drag_data_get),
3881                            summaryview);
3882
3883         return ctree;
3884 }
3885
3886 void summary_set_column_order(SummaryView *summaryview)
3887 {
3888         GtkWidget *ctree;
3889         GtkWidget *scrolledwin = summaryview->scrolledwin;
3890         GtkWidget *pixmap;
3891         FolderItem *item;
3892
3893         item = summaryview->folder_item;
3894         summary_clear_all(summaryview);
3895         gtk_widget_destroy(summaryview->ctree);
3896
3897         summaryview->ctree = ctree = summary_ctree_create(summaryview);
3898         pixmap = gtk_pixmap_new(clipxpm, clipxpmmask);
3899         gtk_clist_set_column_widget(GTK_CLIST(ctree),
3900                                     summaryview->col_pos[S_COL_MIME], pixmap);
3901         gtk_widget_show(pixmap);
3902         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
3903                                             GTK_CLIST(ctree)->hadjustment);
3904         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
3905                                             GTK_CLIST(ctree)->vadjustment);
3906         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
3907         gtk_widget_show(ctree);
3908
3909         summary_show(summaryview, item, FALSE);
3910 }
3911
3912
3913 /* callback functions */
3914
3915 static void summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
3916                                    SummaryView *summaryview)
3917 {
3918         if (!event)
3919                 return;
3920
3921         if (!summaryview->msg_is_toggled_on && summaryview->selected)
3922                 summary_display_msg(summaryview, summaryview->selected, FALSE);
3923         else
3924                 summary_toggle_view(summaryview);
3925 }
3926
3927 static void summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
3928                                    SummaryView *summaryview)
3929 {
3930         if (!event) return;
3931
3932         if (event->button == 3) {
3933                 /* right clicked */
3934                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
3935                                NULL, NULL, event->button, event->time);
3936         } else if (event->button == 2) {
3937                 summaryview->display_msg = TRUE;
3938         } else if (event->button == 1) {
3939                 if (!prefs_common.emulate_emacs &&
3940                     summaryview->msg_is_toggled_on)
3941                         summaryview->display_msg = TRUE;
3942         }
3943 }
3944
3945 static void summary_button_released(GtkWidget *ctree, GdkEventButton *event,
3946                                     SummaryView *summaryview)
3947 {
3948 }
3949
3950 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
3951 {
3952         summary_key_pressed(summaryview->ctree, event, summaryview);
3953 }
3954
3955 #define BREAK_ON_MODIFIER_KEY() \
3956         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
3957
3958 #define RETURN_IF_LOCKED() \
3959         if (summaryview->mainwin->lock_count) return
3960
3961 #define KEY_PRESS_EVENT_STOP() \
3962         if (gtk_signal_n_emissions_by_name \
3963                 (GTK_OBJECT(ctree), "key_press_event") > 0) { \
3964                 gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree), \
3965                                              "key_press_event"); \
3966         }
3967
3968 static void summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
3969                                 SummaryView *summaryview)
3970 {
3971         GtkCTree *ctree = GTK_CTREE(widget);
3972         GtkCTreeNode *node;
3973         FolderItem *to_folder;
3974
3975         if (summary_is_locked(summaryview)) return;
3976         if (!event) return;
3977
3978         switch (event->keyval) {
3979         case GDK_g:             /* Go */
3980         case GDK_G:
3981                 RETURN_IF_LOCKED();
3982                 BREAK_ON_MODIFIER_KEY();
3983                 KEY_PRESS_EVENT_STOP();
3984                 to_folder = foldersel_folder_sel(NULL, NULL);
3985                 if (to_folder) {
3986                         debug_print(_("Go to %s\n"), to_folder->path);
3987                         folderview_select(summaryview->folderview, to_folder);
3988                 }
3989                 return;
3990         case GDK_w:             /* Write new message */
3991                 BREAK_ON_MODIFIER_KEY();
3992                 if (summaryview->folder_item) {
3993                         PrefsAccount *ac;
3994                         ac = summaryview->folder_item->folder->account;
3995                         if (ac && ac->protocol == A_NNTP)
3996                                 compose_new_with_recipient
3997                                         (ac, summaryview->folder_item->path);
3998                         else
3999                                 compose_new_with_folderitem(ac, summaryview->folder_item);
4000                 } else
4001                         compose_new(NULL);
4002                 return;
4003         case GDK_D:             /* Empty trash */
4004                 RETURN_IF_LOCKED();
4005                 BREAK_ON_MODIFIER_KEY();
4006                 KEY_PRESS_EVENT_STOP();
4007                 main_window_empty_trash(summaryview->mainwin, TRUE);
4008                 return;
4009         case GDK_Q:             /* Quit */
4010                 RETURN_IF_LOCKED();
4011                 BREAK_ON_MODIFIER_KEY();
4012
4013                 if (prefs_common.confirm_on_exit) {
4014                         if (alertpanel(_("Exit"), _("Exit this program?"),
4015                                        _("OK"), _("Cancel"), NULL)
4016                                        == G_ALERTDEFAULT) {
4017                                 manage_window_focus_in
4018                                         (summaryview->mainwin->window,
4019                                          NULL, NULL);
4020                                 app_will_exit(NULL, summaryview->mainwin);
4021                         }
4022                 }
4023                 return;
4024         case GDK_Left:          /* Move focus */
4025         case GDK_Escape:
4026                 gtk_widget_grab_focus(summaryview->folderview->ctree);
4027                 return;
4028         default:
4029                 break;
4030         }
4031
4032         if (!summaryview->selected) {
4033                 node = gtk_ctree_node_nth(ctree, 0);
4034                 if (node)
4035                         gtk_ctree_select(ctree, node);
4036                 else
4037                         return;
4038         }
4039
4040         switch (event->keyval) {
4041         case GDK_space:         /* Page down or go to the next */
4042                 if (summaryview->displayed != summaryview->selected) {
4043                         summary_display_msg(summaryview,
4044                                             summaryview->selected, FALSE);
4045                         break;
4046                 }
4047                 if (!textview_scroll_page(summaryview->messageview->textview,
4048                                           FALSE))
4049                         summary_select_next_unread(summaryview);
4050                 break;
4051         case GDK_n:             /* Next */
4052         case GDK_N:
4053                 BREAK_ON_MODIFIER_KEY();
4054                 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
4055                 break;
4056         case GDK_BackSpace:     /* Page up */
4057                 textview_scroll_page(summaryview->messageview->textview, TRUE);
4058                 break;
4059         case GDK_p:             /* Prev */
4060         case GDK_P:
4061                 BREAK_ON_MODIFIER_KEY();
4062                 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
4063                 break;
4064         case GDK_v:             /* Toggle summary mode / message mode */
4065         case GDK_V:
4066                 BREAK_ON_MODIFIER_KEY();
4067
4068                 if (!summaryview->msg_is_toggled_on && summaryview->selected)
4069                         summary_display_msg(summaryview,
4070                                             summaryview->selected, FALSE);
4071                 else
4072                         summary_toggle_view(summaryview);
4073                 break;
4074         case GDK_Return:        /* Scroll up/down one line */
4075                 if (summaryview->displayed != summaryview->selected) {
4076                         summary_display_msg(summaryview,
4077                                             summaryview->selected, FALSE);
4078                         break;
4079                 }
4080                 textview_scroll_one_line(summaryview->messageview->textview,
4081                                          (event->state & GDK_MOD1_MASK) != 0);
4082                 break;
4083         case GDK_asterisk:      /* Mark */
4084                 summary_mark(summaryview);
4085                 break;
4086         case GDK_exclam:        /* Mark as unread */
4087                 summary_mark_as_unread(summaryview);
4088                 break;
4089         case GDK_d:             /* Delete */
4090         case GDK_Delete:
4091                 RETURN_IF_LOCKED();
4092                 BREAK_ON_MODIFIER_KEY();
4093                 summary_delete(summaryview);
4094                 break;
4095         case GDK_u:             /* Unmark */
4096         case GDK_U:
4097                 BREAK_ON_MODIFIER_KEY();
4098                 summary_unmark(summaryview);
4099                 break;
4100         case GDK_o:             /* Move */
4101                 RETURN_IF_LOCKED();
4102                 BREAK_ON_MODIFIER_KEY();
4103                 summary_move_to(summaryview);
4104                 break;
4105         case GDK_O:             /* Copy */
4106                 RETURN_IF_LOCKED();
4107                 BREAK_ON_MODIFIER_KEY();
4108                 summary_copy_to(summaryview);
4109                 break;
4110         case GDK_x:             /* Execute */
4111         case GDK_X:
4112                 RETURN_IF_LOCKED();
4113                 BREAK_ON_MODIFIER_KEY();
4114                 KEY_PRESS_EVENT_STOP();
4115                 summary_execute(summaryview);
4116                 break;
4117         case GDK_a:             /* Reply to the message */
4118                 BREAK_ON_MODIFIER_KEY();
4119                 summary_reply_cb(summaryview,
4120                                  COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE, NULL);
4121                 break;
4122         case GDK_A:             /* Reply to the message with quotation */
4123                 BREAK_ON_MODIFIER_KEY();
4124                 summary_reply_cb(summaryview,
4125                                  COMPOSE_REPLY_TO_ALL_WITH_QUOTE, NULL);
4126                 break;
4127         case GDK_f:             /* Forward the message */
4128                 BREAK_ON_MODIFIER_KEY();
4129                 summary_reply_cb(summaryview, COMPOSE_FORWARD, NULL);
4130                 break;
4131         case GDK_F:
4132                 BREAK_ON_MODIFIER_KEY();
4133                 summary_reply_cb(summaryview, COMPOSE_FORWARD_AS_ATTACH, NULL);
4134                 break;
4135         case GDK_y:             /* Save the message */
4136                 BREAK_ON_MODIFIER_KEY();
4137                 summary_save_as(summaryview);
4138                 break;
4139         default:
4140                 break;
4141         }
4142 }
4143
4144 #undef BREAK_ON_MODIFIER_KEY
4145 #undef RETURN_IF_LOCKED
4146 #undef KEY_PRESS_EVENT_STOP
4147
4148 static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
4149 {
4150         if (summaryview->folder_item->stype == F_OUTBOX ||
4151             summaryview->folder_item->stype == F_DRAFT  ||
4152             summaryview->folder_item->stype == F_QUEUE)
4153                 summary_reedit(summaryview);
4154         else
4155                 summary_open_msg(summaryview);
4156
4157         summaryview->display_msg = FALSE;
4158 }
4159
4160 static void summary_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
4161                                   SummaryView *summaryview)
4162 {
4163         summary_set_row_marks(summaryview, node);
4164 }
4165
4166 static void summary_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
4167                                    SummaryView *summaryview)
4168 {
4169         summary_set_row_marks(summaryview, node);
4170 }
4171
4172 static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
4173                              gint column, SummaryView *summaryview)
4174 {
4175         MsgInfo *msginfo;
4176
4177         summary_status_show(summaryview);
4178         summary_set_menu_sensitive(summaryview);
4179
4180         if (GTK_CLIST(ctree)->selection &&
4181              GTK_CLIST(ctree)->selection->next) {
4182                 summaryview->display_msg = FALSE;
4183                 return;
4184         }
4185
4186         summaryview->selected = row;
4187
4188         msginfo = gtk_ctree_node_get_row_data(ctree, row);
4189
4190         switch (column < 0 ? column : summaryview->col_state[column].type) {
4191         case S_COL_MARK:
4192                 if (MSG_IS_MARKED(msginfo->flags)) {
4193                         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
4194                         CHANGE_FLAGS(msginfo);
4195                         summary_set_row_marks(summaryview, row);
4196                 } else
4197                         summary_mark_row(summaryview, row);
4198                 break;
4199         case S_COL_UNREAD:
4200                 if (MSG_IS_UNREAD(msginfo->flags)) {
4201                         summary_mark_row_as_read(summaryview, row);
4202                         summary_status_show(summaryview);
4203                 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
4204                          !MSG_IS_FORWARDED(msginfo->flags)) {
4205                         summary_mark_row_as_unread(summaryview, row);
4206                         summary_status_show(summaryview);
4207                 }
4208                 break;
4209         default:
4210                 break;
4211         }
4212
4213         if (summaryview->display_msg)
4214                 summary_display_msg(summaryview, row, FALSE);
4215
4216         summaryview->display_msg = FALSE;
4217 }
4218
4219 static void summary_col_resized(GtkCList *clist, gint column, gint width,
4220                                 SummaryView *summaryview)
4221 {
4222         SummaryColumnType type = summaryview->col_state[column].type;
4223
4224         prefs_common.summary_col_size[type] = width;
4225 }
4226
4227 static void summary_reply_cb(SummaryView *summaryview, guint action,
4228                              GtkWidget *widget)
4229 {
4230         MsgInfo *msginfo;
4231         GList  *sel = GTK_CLIST(summaryview->ctree)->selection;
4232
4233         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
4234                                               summaryview->selected);
4235         if (!msginfo) return;
4236
4237         switch ((ComposeMode)action) {
4238         case COMPOSE_REPLY:
4239                 compose_reply(msginfo, prefs_common.reply_with_quote,
4240                               FALSE, FALSE);
4241                 break;
4242         case COMPOSE_REPLY_WITH_QUOTE:
4243                 compose_reply(msginfo, TRUE, FALSE, FALSE);
4244                 break;
4245         case COMPOSE_REPLY_WITHOUT_QUOTE:
4246                 compose_reply(msginfo, FALSE, FALSE, FALSE);
4247                 break;
4248         case COMPOSE_REPLY_TO_SENDER:
4249                 compose_reply(msginfo, prefs_common.reply_with_quote,
4250                               FALSE, TRUE);
4251                 break;
4252         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
4253                 compose_followup_and_reply_to(msginfo,
4254                                               prefs_common.reply_with_quote,
4255                                               FALSE, TRUE);
4256                 break;
4257         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
4258                 compose_reply(msginfo, TRUE, FALSE, TRUE);
4259                 break;
4260         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
4261                 compose_reply(msginfo, FALSE, FALSE, TRUE);
4262                 break;
4263         case COMPOSE_REPLY_TO_ALL:
4264                 compose_reply(msginfo, prefs_common.reply_with_quote,
4265                               TRUE, TRUE);
4266                 break;
4267         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
4268                 compose_reply(msginfo, TRUE, TRUE, FALSE);
4269                 break;
4270         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
4271                 compose_reply(msginfo, FALSE, TRUE, FALSE);
4272                 break;
4273         case COMPOSE_FORWARD:
4274                 if (!sel->next) {
4275                         compose_forward(NULL, msginfo, FALSE);
4276                         break;
4277                 }
4278                 /* if (sel->next) FALL THROUGH */
4279         case COMPOSE_FORWARD_AS_ATTACH:
4280                 {
4281                         GSList *msginfo_list = NULL;
4282                         for ( ; sel != NULL; sel = sel->next)
4283                                 msginfo_list = g_slist_append(msginfo_list, 
4284                                         gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
4285                                                 GTK_CTREE_NODE(sel->data)));
4286                         compose_forward_multiple(NULL, msginfo_list);
4287                         g_slist_free(msginfo_list);
4288                 }                       
4289                 break;
4290         case COMPOSE_BOUNCE:
4291                 compose_bounce(NULL, msginfo);
4292                 break;
4293         default:
4294                 g_warning("summary_reply_cb(): invalid action: %d\n", action);
4295         }
4296
4297         summary_set_marks_selected(summaryview);
4298 }
4299
4300 static void summary_execute_cb(SummaryView *summaryview, guint action,
4301                                GtkWidget *widget)
4302 {
4303         summary_execute(summaryview);
4304 }
4305
4306 static void summary_show_all_header_cb(SummaryView *summaryview,
4307                                        guint action, GtkWidget *widget)
4308 {
4309         header_window_show_cb(summaryview->mainwin, action, widget);
4310 }
4311
4312 static void summary_add_address_cb(SummaryView *summaryview,
4313                                    guint action, GtkWidget *widget)
4314 {
4315         summary_add_address(summaryview);
4316 }
4317
4318 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
4319 {
4320         summary_sort(summaryview, SORT_BY_MARK);
4321 }
4322
4323 static void summary_unread_clicked(GtkWidget *button, SummaryView *summaryview)
4324 {
4325         summary_sort(summaryview, SORT_BY_UNREAD);
4326 }
4327
4328 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
4329 {
4330         summary_sort(summaryview, SORT_BY_MIME);
4331 }
4332
4333 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
4334 {
4335         summary_sort(summaryview, SORT_BY_NUMBER);
4336 }
4337
4338 static void summary_score_clicked(GtkWidget *button,
4339                                   SummaryView *summaryview)
4340 {
4341         summary_sort(summaryview, SORT_BY_SCORE);
4342 }
4343
4344 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
4345 {
4346         summary_sort(summaryview, SORT_BY_SIZE);
4347 }
4348
4349 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
4350 {
4351         summary_sort(summaryview, SORT_BY_DATE);
4352 }
4353
4354 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
4355 {
4356         summary_sort(summaryview, SORT_BY_FROM);
4357 }
4358
4359 static void summary_subject_clicked(GtkWidget *button,
4360                                     SummaryView *summaryview)
4361 {
4362         summary_sort(summaryview, SORT_BY_SUBJECT);
4363 }
4364
4365 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
4366                                SummaryView *summaryview)
4367 {
4368         GtkTargetList *list;
4369         GdkDragContext *context;
4370
4371         g_return_if_fail(summaryview != NULL);
4372         g_return_if_fail(summaryview->folder_item != NULL);
4373         g_return_if_fail(summaryview->folder_item->folder != NULL);
4374         if (summaryview->folder_item->folder->type == F_NEWS ||
4375             summaryview->selected == NULL)
4376                 return;
4377
4378         list = gtk_target_list_new(summary_drag_types, 1);
4379
4380         context = gtk_drag_begin(widget, list,
4381                                  GDK_ACTION_MOVE, button, event);
4382         gtk_drag_set_icon_default(context);
4383 }
4384
4385 static void summary_drag_data_get(GtkWidget        *widget,
4386                                   GdkDragContext   *drag_context,
4387                                   GtkSelectionData *selection_data,
4388                                   guint             info,
4389                                   guint             time,
4390                                   SummaryView      *summaryview)
4391 {
4392         if (info == TARGET_MAIL_URI_LIST) {
4393                 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4394                 GList *cur;
4395                 MsgInfo *msginfo;
4396                 gchar *mail_list = NULL, *tmp1, *tmp2;
4397
4398                 for (cur = GTK_CLIST(ctree)->selection;
4399                      cur != NULL; cur = cur->next) {
4400                         msginfo = gtk_ctree_node_get_row_data
4401                                 (ctree, GTK_CTREE_NODE(cur->data));
4402                         tmp2 = procmsg_get_message_file_path(msginfo);
4403                         if (!tmp2) continue;
4404                         tmp1 = g_strconcat("file:/", tmp2, NULL);
4405                         g_free(tmp2);
4406
4407                         if (!mail_list) {
4408                                 mail_list = tmp1;
4409                         } else {
4410                                 tmp2 = g_strconcat(mail_list, tmp1, NULL);
4411                                 g_free(mail_list);
4412                                 g_free(tmp1);
4413                                 mail_list = tmp2;
4414                         }
4415                 }
4416
4417                 if (mail_list != NULL) {
4418                         gtk_selection_data_set(selection_data,
4419                                                selection_data->target, 8,
4420                                                mail_list, strlen(mail_list));
4421                         g_free(mail_list);
4422                 } 
4423         } else if (info == TARGET_DUMMY) {
4424                 if (GTK_CLIST(summaryview->ctree)->selection)
4425                         gtk_selection_data_set(selection_data,
4426                                                selection_data->target, 8,
4427                                                "Dummy", 6);
4428         }
4429 }
4430
4431
4432 /* custom compare functions for sorting */
4433
4434 static gint summary_cmp_by_mark(GtkCList *clist,
4435                                 gconstpointer ptr1, gconstpointer ptr2)
4436 {
4437         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4438         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4439
4440         return MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags);
4441 }
4442
4443 static gint summary_cmp_by_unread(GtkCList *clist,
4444                                   gconstpointer ptr1, gconstpointer ptr2)
4445 {
4446         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4447         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4448
4449         return MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags);
4450 }
4451
4452 static gint summary_cmp_by_mime(GtkCList *clist,
4453                                 gconstpointer ptr1, gconstpointer ptr2)
4454 {
4455         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4456         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4457
4458         return MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags);
4459 }
4460
4461 static gint summary_cmp_by_num(GtkCList *clist,
4462                                gconstpointer ptr1, gconstpointer ptr2)
4463 {
4464         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4465         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4466
4467         return msginfo1->msgnum - msginfo2->msgnum;
4468 }
4469
4470 static gint summary_cmp_by_size(GtkCList *clist,
4471                                 gconstpointer ptr1, gconstpointer ptr2)
4472 {
4473         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4474         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4475
4476         return msginfo1->size - msginfo2->size;
4477 }
4478
4479 static gint summary_cmp_by_date(GtkCList *clist,
4480                                gconstpointer ptr1, gconstpointer ptr2)
4481 {
4482         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4483         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4484
4485         return msginfo1->date_t - msginfo2->date_t;
4486 }
4487
4488 static gint summary_cmp_by_from(GtkCList *clist,
4489                                gconstpointer ptr1, gconstpointer ptr2)
4490 {
4491         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4492         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4493
4494         if (!msginfo1->fromname)
4495                 return (msginfo2->fromname != NULL);
4496         if (!msginfo2->fromname)
4497                 return -1;
4498
4499         return strcasecmp(msginfo1->fromname, msginfo2->fromname);
4500 }
4501
4502 static gint summary_cmp_by_subject(GtkCList *clist,
4503                                gconstpointer ptr1, gconstpointer ptr2)
4504 {
4505         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4506         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4507
4508         if (!msginfo1->subject)
4509                 return (msginfo2->subject != NULL);
4510         if (!msginfo2->subject)
4511                 return -1;
4512
4513         return strcasecmp(msginfo1->subject, msginfo2->subject);
4514 }
4515
4516 static gint summary_cmp_by_label(GtkCList *clist,
4517                                  gconstpointer ptr1, gconstpointer ptr2)
4518 {
4519         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4520         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4521
4522         return MSG_GET_COLORLABEL(msginfo1->flags) -
4523                 MSG_GET_COLORLABEL(msginfo2->flags);
4524 }
4525 static gint summary_cmp_by_score(GtkCList *clist,
4526                                  gconstpointer ptr1, gconstpointer ptr2)
4527 {
4528         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4529         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4530         int diff;
4531
4532         /* if score are equal, sort by date */
4533
4534         diff = msginfo1->threadscore - msginfo2->threadscore;
4535         if (diff != 0)
4536                 return diff;
4537         else
4538                 return summary_cmp_by_date(clist, ptr1, ptr2);
4539 }
4540
4541 static void summary_ignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
4542 {
4543         SummaryView *summaryview = (SummaryView *) data;
4544         MsgInfo *msginfo;
4545
4546         msginfo = gtk_ctree_node_get_row_data(ctree, row);
4547         if (MSG_IS_NEW(msginfo->flags))
4548                 summaryview->newmsgs--;
4549         if (MSG_IS_UNREAD(msginfo->flags))
4550                 summaryview->unread--;
4551         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
4552
4553         CHANGE_FLAGS(msginfo);
4554
4555         summary_set_row_marks(summaryview, row);
4556         debug_print(_("Message %d is marked as ignore thread\n"),
4557             msginfo->msgnum);
4558 }
4559
4560 static void summary_ignore_thread(SummaryView *summaryview)
4561 {
4562         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4563         GList *cur;
4564
4565         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
4566                 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), GTK_CTREE_FUNC(summary_ignore_thread_func), summaryview);
4567         }
4568
4569         summary_status_show(summaryview);
4570 }
4571
4572 static void summary_unignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
4573 {
4574         SummaryView *summaryview = (SummaryView *) data;
4575         MsgInfo *msginfo;
4576
4577         msginfo = gtk_ctree_node_get_row_data(ctree, row);
4578         if (MSG_IS_NEW(msginfo->flags))
4579                 summaryview->newmsgs++;
4580         if (MSG_IS_UNREAD(msginfo->flags))
4581                 summaryview->unread++;
4582         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
4583
4584         CHANGE_FLAGS(msginfo);
4585                 
4586         summary_set_row_marks(summaryview, row);
4587         debug_print(_("Message %d is marked as unignore thread\n"),
4588             msginfo->msgnum);
4589 }
4590
4591 static void summary_unignore_thread(SummaryView *summaryview)
4592 {
4593         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4594         GList *cur;
4595
4596         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
4597                 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), GTK_CTREE_FUNC(summary_unignore_thread_func), summaryview);
4598         }
4599
4600         summary_status_show(summaryview);
4601 }
4602 /*
4603  * End of Source.
4604  */
4605