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