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