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