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