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