sync with sylpheed 0.6.3cvs7
[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         //if (msgid && g_hash_table_lookup(msgid_table, msgid) == NULL)
1951                 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
1952
1953         return TRUE;
1954 }
1955
1956 static GtkCTreeNode * subject_table_lookup(GHashTable *subject_table,
1957                                            gchar * subject)
1958 {
1959         if (g_strncasecmp(subject, "Re: ", 4) == 0)
1960                 return g_hash_table_lookup(subject_table, subject + 4);
1961         else
1962                 return g_hash_table_lookup(subject_table, subject);
1963 }
1964
1965 static void subject_table_insert(GHashTable *subject_table, gchar * subject,
1966                                  GtkCTreeNode * node)
1967 {
1968         if (g_strncasecmp(subject, "Re: ", 4) == 0)
1969                 g_hash_table_insert(subject_table, subject + 4, node);
1970         else
1971                 g_hash_table_insert(subject_table, subject, node);
1972 }
1973
1974 static void summary_set_ctree_from_list(SummaryView *summaryview,
1975                                         GSList *mlist)
1976 {
1977         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1978         MsgInfo *msginfo;
1979         MsgInfo *parentinfo;
1980         MsgInfo *cur_msginfo;
1981         GtkCTreeNode *node = NULL;
1982         GHashTable *msgid_table;
1983         GHashTable *subject_table;
1984         GSList * cur;
1985         GtkCTreeNode *cur_parent;
1986
1987         if (!mlist) return;
1988
1989         debug_print(_("\tSetting summary from message data..."));
1990         STATUSBAR_PUSH(summaryview->mainwin,
1991                        _("Setting summary from message data..."));
1992         gdk_flush();
1993
1994         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
1995         summaryview->msgid_table = msgid_table;
1996         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1997         summaryview->subject_table = subject_table;
1998
1999         if (prefs_common.use_addr_book)
2000                 start_address_completion();
2001         
2002         for (cur = mlist ; cur != NULL; cur = cur->next) {
2003                 msginfo = (MsgInfo *)cur->data;
2004                 msginfo->threadscore = msginfo->score;
2005         }
2006
2007         if (global_scoring || summaryview->folder_item->prefs->scoring) {
2008                 summaryview->important_score = prefs_common.important_score;
2009                 if (summaryview->folder_item->prefs->important_score >
2010                     summaryview->important_score)
2011                         summaryview->important_score =
2012                                 summaryview->folder_item->prefs->important_score;
2013         }
2014         
2015         if (summaryview->folder_item->threaded) {
2016                 GNode *root, *gnode;
2017
2018                 root = procmsg_get_thread_tree(mlist);
2019
2020                 for (gnode = root->children; gnode != NULL;
2021                      gnode = gnode->next) {
2022                         node = gtk_ctree_insert_gnode
2023                                 (ctree, NULL, node, gnode,
2024                                  summary_insert_gnode_func, summaryview);
2025                 }
2026
2027                 g_node_destroy(root);
2028
2029                 summary_thread_init(summaryview);
2030         } else {
2031                 gchar *text[N_SUMMARY_COLS];
2032
2033                 mlist = g_slist_reverse(mlist);
2034                 for (; mlist != NULL; mlist = mlist->next) {
2035                         msginfo = (MsgInfo *)mlist->data;
2036
2037                         summary_set_header(summaryview, text, msginfo);
2038
2039                         node = gtk_ctree_insert_node
2040                                 (ctree, NULL, node, text, 2,
2041                                  NULL, NULL, NULL, NULL, FALSE, FALSE);
2042                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
2043                         summary_set_marks_func(ctree, node, summaryview);
2044
2045                         if (msginfo->msgid)
2046                                 g_hash_table_insert(msgid_table,
2047                                                     msginfo->msgid, node);
2048                 }
2049                 mlist = g_slist_reverse(mlist);
2050         }
2051
2052         if (prefs_common.enable_hscrollbar &&
2053             summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
2054                 gint optimal_width;
2055
2056                 optimal_width = gtk_clist_optimal_column_width
2057                         (GTK_CLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
2058                 gtk_clist_set_column_width(GTK_CLIST(ctree),
2059                                            summaryview->col_pos[S_COL_SUBJECT],
2060                                            optimal_width);
2061         }
2062
2063         if (prefs_common.use_addr_book)
2064                 end_address_completion();
2065
2066         debug_print(_("done.\n"));
2067         STATUSBAR_POP(summaryview->mainwin);
2068         if (debug_mode) {
2069                 debug_print("\tmsgid hash table size = %d\n",
2070                             g_hash_table_size(msgid_table));
2071                 debug_print("\tsubject hash table size = %d\n",
2072                             g_hash_table_size(subject_table));
2073         }
2074 }
2075
2076 struct wcachefp
2077 {
2078         FILE *cache_fp;
2079         FILE *mark_fp;
2080 };
2081
2082 gint summary_write_cache(SummaryView *summaryview)
2083 {
2084         struct wcachefp fps;
2085         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2086         gint ver = CACHE_VERSION;
2087         gchar *buf;
2088         gchar *cachefile, *markfile;
2089         GSList * cur;
2090         gint filemode = 0;
2091         PrefsFolderItem *prefs;
2092
2093         if (!summaryview->folder_item || !summaryview->folder_item->path)
2094                 return -1;
2095
2096         if (summaryview->folder_item->folder->update_mark != NULL)
2097                 summaryview->folder_item->folder->update_mark(summaryview->folder_item->folder, summaryview->folder_item);
2098
2099         cachefile = folder_item_get_cache_file(summaryview->folder_item);
2100         g_return_val_if_fail(cachefile != NULL, -1);
2101         if ((fps.cache_fp = fopen(cachefile, "w")) == NULL) {
2102                 FILE_OP_ERROR(cachefile, "fopen");
2103                 g_free(cachefile);
2104                 return -1;
2105         }
2106         if (change_file_mode_rw(fps.cache_fp, cachefile) < 0)
2107                 FILE_OP_ERROR(cachefile, "chmod");
2108
2109         prefs = summaryview->folder_item->prefs;
2110         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2111                 /* for cache file */
2112                 filemode = prefs->folder_chmod;
2113                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
2114                 if (filemode & S_IROTH) filemode |= S_IWOTH;
2115 #if HAVE_FCHMOD
2116                 fchmod(fileno(fps.cache_fp), filemode);
2117 #else
2118                 chmod(cachefile, filemode);
2119 #endif
2120         }
2121
2122         g_free(cachefile);
2123
2124         markfile = folder_item_get_mark_file(summaryview->folder_item);
2125         if ((fps.mark_fp = fopen(markfile, "w")) == NULL) {
2126                 FILE_OP_ERROR(markfile, "fopen");
2127                 fclose(fps.cache_fp);
2128                 g_free(markfile);
2129                 return -1;
2130         }
2131         if (change_file_mode_rw(fps.mark_fp, markfile) < 0)
2132                 FILE_OP_ERROR(markfile, "chmod");
2133         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2134 #if HAVE_FCHMOD
2135                 fchmod(fileno(fps.mark_fp), filemode);
2136 #else
2137                 chmod(markfile, filemode);
2138 #endif
2139         }
2140
2141         g_free(markfile);
2142
2143         buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2144                               summaryview->folder_item->path);
2145         debug_print(buf);
2146         STATUSBAR_PUSH(summaryview->mainwin, buf);
2147         g_free(buf);
2148
2149         WRITE_CACHE_DATA_INT(ver, fps.cache_fp);
2150         ver = MARK_VERSION;
2151         WRITE_CACHE_DATA_INT(ver, fps.mark_fp);
2152
2153         gtk_ctree_pre_recursive(ctree, NULL, summary_write_cache_func, &fps);
2154
2155         for(cur = summaryview->killed_messages ; cur != NULL ;
2156             cur = g_slist_next(cur)) {
2157                 MsgInfo *msginfo = (MsgInfo *) cur->data;
2158
2159                 procmsg_write_cache(msginfo, fps.cache_fp);
2160                 procmsg_write_flags(msginfo, fps.mark_fp);
2161         }
2162
2163         fclose(fps.cache_fp);
2164         fclose(fps.mark_fp);
2165
2166         debug_print(_("done.\n"));
2167         STATUSBAR_POP(summaryview->mainwin);
2168
2169         return 0;
2170 }
2171
2172 static void summary_write_cache_func(GtkCTree *ctree, GtkCTreeNode *node,
2173                                      gpointer data)
2174 {
2175         struct wcachefp *fps = data;
2176         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
2177
2178         if (msginfo == NULL) return;
2179
2180         procmsg_write_cache(msginfo, fps->cache_fp);
2181         procmsg_write_flags(msginfo, fps->mark_fp);
2182 }
2183
2184 static void summary_set_header(SummaryView *summaryview, gchar *text[],
2185                                MsgInfo *msginfo)
2186 {
2187         static gchar date_modified[80];
2188         static gchar *to = NULL;
2189         static gchar *from_name = NULL;
2190         static gchar col_number[11];
2191         static gchar col_score[11];
2192         gint *col_pos = summaryview->col_pos;
2193
2194         text[col_pos[S_COL_MARK]]   = NULL;
2195         text[col_pos[S_COL_UNREAD]] = NULL;
2196         text[col_pos[S_COL_MIME]]   = NULL;
2197         text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
2198         text[col_pos[S_COL_SIZE]]   = to_human_readable(msginfo->size);
2199
2200 #if 0
2201         text[col_pos[S_COL_SCORE]]  = itos_buf(col_score, msginfo->threadscore);
2202 #else
2203         text[col_pos[S_COL_SCORE]]  = itos_buf(col_score, msginfo->score);
2204 #endif
2205
2206         if (msginfo->date_t) {
2207                 procheader_date_get_localtime(date_modified,
2208                                               sizeof(date_modified),
2209                                               msginfo->date_t);
2210                 text[col_pos[S_COL_DATE]] = date_modified;
2211         } else if (msginfo->date)
2212                 text[col_pos[S_COL_DATE]] = msginfo->date;
2213         else
2214                 text[col_pos[S_COL_DATE]] = _("(No Date)");
2215
2216         text[S_COL_FROM] = msginfo->fromname ? msginfo->fromname :
2217                 _("(No From)");
2218         if (prefs_common.swap_from && msginfo->from && msginfo->to &&
2219             !MSG_IS_NEWS(msginfo->flags)) {
2220                 gchar *from;
2221
2222                 Xalloca(from, strlen(msginfo->from) + 1, return);
2223                 strcpy(from, msginfo->from);
2224                 extract_address(from);
2225                 if (account_find_from_address(from)) {
2226                         g_free(to);
2227                         to = g_strconcat("-->", msginfo->to, NULL);
2228                         text[col_pos[S_COL_FROM]] = to;
2229                 }
2230         }
2231
2232         if ((text[S_COL_FROM] != to) && prefs_common.use_addr_book &&
2233             msginfo->from) {
2234                 gint count;
2235                 gchar *from;
2236   
2237                 Xalloca(from, strlen(msginfo->from) + 1, return);
2238                 strcpy(from, msginfo->from);
2239                 extract_address(from);
2240                 if (*from) {
2241                         count = complete_address(from);
2242                         if (count > 1) {
2243                                 g_free(from_name);
2244                                 from = get_complete_address(1);
2245                                 from_name = procheader_get_fromname(from);
2246                                 g_free(from);
2247                                 text[S_COL_FROM] = from_name;
2248                         }
2249                 }
2250         }
2251
2252         text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
2253                 _("(No Subject)");
2254 }
2255
2256 #define CHANGE_FLAGS(msginfo) \
2257 { \
2258 if (msginfo->folder->folder->change_flags != NULL) \
2259 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
2260                                       msginfo->folder, \
2261                                       msginfo); \
2262 }
2263
2264 static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row,
2265                                 gboolean new_window)
2266 {
2267         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2268         MsgInfo *msginfo;
2269         gchar *filename;
2270
2271         if (!new_window && summaryview->displayed == row) return;
2272         g_return_if_fail(row != NULL);
2273
2274         if (summary_is_locked(summaryview)) return;
2275         summary_lock(summaryview);
2276
2277         STATUSBAR_POP(summaryview->mainwin);
2278         GTK_EVENTS_FLUSH();
2279
2280         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2281
2282         filename = procmsg_get_message_file(msginfo);
2283         if (!filename) {
2284                 summary_unlock(summaryview);
2285                 return;
2286         }
2287         g_free(filename);
2288
2289         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2290                 summaryview->newmsgs--;
2291         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2292                 summaryview->unread--;
2293         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2294                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2295                 CHANGE_FLAGS(msginfo);
2296                 summary_set_row_marks(summaryview, row);
2297                 gtk_clist_thaw(GTK_CLIST(ctree));
2298                 summary_status_show(summaryview);
2299         }
2300
2301         if (new_window) {
2302                 MessageView *msgview;
2303
2304                 msgview = messageview_create_with_new_window();
2305                 messageview_show(msgview, msginfo);
2306         } else {
2307                 MessageView *msgview;
2308
2309                 msgview = summaryview->messageview;
2310
2311                 summaryview->displayed = row;
2312                 if (!summaryview->msg_is_toggled_on)
2313                         summary_toggle_view(summaryview);
2314                 messageview_show(msgview, msginfo);
2315                 if (msgview->type == MVIEW_TEXT ||
2316                     (msgview->type == MVIEW_MIME &&
2317                      GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL))
2318                         gtk_widget_grab_focus(summaryview->ctree);
2319                 GTK_EVENTS_FLUSH();
2320                 gtkut_ctree_node_move_if_on_the_edge(ctree, row);
2321         }
2322
2323         if (GTK_WIDGET_VISIBLE(summaryview->headerwin->window))
2324                 header_window_show(summaryview->headerwin, msginfo);
2325
2326         summary_unlock(summaryview);
2327 }
2328
2329 void summary_redisplay_msg(SummaryView *summaryview)
2330 {
2331         GtkCTreeNode *node;
2332
2333         if (summaryview->displayed) {
2334                 node = summaryview->displayed;
2335                 summaryview->displayed = NULL;
2336                 summary_display_msg(summaryview, node, FALSE);
2337         }
2338 }
2339
2340 void summary_open_msg(SummaryView *summaryview)
2341 {
2342         if (!summaryview->selected) return;
2343
2344         summary_display_msg(summaryview, summaryview->selected, TRUE);
2345 }
2346
2347 void summary_view_source(SummaryView * summaryview)
2348 {
2349         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2350         MsgInfo *msginfo;
2351         SourceWindow *srcwin;
2352
2353         if (!summaryview->selected) return;
2354
2355         srcwin = source_window_create();
2356         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2357         source_window_show_msg(srcwin, msginfo);
2358         source_window_show(srcwin);
2359 }
2360
2361 void summary_reedit(SummaryView *summaryview)
2362 {
2363         MsgInfo *msginfo;
2364
2365         if (!summaryview->selected) return;
2366         if (!summaryview->folder_item ||
2367             (summaryview->folder_item->stype != F_DRAFT &&
2368              summaryview->folder_item->stype != F_OUTBOX &&
2369              summaryview->folder_item->stype != F_QUEUE)) return;
2370
2371         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2372                                               summaryview->selected);
2373         if (!msginfo) return;
2374
2375         compose_reedit(msginfo);
2376 }
2377
2378 void summary_step(SummaryView *summaryview, GtkScrollType type)
2379 {
2380         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2381
2382         if (type == GTK_SCROLL_STEP_FORWARD) {
2383                 GtkCTreeNode *node;
2384                 node = gtkut_ctree_node_next(ctree, summaryview->selected);
2385                 if (node)
2386                         gtkut_ctree_expand_parent_all(ctree, node);
2387         }
2388
2389         gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
2390                                 type, 0.0);
2391         
2392         if (summaryview->msg_is_toggled_on)
2393                 summary_display_msg(summaryview, summaryview->selected, FALSE);
2394 }
2395
2396 static void summary_toggle_view(SummaryView *summaryview)
2397 {
2398         MainWindow *mainwin = summaryview->mainwin;
2399         union CompositeWin *cwin = &mainwin->win;
2400         GtkWidget *vpaned = NULL;
2401         GtkWidget *container = NULL;
2402
2403         switch (mainwin->type) {
2404         case SEPARATE_NONE:
2405                 vpaned = cwin->sep_none.vpaned;
2406                 container = cwin->sep_none.hpaned;
2407                 break;
2408         case SEPARATE_FOLDER:
2409                 vpaned = cwin->sep_folder.vpaned;
2410                 container = mainwin->vbox_body;
2411                 break;
2412         case SEPARATE_MESSAGE:
2413         case SEPARATE_BOTH:
2414                 return;
2415         }
2416
2417         if (vpaned->parent != NULL) {
2418                 summaryview->msg_is_toggled_on = FALSE;
2419                 summaryview->displayed = NULL;
2420                 gtk_widget_ref(vpaned);
2421                 gtk_container_remove(GTK_CONTAINER(container), vpaned);
2422                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), container);
2423                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2424                               GTK_ARROW_UP, GTK_SHADOW_OUT);
2425         } else {
2426                 summaryview->msg_is_toggled_on = TRUE;
2427                 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), vpaned);
2428                 gtk_container_add(GTK_CONTAINER(container), vpaned);
2429                 gtk_widget_unref(vpaned);
2430                 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2431                               GTK_ARROW_DOWN, GTK_SHADOW_OUT);
2432         }
2433
2434         gtk_widget_grab_focus(summaryview->ctree);
2435 }
2436
2437 static gboolean summary_search_unread_recursive(GtkCTree *ctree,
2438                                                 GtkCTreeNode *node)
2439 {
2440         MsgInfo *msginfo;
2441
2442         if (node) {
2443                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
2444                 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2445                         return TRUE;
2446                 node = GTK_CTREE_ROW(node)->children;
2447         } else
2448                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2449
2450         while (node) {
2451                 if (summary_search_unread_recursive(ctree, node) == TRUE)
2452                         return TRUE;
2453                 node = GTK_CTREE_ROW(node)->sibling;
2454         }
2455
2456         return FALSE;
2457 }
2458
2459 static gboolean summary_have_unread_children(SummaryView *summaryview,
2460                                              GtkCTreeNode *node)
2461 {
2462         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2463
2464         if (!node) return FALSE;
2465
2466         node = GTK_CTREE_ROW(node)->children;
2467
2468         while (node) {
2469                 if (summary_search_unread_recursive(ctree, node) == TRUE)
2470                         return TRUE;
2471                 node = GTK_CTREE_ROW(node)->sibling;
2472         }
2473
2474         return FALSE;
2475 }
2476
2477 static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2478 {
2479         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2480         GtkStyle *style = NULL;
2481         MsgInfo *msginfo;
2482         MsgFlags flags;
2483         gint *col_pos = summaryview->col_pos;
2484
2485         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2486         if (!msginfo) return;
2487
2488         flags = msginfo->flags;
2489
2490         gtk_ctree_node_set_foreground(ctree, row, NULL);
2491
2492         /* set new/unread column */
2493         if (MSG_IS_IGNORE_THREAD(flags)) {
2494                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2495                                           ignorethreadxpm, ignorethreadxpmmask);
2496         } else if (MSG_IS_NEW(flags)) {
2497                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2498                                           newxpm, newxpmmask);
2499         } else if (MSG_IS_UNREAD(flags)) {
2500                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2501                                           unreadxpm, unreadxpmmask);
2502         } else if (MSG_IS_REPLIED(flags)) {
2503                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2504                                           repliedxpm, repliedxpmmask);
2505         } else if (MSG_IS_FORWARDED(flags)) {
2506                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2507                                           forwardedxpm, forwardedxpmmask);
2508         } else {
2509                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_UNREAD],
2510                                         NULL);
2511         }
2512
2513         if (prefs_common.bold_unread &&
2514             ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
2515              (!GTK_CTREE_ROW(row)->expanded &&
2516               GTK_CTREE_ROW(row)->children &&
2517               summary_have_unread_children(summaryview, row))))
2518                 style = bold_style;
2519
2520         /* set mark column */
2521         if (MSG_IS_DELETED(flags)) {
2522                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2523                                           deletedxpm, deletedxpmmask);
2524                 if (style)
2525                         style = bold_deleted_style;
2526                 else {
2527                         style = small_deleted_style;
2528                 }
2529                         gtk_ctree_node_set_foreground
2530                                 (ctree, row, &summaryview->color_dim);
2531         } else if (MSG_IS_MARKED(flags)) {
2532                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2533                                           markxpm, markxpmmask);
2534         } else if (MSG_IS_MOVE(flags)) {
2535                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "o");
2536                 if (style)
2537                         style = bold_marked_style;
2538                 else {
2539                         style = small_marked_style;
2540                 }
2541                         gtk_ctree_node_set_foreground
2542                                 (ctree, row, &summaryview->color_marked);
2543         } else if (MSG_IS_COPY(flags)) {
2544                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "O");
2545                 if (style)
2546                         style = bold_marked_style;
2547                 else {
2548                         style = small_marked_style;
2549                 }
2550                         gtk_ctree_node_set_foreground
2551                                 (ctree, row, &summaryview->color_marked);
2552         }
2553         else if ((global_scoring ||
2554                   summaryview->folder_item->prefs->scoring) &&
2555                  (msginfo->score >= summaryview->important_score) &&
2556                  (MSG_IS_MARKED(msginfo->flags) || MSG_IS_MOVE(msginfo->flags) || MSG_IS_COPY(msginfo->flags))) {
2557                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "!");
2558                 gtk_ctree_node_set_foreground(ctree, row,
2559                                               &summaryview->color_important);
2560         } else {
2561                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], NULL);
2562         }
2563
2564         if (MSG_IS_MIME(flags)) {
2565                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
2566                                           clipxpm, clipxpmmask);
2567         } else {
2568                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MIME], NULL);
2569         }
2570         if (!style)
2571                 style = small_style;
2572
2573         gtk_ctree_node_set_row_style(ctree, row, style);
2574
2575         if (MSG_GET_COLORLABEL(flags))
2576                 summary_set_colorlabel_color(ctree, row,
2577                                              MSG_GET_COLORLABEL_VALUE(flags));
2578 }
2579
2580 void summary_set_marks_selected(SummaryView *summaryview)
2581 {
2582         summary_set_row_marks(summaryview, summaryview->selected);
2583 }
2584
2585 static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
2586 {
2587         gboolean changed = FALSE;
2588         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2589         MsgInfo *msginfo;
2590
2591         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2592         if (MSG_IS_DELETED(msginfo->flags))
2593                 summaryview->deleted--;
2594         if (MSG_IS_MOVE(msginfo->flags)) {
2595                 summaryview->moved--;
2596                 changed = TRUE;
2597         }
2598         if (MSG_IS_COPY(msginfo->flags)) {
2599                 summaryview->copied--;
2600                 changed = TRUE;
2601         }
2602         if (changed && !prefs_common.immediate_exec) {
2603                 msginfo->to_folder->op_count--;
2604                 if (msginfo->to_folder->op_count == 0)
2605                         folderview_update_item(msginfo->to_folder, 0);
2606         }
2607         msginfo->to_folder = NULL;
2608         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2609         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2610         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2611         CHANGE_FLAGS(msginfo);
2612         summary_set_row_marks(summaryview, row);
2613         debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2614 }
2615
2616 void summary_mark(SummaryView *summaryview)
2617 {
2618         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2619         GList *cur;
2620
2621         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2622                 summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
2623
2624         /* summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); */
2625         summary_status_show(summaryview);
2626 }
2627
2628 static void summary_mark_row_as_read(SummaryView *summaryview,
2629                                      GtkCTreeNode *row)
2630 {
2631         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2632         MsgInfo *msginfo;
2633
2634         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2635         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2636                 summaryview->newmsgs--;
2637         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2638                 summaryview->unread--;
2639         if (MSG_IS_NEW(msginfo->flags) ||
2640             MSG_IS_UNREAD(msginfo->flags)) {
2641                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2642                 CHANGE_FLAGS(msginfo);
2643                 summary_set_row_marks(summaryview, row);
2644                 debug_print(_("Message %d is marked as read\n"),
2645                             msginfo->msgnum);
2646         }
2647 }
2648
2649 void summary_mark_as_read(SummaryView *summaryview)
2650 {
2651         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2652         GList *cur;
2653
2654         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2655                 summary_mark_row_as_read(summaryview,
2656                                          GTK_CTREE_NODE(cur->data));
2657
2658         summary_status_show(summaryview);
2659 }
2660
2661 void summary_mark_all_read(SummaryView *summaryview)
2662 {
2663         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2664         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2665         GtkCTreeNode *node;
2666
2667         gtk_clist_freeze(clist);
2668         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2669              node = gtkut_ctree_node_next(ctree, node))
2670                 summary_mark_row_as_read(summaryview, node);
2671         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2672              node = gtkut_ctree_node_next(ctree, node)) {
2673                 if (!GTK_CTREE_ROW(node)->expanded)
2674                         summary_set_row_marks(summaryview, node);
2675         }
2676         gtk_clist_thaw(clist);
2677
2678         summary_status_show(summaryview);
2679 }
2680
2681 static void summary_mark_row_as_unread(SummaryView *summaryview,
2682                                        GtkCTreeNode *row)
2683 {
2684         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2685         MsgInfo *msginfo;
2686
2687         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2688         if (MSG_IS_DELETED(msginfo->flags)) {
2689                 msginfo->to_folder = NULL;
2690                 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2691                 summaryview->deleted--;
2692         }
2693         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
2694         if (!MSG_IS_UNREAD(msginfo->flags)) {
2695                 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
2696                 summaryview->unread++;
2697                 debug_print(_("Message %d is marked as unread\n"),
2698                             msginfo->msgnum);
2699         }
2700
2701         CHANGE_FLAGS(msginfo);
2702
2703         summary_set_row_marks(summaryview, row);
2704 }
2705
2706 void summary_mark_as_unread(SummaryView *summaryview)
2707 {
2708         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2709         GList *cur;
2710
2711         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2712                 summary_mark_row_as_unread(summaryview,
2713                                            GTK_CTREE_NODE(cur->data));
2714
2715         summary_status_show(summaryview);
2716 }
2717
2718 static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
2719 {
2720         gboolean changed = FALSE;
2721         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2722         MsgInfo *msginfo;
2723
2724         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2725
2726         if (MSG_IS_DELETED(msginfo->flags)) return;
2727
2728         if (MSG_IS_MOVE(msginfo->flags)) {
2729                 summaryview->moved--;
2730                 changed = TRUE;
2731         }
2732         if (MSG_IS_COPY(msginfo->flags)) {
2733                 summaryview->copied--;
2734                 changed = TRUE;
2735         }
2736         if (changed && !prefs_common.immediate_exec) {
2737                 msginfo->to_folder->op_count--;
2738                 if (msginfo->to_folder->op_count == 0)
2739                         folderview_update_item(msginfo->to_folder, 0);
2740         }
2741         msginfo->to_folder = NULL;
2742         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2743         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2744         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2745         CHANGE_FLAGS(msginfo);
2746         summaryview->deleted++;
2747
2748         if (!prefs_common.immediate_exec && 
2749             summaryview->folder_item->stype != F_TRASH)
2750                 summary_set_row_marks(summaryview, row);
2751
2752         debug_print(_("Message %s/%d is set to delete\n"),
2753                     msginfo->folder->path, msginfo->msgnum);
2754 }
2755
2756 void summary_delete(SummaryView *summaryview)
2757 {
2758         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2759         FolderItem *item = summaryview->folder_item;
2760         GList *cur;
2761         GtkCTreeNode *sel_last = NULL;
2762         GtkCTreeNode *node;
2763
2764         if (!item || item->folder->type == F_NEWS) return;
2765
2766         if (summary_is_locked(summaryview)) return;
2767
2768         /* if current folder is trash, ask for confirmation */
2769         if (item->stype == F_TRASH) {
2770                 AlertValue aval;
2771
2772                 aval = alertpanel(_("Delete message(s)"),
2773                                   _("Do you really want to delete message(s) from the trash?"),
2774                                   _("Yes"), _("No"), NULL);
2775                 if (aval != G_ALERTDEFAULT) return;
2776         }
2777
2778         /* next code sets current row focus right. We need to find a row
2779          * that is not deleted. */
2780         for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
2781                 sel_last = GTK_CTREE_NODE(cur->data);
2782                 summary_delete_row(summaryview, sel_last);
2783         }
2784
2785         node = summary_find_next_msg(summaryview, sel_last);
2786         if (!node)
2787                 node = summary_find_prev_msg(summaryview, sel_last);
2788         if (node == gtkut_ctree_node_next(ctree, sel_last))
2789                 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2790         else if (node == GTK_CTREE_NODE_PREV(sel_last))
2791                 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
2792         else
2793                 summary_select_node
2794                         (summaryview, node,
2795                          summaryview->msg_is_toggled_on);
2796
2797         if (prefs_common.immediate_exec || item->stype == F_TRASH)
2798                 summary_execute(summaryview);
2799         else
2800                 summary_status_show(summaryview);
2801 }
2802
2803 void summary_delete_duplicated(SummaryView *summaryview)
2804 {
2805         if (!summaryview->folder_item ||
2806             summaryview->folder_item->folder->type == F_NEWS) return;
2807         if (summaryview->folder_item->stype == F_TRASH) return;
2808
2809         main_window_cursor_wait(summaryview->mainwin);
2810         debug_print(_("Deleting duplicated messages..."));
2811         STATUSBAR_PUSH(summaryview->mainwin,
2812                        _("Deleting duplicated messages..."));
2813
2814         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2815                                 GTK_CTREE_FUNC(summary_delete_duplicated_func),
2816                                 summaryview);
2817
2818         if (prefs_common.immediate_exec)
2819                 summary_execute(summaryview);
2820         else
2821                 summary_status_show(summaryview);
2822
2823         debug_print(_("done.\n"));
2824         STATUSBAR_POP(summaryview->mainwin);
2825         main_window_cursor_normal(summaryview->mainwin);
2826 }
2827
2828 static void summary_delete_duplicated_func(GtkCTree *ctree, GtkCTreeNode *node,
2829                                            SummaryView *summaryview)
2830 {
2831         GtkCTreeNode *found;
2832         MsgInfo *msginfo = GTK_CTREE_ROW(node)->row.data;
2833
2834         if (!msginfo->msgid || !*msginfo->msgid) return;
2835
2836         found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
2837
2838         if (found && found != node)
2839                 summary_delete_row(summaryview, node);
2840 }
2841
2842 static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
2843 {
2844         gboolean changed = FALSE;
2845         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2846         MsgInfo *msginfo;
2847
2848         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2849         if (MSG_IS_DELETED(msginfo->flags))
2850                 summaryview->deleted--;
2851         if (MSG_IS_MOVE(msginfo->flags)) {
2852                 summaryview->moved--;
2853                 changed = TRUE;
2854         }
2855         if (MSG_IS_COPY(msginfo->flags)) {
2856                 summaryview->copied--;
2857                 changed = TRUE;
2858         }
2859         if (changed && !prefs_common.immediate_exec) {
2860                 msginfo->to_folder->op_count--;
2861                 if (msginfo->to_folder->op_count == 0)
2862                         folderview_update_item(msginfo->to_folder, 0);
2863         }
2864         msginfo->to_folder = NULL;
2865         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2866         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2867         CHANGE_FLAGS(msginfo);
2868         summary_set_row_marks(summaryview, row);
2869
2870         debug_print(_("Message %s/%d is unmarked\n"),
2871                     msginfo->folder->path, msginfo->msgnum);
2872 }
2873
2874 void summary_unmark(SummaryView *summaryview)
2875 {
2876         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2877         GList *cur;
2878
2879         for (cur = GTK_CLIST(ctree)->selection; cur != NULL;
2880              cur = cur->next)
2881                 summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
2882
2883         summary_status_show(summaryview);
2884 }
2885
2886 static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2887                                 FolderItem *to_folder)
2888 {
2889         gboolean changed = FALSE;
2890         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2891         MsgInfo *msginfo;
2892
2893         g_return_if_fail(to_folder != NULL);
2894
2895         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2896         if (MSG_IS_MOVE(msginfo->flags)) {
2897                 if (!prefs_common.immediate_exec) {
2898                         msginfo->to_folder->op_count--;
2899                         if (msginfo->to_folder->op_count == 0) {
2900                                 folderview_update_item(msginfo->to_folder, 0);
2901                                 changed = TRUE;
2902                         }
2903                 }
2904         }
2905         msginfo->to_folder = to_folder;
2906         if (MSG_IS_DELETED(msginfo->flags))
2907                 summaryview->deleted--;
2908         if (MSG_IS_COPY(msginfo->flags)) {
2909                 summaryview->copied--;
2910                 if (!prefs_common.immediate_exec)
2911                         msginfo->to_folder->op_count--;
2912         }
2913         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2914         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2915         if (!MSG_IS_MOVE(msginfo->flags)) {
2916                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2917                 summaryview->moved++;
2918                 changed = TRUE;
2919         }
2920         if (!prefs_common.immediate_exec) {
2921                 summary_set_row_marks(summaryview, row);
2922                 if (changed) {
2923                         msginfo->to_folder->op_count++;
2924                         if (msginfo->to_folder->op_count == 1)
2925                                 folderview_update_item(msginfo->to_folder, 0);
2926                 }
2927         }
2928
2929         debug_print(_("Message %d is set to move to %s\n"),
2930                     msginfo->msgnum, to_folder->path);
2931 }
2932
2933 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2934 {
2935         GList *cur;
2936
2937         if (!to_folder) return;
2938         if (!summaryview->folder_item ||
2939             summaryview->folder_item->folder->type == F_NEWS) return;
2940
2941         if (summary_is_locked(summaryview)) return;
2942
2943         if (summaryview->folder_item == to_folder) {
2944                 alertpanel_notice(_("Destination is same as current folder."));
2945                 return;
2946         }
2947
2948         for (cur = GTK_CLIST(summaryview->ctree)->selection;
2949              cur != NULL; cur = cur->next)
2950                 summary_move_row_to
2951                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2952
2953         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2954
2955         if (prefs_common.immediate_exec)
2956                 summary_execute(summaryview);
2957         else {
2958                 summary_status_show(summaryview);
2959
2960                 folderview_update_item(to_folder, 0);
2961         }
2962 }
2963
2964 void summary_move_to(SummaryView *summaryview)
2965 {
2966         FolderItem *to_folder;
2967
2968         if (!summaryview->folder_item ||
2969             summaryview->folder_item->folder->type == F_NEWS) return;
2970
2971         to_folder = foldersel_folder_sel(NULL, NULL);
2972         summary_move_selected_to(summaryview, to_folder);
2973 }
2974
2975 static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2976                                 FolderItem *to_folder)
2977 {
2978         gboolean changed = FALSE;
2979         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2980         MsgInfo *msginfo;
2981
2982         g_return_if_fail(to_folder != NULL);
2983
2984         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2985         if (MSG_IS_COPY(msginfo->flags)) {
2986                 if (!prefs_common.immediate_exec) {
2987                         msginfo->to_folder->op_count--;
2988                         if (msginfo->to_folder->op_count == 0) {
2989                                 folderview_update_item(msginfo->to_folder, 0);
2990                                 changed = TRUE;
2991                         }
2992                 }
2993         }
2994         msginfo->to_folder = to_folder;
2995         if (MSG_IS_DELETED(msginfo->flags))
2996                 summaryview->deleted--;
2997         if (MSG_IS_MOVE(msginfo->flags)) {
2998                 summaryview->moved--;
2999                 if (!prefs_common.immediate_exec)
3000                         msginfo->to_folder->op_count--;
3001         }
3002         MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
3003         MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3004         if (!MSG_IS_COPY(msginfo->flags)) {
3005                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3006                 summaryview->copied++;
3007                 changed = TRUE;
3008         }
3009         if (!prefs_common.immediate_exec) {
3010                 summary_set_row_marks(summaryview, row);
3011                 if (changed) {
3012                         msginfo->to_folder->op_count++;
3013                         if (msginfo->to_folder->op_count == 1)
3014                                 folderview_update_item(msginfo->to_folder, 0);
3015                 }
3016         }
3017
3018         debug_print(_("Message %d is set to copy to %s\n"),
3019                     msginfo->msgnum, to_folder->path);
3020 }
3021
3022 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3023 {
3024         GList *cur;
3025
3026         if (!to_folder) return;
3027         if (!summaryview->folder_item ||
3028             summaryview->folder_item->folder->type == F_NEWS) return;
3029
3030         if (summary_is_locked(summaryview)) return;
3031
3032         if (summaryview->folder_item == to_folder) {
3033                 alertpanel_notice
3034                         (_("Destination to copy is same as current folder."));
3035                 return;
3036         }
3037
3038         for (cur = GTK_CLIST(summaryview->ctree)->selection;
3039              cur != NULL; cur = cur->next)
3040                 summary_copy_row_to
3041                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
3042
3043         summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3044
3045         if (prefs_common.immediate_exec)
3046                 summary_execute(summaryview);
3047         else {
3048                 summary_status_show(summaryview);
3049
3050                 folderview_update_item(to_folder, 0);
3051         }
3052 }
3053
3054 void summary_copy_to(SummaryView *summaryview)
3055 {
3056         FolderItem *to_folder;
3057
3058         if (!summaryview->folder_item ||
3059             summaryview->folder_item->folder->type == F_NEWS) return;
3060
3061         to_folder = foldersel_folder_sel(NULL, NULL);
3062         summary_copy_selected_to(summaryview, to_folder);
3063 }
3064
3065 void summary_add_address(SummaryView *summaryview)
3066 {
3067         MsgInfo *msginfo;
3068         gchar *from;
3069
3070         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
3071                                               summaryview->selected);
3072         if (!msginfo) return;
3073
3074         Xstrdup_a(from, msginfo->from, return);
3075         eliminate_address_comment(from);
3076         extract_address(from);
3077         addressbook_add_contact(msginfo->fromname, from, NULL);
3078 }
3079
3080 void summary_select_all(SummaryView *summaryview)
3081 {
3082         if (summaryview->messages >= 500) {
3083                 STATUSBAR_PUSH(summaryview->mainwin,
3084                                _("Selecting all messages..."));
3085                 main_window_cursor_wait(summaryview->mainwin);
3086         }
3087
3088         gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
3089
3090         if (summaryview->messages >= 500) {
3091                 STATUSBAR_POP(summaryview->mainwin);
3092                 main_window_cursor_normal(summaryview->mainwin);
3093         }
3094 }
3095
3096 void summary_unselect_all(SummaryView *summaryview)
3097 {
3098         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
3099 }
3100
3101 void summary_save_as(SummaryView *summaryview)
3102 {
3103         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3104         MsgInfo *msginfo;
3105         gchar *src, *dest;
3106
3107         if (!summaryview->selected) return;
3108         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
3109         if (!msginfo) return;
3110
3111         dest = filesel_select_file(_("Save as"), NULL);
3112         if (!dest) return;
3113         if (is_file_exist(dest)) {
3114                 AlertValue aval;
3115
3116                 aval = alertpanel(_("Overwrite"),
3117                                   _("Overwrite existing file?"),
3118                                   _("OK"), _("Cancel"), NULL);
3119                 if (G_ALERTDEFAULT != aval) return;
3120         }
3121
3122         src = procmsg_get_message_file(msginfo);
3123         copy_file(src, dest);
3124         g_free(src);
3125 }
3126
3127 void summary_print(SummaryView *summaryview)
3128 {
3129         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3130         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3131         MsgInfo *msginfo;
3132         GList *cur;
3133         gchar *cmdline;
3134         gchar *p;
3135
3136         if (clist->selection == NULL) return;
3137
3138         cmdline = input_dialog(_("Print"),
3139                                _("Enter the print command line:\n"
3140                                  "(`%s' will be replaced with file name)"),
3141                                prefs_common.print_cmd);
3142         if (!cmdline) return;
3143         if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
3144             strchr(p + 2, '%')) {
3145                 alertpanel_error(_("Print command line is invalid:\n`%s'"),
3146                                  cmdline);
3147                 g_free(cmdline);
3148                 return;
3149         }
3150
3151         for (cur = clist->selection; cur != NULL; cur = cur->next) {
3152                 msginfo = gtk_ctree_node_get_row_data
3153                         (ctree, GTK_CTREE_NODE(cur->data));
3154                 if (msginfo) procmsg_print_message(msginfo, cmdline);
3155         }
3156
3157         g_free(cmdline);
3158 }
3159
3160 gboolean summary_execute(SummaryView *summaryview)
3161 {
3162         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3163         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3164         GtkCTreeNode *node, *next;
3165
3166         if (!summaryview->folder_item ||
3167             summaryview->folder_item->folder->type == F_NEWS) return FALSE;
3168
3169         if (summary_is_locked(summaryview)) return FALSE;
3170         summary_lock(summaryview);
3171
3172         gtk_clist_freeze(clist);
3173
3174         if (summaryview->folder_item->threaded)
3175                 summary_unthread_for_exec(summaryview);
3176
3177         summary_execute_move(summaryview);
3178         summary_execute_copy(summaryview);
3179         summary_execute_delete(summaryview);
3180
3181         node = GTK_CTREE_NODE(clist->row_list);
3182         while (node != NULL) {
3183                 next = gtkut_ctree_node_next(ctree, node);
3184                 if (gtk_ctree_node_get_row_data(ctree, node) == NULL) {
3185                         if (node == summaryview->displayed) {
3186                                 messageview_clear(summaryview->messageview);
3187                                 summaryview->displayed = NULL;
3188                         }
3189                         if (GTK_CTREE_ROW(node)->children != NULL)
3190                                 g_warning("summary_execute(): children != NULL\n");
3191                         else
3192                                 gtk_ctree_remove_node(ctree, node);
3193                 }
3194                 node = next;
3195         }
3196
3197         if (summaryview->folder_item->threaded)
3198                 summary_thread_build(summaryview);
3199
3200         summaryview->selected = clist->selection ?
3201                 GTK_CTREE_NODE(clist->selection->data) : NULL;
3202
3203         if (!GTK_CLIST(summaryview->ctree)->row_list) {
3204                 menu_set_insensitive_all
3205                         (GTK_MENU_SHELL(summaryview->popupmenu));
3206                 gtk_widget_grab_focus(summaryview->folderview->ctree);
3207         } else
3208                 gtk_widget_grab_focus(summaryview->ctree);
3209
3210         summary_update_status(summaryview);
3211         summary_status_show(summaryview);
3212
3213         summary_write_cache(summaryview);
3214
3215         gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
3216
3217         gtk_clist_thaw(clist);
3218
3219         summary_unlock(summaryview);
3220         return TRUE;
3221 }
3222
3223 static void summary_execute_move(SummaryView *summaryview)
3224 {
3225         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3226         GSList *cur;
3227
3228         summaryview->folder_table = g_hash_table_new(NULL, NULL);
3229
3230         /* search moving messages and execute */
3231         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
3232                                 summaryview);
3233
3234         if (summaryview->mlist) {
3235                 procmsg_move_messages(summaryview->mlist);
3236
3237                 folder_item_scan_foreach(summaryview->folder_table);
3238                 folderview_update_item_foreach(summaryview->folder_table);
3239
3240                 for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
3241                         procmsg_msginfo_free((MsgInfo *)cur->data);
3242                 g_slist_free(summaryview->mlist);
3243                 summaryview->mlist = NULL;
3244         }
3245
3246         g_hash_table_destroy(summaryview->folder_table);
3247         summaryview->folder_table = NULL;
3248 }
3249
3250 static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
3251                                       gpointer data)
3252 {
3253         SummaryView *summaryview = data;
3254         MsgInfo *msginfo;
3255
3256         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3257
3258         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
3259                 g_hash_table_insert(summaryview->folder_table,
3260                                     msginfo->to_folder, GINT_TO_POINTER(1));
3261
3262                 summaryview->mlist =
3263                         g_slist_append(summaryview->mlist, msginfo);
3264                 gtk_ctree_node_set_row_data(ctree, node, NULL);
3265
3266                 if (msginfo->msgid && *msginfo->msgid &&
3267                     node == g_hash_table_lookup(summaryview->msgid_table,
3268                                                 msginfo->msgid))
3269                         g_hash_table_remove(summaryview->msgid_table,
3270                                             msginfo->msgid);
3271         }
3272 }
3273
3274 static void summary_execute_copy(SummaryView *summaryview)
3275 {
3276         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3277
3278         summaryview->folder_table = g_hash_table_new(NULL, NULL);
3279
3280         /* search copying messages and execute */
3281         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
3282                                 summaryview);
3283
3284         if (summaryview->mlist) {
3285                 procmsg_copy_messages(summaryview->mlist);
3286
3287                 folder_item_scan_foreach(summaryview->folder_table);
3288                 folderview_update_item_foreach(summaryview->folder_table);
3289
3290                 g_slist_free(summaryview->mlist);
3291                 summaryview->mlist = NULL;
3292         }
3293
3294         g_hash_table_destroy(summaryview->folder_table);
3295         summaryview->folder_table = NULL;
3296 }
3297
3298 static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
3299                                       gpointer data)
3300 {
3301         SummaryView *summaryview = data;
3302         MsgInfo *msginfo;
3303
3304         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3305
3306         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
3307                 g_hash_table_insert(summaryview->folder_table,
3308                                     msginfo->to_folder, GINT_TO_POINTER(1));
3309
3310                 summaryview->mlist =
3311                         g_slist_append(summaryview->mlist, msginfo);
3312
3313                 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3314                 summary_set_row_marks(summaryview, node);
3315         }
3316 }
3317
3318 static void summary_execute_delete(SummaryView *summaryview)
3319 {
3320         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3321         FolderItem *trash;
3322         GSList *cur;
3323
3324         trash = summaryview->folder_item->folder->trash;
3325         if (summaryview->folder_item->folder->type == F_MH) {
3326                 g_return_if_fail(trash != NULL);
3327         }
3328
3329         /* search deleting messages and execute */
3330         gtk_ctree_pre_recursive
3331                 (ctree, NULL, summary_execute_delete_func, summaryview);
3332
3333         if (!summaryview->mlist) return;
3334
3335         if (summaryview->folder_item != trash)
3336                 folder_item_move_msgs_with_dest(trash, summaryview->mlist);
3337         else
3338                 folder_item_remove_msgs(trash, summaryview->mlist);
3339
3340         for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
3341                 procmsg_msginfo_free((MsgInfo *)cur->data);
3342
3343         g_slist_free(summaryview->mlist);
3344         summaryview->mlist = NULL;
3345
3346         if (summaryview->folder_item != trash) {
3347                 folder_item_scan(trash);
3348                 folderview_update_item(trash, FALSE);
3349         }
3350 }
3351
3352 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
3353                                         gpointer data)
3354 {
3355         SummaryView *summaryview = data;
3356         MsgInfo *msginfo;
3357
3358         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3359
3360         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
3361                 summaryview->mlist =
3362                         g_slist_append(summaryview->mlist, msginfo);
3363                 gtk_ctree_node_set_row_data(ctree, node, NULL);
3364
3365                 if (msginfo->msgid && *msginfo->msgid &&
3366                     node == g_hash_table_lookup(summaryview->msgid_table,
3367                                                 msginfo->msgid))
3368                         g_hash_table_remove(summaryview->msgid_table,
3369                                             msginfo->msgid);
3370