90698bb5066d45fc47a76095ef0f09884363c2aa
[claws.git] / src / summaryview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtkscrolledwindow.h>
26 #include <gtk/gtkwidget.h>
27 #include <gtk/gtkpixmap.h>
28 #include <gtk/gtkctree.h>
29 #include <gtk/gtkcontainer.h>
30 #include <gtk/gtksignal.h>
31 #include <gtk/gtktext.h>
32 #include <gtk/gtkmenu.h>
33 #include <gtk/gtkmenuitem.h>
34 #include <gtk/gtkitemfactory.h>
35 #include <gtk/gtkvbox.h>
36 #include <gtk/gtkhbox.h>
37 #include <gtk/gtkwindow.h>
38 #include <gtk/gtkstyle.h>
39 #include <gtk/gtkarrow.h>
40 #include <gtk/gtkeventbox.h>
41 #include <gtk/gtkstatusbar.h>
42 #include <gtk/gtkmenuitem.h>
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48
49 #include "main.h"
50 #include "menu.h"
51 #include "mbox.h"
52 #include "mainwindow.h"
53 #include "folderview.h"
54 #include "summaryview.h"
55 #include "messageview.h"
56 #include "foldersel.h"
57 #include "procmsg.h"
58 #include "procheader.h"
59 #include "sourcewindow.h"
60 #include "prefs_common.h"
61 #include "prefs_summary_column.h"
62 #include "prefs_filtering.h"
63 #include "account.h"
64 #include "compose.h"
65 #include "utils.h"
66 #include "gtkutils.h"
67 #include "stock_pixmap.h"
68 #include "filesel.h"
69 #include "alertpanel.h"
70 #include "inputdialog.h"
71 #include "statusbar.h"
72 #include "folder.h"
73 #include "colorlabel.h"
74 #include "inc.h"
75 #include "imap.h"
76 #include "addressbook.h"
77 #include "addr_compl.h"
78 #include "folder_item_prefs.h"
79 #include "filtering.h"
80 #include "string_match.h"
81 #include "toolbar.h"
82 #include "news.h"
83 #include "hooks.h"
84 #include "description_window.h"
85 #include "folderutils.h"
86 #include "quicksearch.h"
87 #include "partial_download.h"
88 #include "timing.h"
89 #include "gedit-print.h"
90
91 #define SUMMARY_COL_MARK_WIDTH          10
92 #define SUMMARY_COL_STATUS_WIDTH        13
93 #define SUMMARY_COL_LOCKED_WIDTH        13
94 #define SUMMARY_COL_MIME_WIDTH          11
95
96
97 static GtkStyle *bold_style;
98 static GtkStyle *bold_marked_style;
99 static GtkStyle *bold_deleted_style;
100 static GtkStyle *small_style;
101 static GtkStyle *small_marked_style;
102 static GtkStyle *small_deleted_style;
103
104 static GdkPixmap *markxpm;
105 static GdkBitmap *markxpmmask;
106 static GdkPixmap *deletedxpm;
107 static GdkBitmap *deletedxpmmask;
108
109 static GdkPixmap *newxpm;
110 static GdkBitmap *newxpmmask;
111 static GdkPixmap *unreadxpm;
112 static GdkBitmap *unreadxpmmask;
113 static GdkPixmap *repliedxpm;
114 static GdkBitmap *repliedxpmmask;
115 static GdkPixmap *forwardedxpm;
116 static GdkBitmap *forwardedxpmmask;
117 static GdkPixmap *ignorethreadxpm;
118 static GdkBitmap *ignorethreadxpmmask;
119 static GdkPixmap *lockedxpm;
120 static GdkBitmap *lockedxpmmask;
121
122 static GdkPixmap *clipxpm;
123 static GdkBitmap *clipxpmmask;
124 static GdkPixmap *keyxpm;
125 static GdkBitmap *keyxpmmask;
126 static GdkPixmap *clipkeyxpm;
127 static GdkBitmap *clipkeyxpmmask;
128 static GdkPixmap *gpgsignedxpm;
129 static GdkBitmap *gpgsignedxpmmask;
130 static GdkPixmap *clipgpgsignedxpm;
131 static GdkBitmap *clipgpgsignedxpmmask;
132
133 static void summary_free_msginfo_func   (GtkCTree               *ctree,
134                                          GtkCTreeNode           *node,
135                                          gpointer                data);
136 static void summary_set_marks_func      (GtkCTree               *ctree,
137                                          GtkCTreeNode           *node,
138                                          gpointer                data);
139
140 static void summary_set_menu_sensitive  (SummaryView            *summaryview);
141
142 static void summary_set_hide_read_msgs_menu (SummaryView *summaryview,
143                                              guint action);
144
145 static guint summary_get_msgnum         (SummaryView            *summaryview,
146                                          GtkCTreeNode           *node);
147
148 static GtkCTreeNode *summary_find_prev_msg
149                                         (SummaryView            *summaryview,
150                                          GtkCTreeNode           *current_node);
151 static GtkCTreeNode *summary_find_next_msg
152                                         (SummaryView            *summaryview,
153                                          GtkCTreeNode           *current_node);
154
155 static GtkCTreeNode *summary_find_prev_flagged_msg
156                                         (SummaryView    *summaryview,
157                                          GtkCTreeNode   *current_node,
158                                          MsgPermFlags    flags,
159                                          gboolean        start_from_prev);
160 static GtkCTreeNode *summary_find_next_flagged_msg
161                                         (SummaryView    *summaryview,
162                                          GtkCTreeNode   *current_node,
163                                          MsgPermFlags    flags,
164                                          gboolean        start_from_next);
165
166 static GtkCTreeNode *summary_find_msg_by_msgnum
167                                         (SummaryView            *summaryview,
168                                          guint                   msgnum);
169
170 static void summary_update_status       (SummaryView            *summaryview);
171
172 /* display functions */
173 static void summary_status_show         (SummaryView            *summaryview);
174 static void summary_set_column_titles   (SummaryView            *summaryview);
175 static void summary_set_ctree_from_list (SummaryView            *summaryview,
176                                          GSList                 *mlist);
177 static void summary_set_header          (SummaryView            *summaryview,
178                                          gchar                  *text[],
179                                          MsgInfo                *msginfo,
180                                          gboolean               *free_from);
181 static void summary_display_msg         (SummaryView            *summaryview,
182                                          GtkCTreeNode           *row);
183 static void summary_display_msg_full    (SummaryView            *summaryview,
184                                          GtkCTreeNode           *row,
185                                          gboolean                new_window,
186                                          gboolean                all_headers);
187 static void summary_set_row_marks       (SummaryView            *summaryview,
188                                          GtkCTreeNode           *row);
189
190 /* message handling */
191 static void summary_mark_row            (SummaryView            *summaryview,
192                                          GtkCTreeNode           *row);
193 static void summary_lock_row            (SummaryView            *summaryview,
194                                          GtkCTreeNode           *row);
195 static void summary_unlock_row          (SummaryView            *summaryview,
196                                          GtkCTreeNode           *row);
197 static void summary_mark_row_as_read    (SummaryView            *summaryview,
198                                          GtkCTreeNode           *row);
199 static void summary_mark_row_as_unread  (SummaryView            *summaryview,
200                                          GtkCTreeNode           *row);
201 static void summary_delete_row          (SummaryView            *summaryview,
202                                          GtkCTreeNode           *row);
203 static void summary_unmark_row          (SummaryView            *summaryview,
204                                          GtkCTreeNode           *row);
205 static void summary_move_row_to         (SummaryView            *summaryview,
206                                          GtkCTreeNode           *row,
207                                          FolderItem             *to_folder);
208 static void summary_copy_row_to         (SummaryView            *summaryview,
209                                          GtkCTreeNode           *row,
210                                          FolderItem             *to_folder);
211
212 static gint summary_execute_move        (SummaryView            *summaryview);
213 static void summary_execute_move_func   (GtkCTree               *ctree,
214                                          GtkCTreeNode           *node,
215                                          gpointer                data);
216 static void summary_execute_copy        (SummaryView            *summaryview);
217 static void summary_execute_copy_func   (GtkCTree               *ctree,
218                                          GtkCTreeNode           *node,
219                                          gpointer                data);
220 static void summary_execute_delete      (SummaryView            *summaryview);
221 static void summary_execute_delete_func (GtkCTree               *ctree,
222                                          GtkCTreeNode           *node,
223                                          gpointer                data);
224
225 static void summary_thread_init         (SummaryView            *summaryview);
226 static void summary_ignore_thread       (SummaryView            *summaryview);
227 static void summary_unignore_thread     (SummaryView            *summaryview);
228
229 static void summary_unthread_for_exec           (SummaryView    *summaryview);
230 static void summary_unthread_for_exec_func      (GtkCTree       *ctree,
231                                                  GtkCTreeNode   *node,
232                                                  gpointer        data);
233
234 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp,
235                               GSList * mlist);
236
237 #if 0
238 void summary_processing(SummaryView *summaryview, GSList * mlist);
239 #endif
240 static void summary_filter_func         (MsgInfo *msginfo);
241
242 static void summary_colorlabel_menu_item_activate_cb
243                                           (GtkWidget    *widget,
244                                            gpointer      data);
245 static void summary_colorlabel_menu_item_activate_item_cb
246                                           (GtkMenuItem  *label_menu_item,
247                                            gpointer      data);
248 static void summary_colorlabel_menu_create(SummaryView  *summaryview);
249
250 static GtkWidget *summary_ctree_create  (SummaryView    *summaryview);
251
252 /* callback functions */
253 static gint summary_toggle_pressed      (GtkWidget              *eventbox,
254                                          GdkEventButton         *event,
255                                          SummaryView            *summaryview);
256 static gboolean summary_button_pressed  (GtkWidget              *ctree,
257                                          GdkEventButton         *event,
258                                          SummaryView            *summaryview);
259 static gboolean summary_button_released (GtkWidget              *ctree,
260                                          GdkEventButton         *event,
261                                          SummaryView            *summaryview);
262 static gboolean summary_key_pressed     (GtkWidget              *ctree,
263                                          GdkEventKey            *event,
264                                          SummaryView            *summaryview);
265 static void summary_open_row            (GtkSCTree              *sctree,
266                                          SummaryView            *summaryview);
267 static void summary_tree_expanded       (GtkCTree               *ctree,
268                                          GtkCTreeNode           *node,
269                                          SummaryView            *summaryview);
270 static void summary_tree_collapsed      (GtkCTree               *ctree,
271                                          GtkCTreeNode           *node,
272                                          SummaryView            *summaryview);
273 static void summary_selected            (GtkCTree               *ctree,
274                                          GtkCTreeNode           *row,
275                                          gint                    column,
276                                          SummaryView            *summaryview);
277 static void summary_unselected          (GtkCTree               *ctree,
278                                          GtkCTreeNode           *row,
279                                          gint                    column,
280                                          SummaryView            *summaryview);
281 static void summary_col_resized         (GtkCList               *clist,
282                                          gint                    column,
283                                          gint                    width,
284                                          SummaryView            *summaryview);
285 static void summary_reply_cb            (SummaryView            *summaryview,
286                                          guint                   action,
287                                          GtkWidget              *widget);
288 static void summary_show_all_header_cb  (SummaryView            *summaryview,
289                                          guint                   action,
290                                          GtkWidget              *widget);
291
292 static void summary_add_address_cb      (SummaryView            *summaryview,
293                                          guint                   action,
294                                          GtkWidget              *widget);
295 static void summary_create_filter_cb    (SummaryView            *summaryview,
296                                          guint                   action,
297                                          GtkWidget              *widget);
298 static void summary_create_processing_cb(SummaryView            *summaryview,
299                                          guint                   action,
300                                          GtkWidget              *widget);
301
302 static void summary_mark_clicked        (GtkWidget              *button,
303                                          SummaryView            *summaryview);
304 static void summary_status_clicked      (GtkWidget              *button,
305                                          SummaryView            *summaryview);
306 static void summary_mime_clicked        (GtkWidget              *button,
307                                          SummaryView            *summaryview);
308 static void summary_num_clicked         (GtkWidget              *button,
309                                          SummaryView            *summaryview);
310 static void summary_score_clicked       (GtkWidget *button,
311                                          SummaryView *summaryview);
312 static void summary_size_clicked        (GtkWidget              *button,
313                                          SummaryView            *summaryview);
314 static void summary_date_clicked        (GtkWidget              *button,
315                                          SummaryView            *summaryview);
316 static void summary_from_clicked        (GtkWidget              *button,
317                                          SummaryView            *summaryview);
318 static void summary_to_clicked          (GtkWidget              *button,
319                                          SummaryView            *summaryview);
320 static void summary_subject_clicked     (GtkWidget              *button,
321                                          SummaryView            *summaryview);
322 static void summary_score_clicked       (GtkWidget              *button,
323                                          SummaryView            *summaryview);
324 static void summary_locked_clicked      (GtkWidget              *button,
325                                          SummaryView            *summaryview);
326
327 static void summary_start_drag          (GtkWidget        *widget, 
328                                          int button,
329                                          GdkEvent *event,
330                                          SummaryView      *summaryview);
331 static void summary_drag_data_get       (GtkWidget        *widget,
332                                          GdkDragContext   *drag_context,
333                                          GtkSelectionData *selection_data,
334                                          guint             info,
335                                          guint             time,
336                                          SummaryView      *summaryview);
337 static void summary_drag_data_received(GtkWidget        *widget,
338                                         GdkDragContext   *drag_context,
339                                         gint              x,
340                                         gint              y,
341                                         GtkSelectionData *data,
342                                         guint             info,
343                                         guint             time,
344                                         SummaryView       *summaryview);
345 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
346                                           GdkDragContext *context,
347                                           gint            x,
348                                           gint            y,
349                                           guint           time,
350                                           SummaryView    *summaryview);
351
352 /* custom compare functions for sorting */
353
354 static gint summary_cmp_by_mark         (GtkCList               *clist,
355                                          gconstpointer           ptr1,
356                                          gconstpointer           ptr2);
357 static gint summary_cmp_by_status       (GtkCList               *clist,
358                                          gconstpointer           ptr1,
359                                          gconstpointer           ptr2);
360 static gint summary_cmp_by_mime         (GtkCList               *clist,
361                                          gconstpointer           ptr1,
362                                          gconstpointer           ptr2);
363 static gint summary_cmp_by_num          (GtkCList               *clist,
364                                          gconstpointer           ptr1,
365                                          gconstpointer           ptr2);
366 static gint summary_cmp_by_size         (GtkCList               *clist,
367                                          gconstpointer           ptr1,
368                                          gconstpointer           ptr2);
369 static gint summary_cmp_by_date         (GtkCList               *clist,
370                                          gconstpointer           ptr1,
371                                          gconstpointer           ptr2);
372 static gint summary_cmp_by_from         (GtkCList               *clist,
373                                          gconstpointer           ptr1,
374                                          gconstpointer           ptr2);
375 static gint summary_cmp_by_simplified_subject
376                                         (GtkCList               *clist, 
377                                          gconstpointer           ptr1, 
378                                          gconstpointer           ptr2);
379 static gint summary_cmp_by_score        (GtkCList               *clist,
380                                          gconstpointer           ptr1,
381                                          gconstpointer           ptr2);
382 static gint summary_cmp_by_label        (GtkCList               *clist,
383                                          gconstpointer           ptr1,
384                                          gconstpointer           ptr2);
385 static gint summary_cmp_by_to           (GtkCList               *clist,
386                                          gconstpointer           ptr1,
387                                          gconstpointer           ptr2);
388 static gint summary_cmp_by_subject      (GtkCList               *clist,
389                                          gconstpointer           ptr1,
390                                          gconstpointer           ptr2);
391 static gint summary_cmp_by_locked       (GtkCList               *clist,
392                                          gconstpointer           ptr1, 
393                                          gconstpointer           ptr2);
394
395 static void news_flag_crosspost         (MsgInfo *msginfo);
396
397 static void quicksearch_execute_cb      (QuickSearch    *quicksearch,
398                                          gpointer        data);
399 static void tog_searchbar_cb            (GtkWidget      *w,
400                                          gpointer        data);
401
402 static void summary_find_answers        (SummaryView    *summaryview, 
403                                          MsgInfo        *msg);
404
405 static gboolean summary_update_msg      (gpointer source, gpointer data);
406
407 GtkTargetEntry summary_drag_types[2] =
408 {
409         {"text/uri-list", 0, TARGET_MAIL_URI_LIST},
410         {"sylpheed-claws/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}
411 };
412
413 static GtkItemFactoryEntry summary_popup_entries[] =
414 {
415         {N_("/_Reply"),                 "<control>R", summary_reply_cb, COMPOSE_REPLY, NULL},
416         {N_("/Repl_y to"),              NULL, NULL,             0, "<Branch>"},
417         {N_("/Repl_y to/_all"),         "<shift><control>R", summary_reply_cb,  COMPOSE_REPLY_TO_ALL, NULL},
418         {N_("/Repl_y to/_sender"),      NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
419         {N_("/Repl_y to/mailing _list"),
420                                         "<control>L", summary_reply_cb, COMPOSE_REPLY_TO_LIST, NULL},
421         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
422         {N_("/_Forward"),               "<control><alt>F", summary_reply_cb, COMPOSE_FORWARD_INLINE, NULL},
423         {N_("/For_ward as attachment"), NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
424         {N_("/Redirect"),               NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL},
425         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
426         {N_("/M_ove..."),               "<control>O", summary_move_to,  0, NULL},
427         {N_("/_Copy..."),               "<shift><control>O", summary_copy_to,   0, NULL},
428         {N_("/Move to _trash"),         "<control>D", summary_delete_trash,     0, NULL},
429         {N_("/_Delete..."),             NULL, summary_delete, 0, NULL},
430         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
431         {N_("/_Mark"),                  NULL, NULL,             0, "<Branch>"},
432         {N_("/_Mark/_Mark"),            NULL, summary_mark,     0, NULL},
433         {N_("/_Mark/_Unmark"),          NULL, summary_unmark,   0, NULL},
434         {N_("/_Mark/---"),              NULL, NULL,             0, "<Separator>"},
435         {N_("/_Mark/Mark as unr_ead"),  NULL, summary_mark_as_unread, 0, NULL},
436         {N_("/_Mark/Mark as rea_d"),    NULL, summary_mark_as_read, 0, NULL},
437         {N_("/_Mark/Mark all read"),    NULL, summary_mark_all_read, 0, NULL},
438         {N_("/_Mark/Ignore thread"),    NULL, summary_ignore_thread, 0, NULL},
439         {N_("/_Mark/Unignore thread"),  NULL, summary_unignore_thread, 0, NULL},
440         {N_("/_Mark/Lock"),             NULL, summary_msgs_lock, 0, NULL},
441         {N_("/_Mark/Unlock"),           NULL, summary_msgs_unlock, 0, NULL},
442         {N_("/Color la_bel"),           NULL, NULL,             0, NULL},
443
444         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
445         {N_("/Add sender to address boo_k"),
446                                         NULL, summary_add_address_cb, 0, NULL},
447         {N_("/Create f_ilter rule"),    NULL, NULL,             0, "<Branch>"},
448         {N_("/Create f_ilter rule/_Automatically"),
449                                         NULL, summary_create_filter_cb, FILTER_BY_AUTO, NULL},
450         {N_("/Create f_ilter rule/by _From"),
451                                         NULL, summary_create_filter_cb, FILTER_BY_FROM, NULL},
452         {N_("/Create f_ilter rule/by _To"),
453                                         NULL, summary_create_filter_cb, FILTER_BY_TO, NULL},
454         {N_("/Create f_ilter rule/by _Subject"),
455                                         NULL, summary_create_filter_cb, FILTER_BY_SUBJECT, NULL},
456         {N_("/Create processing rule"), NULL, NULL,             0, "<Branch>"},
457         {N_("/Create processing rule/_Automatically"),
458                                         NULL, summary_create_processing_cb, FILTER_BY_AUTO, NULL},
459         {N_("/Create processing rule/by _From"),
460                                         NULL, summary_create_processing_cb, FILTER_BY_FROM, NULL},
461         {N_("/Create processing rule/by _To"),
462                                         NULL, summary_create_processing_cb, FILTER_BY_TO, NULL},
463         {N_("/Create processing rule/by _Subject"),
464                                         NULL, summary_create_processing_cb, FILTER_BY_SUBJECT, NULL},
465         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
466         {N_("/_View"),                  NULL, NULL,             0, "<Branch>"},
467         {N_("/_View/Open in new _window"),
468                                         "<control><alt>N", summary_open_msg,    0, NULL},
469         {N_("/_View/_Source"),          "<control>U", summary_view_source, 0, NULL},
470         {N_("/_View/All _header"),      "<control>H", summary_show_all_header_cb, 0, "<ToggleItem>"},
471         {N_("/---"),                    NULL, NULL,             0, "<Separator>"},
472         {N_("/_Save as..."),            "<control>S", summary_save_as,   0, NULL},
473         {N_("/_Print..."),              "<control>P", summary_print,   0, NULL},
474 };  /* see also list in menu_connect_identical_items() in menu.c if this changes */
475
476 static const gchar *const col_label[N_SUMMARY_COLS] = {
477         "",             /* S_COL_MARK    */
478         N_("S"),        /* S_COL_STATUS  */
479         "",             /* S_COL_MIME    */
480         N_("Subject"),  /* S_COL_SUBJECT */
481         N_("From"),     /* S_COL_FROM    */
482         N_("To"),       /* S_COL_TO      */
483         N_("Date"),     /* S_COL_DATE    */
484         N_("Size"),     /* S_COL_SIZE    */
485         N_("#"),        /* S_COL_NUMBER  */
486         N_("Score"),    /* S_COL_SCORE   */
487         "",             /* S_COL_LOCKED  */
488 };
489
490 #define START_LONG_OPERATION(summaryview) {                     \
491         summary_lock(summaryview);                              \
492         main_window_cursor_wait(summaryview->mainwin);          \
493         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));        \
494         folder_item_update_freeze();                            \
495         inc_lock();                                             \
496 }
497 #define END_LONG_OPERATION(summaryview) {                       \
498         inc_unlock();                                           \
499         folder_item_update_thaw();                              \
500         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));          \
501         main_window_cursor_normal(summaryview->mainwin);        \
502         summary_unlock(summaryview);                            \
503 }
504
505 SummaryView *summary_create(void)
506 {
507         SummaryView *summaryview;
508         GtkWidget *vbox;
509         GtkWidget *scrolledwin;
510         GtkWidget *ctree;
511         GtkWidget *hbox;
512         GtkWidget *hbox_l;
513         GtkWidget *statlabel_folder;
514         GtkWidget *statlabel_select;
515         GtkWidget *statlabel_msgs;
516         GtkWidget *hbox_spc;
517         GtkWidget *toggle_eventbox;
518         GtkWidget *toggle_arrow;
519         GtkWidget *popupmenu;
520         GtkWidget *toggle_search;
521         GtkTooltips *search_tip;
522         GtkItemFactory *popupfactory;
523         gint n_entries;
524         QuickSearch *quicksearch;
525
526         debug_print("Creating summary view...\n");
527         summaryview = g_new0(SummaryView, 1);
528
529 #define SUMMARY_VBOX_SPACING 3
530         vbox = gtk_vbox_new(FALSE, SUMMARY_VBOX_SPACING);
531         
532         /* create status label */
533         hbox = gtk_hbox_new(FALSE, 0);
534         gtk_widget_show(hbox);
535
536         search_tip = gtk_tooltips_new();
537         toggle_search = gtk_toggle_button_new();
538         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_search),
539                                      prefs_common.show_searchbar);
540         gtk_widget_show(toggle_search);
541
542         gtk_tooltips_set_tip(GTK_TOOLTIPS(search_tip),
543                              toggle_search,
544                              _("Toggle quick search bar"), NULL);
545         
546         gtk_box_pack_start(GTK_BOX(hbox), toggle_search, FALSE, FALSE, 2);      
547
548         hbox_l = gtk_hbox_new(FALSE, 0);
549         gtk_widget_show(hbox_l);
550         gtk_box_pack_start(GTK_BOX(hbox), hbox_l, TRUE, TRUE, 0);
551  
552         statlabel_folder = gtk_label_new("");
553         gtk_widget_show(statlabel_folder);
554         gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
555         statlabel_select = gtk_label_new("");
556         gtk_widget_show(statlabel_select);
557         gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
558  
559         /* toggle view button */
560         toggle_eventbox = gtk_event_box_new();
561         gtk_widget_show(toggle_eventbox);
562         gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
563         toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
564         gtk_widget_show(toggle_arrow);
565         gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
566         g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
567                          G_CALLBACK(summary_toggle_pressed),
568                          summaryview);
569         
570         
571         statlabel_msgs = gtk_label_new("");
572         gtk_widget_show(statlabel_msgs);
573         gtk_box_pack_end(GTK_BOX(hbox), statlabel_msgs, FALSE, FALSE, 4);
574
575         hbox_spc = gtk_hbox_new(FALSE, 0);
576         gtk_widget_show(hbox_spc);
577         gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
578
579         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
580         gtk_widget_show(scrolledwin);
581         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
582                                        GTK_POLICY_AUTOMATIC,
583                                        GTK_POLICY_AUTOMATIC);
584         gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
585         gtk_widget_set_size_request(vbox,
586                              prefs_common.summaryview_width,
587                              prefs_common.summaryview_height);
588
589         ctree = summary_ctree_create(summaryview);
590         gtk_widget_show(ctree);
591
592         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
593                                             GTK_CLIST(ctree)->hadjustment);
594         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
595                                             GTK_CLIST(ctree)->vadjustment);
596         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
597
598         /* status label */
599         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
600
601         /* quick search */
602         quicksearch = quicksearch_new();
603         gtk_box_pack_start(GTK_BOX(vbox), quicksearch_get_widget(quicksearch), FALSE, FALSE, 0);
604
605         quicksearch_set_execute_callback(quicksearch, quicksearch_execute_cb, summaryview);
606
607         g_signal_connect (G_OBJECT(toggle_search), "toggled",
608                           G_CALLBACK(tog_searchbar_cb), summaryview);
609
610         /* create popup menu */
611         n_entries = sizeof(summary_popup_entries) /
612                 sizeof(summary_popup_entries[0]);
613         popupmenu = menu_create_items(summary_popup_entries, n_entries,
614                                       "<SummaryView>", &popupfactory,
615                                       summaryview);
616
617         summaryview->vbox = vbox;
618         summaryview->scrolledwin = scrolledwin;
619         summaryview->ctree = ctree;
620         summaryview->hbox = hbox;
621         summaryview->hbox_l = hbox_l;
622         summaryview->statlabel_folder = statlabel_folder;
623         summaryview->statlabel_select = statlabel_select;
624         summaryview->statlabel_msgs = statlabel_msgs;
625         summaryview->toggle_eventbox = toggle_eventbox;
626         summaryview->toggle_arrow = toggle_arrow;
627         summaryview->toggle_search = toggle_search;
628         summaryview->popupmenu = popupmenu;
629         summaryview->popupfactory = popupfactory;
630         summaryview->lock_count = 0;
631         summaryview->msginfo_update_callback_id =
632                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview);
633
634         summaryview->target_list = gtk_target_list_new(summary_drag_types, 2);
635
636         summaryview->quicksearch = quicksearch;
637
638         /* CLAWS: need this to get the SummaryView * from
639          * the CList */
640         g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 
641
642         gtk_widget_show_all(vbox);
643
644         gtk_widget_show(vbox);
645
646         if (prefs_common.show_searchbar)
647                 quicksearch_show(quicksearch);
648         else
649                 quicksearch_hide(quicksearch);
650         
651         return summaryview;
652 }
653
654 static void summary_set_fonts(SummaryView *summaryview)
655 {
656         PangoFontDescription *font_desc;
657         gint size;
658
659         font_desc = pango_font_description_from_string(NORMAL_FONT);
660         gtk_widget_modify_font(summaryview->ctree, font_desc);
661         pango_font_description_free(font_desc);
662
663         if (!bold_style) {
664                 bold_style = gtk_style_copy
665                         (gtk_widget_get_style(summaryview->ctree));
666                 font_desc = pango_font_description_from_string(NORMAL_FONT);
667                 if (font_desc) {
668                         pango_font_description_free(bold_style->font_desc);
669                         bold_style->font_desc = font_desc;
670                 }
671                 
672                 pango_font_description_set_weight
673                                 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
674                 bold_marked_style = gtk_style_copy(bold_style);
675                 bold_marked_style->fg[GTK_STATE_NORMAL] =
676                         summaryview->color_marked;
677                 bold_deleted_style = gtk_style_copy(bold_style);
678                 bold_deleted_style->fg[GTK_STATE_NORMAL] =
679                         summaryview->color_dim;
680         }
681
682         font_desc = pango_font_description_new();
683         size = pango_font_description_get_size
684                 (summaryview->ctree->style->font_desc);
685         pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
686         gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
687         gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
688         gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
689         pango_font_description_free(font_desc);
690 }
691
692 void summary_init(SummaryView *summaryview)
693 {
694         GtkWidget *pixmap;
695
696         gtk_widget_realize(summaryview->ctree);
697         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_MARK,
698                          &markxpm, &markxpmmask);
699         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_DELETED,
700                          &deletedxpm, &deletedxpmmask);
701         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_NEW,
702                          &newxpm, &newxpmmask);
703         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_UNREAD,
704                          &unreadxpm, &unreadxpmmask);
705         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_REPLIED,
706                          &repliedxpm, &repliedxpmmask);
707         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_FORWARDED,
708                          &forwardedxpm, &forwardedxpmmask);
709         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP,
710                          &clipxpm, &clipxpmmask);
711         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_LOCKED,
712                          &lockedxpm, &lockedxpmmask);
713         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_IGNORETHREAD,
714                          &ignorethreadxpm, &ignorethreadxpmmask);
715         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP_KEY,
716                          &clipkeyxpm, &clipkeyxpmmask);
717         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_KEY,
718                          &keyxpm, &keyxpmmask);
719         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_GPG_SIGNED,
720                          &gpgsignedxpm, &gpgsignedxpmmask);
721         stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP_GPG_SIGNED,
722                          &clipgpgsignedxpm, &clipgpgsignedxpmmask);
723
724         summary_set_fonts(summaryview);
725
726         pixmap = stock_pixmap_widget(summaryview->hbox_l, STOCK_PIXMAP_DIR_OPEN);
727         gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), pixmap, FALSE, FALSE, 4);
728         gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), pixmap, 0);
729         gtk_widget_show(pixmap);
730         summaryview->folder_pixmap = pixmap;
731
732         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_QUICKSEARCH);
733         gtk_container_add (GTK_CONTAINER(summaryview->toggle_search), pixmap);
734         gtk_widget_show(pixmap);
735         summaryview->quick_search_pixmap = pixmap;
736
737         /* Init summaryview prefs */
738         summaryview->sort_key = SORT_BY_NONE;
739         summaryview->sort_type = SORT_ASCENDING;
740
741         /* Init summaryview extra data */
742         summaryview->simplify_subject_preg = NULL;
743
744         summary_clear_list(summaryview);
745         summary_set_column_titles(summaryview);
746         summary_colorlabel_menu_create(summaryview);
747         summary_set_menu_sensitive(summaryview);
748
749 }
750
751 #if 0
752 GtkCTreeNode * summary_find_next_important_score(SummaryView *summaryview,
753                                                  GtkCTreeNode *current_node)
754 {
755         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
756         GtkCTreeNode *node;
757         MsgInfo *msginfo;
758         gint best_score = MIN_SCORE;
759         GtkCTreeNode *best_node = NULL;
760
761         if (current_node)
762                 /*node = current_node;*/
763                 node = GTK_CTREE_NODE_NEXT(current_node);
764         else
765                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
766
767         for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
768                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
769                 if (msginfo->score >= summaryview->important_score)
770                         break;
771                 if (msginfo->score > best_score) {
772                         best_score = msginfo->score;
773                         best_node = node;
774                 }
775         }
776
777         if (node != NULL)
778                 return node;
779         else
780                 return best_node;
781 }
782
783 GtkCTreeNode * summary_find_prev_important_score(SummaryView *summaryview,
784                                                  GtkCTreeNode *current_node)
785 {
786         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
787         GtkCTreeNode *node;
788         MsgInfo *msginfo;
789         gint best_score = MIN_SCORE;
790         GtkCTreeNode *best_node = NULL;
791
792         if (current_node)
793                 /*node = current_node;*/
794                 node = GTK_CTREE_NODE_PREV(current_node);
795         else
796                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
797
798         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
799                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
800                 if (msginfo->score >= summaryview->important_score)
801                         break;
802                 if (msginfo->score > best_score) {
803                         best_score = msginfo->score;
804                         best_node = node;
805                 }
806         }
807
808         if (node != NULL)
809                 return node;
810         else
811                 return best_node;
812 }
813 #endif
814
815 #define CURRENTLY_DISPLAYED(m) \
816 ( (m->msgnum == displayed_msgnum) \
817   && (!g_ascii_strcasecmp(m->folder->name,item->name)) )
818
819 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
820 {
821         gboolean show_from = FALSE, show_to = FALSE;
822         gboolean showing_from = FALSE, showing_to = FALSE;
823         gint from_pos = 0, to_pos = 0;
824         SummaryColumnState *col_state = summaryview->col_state;
825         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
826         
827         if (!item)
828                 return;
829         if (folder_has_parent_of_type(item, F_OUTBOX)
830         ||  folder_has_parent_of_type(item, F_DRAFT)
831         ||  folder_has_parent_of_type(item, F_QUEUE))
832                 show_to = TRUE;
833         else
834                 show_from = TRUE;
835         
836         from_pos = summaryview->col_pos[S_COL_FROM];
837         to_pos = summaryview->col_pos[S_COL_TO];
838         showing_from = col_state[from_pos].visible;
839         showing_to = col_state[to_pos].visible;
840         
841         if (showing_from && showing_to) {
842                 debug_print("showing both\n");
843                 return;
844         }
845
846         if (!showing_from && !showing_to) {
847                 debug_print("showing none\n");
848                 return;
849         }
850
851         debug_print("showing %s %s, must show %s %s\n", 
852                 showing_from?"From":"",
853                 showing_to?"To":"",
854                 show_from?"From":"",
855                 show_to?"To":"");
856
857         if (showing_from == show_from && showing_to == show_to)
858                 return;
859         /* else we'll switch both */
860
861         debug_print("switching columns\n");
862         col_state[from_pos].type = S_COL_TO;
863         col_state[from_pos].visible = show_to;
864
865         col_state[to_pos].type = S_COL_FROM;
866         col_state[to_pos].visible = show_from;
867
868         summaryview->col_pos[S_COL_TO] = from_pos;
869         summaryview->col_pos[S_COL_FROM] = to_pos;
870
871         gtk_clist_set_column_visibility
872                 (GTK_CLIST(ctree), from_pos, col_state[from_pos].visible);
873         gtk_clist_set_column_visibility
874                 (GTK_CLIST(ctree), to_pos, col_state[to_pos].visible);
875
876         summary_set_column_titles(summaryview);
877 }
878
879 gboolean summary_show(SummaryView *summaryview, FolderItem *item)
880 {
881         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
882         GtkCTreeNode *node;
883         GSList *mlist = NULL;
884         gchar *buf;
885         gboolean is_refresh;
886         guint selected_msgnum = 0;
887         guint displayed_msgnum = 0;
888         GSList *cur;
889         GSList *not_killed;
890         gboolean hidden_removed = FALSE;
891
892         if (summary_is_locked(summaryview)) return FALSE;
893
894         if (!summaryview->mainwin)
895                 return FALSE;
896
897         summary_switch_from_to(summaryview, item);
898
899         inc_lock();
900         summary_lock(summaryview);
901
902         if (!prefs_common.summary_quicksearch_sticky
903          && !prefs_common.summary_quicksearch_recurse
904          && !quicksearch_is_running(summaryview->quicksearch)) {
905                 quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
906         }
907
908         /* STATUSBAR_POP(summaryview->mainwin); */
909
910         is_refresh = (item == summaryview->folder_item) ? TRUE : FALSE;
911
912         if (is_refresh) {
913                 selected_msgnum = summary_get_msgnum(summaryview,
914                                                      summaryview->selected);
915                 displayed_msgnum = summary_get_msgnum(summaryview,
916                                                       summaryview->displayed);
917         }
918
919         /* process the marks if any */
920         if (summaryview->mainwin->lock_count == 0 &&
921             (summaryview->moved > 0 || summaryview->copied > 0)) {
922                 AlertValue val;
923                 gboolean changed = FALSE;
924
925                 val = alertpanel(_("Process mark"),
926                                  _("Some marks are left. Process them?"),
927                                  GTK_STOCK_NO, GTK_STOCK_YES, GTK_STOCK_CANCEL);
928                 if (G_ALERTALTERNATE == val) {
929                         summary_unlock(summaryview);
930                         summary_execute(summaryview);
931                         summary_lock(summaryview);
932                         changed = TRUE;
933                 } else if (G_ALERTDEFAULT == val) {
934                         /* DO NOTHING */
935                 } else {
936                         summary_unlock(summaryview);
937                         inc_unlock();
938                         return FALSE;
939                 }
940                 if (changed || !quicksearch_is_active(summaryview->quicksearch))
941                         folder_update_op_count();
942         }
943         
944         gtk_clist_freeze(GTK_CLIST(ctree));
945
946         summary_clear_list(summaryview);
947
948         buf = NULL;
949         if (!item || !item->path || !folder_item_parent(item) || item->no_select) {
950                 g_free(buf);
951                 debug_print("empty folder\n\n");
952                 summary_set_hide_read_msgs_menu(summaryview, FALSE);
953                 summary_clear_all(summaryview);
954                 summaryview->folder_item = item;
955                 gtk_clist_thaw(GTK_CLIST(ctree));
956                 summary_unlock(summaryview);
957                 inc_unlock();
958                 if (item && quicksearch_is_running(summaryview->quicksearch)) {
959                         main_window_cursor_wait(summaryview->mainwin);
960                         quicksearch_reset_cur_folder_item(summaryview->quicksearch);
961                         if (quicksearch_is_active(summaryview->quicksearch))
962                                 quicksearch_search_subfolders(summaryview->quicksearch, 
963                                               summaryview->folderview,
964                                               summaryview->folder_item);
965                         main_window_cursor_normal(summaryview->mainwin);
966                 }                       
967                 return TRUE;
968         }
969         g_free(buf);
970
971         if (!is_refresh)
972                 messageview_clear(summaryview->messageview);
973
974         summaryview->folder_item = item;
975         item->opened = TRUE;
976
977         buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
978         debug_print("%s\n", buf);
979         STATUSBAR_PUSH(summaryview->mainwin, buf);
980         g_free(buf);
981
982         main_window_cursor_wait(summaryview->mainwin);
983
984         mlist = folder_item_get_msg_list(item);
985
986         if (summaryview->folder_item->hide_read_msgs &&
987             quicksearch_is_active(summaryview->quicksearch) == FALSE) {
988                 GSList *not_killed;
989                 
990                 summary_set_hide_read_msgs_menu(summaryview, TRUE);
991                 not_killed = NULL;
992                 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
993                         MsgInfo * msginfo = (MsgInfo *) cur->data;
994                         
995                         if (!msginfo->hidden) {
996                                 if (MSG_IS_UNREAD(msginfo->flags) &&
997                                     !MSG_IS_IGNORE_THREAD(msginfo->flags))
998                                         not_killed = g_slist_prepend(not_killed, msginfo);
999                                 else if (MSG_IS_MARKED(msginfo->flags) ||
1000                                          MSG_IS_LOCKED(msginfo->flags))
1001                                         not_killed = g_slist_prepend(not_killed, msginfo);
1002                                 else if (is_refresh &&
1003                                         (msginfo->msgnum == selected_msgnum ||
1004                                          msginfo->msgnum == displayed_msgnum))
1005                                         not_killed = g_slist_prepend(not_killed, msginfo);
1006                                 else
1007                                         procmsg_msginfo_free(msginfo);
1008                          } else
1009                                 procmsg_msginfo_free(msginfo);
1010                 }
1011                 hidden_removed = TRUE;
1012                 g_slist_free(mlist);
1013                 mlist = not_killed;
1014         } else {
1015                 summary_set_hide_read_msgs_menu(summaryview, FALSE);
1016         }
1017
1018         if (quicksearch_is_active(summaryview->quicksearch)) {
1019                 GSList *not_killed;
1020                 
1021                 not_killed = NULL;
1022                 for (cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1023                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1024
1025                         if (!msginfo->hidden && quicksearch_match(summaryview->quicksearch, msginfo))
1026                                 not_killed = g_slist_prepend(not_killed, msginfo);
1027                         else
1028                                 procmsg_msginfo_free(msginfo);
1029                 }
1030                 hidden_removed = TRUE;
1031                 if (quicksearch_is_running(summaryview->quicksearch)) {
1032                         /* only scan subfolders when quicksearch changed,
1033                          * not when search is the same and folder changed */
1034                         main_window_cursor_wait(summaryview->mainwin);
1035                         quicksearch_reset_cur_folder_item(summaryview->quicksearch);
1036                         quicksearch_search_subfolders(summaryview->quicksearch, 
1037                                               summaryview->folderview,
1038                                               summaryview->folder_item);
1039                         main_window_cursor_normal(summaryview->mainwin);
1040                 }
1041                 
1042                 g_slist_free(mlist);
1043                 mlist = not_killed;
1044         }
1045
1046         if (!hidden_removed) {
1047                 not_killed = NULL;
1048                 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1049                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1050
1051                         if (!msginfo->hidden)
1052                                 not_killed = g_slist_prepend(not_killed, msginfo);
1053                         else
1054                                 procmsg_msginfo_free(msginfo);
1055                 }
1056                 g_slist_free(mlist);
1057                 mlist = not_killed;
1058         }
1059
1060         STATUSBAR_POP(summaryview->mainwin);
1061
1062         /* set ctree and hash table from the msginfo list, and
1063            create the thread */
1064         summary_set_ctree_from_list(summaryview, mlist);
1065
1066         g_slist_free(mlist);
1067
1068         if (is_refresh) {
1069                 summaryview->displayed =
1070                         summary_find_msg_by_msgnum(summaryview,
1071                                                    displayed_msgnum);
1072                 if (!summaryview->displayed)
1073                         messageview_clear(summaryview->messageview);
1074                 summary_unlock(summaryview);
1075                 summary_select_by_msgnum(summaryview, selected_msgnum);
1076                 summary_lock(summaryview);
1077                 if (!summaryview->selected) {
1078                         /* no selected message - select first unread
1079                            message, but do not display it */
1080                         node = summary_find_next_flagged_msg(summaryview, NULL,
1081                                                              MSG_UNREAD, FALSE);
1082                         if (node == NULL && GTK_CLIST(ctree)->row_list != NULL)
1083                                 node = gtk_ctree_node_nth
1084                                         (ctree,
1085                                          item->sort_type == SORT_DESCENDING
1086                                          ? 0 : GTK_CLIST(ctree)->rows - 1);
1087                         summary_unlock(summaryview);
1088                         summary_select_node(summaryview, node, FALSE, TRUE);
1089                         summary_lock(summaryview);
1090                 }
1091         } else {
1092                 switch (prefs_common.select_on_entry) {
1093                         case SELECTONENTRY_NEW:
1094                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1095                                                                      MSG_NEW, FALSE);
1096                                 if (node == NULL)
1097                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1098                                                                      MSG_UNREAD, FALSE);
1099                                 break;
1100                         case SELECTONENTRY_UNREAD:
1101                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1102                                                                      MSG_UNREAD, FALSE);
1103                                 if (node == NULL)
1104                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1105                                                                      MSG_NEW, FALSE);
1106                                 break;
1107                         default:
1108                                 node = NULL;
1109                 }
1110
1111                 if (node == NULL && GTK_CLIST(ctree)->row_list != NULL) {
1112                         node = gtk_ctree_node_nth
1113                                 (ctree,
1114                                  item->sort_type == SORT_DESCENDING
1115                                  ? 0 : GTK_CLIST(ctree)->rows - 1);
1116                 }
1117                 summary_unlock(summaryview);
1118                 summary_select_node(summaryview, node,
1119                                     prefs_common.always_show_msg,
1120                                     TRUE);
1121                 summary_lock(summaryview);
1122         }
1123
1124         summary_status_show(summaryview);
1125         summary_set_menu_sensitive(summaryview);
1126         toolbar_main_set_sensitive(summaryview->mainwin);
1127         
1128         gtk_clist_thaw(GTK_CLIST(ctree));
1129
1130         debug_print("\n");
1131         STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
1132         STATUSBAR_POP(summaryview->mainwin);
1133         main_window_cursor_normal(summaryview->mainwin);
1134         summary_unlock(summaryview);
1135         inc_unlock();
1136
1137         return TRUE;
1138 }
1139
1140 #undef CURRENTLY_DISPLAYED
1141
1142
1143 void summary_clear_list(SummaryView *summaryview)
1144 {
1145         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1146         gint optimal_width;
1147
1148         gtk_clist_freeze(clist);
1149
1150         gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree),
1151                                 NULL, summary_free_msginfo_func, NULL);
1152
1153         if (summaryview->folder_item) {
1154                 summaryview->folder_item->opened = FALSE;
1155                 summaryview->folder_item = NULL;
1156         }
1157
1158         summaryview->display_msg = FALSE;
1159
1160         summaryview->selected = NULL;
1161         summaryview->displayed = NULL;
1162         summaryview->total_size = 0;
1163         summaryview->deleted = summaryview->moved = 0;
1164         summaryview->copied = 0;
1165         if (summaryview->msgid_table) {
1166                 g_hash_table_destroy(summaryview->msgid_table);
1167                 summaryview->msgid_table = NULL;
1168         }
1169         if (summaryview->subject_table) {
1170                 g_hash_table_destroy(summaryview->subject_table);
1171                 summaryview->subject_table = NULL;
1172         }
1173         summaryview->mlist = NULL;
1174
1175         gtk_clist_clear(clist);
1176         if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1177                 optimal_width = gtk_clist_optimal_column_width
1178                         (clist, summaryview->col_pos[S_COL_SUBJECT]);
1179                 gtk_clist_set_column_width
1180                         (clist, summaryview->col_pos[S_COL_SUBJECT],
1181                          optimal_width);
1182         }
1183
1184         gtk_clist_thaw(clist);
1185 }
1186
1187 void summary_clear_all(SummaryView *summaryview)
1188 {
1189         messageview_clear(summaryview->messageview);
1190         summary_clear_list(summaryview);
1191         summary_set_menu_sensitive(summaryview);
1192         toolbar_main_set_sensitive(summaryview->mainwin);
1193         summary_status_show(summaryview);
1194 }
1195
1196 void summary_lock(SummaryView *summaryview)
1197 {
1198         summaryview->lock_count++;
1199 }
1200
1201 void summary_unlock(SummaryView *summaryview)
1202 {
1203         if (summaryview->lock_count)
1204                 summaryview->lock_count--;
1205 }
1206
1207 gboolean summary_is_locked(SummaryView *summaryview)
1208 {
1209         return summaryview->lock_count > 0;
1210 }
1211
1212 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1213 {
1214         GtkCList *clist = GTK_CLIST(summaryview->ctree);
1215         SummarySelection selection;
1216
1217         if (!clist->row_list)
1218                 selection = SUMMARY_NONE;
1219         else if (!clist->selection)
1220                 selection = SUMMARY_SELECTED_NONE;
1221         else if (!clist->selection->next)
1222                 selection = SUMMARY_SELECTED_SINGLE;
1223         else
1224                 selection = SUMMARY_SELECTED_MULTIPLE;
1225
1226         return selection;
1227 }
1228
1229 /*!
1230  *\return       MsgInfo * Selected message if there's one selected;
1231  *              if multiple selected, or none, return NULL.
1232  */ 
1233 MsgInfo *summary_get_selected_msg(SummaryView *summaryview)
1234 {
1235         /* summaryview->selected may be valid when multiple 
1236          * messages were selected */
1237         GList *sellist = GTK_CLIST(summaryview->ctree)->selection;
1238
1239         if (sellist == NULL || sellist->next) 
1240                 return NULL;
1241         
1242         return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data);
1243 }
1244
1245 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1246 {
1247         GSList *mlist = NULL;
1248         GList *cur;
1249         MsgInfo *msginfo;
1250
1251         for (cur = GTK_CLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL;
1252              cur = cur->next) {
1253                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
1254                 mlist = g_slist_prepend(mlist, msginfo);
1255         }
1256
1257         mlist = g_slist_reverse(mlist);
1258
1259         return mlist;
1260 }
1261
1262 GSList *summary_get_msg_list(SummaryView *summaryview)
1263 {
1264         GSList *mlist = NULL;
1265         GtkCTree *ctree;
1266         GtkCTreeNode *node;
1267         MsgInfo *msginfo;
1268
1269         ctree = GTK_CTREE(summaryview->ctree);
1270
1271         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1272              node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1273                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1274                 mlist = g_slist_prepend(mlist, msginfo);
1275         }
1276
1277         mlist = g_slist_reverse(mlist);
1278
1279         return mlist;
1280 }
1281
1282 static void summary_set_menu_sensitive(SummaryView *summaryview)
1283 {
1284         GtkItemFactory *ifactory = summaryview->popupfactory;
1285         SensitiveCond state;
1286         gboolean sensitive;
1287         GtkWidget *menuitem;
1288         gint i;
1289
1290         static const struct {
1291                 gchar *const entry;
1292                 SensitiveCond cond;
1293         } entry[] = {
1294                 {"/Reply"                       , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST},
1295                 {"/Reply to"                    , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST},
1296                 {"/Reply to/all"                , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST},
1297                 {"/Reply to/sender"             , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST},
1298                 {"/Reply to/mailing list"       , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST},
1299
1300                 {"/Forward"                     , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1301                 {"/Forward as attachment"       , M_HAVE_ACCOUNT|M_TARGET_EXIST},
1302                 {"/Redirect"                    , M_HAVE_ACCOUNT|M_SINGLE_TARGET_EXIST},
1303
1304                 {"/Move..."                     , M_TARGET_EXIST|M_ALLOW_DELETE|M_NOT_NEWS},
1305                 {"/Copy..."                     , M_TARGET_EXIST|M_EXEC},
1306                 {"/Move to trash"               , M_TARGET_EXIST|M_ALLOW_DELETE|M_NOT_NEWS},
1307                 {"/Delete..."                   , M_TARGET_EXIST|M_ALLOW_DELETE},
1308
1309                 {"/Mark"                        , M_TARGET_EXIST},
1310                 {"/Mark/Mark"                   , M_TARGET_EXIST},
1311                 {"/Mark/Unmark"                 , M_TARGET_EXIST},
1312                 {"/Mark/Mark as unread"         , M_TARGET_EXIST},
1313                 {"/Mark/Mark all read"          , M_TARGET_EXIST},
1314                 {"/Mark/Ignore thread"          , M_TARGET_EXIST},
1315                 {"/Mark/Lock"                   , M_TARGET_EXIST},
1316                 {"/Mark/Unlock"                 , M_TARGET_EXIST},
1317                 {"/Color label"                 , M_TARGET_EXIST},
1318
1319                 {"/Add sender to address book"  , M_SINGLE_TARGET_EXIST},
1320                 {"/Create filter rule"          , M_SINGLE_TARGET_EXIST|M_UNLOCKED},
1321                 {"/Create processing rule"      , M_SINGLE_TARGET_EXIST|M_UNLOCKED},
1322
1323                 {"/View"                        , M_SINGLE_TARGET_EXIST},
1324                 {"/View/Open in new window"     , M_SINGLE_TARGET_EXIST},
1325                 {"/View/Source"                 , M_SINGLE_TARGET_EXIST},
1326                 {"/View/All header"             , M_SINGLE_TARGET_EXIST},
1327                 {"/Save as..."                  , M_TARGET_EXIST},
1328                 {"/Print..."                    , M_TARGET_EXIST},
1329                 {NULL, 0}
1330         };
1331
1332         main_window_set_menu_sensitive(summaryview->mainwin);
1333
1334         state = main_window_get_current_state(summaryview->mainwin);
1335
1336         for (i = 0; entry[i].entry != NULL; i++) {
1337                 sensitive = ((entry[i].cond & state) == entry[i].cond);
1338                 menu_set_sensitive(ifactory, entry[i].entry, sensitive);
1339         }
1340
1341
1342         summary_lock(summaryview);
1343         menuitem = gtk_item_factory_get_widget(ifactory, "/View/All header");
1344         if (summaryview->messageview 
1345         &&  summaryview->messageview->mimeview
1346         &&  summaryview->messageview->mimeview->textview)
1347                 gtk_check_menu_item_set_active
1348                         (GTK_CHECK_MENU_ITEM(menuitem),
1349                          summaryview->messageview->mimeview->textview->show_all_headers);
1350         summary_unlock(summaryview);
1351 }
1352
1353 void summary_select_prev_unread(SummaryView *summaryview)
1354 {
1355         GtkCTreeNode *node;
1356         gboolean skip_cur = FALSE;
1357
1358         if (summaryview->displayed 
1359         &&  summaryview->selected == summaryview->displayed) {
1360                 debug_print("skipping current\n");
1361                 skip_cur = TRUE;
1362         }
1363
1364         node = summary_find_prev_flagged_msg
1365                 (summaryview, summaryview->selected, MSG_UNREAD, skip_cur);
1366
1367         if (!node || node == summaryview->selected) {
1368                 AlertValue val = 0;
1369
1370                 switch (prefs_common.next_unread_msg_dialog) {
1371                         case NEXTUNREADMSGDIALOG_ALWAYS:
1372                                 val = alertpanel(_("No more unread messages"),
1373                                                  _("No unread message found. "
1374                                                    "Search from the end?"),
1375                                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1376                                 break;
1377                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1378                                 val = G_ALERTALTERNATE;
1379                                 break;
1380                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1381                                 val = !G_ALERTALTERNATE;
1382                                 break;
1383                         default:
1384                                 debug_print(
1385                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1386                 }
1387                 if (val != G_ALERTALTERNATE) return;
1388                 node = summary_find_prev_flagged_msg(summaryview, NULL,
1389                                                      MSG_UNREAD, FALSE);
1390         }
1391
1392         if (!node)
1393                 alertpanel_notice(_("No unread messages."));
1394         else
1395                 summary_select_node(summaryview, node, TRUE, FALSE);
1396 }
1397
1398 void summary_select_next_unread(SummaryView *summaryview)
1399 {
1400         GtkCTreeNode *node = summaryview->selected;
1401         gboolean skip_cur = FALSE;
1402         
1403         if (summaryview->displayed 
1404         &&  summaryview->selected == summaryview->displayed) {
1405                 debug_print("skipping cur (%p %p)\n",
1406                         summaryview->displayed, summaryview->selected);
1407                 skip_cur = TRUE;
1408         }
1409
1410
1411         node = summary_find_next_flagged_msg
1412                 (summaryview, node, MSG_UNREAD, skip_cur);
1413         
1414         if (node)
1415                 summary_select_node(summaryview, node, TRUE, FALSE);
1416         else {
1417                 node = summary_find_next_flagged_msg
1418                         (summaryview, NULL, MSG_UNREAD, FALSE);
1419                 if (node == NULL || node == summaryview->selected) {
1420                         AlertValue val = 0;
1421
1422                         switch (prefs_common.next_unread_msg_dialog) {
1423                                 case NEXTUNREADMSGDIALOG_ALWAYS:
1424                                         val = alertpanel(_("No more unread messages"),
1425                                                          _("No unread message found. "
1426                                                            "Go to next folder?"),
1427                                                          GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1428                                         break;
1429                                 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1430                                         val = G_ALERTALTERNATE;
1431                                         break;
1432                                 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1433                                         val = G_ALERTOTHER;
1434                                         break;
1435                                 default:
1436                                         debug_print(
1437                                                 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1438                         }
1439
1440                         if (val == G_ALERTALTERNATE) {
1441                                 folderview_select_next_unread(summaryview->folderview);
1442                                 return;
1443                         } 
1444                         else
1445                                 return;
1446                 } else
1447                         summary_select_node(summaryview, node, TRUE, FALSE);
1448
1449         }
1450 }
1451
1452 void summary_select_prev_new(SummaryView *summaryview)
1453 {
1454         GtkCTreeNode *node;
1455         gboolean skip_cur = FALSE;
1456
1457         if (summaryview->displayed 
1458         &&  summaryview->selected == summaryview->displayed) {
1459                 debug_print("skipping current\n");
1460                 skip_cur = TRUE;
1461         }
1462
1463         node = summary_find_prev_flagged_msg
1464                 (summaryview, summaryview->selected, MSG_NEW, skip_cur);
1465
1466         if (!node || node == summaryview->selected) {
1467                 AlertValue val = 0;
1468
1469                 switch (prefs_common.next_unread_msg_dialog) {
1470                         case NEXTUNREADMSGDIALOG_ALWAYS:
1471                                 val = alertpanel(_("No more new messages"),
1472                                                  _("No new message found. "
1473                                                    "Search from the end?"),
1474                                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1475                                 break;
1476                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1477                                 val = G_ALERTALTERNATE;
1478                                 break;
1479                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1480                                 val = !G_ALERTALTERNATE;
1481                                 break;
1482                         default:
1483                                 debug_print(
1484                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1485                 }
1486                 if (val != G_ALERTALTERNATE) return;
1487                 node = summary_find_prev_flagged_msg(summaryview, NULL,
1488                                                      MSG_NEW, FALSE);
1489         }
1490
1491         if (!node)
1492                 alertpanel_notice(_("No new messages."));
1493         else
1494                 summary_select_node(summaryview, node, TRUE, FALSE);
1495 }
1496
1497 void summary_select_next_new(SummaryView *summaryview)
1498 {
1499         GtkCTreeNode *node = summaryview->selected;
1500         gboolean skip_cur = FALSE;
1501         
1502         if (summaryview->displayed 
1503         &&  summaryview->selected == summaryview->displayed) {
1504                 debug_print("skipping cur (%p %p)\n",
1505                         summaryview->displayed, summaryview->selected);
1506                 skip_cur = TRUE;
1507         }
1508
1509
1510         node = summary_find_next_flagged_msg
1511                 (summaryview, node, MSG_NEW, skip_cur);
1512         
1513         if (node)
1514                 summary_select_node(summaryview, node, TRUE, FALSE);
1515         else {
1516                 node = summary_find_next_flagged_msg
1517                         (summaryview, NULL, MSG_NEW, FALSE);
1518                 if (node == NULL || node == summaryview->selected) {
1519                         AlertValue val = 0;
1520
1521                         switch (prefs_common.next_unread_msg_dialog) {
1522                                 case NEXTUNREADMSGDIALOG_ALWAYS:
1523                                         val = alertpanel(_("No more new messages"),
1524                                                          _("No new message found. "
1525                                                            "Go to next folder?"),
1526                                                          GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1527                                         break;
1528                                 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1529                                         val = G_ALERTALTERNATE;
1530                                         break;
1531                                 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1532                                         val = G_ALERTOTHER;
1533                                         break;
1534                                 default:
1535                                         debug_print(
1536                                                 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1537                         }
1538
1539                         if (val == G_ALERTALTERNATE) {
1540                                 folderview_select_next_new(summaryview->folderview);
1541                                 return;
1542                         } 
1543                         else
1544                                 return;
1545                 } else
1546                         summary_select_node(summaryview, node, TRUE, FALSE);
1547
1548         }
1549 }
1550
1551 void summary_select_prev_marked(SummaryView *summaryview)
1552 {
1553         GtkCTreeNode *node;
1554
1555         node = summary_find_prev_flagged_msg
1556                 (summaryview, summaryview->selected, MSG_MARKED, TRUE);
1557
1558         if (!node) {
1559                 AlertValue val;
1560
1561                 val = alertpanel(_("No more marked messages"),
1562                                  _("No marked message found. "
1563                                    "Search from the end?"),
1564                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1565                 if (val != G_ALERTALTERNATE) return;
1566                 node = summary_find_prev_flagged_msg(summaryview, NULL,
1567                                                      MSG_MARKED, TRUE);
1568         }
1569
1570         if (!node)
1571                 alertpanel_notice(_("No marked messages."));
1572         else
1573                 summary_select_node(summaryview, node, TRUE, FALSE);
1574 }
1575
1576 void summary_select_next_marked(SummaryView *summaryview)
1577 {
1578         GtkCTreeNode *node;
1579
1580         node = summary_find_next_flagged_msg
1581                 (summaryview, summaryview->selected, MSG_MARKED, TRUE);
1582
1583         if (!node) {
1584                 AlertValue val;
1585
1586                 val = alertpanel(_("No more marked messages"),
1587                                  _("No marked message found. "
1588                                    "Search from the beginning?"),
1589                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1590                 if (val != G_ALERTALTERNATE) return;
1591                 node = summary_find_next_flagged_msg(summaryview, NULL,
1592                                                      MSG_MARKED, TRUE);
1593         }
1594
1595         if (!node)
1596                 alertpanel_notice(_("No marked messages."));
1597         else
1598                 summary_select_node(summaryview, node, TRUE, FALSE);
1599 }
1600
1601 void summary_select_prev_labeled(SummaryView *summaryview)
1602 {
1603         GtkCTreeNode *node;
1604
1605         node = summary_find_prev_flagged_msg
1606                 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
1607
1608         if (!node) {
1609                 AlertValue val;
1610
1611                 val = alertpanel(_("No more labeled messages"),
1612                                  _("No labeled message found. "
1613                                    "Search from the end?"),
1614                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1615                 if (val != G_ALERTALTERNATE) return;
1616                 node = summary_find_prev_flagged_msg(summaryview, NULL,
1617                                                      MSG_CLABEL_FLAG_MASK, TRUE);
1618         }
1619
1620         if (!node)
1621                 alertpanel_notice(_("No labeled messages."));
1622         else
1623                 summary_select_node(summaryview, node, TRUE, FALSE);
1624 }
1625
1626 void summary_select_next_labeled(SummaryView *summaryview)
1627 {
1628         GtkCTreeNode *node;
1629
1630         node = summary_find_next_flagged_msg
1631                 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
1632
1633         if (!node) {
1634                 AlertValue val;
1635
1636                 val = alertpanel(_("No more labeled messages"),
1637                                  _("No labeled message found. "
1638                                    "Search from the beginning?"),
1639                                  GTK_STOCK_NO, "+"GTK_STOCK_YES, NULL);
1640                 if (val != G_ALERTALTERNATE) return;
1641                 node = summary_find_next_flagged_msg(summaryview, NULL,
1642                                                      MSG_CLABEL_FLAG_MASK, TRUE);
1643         }
1644
1645         if (!node)
1646                 alertpanel_notice(_("No labeled messages."));
1647         else
1648                 summary_select_node(summaryview, node, TRUE, FALSE);
1649 }
1650
1651 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1652 {
1653         GtkCTreeNode *node;
1654
1655         node = summary_find_msg_by_msgnum(summaryview, msgnum);
1656         summary_select_node(summaryview, node, FALSE, TRUE);
1657 }
1658
1659 /**
1660  * summary_select_node:
1661  * @summaryview: Summary view.
1662  * @node: Summary tree node.
1663  * @display_msg: TRUE to display the selected message.
1664  * @do_refresh: TRUE to refresh the widget.
1665  *
1666  * Select @node (bringing it into view by scrolling and expanding its
1667  * thread, if necessary) and unselect all others.  If @display_msg is
1668  * TRUE, display the corresponding message in the message view.
1669  * If @do_refresh is TRUE, the widget is refreshed.
1670  **/
1671 void summary_select_node(SummaryView *summaryview, GtkCTreeNode *node,
1672                          gboolean display_msg, gboolean do_refresh)
1673 {
1674         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1675
1676         if (!summaryview->folder_item)
1677                 return;
1678         if (node) {
1679                 gtkut_ctree_expand_parent_all(ctree, node);
1680                 if (do_refresh) {
1681                         GTK_EVENTS_FLUSH();
1682                         gtk_widget_grab_focus(GTK_WIDGET(ctree));
1683                         if (GTK_CTREE_ROW(node) == NULL) {
1684                                 g_warning("crash avoidance hack 1\n");
1685                                 return;
1686                         }
1687                         if (((GtkCListRow *)(GTK_CTREE_ROW(node)))->state < GTK_STATE_NORMAL
1688                         ||  ((GtkCListRow *)(GTK_CTREE_ROW(node)))->state > GTK_STATE_INSENSITIVE) {
1689                                 g_warning("crash avoidance hack 2\n");
1690                                 return;
1691                         }
1692                         gtk_ctree_node_moveto(ctree, node, 0, 0.5, 0);
1693                 }
1694                 summary_unselect_all(summaryview);
1695                 if (display_msg && summaryview->displayed == node)
1696                         summaryview->displayed = NULL;
1697                 summaryview->display_msg = display_msg;
1698                 gtk_sctree_select(GTK_SCTREE(ctree), node);
1699                 if (summaryview->selected == NULL)
1700                         summaryview->selected = node;
1701         }
1702 }
1703
1704 static guint summary_get_msgnum(SummaryView *summaryview, GtkCTreeNode *node)
1705 {
1706         GtkCTree *ctree =NULL;
1707         MsgInfo *msginfo;
1708
1709         if (!summaryview)
1710                 return 0;
1711         ctree = GTK_CTREE(summaryview->ctree);
1712         if (!node)
1713                 return 0;
1714         msginfo = gtk_ctree_node_get_row_data(ctree, node);
1715         return msginfo->msgnum;
1716 }
1717
1718 static GtkCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
1719                                            GtkCTreeNode *current_node)
1720 {
1721         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1722         GtkCTreeNode *node;
1723         MsgInfo *msginfo;
1724
1725         if (current_node)
1726                 node = current_node;
1727         else
1728                 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1729
1730         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1731                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1732                 if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
1733         }
1734
1735         return node;
1736 }
1737
1738 static GtkCTreeNode *summary_find_next_msg(SummaryView *summaryview,
1739                                            GtkCTreeNode *current_node)
1740 {
1741         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1742         GtkCTreeNode *node;
1743         MsgInfo *msginfo;
1744
1745         if (current_node)
1746                 node = current_node;
1747         else
1748                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1749
1750         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1751                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1752                 if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
1753         }
1754
1755         return node;
1756 }
1757
1758 static GtkCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
1759                                                    GtkCTreeNode *current_node,
1760                                                    MsgPermFlags flags,
1761                                                    gboolean start_from_prev)
1762 {
1763         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1764         GtkCTreeNode *node;
1765         MsgInfo *msginfo;
1766
1767         if (current_node) {
1768                 if (start_from_prev)
1769                         node = GTK_CTREE_NODE_PREV(current_node);
1770                 else
1771                         node = current_node;
1772         } else
1773                 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1774
1775         for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1776                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1777                 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
1778         }
1779
1780         return node;
1781 }
1782
1783 static GtkCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
1784                                                    GtkCTreeNode *current_node,
1785                                                    MsgPermFlags flags,
1786                                                    gboolean start_from_next)
1787 {
1788         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1789         GtkCTreeNode *node;
1790         MsgInfo *msginfo;
1791
1792         if (current_node) {
1793                 if (start_from_next)
1794                         node = gtkut_ctree_node_next(ctree, current_node);
1795                 else
1796                         node = current_node;
1797         } else
1798                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1799
1800         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1801                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1802                 /* Find msg with matching flags but ignore messages with
1803                    ignore flags, if searching for new or unread messages */
1804                 if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) &&
1805                     !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags)) 
1806                         )
1807                         break;
1808         }
1809
1810         return node;
1811 }
1812
1813 static GtkCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
1814                                                 guint msgnum)
1815 {
1816         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1817         GtkCTreeNode *node;
1818         MsgInfo *msginfo;
1819
1820         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1821
1822         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1823                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1824                 if (msginfo && msginfo->msgnum == msgnum) break;
1825         }
1826
1827         return node;
1828 }
1829
1830 static guint attract_hash_func(gconstpointer key)
1831 {
1832         gchar *str;
1833         gchar *p;
1834         guint h;
1835
1836         Xstrdup_a(str, (const gchar *)key, return 0);
1837         trim_subject(str);
1838
1839         p = str;
1840         h = *p;
1841
1842         if (h) {
1843                 for (p += 1; *p != '\0'; p++)
1844                         h = (h << 5) - h + *p;
1845         }
1846
1847         return h;
1848 }
1849
1850 static gint attract_compare_func(gconstpointer a, gconstpointer b)
1851 {
1852         return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1853 }
1854
1855 void summary_attract_by_subject(SummaryView *summaryview)
1856 {
1857         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1858         GtkCList *clist = GTK_CLIST(ctree);
1859         GtkCTreeNode *src_node;
1860         GtkCTreeNode *dst_node, *sibling;
1861         GtkCTreeNode *tmp;
1862         MsgInfo *src_msginfo, *dst_msginfo;
1863         GHashTable *subject_table;
1864
1865         debug_print("Attracting messages by subject...");
1866         STATUSBAR_PUSH(summaryview->mainwin,
1867                        _("Attracting messages by subject..."));
1868
1869         main_window_cursor_wait(summaryview->mainwin);
1870         gtk_clist_freeze(clist);
1871
1872         subject_table = g_hash_table_new(attract_hash_func,
1873                                          attract_compare_func);
1874
1875         for (src_node = GTK_CTREE_NODE(clist->row_list);
1876              src_node != NULL;
1877              src_node = tmp) {
1878                 tmp = GTK_CTREE_ROW(src_node)->sibling;
1879                 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
1880                 if (!src_msginfo) continue;
1881                 if (!src_msginfo->subject) continue;
1882
1883                 /* find attracting node */
1884                 dst_node = g_hash_table_lookup(subject_table,
1885                                                src_msginfo->subject);
1886
1887                 if (dst_node) {
1888                         dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
1889
1890                         /* if the time difference is more than 20 days,
1891                            don't attract */
1892                         if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
1893                             > 60 * 60 * 24 * 20)
1894                                 continue;
1895
1896                         sibling = GTK_CTREE_ROW(dst_node)->sibling;
1897                         if (src_node != sibling)
1898                                 gtk_ctree_move(ctree, src_node, NULL, sibling);
1899                 }
1900
1901                 g_hash_table_insert(subject_table,
1902                                     src_msginfo->subject, src_node);
1903         }
1904
1905         g_hash_table_destroy(subject_table);
1906
1907         gtk_ctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
1908
1909         gtk_clist_thaw(clist);
1910
1911         debug_print("done.\n");
1912         STATUSBAR_POP(summaryview->mainwin);
1913
1914         main_window_cursor_normal(summaryview->mainwin);
1915 }
1916
1917 static void summary_free_msginfo_func(GtkCTree *ctree, GtkCTreeNode *node,
1918                                       gpointer data)
1919 {
1920         MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1921
1922         if (msginfo)
1923                 procmsg_msginfo_free(msginfo);
1924 }
1925
1926 static void summary_set_marks_func(GtkCTree *ctree, GtkCTreeNode *node,
1927                                    gpointer data)
1928 {
1929         SummaryView *summaryview = data;
1930         MsgInfo *msginfo;
1931
1932         msginfo = gtk_ctree_node_get_row_data(ctree, node);
1933
1934         if (msginfo->folder && msginfo->folder->folder &&
1935             msginfo->folder->folder->klass->type == F_NEWS)
1936                 news_flag_crosspost(msginfo);
1937
1938         if (MSG_IS_DELETED(msginfo->flags))
1939                 summaryview->deleted++;
1940
1941         summaryview->total_size += msginfo->size;
1942
1943         summary_set_row_marks(summaryview, node);
1944 }
1945
1946 static void summary_update_status(SummaryView *summaryview)
1947 {
1948         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1949         GtkCTreeNode *node;
1950         MsgInfo *msginfo;
1951
1952         summaryview->total_size =
1953         summaryview->deleted = summaryview->moved = summaryview->copied = 0;
1954
1955         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1956              node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1957                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1958
1959                 if (MSG_IS_DELETED(msginfo->flags))
1960                         summaryview->deleted++;
1961                 if (MSG_IS_MOVE(msginfo->flags))
1962                         summaryview->moved++;
1963                 if (MSG_IS_COPY(msginfo->flags))
1964                         summaryview->copied++;
1965                 summaryview->total_size += msginfo->size;
1966         }
1967 }
1968
1969 static void summary_status_show(SummaryView *summaryview)
1970 {
1971         gchar *str;
1972         gchar *del, *mv, *cp;
1973         gchar *sel;
1974         gchar *spc;
1975         gchar *itstr;
1976         GList *rowlist, *cur;
1977         guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0;
1978         off_t sel_size = 0, n_size = 0;
1979         MsgInfo *msginfo;
1980         gchar *name;
1981         
1982         if (!summaryview->folder_item) {
1983                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), "");
1984                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), "");
1985                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs),   "");
1986                 return;
1987         }
1988
1989         rowlist = GTK_CLIST(summaryview->ctree)->selection;
1990         for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
1991                 msginfo = gtk_ctree_node_get_row_data
1992                         (GTK_CTREE(summaryview->ctree),
1993                          GTK_CTREE_NODE(cur->data));
1994                 if (!msginfo)
1995                         g_warning("summary_status_show(): msginfo == NULL\n");
1996                 else {
1997                         sel_size += msginfo->size;
1998                         n_selected++;
1999                         
2000                 }
2001         }
2002         
2003         if (summaryview->folder_item->hide_read_msgs 
2004         || quicksearch_is_active(summaryview->quicksearch)) {
2005                 rowlist = GTK_CLIST(summaryview->ctree)->row_list;
2006                 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2007                         msginfo = gtk_ctree_node_get_row_data
2008                                 (GTK_CTREE(summaryview->ctree),
2009                                  GTK_CTREE_NODE(cur));
2010                         if (!msginfo)
2011                                 g_warning("summary_status_show(): msginfo == NULL\n");
2012                         else {
2013                                 n_size += msginfo->size;
2014                                 n_total++;
2015                                 if (MSG_IS_NEW(msginfo->flags))
2016                                         n_new++;
2017                                 if (MSG_IS_UNREAD(msginfo->flags))
2018                                         n_unread++;
2019                         }
2020                 }
2021         } else {
2022                 n_new = summaryview->folder_item->new_msgs;
2023                 n_unread = summaryview->folder_item->unread_msgs;
2024                 n_total = summaryview->folder_item->total_msgs;
2025                 n_size = summaryview->total_size;
2026         }
2027
2028         name = folder_item_get_name(summaryview->folder_item);
2029         gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
2030         g_free(name);
2031
2032         if (summaryview->deleted)
2033                 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
2034         else
2035                 del = g_strdup("");
2036         if (summaryview->moved)
2037                 mv = g_strdup_printf(_("%s%d moved"),
2038                                      summaryview->deleted ? _(", ") : "",
2039                                      summaryview->moved);
2040         else
2041                 mv = g_strdup("");
2042         if (summaryview->copied)
2043                 cp = g_strdup_printf(_("%s%d copied"),
2044                                      summaryview->deleted ||
2045                                      summaryview->moved ? _(", ") : "",
2046                                      summaryview->copied);
2047         else
2048                 cp = g_strdup("");
2049
2050         if (summaryview->deleted || summaryview->moved || summaryview->copied)
2051                 spc = "    ";
2052         else
2053                 spc = "";
2054
2055         if (n_selected) {
2056                 sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
2057                 if (n_selected == 1)
2058                         itstr = g_strdup(_(" item selected"));
2059                 else
2060                         itstr = g_strdup(_(" items selected"));
2061         } else {
2062                 sel = g_strdup("");
2063                 itstr = g_strdup("");
2064         }
2065                 
2066         str = g_strconcat(n_selected ? itos(n_selected) : "",
2067                                         itstr, sel, spc, del, mv, cp, NULL);
2068         gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2069         g_free(str);
2070         g_free(sel);
2071         g_free(del);
2072         g_free(mv);
2073         g_free(cp);
2074         g_free(itstr);
2075
2076         str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2077
2078                                       n_new, n_unread, n_total,
2079                                       to_human_readable(n_size));
2080         gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str);
2081         g_free(str);
2082 }
2083
2084 static void summary_set_column_titles(SummaryView *summaryview)
2085 {
2086         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2087         GtkWidget *hbox;
2088         GtkWidget *label;
2089         GtkWidget *arrow;
2090         gint pos;
2091         const gchar *title;
2092         SummaryColumnType type;
2093         GtkJustification justify;
2094
2095         static FolderSortKey sort_by[N_SUMMARY_COLS] = {
2096                 SORT_BY_MARK,
2097                 SORT_BY_STATUS,
2098                 SORT_BY_MIME,
2099                 SORT_BY_SUBJECT,
2100                 SORT_BY_FROM,
2101                 SORT_BY_TO,
2102                 SORT_BY_DATE,
2103                 SORT_BY_SIZE,
2104                 SORT_BY_NUMBER,
2105                 SORT_BY_SCORE,
2106                 SORT_BY_LOCKED
2107         };
2108
2109         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
2110                 type = summaryview->col_state[pos].type;
2111
2112                 /* CLAWS: mime and unread are single char headers */
2113                 justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
2114                         ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
2115
2116                 switch (type) {
2117                 case S_COL_SUBJECT:
2118                 case S_COL_FROM:
2119                 case S_COL_TO:
2120                 case S_COL_DATE:
2121                 case S_COL_NUMBER:
2122                         if (prefs_common.trans_hdr)
2123                                 title = gettext(col_label[type]);
2124                         else
2125                                 title = col_label[type];
2126                         break;
2127                 default:
2128                         title = gettext(col_label[type]);
2129                 }
2130
2131                 if (type == S_COL_MIME) {
2132                         label = gtk_image_new_from_pixmap(clipxpm, clipxpmmask);
2133                         gtk_widget_show(label);
2134                         gtk_clist_set_column_widget(clist, pos, label);
2135                         continue;
2136                 } else if (type == S_COL_MARK) {
2137                         label = gtk_image_new_from_pixmap(markxpm, markxpmmask);
2138                         gtk_widget_show(label);
2139                         gtk_clist_set_column_widget(clist, pos, label);
2140                         continue;
2141                 } else if (type == S_COL_LOCKED) {
2142                         label = gtk_image_new_from_pixmap(lockedxpm, lockedxpmmask);
2143                         gtk_widget_show(label);
2144                         gtk_clist_set_column_widget(clist, pos, label);
2145                         continue;
2146                 } else if (type == S_COL_STATUS) {
2147                         gtk_clist_set_column_title(clist, pos, title);
2148                         continue;
2149                 }
2150
2151                 hbox  = gtk_hbox_new(FALSE, 4);
2152                 label = gtk_label_new(title);
2153
2154                 if (justify == GTK_JUSTIFY_RIGHT)
2155                         gtk_box_pack_end(GTK_BOX(hbox), label,
2156                                          FALSE, FALSE, 0);
2157                 else
2158                         gtk_box_pack_start(GTK_BOX(hbox), label,
2159                                            FALSE, FALSE, 0);
2160
2161                 if (summaryview->sort_key == sort_by[type]) {
2162                         arrow = gtk_arrow_new
2163                                 (summaryview->sort_type == SORT_ASCENDING
2164                                  ? GTK_ARROW_DOWN : GTK_ARROW_UP,
2165                                  GTK_SHADOW_IN);
2166                         if (justify == GTK_JUSTIFY_RIGHT)
2167                                 gtk_box_pack_start(GTK_BOX(hbox), arrow,
2168                                                    FALSE, FALSE, 0);
2169                         else
2170                                 gtk_box_pack_end(GTK_BOX(hbox), arrow,
2171                                                  FALSE, FALSE, 0);
2172                 }
2173
2174                 gtk_widget_show_all(hbox);
2175                 gtk_clist_set_column_widget(clist, pos, hbox);
2176         }
2177 }
2178
2179 void summary_reflect_prefs(void)
2180 {
2181         static gchar *last_font = NULL;
2182         gboolean update_font = TRUE;
2183         SummaryView *summaryview = NULL;
2184
2185         if (!mainwindow_get_mainwindow())
2186                 return;
2187         summaryview = mainwindow_get_mainwindow()->summaryview;
2188
2189         if (last_font && !strcmp(last_font, NORMAL_FONT))
2190                 update_font = FALSE;
2191
2192         if (last_font)
2193                 g_free(last_font);
2194         
2195         last_font = g_strdup(NORMAL_FONT);
2196
2197         if (update_font) {      
2198                 bold_style = bold_marked_style = bold_deleted_style = 
2199                         small_style = small_marked_style = small_deleted_style = NULL;
2200                 summary_set_fonts(summaryview);
2201         }
2202
2203         summary_set_column_titles(summaryview);
2204         summary_show(summaryview, summaryview->folder_item);
2205 }
2206
2207 void summary_sort(SummaryView *summaryview,
2208                   FolderSortKey sort_key, FolderSortType sort_type)
2209 {
2210         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2211         GtkCList *clist = GTK_CLIST(summaryview->ctree);
2212         GtkCListCompareFunc cmp_func = NULL;
2213         g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
2214                                        G_CALLBACK(summary_tree_expanded), summaryview);
2215         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
2216
2217         switch (sort_key) {
2218         case SORT_BY_MARK:
2219                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_mark;
2220                 break;
2221         case SORT_BY_STATUS:
2222                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_status;
2223                 break;
2224         case SORT_BY_MIME:
2225                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_mime;
2226                 break;
2227         case SORT_BY_NUMBER:
2228                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_num;
2229                 break;
2230         case SORT_BY_SIZE:
2231                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_size;
2232                 break;
2233         case SORT_BY_DATE:
2234                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_date;
2235                 break;
2236         case SORT_BY_FROM:
2237                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_from;
2238                 break;
2239         case SORT_BY_SUBJECT:
2240                 if (summaryview->simplify_subject_preg)
2241                         cmp_func = (GtkCListCompareFunc)summary_cmp_by_simplified_subject;
2242                 else
2243                         cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
2244                 break;
2245         case SORT_BY_SCORE:
2246                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_score;
2247                 break;
2248         case SORT_BY_LABEL:
2249                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_label;
2250                 break;
2251         case SORT_BY_TO:
2252                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_to;
2253                 break;
2254         case SORT_BY_LOCKED:
2255                 cmp_func = (GtkCListCompareFunc)summary_cmp_by_locked;
2256                 break;
2257         case SORT_BY_NONE:
2258                 break;
2259         default:
2260                 goto unlock;
2261         }
2262
2263         summaryview->sort_key = sort_key;
2264         summaryview->sort_type = sort_type;
2265
2266         summary_set_column_titles(summaryview);
2267         summary_set_menu_sensitive(summaryview);
2268
2269         /* allow fallback to don't sort */
2270         if (summaryview->sort_key == SORT_BY_NONE)
2271                 goto unlock;
2272
2273         if (cmp_func != NULL) {
2274                 debug_print("Sorting summary...");
2275                 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
2276
2277                 main_window_cursor_wait(summaryview->mainwin);
2278
2279                 gtk_clist_freeze(clist);
2280                 gtk_clist_set_compare_func(clist, cmp_func);
2281
2282                 gtk_clist_set_sort_type(clist, (GtkSortType)sort_type);
2283
2284                 gtk_sctree_sort_recursive(ctree, NULL);
2285
2286                 gtk_ctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
2287
2288                 main_window_cursor_normal(summaryview->mainwin);
2289                 gtk_clist_thaw(clist);
2290
2291                 debug_print("done.\n");
2292                 STATUSBAR_POP(summaryview->mainwin);
2293         }
2294 unlock:
2295         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
2296         g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
2297                                        G_CALLBACK(summary_tree_expanded), summaryview);
2298 }
2299
2300 gboolean summary_insert_gnode_func(GtkCTree *ctree, guint depth, GNode *gnode,
2301                                    GtkCTreeNode *cnode, gpointer data)
2302 {
2303         SummaryView *summaryview = (SummaryView *)data;
2304         MsgInfo *msginfo = (MsgInfo *)gnode->data;
2305         gchar *text[N_SUMMARY_COLS];
2306         gint *col_pos = summaryview->col_pos;
2307         const gchar *msgid = msginfo->msgid;
2308         GHashTable *msgid_table = summaryview->msgid_table;
2309         gboolean free_from = FALSE;
2310         
2311         summary_set_header(summaryview, text, msginfo, &free_from);
2312
2313         gtk_sctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
2314                                 NULL, NULL, NULL, NULL, FALSE,
2315                                 gnode->parent->parent ? TRUE : FALSE);
2316 #define SET_TEXT(col) \
2317         gtk_ctree_node_set_text(ctree, cnode, col_pos[col], \
2318                                 text[col_pos[col]])
2319
2320         SET_TEXT(S_COL_NUMBER);
2321         SET_TEXT(S_COL_SCORE);
2322         SET_TEXT(S_COL_SIZE);
2323         SET_TEXT(S_COL_DATE);
2324         SET_TEXT(S_COL_FROM);
2325         SET_TEXT(S_COL_TO);
2326         /* SET_TEXT(S_COL_SUBJECT);  already set by node info */
2327
2328         if (free_from) {
2329                 g_free(text[col_pos[S_COL_FROM]]);
2330                 text[col_pos[S_COL_FROM]] = NULL;
2331         }
2332
2333 #undef SET_TEXT
2334
2335         GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
2336         summary_set_marks_func(ctree, cnode, summaryview);
2337
2338         if (msgid && msgid[0] != '\0')
2339                 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
2340
2341         return TRUE;
2342 }
2343
2344 static void summary_set_ctree_from_list(SummaryView *summaryview,
2345                                         GSList *mlist)
2346 {
2347         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2348         MsgInfo *msginfo;
2349         GtkCTreeNode *node = NULL;
2350         GHashTable *msgid_table;
2351         GHashTable *subject_table;
2352         GSList * cur;
2353         START_TIMING("summary_set_ctree_from_list");
2354         
2355         if (!mlist) return;
2356
2357         debug_print("\tSetting summary from message data...");
2358         STATUSBAR_PUSH(summaryview->mainwin,
2359                        _("Setting summary from message data..."));
2360         gdk_flush();
2361
2362         g_signal_handlers_block_by_func(G_OBJECT(ctree),
2363                                        G_CALLBACK(summary_tree_expanded), summaryview);
2364
2365         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
2366         summaryview->msgid_table = msgid_table;
2367         subject_table = g_hash_table_new(g_str_hash, g_str_equal);
2368         summaryview->subject_table = subject_table;
2369
2370         if (prefs_common.use_addr_book)
2371                 start_address_completion();
2372         
2373         if (summaryview->threaded) {
2374                 GNode *root, *gnode;
2375
2376                 root = procmsg_get_thread_tree(mlist);
2377
2378                 for (gnode = root->children; gnode != NULL;
2379                      gnode = gnode->next) {
2380                         node = gtk_ctree_insert_gnode
2381                                 (ctree, NULL, node, gnode,
2382                                  summary_insert_gnode_func, summaryview);
2383                 }
2384
2385                 g_node_destroy(root);
2386                 
2387                 summary_thread_init(summaryview);
2388         } else {
2389                 gchar *text[N_SUMMARY_COLS];
2390                 gboolean free_from = FALSE;
2391                 gint *col_pos = summaryview->col_pos;
2392
2393                 cur = mlist;
2394                 for (; mlist != NULL; mlist = mlist->next) {
2395                         msginfo = (MsgInfo *)mlist->data;
2396
2397                         summary_set_header(summaryview, text, msginfo, &free_from);
2398
2399                         node = gtk_sctree_insert_node
2400                                 (ctree, NULL, node, text, 2,
2401                                  NULL, NULL, NULL, NULL,
2402                                  FALSE, FALSE);
2403                         if (free_from) {
2404                                 g_free(text[col_pos[S_COL_FROM]]);
2405                                 text[col_pos[S_COL_FROM]] = NULL;
2406                         }
2407                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
2408                         summary_set_marks_func(ctree, node, summaryview);
2409
2410                         if (msginfo->msgid && msginfo->msgid[0] != '\0')
2411                                 g_hash_table_insert(msgid_table,
2412                                                     msginfo->msgid, node);
2413
2414                         subject_table_insert(subject_table,
2415                                              msginfo->subject,
2416                                              node);
2417                 }
2418                 mlist = cur;
2419         }
2420
2421         if (prefs_common.enable_hscrollbar &&
2422             summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
2423                 gint optimal_width;
2424
2425                 optimal_width = gtk_clist_optimal_column_width
2426                         (GTK_CLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
2427                 gtk_clist_set_column_width(GTK_CLIST(ctree),
2428                                            summaryview->col_pos[S_COL_SUBJECT],
2429                                            optimal_width);
2430         }
2431
2432         if (prefs_common.use_addr_book)
2433                 end_address_completion();
2434
2435         debug_print("done.\n");
2436         STATUSBAR_POP(summaryview->mainwin);
2437         if (debug_get_mode()) {
2438                 debug_print("\tmsgid hash table size = %d\n",
2439                             g_hash_table_size(msgid_table));
2440                 debug_print("\tsubject hash table size = %d\n",
2441                             g_hash_table_size(subject_table));
2442         }
2443
2444         summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type);
2445
2446         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2447
2448         if (prefs_common.bold_unread) {
2449                 while (node) {
2450                         GtkCTreeNode *next = GTK_CTREE_NODE_NEXT(node);
2451                         if (GTK_CTREE_ROW(node)->children)
2452                                 summary_set_row_marks(summaryview, node);
2453                         node = next;
2454                 }
2455         }
2456
2457         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
2458                                        G_CALLBACK(summary_tree_expanded), summaryview);
2459         END_TIMING();
2460 }
2461
2462 static gchar *summary_complete_address(const gchar *addr)
2463 {
2464         gint count;
2465         gchar *res, *tmp, *email_addr;
2466         
2467         if (addr == NULL)
2468                 return NULL;
2469
2470         Xstrdup_a(email_addr, addr, return NULL);
2471         extract_address(email_addr);
2472         g_return_val_if_fail(*email_addr, NULL);
2473
2474         /*
2475          * completion stuff must be already initialized
2476          */
2477         res = NULL;
2478         if (1 < (count = complete_address(email_addr))) {
2479                 tmp = get_complete_address(1);
2480                 res = procheader_get_fromname(tmp);
2481                 g_free(tmp);
2482         }
2483
2484         return res;
2485 }
2486
2487 static void summary_set_header(SummaryView *summaryview, gchar *text[],
2488                                MsgInfo *msginfo, gboolean *free_from)
2489 {
2490         static gchar date_modified[80];
2491         static gchar col_score[11];
2492         static gchar buf[BUFFSIZE];
2493         gint *col_pos = summaryview->col_pos;
2494         gchar *from_text = NULL, *to_text = NULL;
2495         gboolean should_swap = FALSE;
2496
2497         text[col_pos[S_COL_FROM]]   = "";
2498         text[col_pos[S_COL_TO]]     = "";
2499         text[col_pos[S_COL_SUBJECT]]= "";
2500         text[col_pos[S_COL_MARK]]   = "";
2501         text[col_pos[S_COL_STATUS]] = "";
2502         text[col_pos[S_COL_MIME]]   = "";
2503         text[col_pos[S_COL_LOCKED]] = "";
2504         text[col_pos[S_COL_DATE]]   = "";
2505         text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
2506         text[col_pos[S_COL_SIZE]]   = to_human_readable(msginfo->size);
2507         text[col_pos[S_COL_SCORE]]  = itos_buf(col_score, msginfo->score);
2508
2509         if (msginfo->date_t) {
2510                 procheader_date_get_localtime(date_modified,
2511                                               sizeof(date_modified),
2512                                               msginfo->date_t);
2513                 text[col_pos[S_COL_DATE]] = date_modified;
2514         } else if (msginfo->date)
2515                 text[col_pos[S_COL_DATE]] = msginfo->date;
2516         else
2517                 text[col_pos[S_COL_DATE]] = _("(No Date)");
2518
2519         if (prefs_common.swap_from && msginfo->from && msginfo->to
2520         &&  !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) {
2521                 gchar *addr = NULL;
2522                 
2523                 addr = g_strdup(msginfo->from);
2524
2525                 if (addr) {
2526                         extract_address(addr);
2527                         if (account_find_from_address(addr)) {
2528                                 should_swap = TRUE;
2529                         }
2530                         g_free(addr);
2531                 }
2532         }
2533
2534         if (!prefs_common.use_addr_book) {
2535                 from_text = msginfo->fromname ? 
2536                                 msginfo->fromname :
2537                                 _("(No From)");
2538         } else {
2539                 gchar *tmp = summary_complete_address(msginfo->from);
2540                 from_text = tmp ? tmp : (msginfo->fromname ?
2541                                          msginfo->fromname: 
2542                                                 _("(No From)"));
2543         }
2544         
2545         to_text = msginfo->to ? msginfo->to : 
2546                    (msginfo->cc ? msginfo->cc :
2547                      (msginfo->newsgroups ? msginfo->newsgroups : _("(No Recipient)")
2548                      )
2549                    );
2550
2551         text[col_pos[S_COL_TO]] = to_text;
2552         if (!should_swap) {
2553                 text[col_pos[S_COL_FROM]] = from_text;
2554                 *free_from = FALSE;
2555         } else {
2556                 gchar *tmp = NULL;
2557                 tmp = g_strconcat("-->", to_text, NULL);
2558                 text[col_pos[S_COL_FROM]] = tmp;
2559                 *free_from = TRUE;
2560         }
2561         
2562         if (summaryview->simplify_subject_preg != NULL)
2563                 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? 
2564                         string_remove_match(buf, BUFFSIZE, msginfo->subject, 
2565                                         summaryview->simplify_subject_preg) : 
2566                         _("(No Subject)");
2567         else 
2568                 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
2569                         _("(No Subject)");
2570 }
2571
2572 static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row)
2573 {
2574         summary_display_msg_full(summaryview, row, FALSE, FALSE);
2575 }
2576
2577 static gboolean defer_change(gpointer data);
2578 typedef struct _ChangeData {
2579         MsgInfo *info;
2580         gint op; /* 0, 1, 2 for unset, set, change */
2581         MsgPermFlags set_flags;
2582         MsgTmpFlags  set_tmp_flags;
2583         MsgPermFlags unset_flags;
2584         MsgTmpFlags  unset_tmp_flags;
2585 } ChangeData;
2586
2587 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
2588 {
2589         if (!msginfo->folder || !msginfo->folder->processing_pending) {
2590                 debug_print("flags: doing unset now\n");
2591                 procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags);
2592         } else {
2593                 ChangeData *unset_data = g_new0(ChangeData, 1);
2594                 unset_data->info = msginfo;
2595                 unset_data->op = 0;
2596                 unset_data->unset_flags = flags;
2597                 unset_data->unset_tmp_flags = tmp_flags;
2598                 debug_print("flags: deferring unset\n");
2599                 g_timeout_add(100, defer_change, unset_data);
2600         }
2601 }
2602
2603 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
2604 {
2605         if (!msginfo->folder || !msginfo->folder->processing_pending) {
2606                 debug_print("flags: doing set now\n");
2607                 procmsg_msginfo_set_flags(msginfo, flags, tmp_flags);
2608         } else {
2609                 ChangeData *set_data = g_new0(ChangeData, 1);
2610                 set_data->info = msginfo;
2611                 set_data->op = 1;
2612                 set_data->set_flags = flags;
2613                 set_data->set_tmp_flags = tmp_flags;
2614                 debug_print("flags: deferring set\n");
2615                 g_timeout_add(100, defer_change, set_data);
2616         }
2617 }
2618
2619 static void summary_msginfo_change_flags(MsgInfo *msginfo, 
2620                 MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags,
2621                 MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags)
2622 {
2623         if (!msginfo->folder || !msginfo->folder->processing_pending) {
2624                 debug_print("flags: doing change now\n");
2625                 procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags,
2626                         rem_flags, rem_tmp_flags);
2627         } else {
2628                 ChangeData *change_data = g_new0(ChangeData, 1);
2629                 change_data->info = msginfo;
2630                 change_data->op = 2;
2631                 change_data->set_flags = add_flags;
2632                 change_data->set_tmp_flags = add_tmp_flags;
2633                 change_data->unset_flags = rem_flags;
2634                 change_data->unset_tmp_flags = rem_tmp_flags;
2635                 debug_print("flags: deferring change\n");
2636                 g_timeout_add(100, defer_change, change_data);
2637         }
2638 }
2639
2640 gboolean defer_change(gpointer data)
2641 {
2642         ChangeData *chg = (ChangeData *)data;
2643         if (chg->info->folder && chg->info->folder->processing_pending) {
2644                 debug_print("flags: trying later\n");
2645                 return TRUE; /* try again */
2646         } else {
2647                 debug_print("flags: finally doing it\n");
2648                 switch(chg->op) {
2649                 case 0:
2650                         procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags);
2651                         break;
2652                 case 1:
2653                         procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags);
2654                         break;
2655                 case 2:
2656                         procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags,
2657                                 chg->unset_flags, chg->unset_tmp_flags);
2658                         break;
2659                 default:
2660                         g_warning("shouldn't happen\n");
2661                 }
2662                 g_free(chg);
2663         }
2664         return FALSE;
2665 }
2666
2667 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo,
2668                                       GtkCTreeNode *row)
2669 {
2670         g_return_if_fail(summaryview != NULL);
2671         g_return_if_fail(msginfo != NULL);
2672         g_return_if_fail(row != NULL);
2673
2674         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2675                 summary_msginfo_unset_flags
2676                         (msginfo, MSG_NEW | MSG_UNREAD, 0);
2677                 summary_set_row_marks(summaryview, row);
2678                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
2679                 summary_status_show(summaryview);
2680         }
2681 }
2682
2683 typedef struct  {
2684         MsgInfo *msginfo;
2685         SummaryView *summaryview;
2686         GtkCTreeNode *row;
2687 } MarkAsReadData;
2688
2689 static int msginfo_mark_as_read_timeout(void *data)
2690 {
2691         MarkAsReadData *mdata = (MarkAsReadData *)data;
2692         if (!mdata)
2693                 return FALSE;
2694         
2695         if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview))
2696                 msginfo_mark_as_read(mdata->summaryview, mdata->msginfo,
2697                                      mdata->row); 
2698
2699         g_free(mdata);
2700
2701         return FALSE;   
2702 }
2703
2704 static void summary_display_msg_full(SummaryView *summaryview,
2705                                      GtkCTreeNode *row,
2706                                      gboolean new_window, gboolean all_headers)
2707 {
2708         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2709         MsgInfo *msginfo;
2710         gint val;
2711
2712         if (!new_window) {
2713                 if (summaryview->displayed == row)
2714                         return;
2715                 else
2716                         summaryview->messageview->filtered = FALSE;
2717         }                       
2718         
2719         g_return_if_fail(row != NULL);
2720
2721         if (summary_is_locked(summaryview)) return;
2722         summary_lock(summaryview);
2723
2724         STATUSBAR_POP(summaryview->mainwin);
2725         GTK_EVENTS_FLUSH();
2726
2727         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2728
2729         if (new_window) {
2730                 MessageView *msgview;
2731
2732                 msgview = messageview_create_with_new_window(summaryview->mainwin);
2733                 val = messageview_show(msgview, msginfo, all_headers);
2734         } else {
2735                 MessageView *msgview;
2736
2737                 msgview = summaryview->messageview;
2738
2739                 summaryview->displayed = row;
2740                 if (!messageview_is_visible(msgview)) {
2741                         main_window_toggle_message_view(summaryview->mainwin);
2742                         GTK_EVENTS_FLUSH();
2743                 }
2744                 val = messageview_show(msgview, msginfo, all_headers);
2745                 if (GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL)
2746                         gtk_widget_grab_focus(summaryview->ctree);
2747                 gtkut_ctree_node_move_if_on_the_edge(ctree, row);
2748         }
2749
2750         if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) {
2751                 if (prefs_common.mark_as_read_delay) {
2752                         MarkAsReadData *data = g_new0(MarkAsReadData, 1);
2753                         data->summaryview = summaryview;
2754                         data->msginfo = msginfo;
2755                         data->row = row;
2756                         gtk_timeout_add(prefs_common.mark_as_read_delay * 1000,
2757                                 msginfo_mark_as_read_timeout, data);
2758                 } else if (new_window || !prefs_common.mark_as_read_on_new_window) {
2759                         msginfo_mark_as_read(summaryview, msginfo, row);
2760                 }
2761         }
2762
2763         summary_set_menu_sensitive(summaryview);
2764         toolbar_main_set_sensitive(summaryview->mainwin);
2765         messageview_set_menu_sensitive(summaryview->messageview);
2766
2767         summary_unlock(summaryview);
2768 }
2769
2770 void summary_display_msg_selected(SummaryView *summaryview,
2771                                   gboolean all_headers)
2772 {
2773         if (summary_is_locked(summaryview)) return;
2774         summaryview->displayed = NULL;
2775         summary_display_msg_full(summaryview, summaryview->selected, FALSE,
2776                                  all_headers);
2777 }
2778
2779 void summary_redisplay_msg(SummaryView *summaryview)
2780 {
2781         GtkCTreeNode *node;
2782
2783         if (summaryview->displayed) {
2784                 node = summaryview->displayed;
2785                 summaryview->displayed = NULL;
2786                 summary_display_msg(summaryview, node);
2787         }
2788 }
2789
2790 void summary_open_msg(SummaryView *summaryview)
2791 {
2792         if (!summaryview->selected) return;
2793         
2794         /* CLAWS: if separate message view, don't open a new window
2795          * but rather use the current separated message view */
2796         summary_display_msg_full(summaryview, summaryview->selected,
2797                                  prefs_common.sep_msg ? FALSE : TRUE, 
2798                                  FALSE);
2799 }
2800
2801 void summary_view_source(SummaryView * summaryview)
2802 {
2803         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2804         MsgInfo *msginfo;
2805         SourceWindow *srcwin;
2806
2807         if (!summaryview->selected) return;
2808
2809         srcwin = source_window_create();
2810         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2811         source_window_show_msg(srcwin, msginfo);
2812         source_window_show(srcwin);
2813 }
2814
2815 void summary_reedit(SummaryView *summaryview)
2816 {
2817         MsgInfo *msginfo;
2818
2819         if (!summaryview->selected) return;
2820         if (!summaryview->folder_item) return;
2821         if (!folder_has_parent_of_type(summaryview->folder_item, F_OUTBOX)
2822         &&  !folder_has_parent_of_type(summaryview->folder_item, F_DRAFT)
2823         &&  !folder_has_parent_of_type(summaryview->folder_item, F_QUEUE))
2824                 return;
2825
2826         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2827                                               summaryview->selected);
2828         if (!msginfo) return;
2829
2830         compose_reedit(msginfo);
2831 }
2832
2833 gboolean summary_step(SummaryView *summaryview, GtkScrollType type)
2834 {
2835         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2836         GtkCTreeNode *node;
2837
2838         if (summary_is_locked(summaryview)) return FALSE;
2839         if (type == GTK_SCROLL_STEP_FORWARD) {
2840                 node = gtkut_ctree_node_next(ctree, summaryview->selected);
2841                 if (node)
2842                         gtkut_ctree_expand_parent_all(ctree, node);
2843                 else
2844                         return FALSE;
2845         } else {
2846                 if (summaryview->selected) {
2847                         node = GTK_CTREE_NODE_PREV(summaryview->selected);
2848                         if (!node) return FALSE;
2849                 }
2850         }
2851
2852         if (messageview_is_visible(summaryview->messageview))
2853                 summaryview->display_msg = TRUE;
2854
2855         g_signal_emit_by_name(G_OBJECT(ctree), "scroll_vertical", type, 0.0);
2856
2857         if (GTK_CLIST(ctree)->selection)
2858                 gtk_sctree_set_anchor_row
2859                         (GTK_SCTREE(ctree),
2860                          GTK_CTREE_NODE(GTK_CLIST(ctree)->selection->data));
2861
2862         return TRUE;
2863 }
2864
2865 void summary_toggle_view(SummaryView *summaryview)
2866 {
2867         if (!messageview_is_visible(summaryview->messageview) &&
2868             summaryview->selected)
2869                 summary_display_msg(summaryview,
2870                                     summaryview->selected);
2871         else
2872                 main_window_toggle_message_view(summaryview->mainwin);
2873 }
2874
2875 static gboolean summary_search_unread_recursive(GtkCTree *ctree,
2876                                                 GtkCTreeNode *node)
2877 {
2878         MsgInfo *msginfo;
2879
2880         if (node) {
2881                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
2882                 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2883                         return TRUE;
2884                 node = GTK_CTREE_ROW(node)->children;
2885         } else
2886                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2887
2888         while (node) {
2889                 if (summary_search_unread_recursive(ctree, node) == TRUE)
2890                         return TRUE;
2891                 node = GTK_CTREE_ROW(node)->sibling;
2892         }
2893
2894         return FALSE;
2895 }
2896
2897 static gboolean summary_have_unread_children(SummaryView *summaryview,
2898                                              GtkCTreeNode *node)
2899 {
2900         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2901
2902         if (!node) return FALSE;
2903
2904         node = GTK_CTREE_ROW(node)->children;
2905
2906         while (node) {
2907                 if (summary_search_unread_recursive(ctree, node) == TRUE)
2908                         return TRUE;
2909                 node = GTK_CTREE_ROW(node)->sibling;
2910         }
2911         return FALSE;
2912 }
2913
2914 static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2915 {
2916         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2917         GtkStyle *style = NULL;
2918         MsgInfo *msginfo;
2919         MsgFlags flags;
2920         gint *col_pos = summaryview->col_pos;
2921
2922         msginfo = gtk_ctree_node_get_row_data(ctree, row);
2923         if (!msginfo) return;
2924
2925         flags = msginfo->flags;
2926
2927         gtk_ctree_node_set_foreground(ctree, row, NULL);
2928
2929         /* set new/unread column */
2930         if (MSG_IS_IGNORE_THREAD(flags)) {
2931                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
2932                                           ignorethreadxpm, ignorethreadxpmmask);
2933         } else if (MSG_IS_NEW(flags)) {
2934                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
2935                                           newxpm, newxpmmask);
2936         } else if (MSG_IS_UNREAD(flags)) {
2937                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
2938                                           unreadxpm, unreadxpmmask);
2939         } else if (MSG_IS_REPLIED(flags)) {
2940                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
2941                                           repliedxpm, repliedxpmmask);
2942         } else if (MSG_IS_FORWARDED(flags)) {
2943                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_STATUS],
2944                                           forwardedxpm, forwardedxpmmask);
2945         } else {
2946                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_STATUS],
2947                                         "");
2948         }
2949
2950         if (prefs_common.bold_unread &&
2951             ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
2952              (!GTK_CTREE_ROW(row)->expanded &&
2953               GTK_CTREE_ROW(row)->children &&
2954               summary_have_unread_children(summaryview, row))))
2955                 style = bold_style;
2956
2957         /* set mark column */
2958         if (MSG_IS_DELETED(flags)) {
2959                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2960                                           deletedxpm, deletedxpmmask);
2961                 if (style)
2962                         style = bold_deleted_style;
2963                 else {
2964                         style = small_deleted_style;
2965                 }
2966                         gtk_ctree_node_set_foreground
2967                                 (ctree, row, &summaryview->color_dim);
2968         } else if (MSG_IS_MARKED(flags)) {
2969                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2970                                           markxpm, markxpmmask);
2971         } else if (MSG_IS_MOVE(flags)) {
2972                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "o");
2973                 if (style)
2974                         style = bold_marked_style;
2975                 else {
2976                         style = small_marked_style;
2977                 }
2978                         gtk_ctree_node_set_foreground
2979                                 (ctree, row, &summaryview->color_marked);
2980         } else if (MSG_IS_COPY(flags)) {
2981                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "O");
2982                 if (style)
2983                         style = bold_marked_style;
2984                 else {
2985                         style = small_marked_style;
2986                 }
2987                         gtk_ctree_node_set_foreground
2988                                 (ctree, row, &summaryview->color_marked);
2989 #if 0
2990         } else if ((global_scoring ||
2991                   summaryview->folder_item->prefs->scoring) &&
2992                  (msginfo->score >= summaryview->important_score) &&
2993                  (MSG_IS_MARKED(msginfo->flags) || MSG_IS_MOVE(msginfo->flags) || MSG_IS_COPY(msginfo->flags))) {
2994                 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "!");
2995                 gtk_ctree_node_set_foreground(ctree, row,
2996                                               &summaryview->color_important);
2997 #endif
2998         } else {
2999                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "");
3000         }
3001
3002         if (MSG_IS_LOCKED(flags)) {
3003                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_LOCKED],
3004                                           lockedxpm, lockedxpmmask);
3005         }
3006         else {
3007                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], "");
3008         }
3009
3010         if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
3011                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3012                                           clipgpgsignedxpm, clipgpgsignedxpmmask);
3013         } else if (MSG_IS_SIGNED(flags)) {
3014                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3015                                           gpgsignedxpm, gpgsignedxpmmask);
3016         } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
3017                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3018                                           clipkeyxpm, clipkeyxpmmask);
3019         } else if (MSG_IS_ENCRYPTED(flags)) {
3020                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3021                                           keyxpm, keyxpmmask);
3022         } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
3023                 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
3024                                           clipxpm, clipxpmmask);
3025         } else {
3026                 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MIME], "");
3027         }
3028         if (!style)
3029                 style = small_style;
3030
3031         gtk_ctree_node_set_row_style(ctree, row, style);
3032
3033         if (MSG_GET_COLORLABEL(flags))
3034                 summary_set_colorlabel_color(ctree, row, MSG_GET_COLORLABEL_VALUE(flags));
3035 }
3036
3037 static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
3038 {
3039         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3040         MsgInfo *msginfo;
3041
3042         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3043         if (MSG_IS_DELETED(msginfo->flags))
3044                 summaryview->deleted--;
3045         if (MSG_IS_MOVE(msginfo->flags))
3046                 summaryview->moved--;
3047         if (MSG_IS_COPY(msginfo->flags))
3048                 summaryview->copied--;
3049
3050         procmsg_msginfo_set_to_folder(msginfo, NULL);
3051         summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED, MSG_MOVE | MSG_COPY);
3052         summary_set_row_marks(summaryview, row);
3053         debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum);
3054 }
3055
3056 static void summary_lock_row(SummaryView *summaryview, GtkCTreeNode *row)
3057 {
3058         gboolean changed = FALSE;
3059         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3060         MsgInfo *msginfo;
3061
3062         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3063         if (MSG_IS_DELETED(msginfo->flags))
3064                 summaryview->deleted--;
3065         if (MSG_IS_MOVE(msginfo->flags)) {
3066                 summaryview->moved--;
3067                 changed = TRUE;
3068         }
3069         if (MSG_IS_COPY(msginfo->flags)) {
3070                 summaryview->copied--;
3071                 changed = TRUE;
3072         }
3073         procmsg_msginfo_set_to_folder(msginfo, NULL);
3074         summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED, MSG_MOVE | MSG_COPY);
3075         
3076         summary_set_row_marks(summaryview, row);
3077         debug_print("Message %d is locked\n", msginfo->msgnum);
3078 }
3079
3080 static void summary_unlock_row(SummaryView *summaryview, GtkCTreeNode *row)
3081 {
3082         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3083         MsgInfo *msginfo;
3084
3085         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3086         if (!MSG_IS_LOCKED(msginfo->flags))
3087                 return;
3088         procmsg_msginfo_set_to_folder(msginfo, NULL);
3089         summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
3090         summary_set_row_marks(summaryview, row);
3091         debug_print("Message %d is unlocked\n", msginfo->msgnum);
3092 }
3093
3094 void summary_mark(SummaryView *summaryview)
3095 {
3096         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3097         GList *cur;
3098
3099         START_LONG_OPERATION(summaryview);
3100         folder_item_set_batch(summaryview->folder_item, TRUE);
3101         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
3102                 summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
3103         folder_item_set_batch(summaryview->folder_item, FALSE);
3104         END_LONG_OPERATION(summaryview);
3105
3106         summary_status_show(summaryview);
3107 }
3108
3109 static void summary_mark_row_as_read(SummaryView *summaryview,
3110                                      GtkCTreeNode *row)
3111 {
3112         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3113         MsgInfo *msginfo;
3114
3115         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3116
3117         if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
3118                 return;
3119
3120         summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
3121         summary_set_row_marks(summaryview, row);
3122         debug_print("Message %d is marked as read\n",
3123                 msginfo->msgnum);
3124 }
3125
3126 void summary_mark_as_read(SummaryView *summaryview)
3127 {
3128         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3129         GList *cur;
3130
3131         START_LONG_OPERATION(summaryview);
3132         folder_item_set_batch(summaryview->folder_item, TRUE);
3133         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
3134                 summary_mark_row_as_read(summaryview,
3135                                          GTK_CTREE_NODE(cur->data));
3136         folder_item_set_batch(summaryview->folder_item, FALSE);
3137         END_LONG_OPERATION(summaryview);
3138         
3139         summary_status_show(summaryview);
3140 }
3141
3142 void summary_msgs_lock(SummaryView *summaryview)
3143 {
3144         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3145         GList *cur;
3146
3147         START_LONG_OPERATION(summaryview);
3148         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
3149                 summary_lock_row(summaryview,
3150                                          GTK_CTREE_NODE(cur->data));
3151         END_LONG_OPERATION(summaryview);
3152         
3153         summary_status_show(summaryview);
3154 }
3155
3156 void summary_msgs_unlock(SummaryView *summaryview)
3157 {
3158         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3159         GList *cur;
3160
3161         START_LONG_OPERATION(summaryview);
3162         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
3163                 summary_unlock_row(summaryview,
3164                                    GTK_CTREE_NODE(cur->data));
3165         END_LONG_OPERATION(summaryview);
3166         
3167         summary_status_show(summaryview);
3168 }
3169
3170 void summary_mark_all_read(SummaryView *summaryview)
3171 {
3172         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3173         GtkCTreeNode *node;
3174
3175         START_LONG_OPERATION(summaryview);
3176         folder_item_set_batch(summaryview->folder_item, TRUE);
3177         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
3178              node = gtkut_ctree_node_next(ctree, node))
3179                 summary_mark_row_as_read(summaryview, node);
3180         folder_item_set_batch(summaryview->folder_item, FALSE);
3181         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
3182              node = gtkut_ctree_node_next(ctree, node)) {
3183                 if (!GTK_CTREE_ROW(node)->expanded)
3184                         summary_set_row_marks(summaryview, node);
3185         }
3186         END_LONG_OPERATION(summaryview);
3187         
3188         summary_status_show(summaryview);
3189 }
3190
3191 static void summary_mark_row_as_unread(SummaryView *summaryview,
3192                                        GtkCTreeNode *row)
3193 {
3194         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3195         MsgInfo *msginfo;
3196
3197         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3198         if (MSG_IS_DELETED(msginfo->flags)) {
3199                 procmsg_msginfo_set_to_folder(msginfo, NULL);
3200                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, 0);
3201                 summaryview->deleted--;
3202         }
3203
3204         summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0);
3205         debug_print("Message %d is marked as unread\n",
3206                 msginfo->msgnum);
3207
3208         summary_set_row_marks(summaryview, row);
3209 }
3210
3211 void summary_mark_as_unread(SummaryView *summaryview)
3212 {
3213         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3214         GList *cur;
3215
3216         START_LONG_OPERATION(summaryview);
3217         folder_item_set_batch(summaryview->folder_item, TRUE);
3218         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; 
3219                 cur = cur->next)
3220                 summary_mark_row_as_unread(summaryview,
3221                                            GTK_CTREE_NODE(cur->data));
3222         folder_item_set_batch(summaryview->folder_item, FALSE);
3223         END_LONG_OPERATION(summaryview);
3224         
3225         summary_status_show(summaryview);
3226 }
3227
3228 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo)
3229 {
3230         GList * cur;
3231         gboolean found;
3232
3233         switch (FOLDER_TYPE(summaryview->folder_item->folder)) {
3234
3235         case F_NEWS:
3236
3237                 /*
3238                   security : checks if one the accounts correspond to
3239                   the author of the post
3240                 */
3241
3242                 found = FALSE;
3243                 for(cur = account_get_list() ; cur != NULL ; cur = cur->next) {
3244                         PrefsAccount * account;
3245                         gchar * from_name;
3246                         
3247                         account = cur->data;
3248                         if (account->name && *account->name)
3249                                 from_name =
3250                                         g_strdup_printf("%s <%s>",
3251                                                         account->name,
3252                                                         account->address);
3253                         else
3254                                 from_name =
3255                                         g_strdup_printf("%s",
3256                                                         account->address);
3257                         
3258                         if (g_utf8_collate(from_name, msginfo->from) == 0) {
3259                                 g_free(from_name);
3260                                 found = TRUE;
3261                                 break;
3262                         }
3263                         g_free(from_name);
3264                 }
3265
3266                 if (!found) {
3267                         alertpanel_error(_("You're not the author of the article.\n"));
3268                 }
3269                 
3270                 return found;
3271
3272         default:
3273                 return TRUE;
3274         }
3275 }
3276
3277 static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
3278 {
3279         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3280         MsgInfo *msginfo;
3281
3282         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3283
3284         if (MSG_IS_LOCKED(msginfo->flags)) return;
3285
3286         if (MSG_IS_DELETED(msginfo->flags)) return;
3287
3288         if (MSG_IS_MOVE(msginfo->flags))
3289                 summaryview->moved--;
3290         if (MSG_IS_COPY(msginfo->flags))
3291                 summaryview->copied--;
3292
3293         procmsg_msginfo_set_to_folder(msginfo, NULL);
3294         summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED, MSG_MOVE | MSG_COPY);
3295         summaryview->deleted++;
3296
3297         if (!prefs_common.immediate_exec && 
3298             !folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
3299                 summary_set_row_marks(summaryview, row);
3300
3301         debug_print("Message %s/%d is set to delete\n",
3302                     msginfo->folder->path, msginfo->msgnum);
3303 }
3304
3305 void summary_cancel(SummaryView *summaryview)
3306 {
3307         MsgInfo * msginfo;
3308         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3309
3310         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
3311                                               summaryview->selected);
3312         if (!msginfo) return;
3313
3314         if (!check_permission(summaryview, msginfo))
3315                 return;
3316
3317         news_cancel_article(summaryview->folder_item->folder, msginfo);
3318         
3319         if (summary_is_locked(summaryview)) return;
3320
3321         summary_lock(summaryview);
3322
3323         gtk_clist_freeze(clist);
3324
3325         summary_update_status(summaryview);
3326         summary_status_show(summaryview);
3327
3328         gtk_clist_thaw(clist);
3329
3330         summary_unlock(summaryview);
3331 }
3332
3333 void summary_delete(SummaryView *summaryview)
3334 {
3335         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3336         FolderItem *item = summaryview->folder_item;
3337         GList *cur;
3338         GtkCTreeNode *sel_last = NULL;
3339         GtkCTreeNode *node;
3340         AlertValue aval;
3341         MsgInfo *msginfo;
3342
3343         if (!item) return;
3344
3345         if (summary_is_locked(summaryview)) return;
3346
3347         if (!summaryview->folder_item) return;
3348
3349         aval = alertpanel(_("Delete message(s)"),
3350                           _("Do you really want to delete selected message(s)?"),
3351                           GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL);
3352         if (aval != G_ALERTALTERNATE) return;
3353
3354         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; 
3355              cur = cur->next) {
3356                 GtkCTreeNode *row = GTK_CTREE_NODE(cur->data);
3357                 msginfo = gtk_ctree_node_get_row_data(ctree, row);
3358                 if (msginfo->total_size != 0 && 
3359                     msginfo->size != (off_t)msginfo->total_size)
3360                         partial_mark_for_delete(msginfo);
3361         }
3362
3363         main_window_cursor_wait(summaryview->mainwin);
3364
3365         /* next code sets current row focus right. We need to find a row
3366          * that is not deleted. */
3367         START_LONG_OPERATION(summaryview);
3368         folder_item_set_batch(summaryview->folder_item, TRUE);
3369         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
3370                 sel_last = GTK_CTREE_NODE(cur->data);
3371                 summary_delete_row(summaryview, sel_last);
3372         }
3373         folder_item_set_batch(summaryview->folder_item, FALSE);
3374         END_LONG_OPERATION(summaryview);
3375
3376         node = summary_find_next_msg(summaryview, sel_last);
3377         if (!node)
3378                 node = summary_find_prev_msg(summaryview, sel_last);
3379
3380         summary_select_node(summaryview, node, prefs_common.always_show_msg, TRUE);
3381         
3382         if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) {
3383                 summary_execute(summaryview);
3384                 /* after deleting, the anchor may be at an invalid row
3385                  * so reset it to the node we found earlier */
3386                 gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node);
3387         } else
3388                 summary_status_show(summaryview);
3389
3390                 
3391         main_window_cursor_normal(summaryview->mainwin);
3392 }
3393
3394 void summary_delete_trash(SummaryView *summaryview)
3395 {
3396         FolderItem *to_folder = NULL;
3397         PrefsAccount *ac;
3398         if (!summaryview->folder_item ||
3399             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3400         
3401         if (NULL != (ac = account_find_from_item(summaryview->folder_item)))
3402                 to_folder = account_get_special_folder(ac, F_TRASH);
3403
3404         if (to_folder == NULL)
3405                 to_folder = summaryview->folder_item->folder->trash;
3406         
3407         if (to_folder == NULL || to_folder == summaryview->folder_item
3408             || folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
3409                 summary_delete(summaryview);
3410         else
3411                 summary_move_selected_to(summaryview, to_folder);
3412 }
3413
3414
3415 static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
3416 {
3417         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3418         MsgInfo *msginfo;
3419
3420         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3421         if (MSG_IS_DELETED(msginfo->flags))
3422                 summaryview->deleted--;
3423         if (MSG_IS_MOVE(msginfo->flags))
3424                 summaryview->moved--;
3425         if (MSG_IS_COPY(msginfo->flags))
3426                 summaryview->copied--;
3427
3428         procmsg_msginfo_set_to_folder(msginfo, NULL);
3429         summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED, MSG_MOVE | MSG_COPY);
3430         summary_set_row_marks(summaryview, row);
3431
3432         debug_print("Message %s/%d is unmarked\n",
3433                     msginfo->folder->path, msginfo->msgnum);
3434 }
3435
3436 void summary_unmark(SummaryView *summaryview)
3437 {
3438         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3439         GList *cur;
3440
3441         START_LONG_OPERATION(summaryview);
3442         folder_item_set_batch(summaryview->folder_item, TRUE);
3443         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
3444                 summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
3445         folder_item_set_batch(summaryview->folder_item, FALSE);
3446         END_LONG_OPERATION(summaryview);
3447         
3448         summary_status_show(summaryview);
3449 }
3450
3451 static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
3452                                 FolderItem *to_folder)
3453 {
3454         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3455         MsgInfo *msginfo;
3456
3457         g_return_if_fail(to_folder != NULL);
3458
3459         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3460         if (MSG_IS_LOCKED(msginfo->flags))
3461                 return;
3462
3463         procmsg_msginfo_set_to_folder(msginfo, to_folder);
3464         if (MSG_IS_DELETED(msginfo->flags))
3465                 summaryview->deleted--;
3466         if (MSG_IS_COPY(msginfo->flags)) {
3467                 summaryview->copied--;
3468         }
3469         if (!MSG_IS_MOVE(msginfo->flags)) {
3470                 summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED, MSG_COPY);
3471                 summaryview->moved++;
3472         } else {
3473                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY);
3474         }
3475         
3476         if (!prefs_common.immediate_exec) {
3477                 summary_set_row_marks(summaryview, row);
3478         }
3479
3480         debug_print("Message %d is set to move to %s\n",
3481                     msginfo->msgnum, to_folder->path);
3482 }
3483
3484 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3485 {
3486         GList *cur;
3487
3488         if (!to_folder) return;
3489         if (!summaryview->folder_item ||
3490             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3491
3492         if (summary_is_locked(summaryview)) return;
3493
3494         if (summaryview->folder_item == to_folder) {
3495                 alertpanel_error(_("Destination is same as current folder."));
3496                 return;
3497         }
3498
3499         START_LONG_OPERATION(summaryview);
3500
3501         for (cur = GTK_CLIST(summaryview->ctree)->selection;
3502              cur != NULL && cur->data != NULL; cur = cur->next)
3503                 summary_move_row_to
3504                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
3505
3506         END_LONG_OPERATION(summaryview);
3507
3508         summaryview->display_msg = prefs_common.always_show_msg;
3509         
3510         if (prefs_common.immediate_exec) {
3511                 summary_execute(summaryview);
3512         } else {
3513                 summary_status_show(summaryview);
3514         }
3515         
3516         if (!summaryview->selected) { /* this was the last message */
3517                 GtkCTreeNode *node = gtk_ctree_node_nth (GTK_CTREE(summaryview->ctree), 
3518                                                          GTK_CLIST(summaryview->ctree)->rows - 1);
3519                 if (node)
3520                         summary_select_node(summaryview, node, prefs_common.always_show_msg, TRUE);
3521         }
3522
3523 }
3524
3525 void summary_move_to(SummaryView *summaryview)
3526 {
3527         FolderItem *to_folder;
3528
3529         if (!summaryview->folder_item ||
3530             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3531
3532         to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
3533                                          FOLDER_SEL_MOVE, NULL);
3534         summary_move_selected_to(summaryview, to_folder);
3535 }
3536
3537 static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
3538                                 FolderItem *to_folder)
3539 {
3540         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3541         MsgInfo *msginfo;
3542
3543         g_return_if_fail(to_folder != NULL);
3544
3545         msginfo = gtk_ctree_node_get_row_data(ctree, row);
3546         procmsg_msginfo_set_to_folder(msginfo, to_folder);
3547         if (MSG_IS_DELETED(msginfo->flags))
3548                 summaryview->deleted--;
3549         if (MSG_IS_MOVE(msginfo->flags)) {
3550                 summaryview->moved--;
3551         }
3552         
3553         if (!MSG_IS_COPY(msginfo->flags)) {
3554                 summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED, MSG_MOVE);
3555                 summaryview->copied++;
3556         } else {
3557                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE);
3558         }
3559         if (!prefs_common.immediate_exec) {
3560                 summary_set_row_marks(summaryview, row);
3561         }
3562
3563         debug_print("Message %d is set to copy to %s\n",
3564                     msginfo->msgnum, to_folder->path);
3565 }
3566
3567 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3568 {
3569         GList *cur;
3570
3571         if (!to_folder) return;
3572         if (!summaryview->folder_item) return;
3573
3574         if (summary_is_locked(summaryview)) return;
3575
3576         if (summaryview->folder_item == to_folder) {
3577                 alertpanel_error
3578                         (_("Destination to copy is same as current folder."));
3579                 return;
3580         }
3581
3582         START_LONG_OPERATION(summaryview);
3583
3584         for (cur = GTK_CLIST(summaryview->ctree)->selection;
3585              cur != NULL && cur->data != NULL; cur = cur->next)
3586                 summary_copy_row_to
3587                         (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
3588
3589         END_LONG_OPERATION(summaryview);
3590
3591         if (prefs_common.immediate_exec)
3592                 summary_execute(summaryview);
3593         else {
3594                 summary_status_show(summaryview);
3595         }
3596 }
3597
3598 void summary_copy_to(SummaryView *summaryview)
3599 {
3600         FolderItem *to_folder;
3601
3602         if (!summaryview->folder_item) return;
3603
3604         to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
3605                                          FOLDER_SEL_COPY, NULL);
3606         summary_copy_selected_to(summaryview, to_folder);
3607 }
3608
3609 void summary_add_address(SummaryView *summaryview)
3610 {
3611         MsgInfo *msginfo;
3612         gchar *from;
3613
3614         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
3615                                               summaryview->selected);
3616         if (!msginfo) return;
3617
3618         Xstrdup_a(from, msginfo->from, return);
3619         eliminate_address_comment(from);
3620         extract_address(from);
3621         addressbook_add_contact(msginfo->fromname, from, NULL);
3622 }
3623
3624 void summary_select_all(SummaryView *summaryview)
3625 {
3626         if (!summaryview->folder_item) return;
3627
3628         summary_lock(summaryview);
3629         gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
3630         summary_unlock(summaryview);
3631         summary_status_show(summaryview);
3632 }
3633
3634 void summary_unselect_all(SummaryView *summaryview)
3635 {
3636         summary_lock(summaryview);
3637         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
3638         summary_unlock(summaryview);
3639         summary_status_show(summaryview);
3640 }
3641
3642 void summary_select_thread(SummaryView *summaryview)
3643 {
3644         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3645         GtkCTreeNode *node = summaryview->selected;
3646
3647         if (!node) return;
3648
3649         while (GTK_CTREE_ROW(node)->parent != NULL)
3650                 node = GTK_CTREE_ROW(node)->parent;
3651
3652         if (node != summaryview->selected)
3653                 summary_select_node
3654                         (summaryview, node,
3655                          messageview_is_visible(summaryview->messageview),
3656                          FALSE);
3657
3658         gtk_ctree_select_recursive(ctree, node);
3659
3660         summary_status_show(summaryview);
3661 }
3662
3663 void summary_save_as(SummaryView *summaryview)
3664 {
3665         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3666         MsgInfo *msginfo;
3667         gchar *filename = NULL;
3668         gchar *src, *dest;
3669         gchar *tmp;
3670
3671         AlertValue aval = 0;
3672
3673         if (!summaryview->selected) return;
3674         msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
3675         if (!msginfo) return;
3676
3677         if (msginfo->subject) {
3678                 Xstrdup_a(filename, msginfo->subject, return);
3679                 subst_for_filename(filename);
3680         }
3681         if (g_getenv ("G_BROKEN_FILENAMES") &&
3682             filename && !g_utf8_validate(filename, -1, NULL)) {
3683                 gchar *oldstr = filename;
3684                 filename = conv_codeset_strdup(filename,
3685                                                conv_get_locale_charset_str(),
3686                                                CS_UTF_8);
3687                 if (!filename) {
3688                         g_warning("summary_save_as(): faild to convert character set.");
3689                         filename = g_strdup(oldstr);
3690                 }
3691                 dest = filesel_select_file_save(_("Save as"), filename);
3692                 g_free(filename);
3693         } else
3694                 dest = filesel_select_file_save(_("Save as"), filename);
3695         filename = NULL;
3696         if (!dest) return;
3697         if (is_file_exist(dest)) {
3698                 aval = alertpanel(_("Append or Overwrite"),
3699                                   _("Append or overwrite existing file?"),
3700                                   _("_Append"), _("_Overwrite"),
3701                                   GTK_STOCK_CANCEL);
3702                 if (aval != 0 && aval != 1)
3703                         return;
3704         }
3705
3706         src = procmsg_get_message_file(msginfo);
3707         tmp = g_path_get_basename(dest);
3708
3709         if ( aval==0 ) { /* append */
3710                 if (append_file(src, dest, TRUE) < 0) 
3711                         alertpanel_error(_("Can't save the file '%s'."), tmp);
3712         } else { /* overwrite */
3713                 if (copy_file(src, dest, TRUE) < 0)
3714                         alertpanel_error(_("Can't save the file '%s'."), tmp);
3715         }
3716         g_free(src);
3717         
3718         /*
3719          * If two or more msgs are selected,
3720          * append them to the output file.
3721          */
3722         if (GTK_CLIST(ctree)->selection->next) {
3723                 GList *item;
3724                 for (item = GTK_CLIST(ctree)->selection->next; item != NULL; item=item->next) {
3725                         msginfo = gtk_ctree_node_get_row_data(ctree, (GtkCTreeNode*)item->data);
3726                         if (!msginfo) break;
3727                         src = procmsg_get_message_file(msginfo);
3728                         if (append_file(src, dest, TRUE) < 0)
3729                                 alertpanel_error(_("Can't save the file '%s'."), tmp);
3730                 }
3731                 g_free(src);
3732         }
3733         g_free(dest);
3734         g_free(tmp);
3735 }
3736
3737 #ifdef USE_GNOMEPRINT
3738 static void print_mimeview(MimeView *mimeview) 
3739 {
3740         if (!mimeview 
3741         ||  !mimeview->textview
3742         ||  !mimeview->textview->text)
3743                 alertpanel_warning(_("Cannot print: the message doesn't "
3744                                      "contain text."));
3745         else {
3746                 gtk_widget_realize(mimeview->textview->text);
3747                 gedit_print(GTK_TEXT_VIEW(mimeview->textview->text));
3748         }
3749 }
3750 #endif
3751
3752 void summary_print(SummaryView *summaryview)
3753 {
3754         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3755 #ifndef USE_GNOMEPRINT
3756         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3757         MsgInfo *msginfo;
3758         gchar *cmdline = NULL;
3759         gchar *p;
3760 #endif
3761         GList *cur;
3762
3763         if (clist->selection == NULL) return;
3764 #ifndef USE_GNOMEPRINT
3765         cmdline = input_dialog(_("Print"),
3766                                _("Enter the print command line:\n"
3767                                  "('%s' will be replaced with file name)"),
3768                                prefs_common.print_cmd);
3769         if (!cmdline) return;
3770         if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
3771             strchr(p + 2, '%')) {
3772                 alertpanel_error(_("Print command line is invalid:\n'%s'"),
3773                                  cmdline);
3774                 g_free(cmdline);
3775                 return;
3776         }
3777         for (cur = clist->selection; 
3778              cur != NULL && cur->data != NULL; 
3779              cur = cur->next) {
3780                 msginfo = gtk_ctree_node_get_row_data
3781                         (ctree, GTK_CTREE_NODE(cur->data));
3782                 if (msginfo) 
3783                         procmsg_print_message(msginfo, cmdline);
3784         }
3785
3786         g_free(cmdline);
3787 #else
3788         for (cur = clist->selection; 
3789              cur != NULL && cur->data != NULL; 
3790              cur = cur->next) {
3791                 GtkCTreeNode *node = GTK_CTREE_NODE(cur->data);
3792                 if (node != summaryview->displayed) {
3793                         MessageView *tmpview = messageview_create(
3794                                                 summaryview->mainwin);
3795                         MsgInfo *msginfo = gtk_ctree_node_get_row_data(
3796                                                 GTK_CTREE(summaryview->ctree),
3797                                                 node);
3798
3799                         messageview_init(tmpview);
3800                         tmpview->all_headers = summaryview->messageview->all_headers;
3801                         if (msginfo && messageview_show(tmpview, msginfo, 
3802                                 tmpview->all_headers) >= 0) {
3803                                         print_mimeview(tmpview->mimeview);
3804                         }
3805                         messageview_destroy(tmpview);
3806                 } else {
3807                         print_mimeview(summaryview->messageview->mimeview);
3808                 }
3809         }
3810 #endif
3811 }
3812
3813 gboolean summary_execute(SummaryView *summaryview)
3814 {
3815         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3816         GtkCList *clist = GTK_CLIST(summaryview->ctree);
3817         GtkCTreeNode *node, *next;
3818         GtkCTreeNode *new_selected = NULL;
3819         gint move_val = -1;
3820
3821         if (!summaryview->folder_item) return FALSE;
3822
3823         if (summary_is_locked(summaryview)) return FALSE;
3824         summary_lock(summaryview);
3825
3826         gtk_clist_freeze(clist);
3827
3828         if (summaryview->threaded)
3829                 summary_unthread_for_exec(summaryview);
3830
3831         folder_item_update_freeze();
3832         move_val = summary_execute_move(summaryview);
3833         summary_execute_copy(summaryview);
3834         summary_execute_delete(summaryview);
3835         
3836         node = GTK_CTREE_NODE(clist->row_list);
3837         for (; node != NULL; node = next) {
3838                 next = gtkut_ctree_node_next(ctree, node);
3839                 if (gtk_ctree_node_get_row_data(ctree, node) != NULL) continue;
3840
3841                 if (node == summaryview->displayed) {
3842                         messageview_clear(summaryview->messageview);
3843                         summaryview->displayed = NULL;
3844                 }
3845                 if (GTK_CTREE_ROW(node)->children != NULL) {
3846                         g_warning("summary_execute(): children != NULL\n");
3847                         continue;
3848                 }
3849
3850                 if (!new_selected &&
3851                     gtkut_ctree_node_is_selected(ctree, node)) {
3852                         summary_unselect_all(summaryview);
3853                         new_selected = summary_find_next_msg(summaryview, node);
3854                         if (!new_selected)
3855                                 new_selected = summary_find_prev_msg
3856                                         (summaryview, node);
3857                 }
3858
3859                 gtk_ctree_remove_node(ctree, node);
3860         }
3861
3862         folder_item_update_thaw();
3863         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3864
3865         if (new_selected) {
3866                 summary_unlock(summaryview);
3867                 gtk_sctree_select
3868                         (GTK_SCTREE(ctree),
3869                          new_selected);
3870                 summary_lock(summaryview);
3871         }
3872
3873         if (summaryview->threaded) {
3874                 gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
3875                 summary_thread_build(summaryview);
3876                 summary_thread_init(summaryview);
3877                 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3878         }
3879
3880         summaryview->selected = clist->selection ?
3881                 GTK_CTREE_NODE(clist->selection->data) : NULL;
3882
3883         if (!GTK_CLIST(summaryview->ctree)->row_list) {
3884                 menu_set_insensitive_all
3885                         (GTK_MENU_SHELL(summaryview->popupmenu));
3886                 gtk_widget_grab_focus(summaryview->folderview->ctree);
3887         } else
3888                 gtk_widget_grab_focus(summaryview->ctree);
3889
3890         summary_update_status(summaryview);
3891         summary_status_show(summaryview);
3892
3893         gtk_ctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
3894
3895         summary_unlock(summaryview);
3896         
3897         if (move_val < 0) 
3898                 summary_show(summaryview, summaryview->folder_item);
3899         return TRUE;
3900 }
3901
3902 static gint summary_execute_move(SummaryView *summaryview)
3903 {
3904         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3905         GSList *cur;
3906         gint val = -1;
3907         /* search moving messages and execute */
3908         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
3909                                 summaryview);
3910
3911         if (summaryview->mlist) {
3912                 val = procmsg_move_messages(summaryview->mlist);
3913
3914                 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
3915                         procmsg_msginfo_free((MsgInfo *)cur->data);
3916                 g_slist_free(summaryview->mlist);
3917                 summaryview->mlist = NULL;
3918         }
3919         return val;
3920 }
3921
3922 static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
3923                                       gpointer data)
3924 {
3925         SummaryView *summaryview = data;
3926         MsgInfo *msginfo;
3927
3928         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3929
3930         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
3931                 summaryview->mlist =
3932                         g_slist_prepend(summaryview->mlist, msginfo);
3933                 gtk_ctree_node_set_row_data(ctree, node, NULL);
3934
3935                 if (msginfo->msgid && *msginfo->msgid &&
3936                     node == g_hash_table_lookup(summaryview->msgid_table,
3937                                                 msginfo->msgid))
3938                         g_hash_table_remove(summaryview->msgid_table,
3939                                             msginfo->msgid);
3940         }
3941 }
3942
3943 static void summary_execute_copy(SummaryView *summaryview)
3944 {
3945         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3946
3947         /* search copying messages and execute */
3948         gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
3949                                 summaryview);
3950
3951         if (summaryview->mlist) {
3952                 summaryview->mlist = g_slist_reverse(summaryview->mlist);
3953                 procmsg_copy_messages(summaryview->mlist);
3954
3955                 g_slist_free(summaryview->mlist);
3956                 summaryview->mlist = NULL;
3957         }
3958 }
3959
3960 static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
3961                                       gpointer data)
3962 {
3963         SummaryView *summaryview = data;
3964         MsgInfo *msginfo;
3965
3966         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3967
3968         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
3969                 summaryview->mlist =
3970                         g_slist_prepend(summaryview->mlist, msginfo);
3971
3972                 summary_msginfo_unset_flags(msginfo, 0, MSG_COPY);
3973                 summary_set_row_marks(summaryview, node);
3974         }
3975 }
3976
3977 static void summary_execute_delete(SummaryView *summaryview)
3978 {
3979         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3980         GSList *cur;
3981
3982         /* search deleting messages and execute */
3983         gtk_ctree_pre_recursive
3984                 (ctree, NULL, summary_execute_delete_func, summaryview);
3985
3986         if (!summaryview->mlist) return;
3987
3988         folder_item_remove_msgs(summaryview->folder_item,
3989                                 summaryview->mlist);
3990
3991         for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
3992                 procmsg_msginfo_free((MsgInfo *)cur->data);
3993
3994         g_slist_free(summaryview->mlist);
3995         summaryview->mlist = NULL;
3996 }
3997
3998 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
3999                                         gpointer data)
4000 {
4001         SummaryView *summaryview = data;
4002         MsgInfo *msginfo;
4003
4004         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
4005
4006         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
4007                 summaryview->mlist =
4008                         g_slist_append(summaryview->mlist, msginfo);
4009                 gtk_ctree_node_set_row_data(ctree, node, NULL);
4010
4011                 if (msginfo->msgid && *msginfo->msgid &&
4012                     node == g_hash_table_lookup(summaryview->msgid_table,
4013                                                 msginfo->msgid)) {
4014                         g_hash_table_remove(summaryview->msgid_table,
4015                                             msginfo->msgid);
4016                 }       
4017                 if (msginfo->subject && *msginfo->subject && 
4018                     node == subject_table_lookup(summaryview->subject_table,
4019                                                  msginfo->subject)) {
4020                         subject_table_remove(summaryview->subject_table,
4021                                              msginfo->subject);
4022                 }                                           
4023         }
4024 }
4025
4026 /* thread functions */
4027
4028 void summary_thread_build(SummaryView *summaryview)
4029 {
4030         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4031         GtkCTreeNode *node;
4032         GtkCTreeNode *next;
4033         GtkCTreeNode *parent;
4034         MsgInfo *msginfo;
4035         GSList *reflist;
4036
4037         summary_lock(summaryview);
4038
4039         debug_print("Building threads...");
4040         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
4041         main_window_cursor_wait(summaryview->mainwin);
4042
4043         g_signal_handlers_block_by_func(G_OBJECT(ctree),
4044                                        G_CALLBACK(summary_tree_expanded), summaryview);
4045         gtk_clist_freeze(GTK_CLIST(ctree));
4046
4047         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
4048         while (node) {
4049                 next = GTK_CTREE_ROW(node)->sibling;
4050
4051                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
4052
4053                 parent = NULL;
4054
4055                 if (msginfo && msginfo->inreplyto) {
4056                         parent = g_hash_table_lookup(summaryview->msgid_table,
4057                                                      msginfo->inreplyto);
4058                                                      
4059                         if (!parent && msginfo->references) {
4060                                 for (reflist = msginfo->references;
4061                                      reflist != NULL; reflist = reflist->next)
4062                                         if ((parent = g_hash_table_lookup
4063                                                 (summaryview->msgid_table,
4064                                                  reflist->data)))
4065                                                 break;
4066                         }
4067                 }
4068
4069                 if (prefs_common.thread_by_subject && parent == NULL) {
4070                         parent = subject_table_lookup
4071                                 (summaryview->subject_table,
4072                                  msginfo->subject);
4073                 }
4074
4075                 if (parent && parent != node) {
4076                         gtk_ctree_move(ctree, node, parent, NULL);
4077                 }
4078
4079                 node = next;
4080         }
4081
4082         gtkut_ctree_set_focus_row(ctree, summaryview->selected);
4083
4084         gtk_clist_thaw(GTK_CLIST(ctree));
4085         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
4086                                          G_CALLBACK(summary_tree_expanded), summaryview);
4087
4088         debug_print("done.\n");
4089         STATUSBAR_POP(summaryview->mainwin);
4090         main_window_cursor_normal(summaryview->mainwin);
4091
4092         summaryview->threaded = TRUE;
4093
4094         summary_unlock(summaryview);
4095 }
4096
4097 static void summary_thread_init(SummaryView *summaryview)
4098 {
4099         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4100         GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
4101         GtkCTreeNode *next;
4102
4103         if (!summaryview->thread_collapsed) {
4104                 g_signal_handlers_block_by_func(G_OBJECT(ctree),
4105                                        G_CALLBACK(summary_tree_expanded), summaryview);
4106                 while (node) {
4107                         next = GTK_CTREE_ROW(node)->sibling;
4108                         if (GTK_CTREE_ROW(node)->children)
4109                                 gtk_ctree_expand_recursive(ctree, node);
4110                         node = next;
4111                 }
4112                 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
4113                                        G_CALLBACK(summary_tree_expanded), summaryview);
4114         } 
4115 }
4116
4117 void summary_unthread(SummaryView *summaryview)
4118 {
4119         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4120         GtkCTreeNode *node;
4121         GtkCTreeNode *child;
4122         GtkCTreeNode *sibling;
4123         GtkCTreeNode *next_child;
4124
4125         summary_lock(summaryview);
4126
4127         debug_print("Unthreading...");
4128         STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
4129         main_window_cursor_wait(summaryview->mainwin);
4130         
4131         g_signal_handlers_block_by_func(G_OBJECT(ctree),
4132                                         summary_tree_collapsed, summaryview);
4133         gtk_clist_freeze(GTK_CLIST(ctree));
4134
4135         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
4136              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
4137                 child = GTK_CTREE_ROW(node)->children;
4138                 sibling = GTK_CTREE_ROW(node)->sibling;
4139
4140                 while (child != NULL) {
4141                         next_child = GTK_CTREE_ROW(child)->sibling;
4142                         gtk_ctree_move(ctree, child, NULL, sibling);
4143                         child = next_child;
4144                 }
4145         }
4146
4147         /* CLAWS: and sort it */
4148         gtk_sctree_sort_recursive(ctree, NULL); 
4149
4150         gtk_clist_thaw(GTK_CLIST(ctree));
4151         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
4152                                            G_CALLBACK(summary_tree_collapsed), summaryview);
4153
4154         debug_print("done.\n");
4155         STATUSBAR_POP(summaryview->mainwin);
4156         main_window_cursor_normal(summaryview->mainwin);
4157
4158         summaryview->threaded = FALSE;
4159
4160         summary_unlock(summaryview);
4161 }
4162
4163 static void summary_unthread_for_exec(SummaryView *summaryview)
4164 {
4165         GtkCTreeNode *node;
4166         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4167
4168         debug_print("Unthreading for execution...");
4169
4170         START_LONG_OPERATION(summaryview);
4171
4172         for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
4173              node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
4174                 summary_unthread_for_exec_func(ctree, node, NULL);
4175         }
4176
4177         END_LONG_OPERATION(summaryview);
4178
4179         debug_print("done.\n");
4180 }
4181
4182 static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
4183                                            gpointer data)
4184 {
4185         MsgInfo *msginfo;
4186         GtkCTreeNode *top_parent;
4187         GtkCTreeNode *child;
4188         GtkCTreeNode *sibling;
4189
4190         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
4191
4192         if (!msginfo ||
4193             (!MSG_IS_MOVE(msginfo->flags) &&
4194              !MSG_IS_DELETED(msginfo->flags)))
4195                 return;
4196         child = GTK_CTREE_ROW(node)->children;
4197         if (!child) return;
4198
4199         for (top_parent = node;
4200              GTK_CTREE_ROW(top_parent)->parent != NULL;
4201              top_parent = GTK_CTREE_ROW(top_parent)->parent)
4202                 ;
4203         sibling = GTK_CTREE_ROW(top_parent)->sibling;
4204
4205         while (child != NULL) {
4206                 GtkCTreeNode *next_child;
4207
4208                 next_child = GTK_CTREE_ROW(child)->sibling;
4209                 gtk_ctree_move(ctree, child, NULL, sibling);
4210                 child = next_child;
4211         }
4212 }
4213
4214 void summary_expand_threads(SummaryView *summaryview)
4215 {
4216         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4217         GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
4218
4219         g_signal_handlers_block_by_func(G_OBJECT(ctree),
4220                                        G_CALLBACK(summary_tree_expanded), summaryview);
4221         gtk_clist_freeze(GTK_CLIST(ctree));
4222
4223         while (node) {
4224                 if (GTK_CTREE_ROW(node)->children) {
4225                         gtk_ctree_expand(ctree, node);
4226                         summary_set_row_marks(summaryview, node);
4227                 }
4228                 node = GTK_CTREE_NODE_NEXT(node);
4229         }
4230
4231         gtk_clist_thaw(GTK_CLIST(ctree));
4232         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
4233                                          G_CALLBACK(summary_tree_expanded), summaryview);
4234
4235         summaryview->thread_collapsed = FALSE;
4236
4237         gtk_ctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
4238 }
4239
4240 void summary_collapse_threads(SummaryView *summaryview)
4241 {
4242         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4243         GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
4244
4245         gtk_clist_freeze(GTK_CLIST(ctree));
4246
4247         while (node) {
4248                 if (GTK_CTREE_ROW(node)->children)
4249                         gtk_ctree_collapse(ctree, node);
4250                 node = GTK_CTREE_ROW(node)->sibling;
4251         }
4252
4253         gtk_clist_thaw(GTK_CLIST(ctree));
4254         
4255         summaryview->thread_collapsed = TRUE;
4256
4257         gtk_ctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
4258 }
4259
4260 void summary_filter(SummaryView *summaryview, gboolean selected_only)
4261 {
4262         GSList *mlist = NULL, *cur_list;
4263         summary_lock(summaryview);
4264
4265         folder_item_update_freeze();
4266         
4267         debug_print("filtering...");
4268         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
4269         main_window_cursor_wait(summaryview->mainwin);
4270
4271         gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
4272
4273         if (selected_only) {
4274                 GList *cur;
4275
4276                 for (cur = GTK_CLIST(summaryview->ctree)->selection;
4277                      cur != NULL && cur->data != NULL; cur = cur->next) {
4278                         mlist = g_slist_prepend(mlist, 
4279                                  procmsg_msginfo_new_ref(
4280                                   GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data)));
4281                 }
4282                 mlist = g_slist_reverse(mlist);
4283         } else {
4284                 mlist = folder_item_get_msg_list(summaryview->folder_item);
4285         }
4286         for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
4287                 summary_filter_func((MsgInfo *)cur_list->data);
4288         }
4289         filtering_move_and_copy_msgs(mlist);
4290         for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
4291                 procmsg_msginfo_free((MsgInfo *)cur_list->data);
4292         }
4293         g_slist_free(mlist);
4294
4295         gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
4296
4297         folder_item_update_thaw();
4298         debug_print("done.\n");
4299         STATUSBAR_POP(summaryview->mainwin);
4300         main_window_cursor_normal(summaryview->mainwin);
4301
4302         summary_unlock(summaryview);
4303
4304         /* 
4305          * CLAWS: summary_show() only valid after having a lock. ideally
4306          * we want the lock to be context aware...  
4307          */
4308         summary_show(summaryview, summaryview->folder_item);
4309 }
4310
4311 static void summary_filter_func(MsgInfo *msginfo)
4312 {
4313         MailFilteringData mail_filtering_data;
4314
4315         mail_filtering_data.msginfo = msginfo;
4316         if (hooks_invoke(MAIL_MANUAL_FILTERING_HOOKLIST, &mail_filtering_data))
4317                 return;
4318
4319         filter_message_by_msginfo(filtering_rules, msginfo);
4320 }
4321
4322 void summary_msginfo_filter_open(FolderItem * item, MsgInfo *msginfo,
4323                                  PrefsFilterType type, gint processing_rule)
4324 {
4325         gchar *header = NULL;
4326         gchar *key = NULL;
4327
4328         procmsg_get_filter_keyword(msginfo, &header, &key, type);
4329         
4330         if (processing_rule) {
4331                 if (item == NULL)
4332                         prefs_filtering_open(&pre_global_processing,
4333                                              _("Processing rules to apply before folder rules"),
4334                                              header, key);
4335                 else
4336                         prefs_filtering_open(&item->prefs->processing,
4337                                              _("Processing configuration"),
4338                                              header, key);
4339         }
4340         else {
4341                 prefs_filtering_open(&filtering_rules,
4342                                      _("Filtering configuration"),
4343                                        header, key);
4344         }
4345         
4346         g_free(header);
4347         g_free(key);
4348 }
4349
4350 void summary_filter_open(SummaryView *summaryview, PrefsFilterType type,
4351                          gint processing_rule)
4352 {
4353         MsgInfo *msginfo;
4354         FolderItem * item;
4355         
4356         if (!summaryview->selected) return;
4357
4358         msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
4359                                               summaryview->selected);
4360         if (!msginfo) return;
4361         
4362         item = summaryview->folder_item;
4363         summary_msginfo_filter_open(item, msginfo, type, processing_rule);
4364 }
4365
4366 /* color label */
4367
4368 #define N_COLOR_LABELS colorlabel_get_color_count()
4369
4370 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
4371                                                      gpointer data)
4372 {
4373         guint color = GPOINTER_TO_UINT(data);
4374         SummaryView *summaryview;
4375
4376         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
4377         g_return_if_fail(summaryview != NULL);
4378
4379         /* "dont_toggle" state set? */
4380         if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
4381                                 "dont_toggle"))
4382                 return;
4383
4384         summary_set_colorlabel(summaryview, color, NULL);
4385 }
4386
4387 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
4388  * for the messsage; not the color index */
4389 void summary_set_colorlabel_color(GtkCTree *ctree, GtkCTreeNode *node,
4390                                   guint labelcolor)
4391 {
4392         GdkColor color;
4393         GtkStyle *style, *prev_style, *ctree_style;
4394         MsgInfo *msginfo;
4395         gint color_index;
4396
4397         msginfo = gtk_ctree_node_get_row_data(ctree, node);
4398
4399         color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
4400         ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
4401         prev_style = gtk_ctree_node_get_row_style(ctree, node);
4402
4403         if (color_index < 0 || color_index >= N_COLOR_LABELS) {
4404                 if (!prev_style) return;
4405                 style = gtk_style_copy(prev_style);
4406                 color = ctree_style->fg[GTK_STATE_NORMAL];
4407                 style->fg[GTK_STATE_NORMAL] = color;
4408                 color = ctree_style->fg[GTK_STATE_SELECTED];
4409                 style->fg[GTK_STATE_SELECTED] = color;
4410         } else {
4411                 if (prev_style)
4412                         style = gtk_style_copy(prev_style);
4413                 else
4414                         style = gtk_style_copy(ctree_style);
4415                 color = colorlabel_get_color(color_index);
4416                 style->fg[GTK_STATE_NORMAL] = color;
4417                 /* get the average of label color and selected fg color
4418                    for visibility */
4419                 style->fg[GTK_STATE_SELECTED].red   = (color.red   + ctree_style->fg[GTK_STATE_SELECTED].red  ) / 2;
4420                 style->fg[GTK_STATE_SELECTED].green = (color.green + ctree_style->fg[GTK_STATE_SELECTED].green) / 2;
4421                 style->fg[GTK_STATE_SELECTED].blue  = (color.blue  + ctree_style->fg[GTK_STATE_SELECTED].blue ) / 2;
4422         }
4423
4424         gtk_ctree_node_set_row_style(ctree, node, style);
4425 }
4426
4427 static void summary_set_row_colorlabel(SummaryView *summaryview, GtkCTreeNode *row, guint labelcolor)
4428 {
4429         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4430         MsgInfo *msginfo;
4431
4432         msginfo = gtk_ctree_node_get_row_data(ctree, row);
4433
4434         summary_msginfo_change_flags(msginfo, MSG_COLORLABEL_TO_FLAGS(labelcolor), 0, 
4435                                         MSG_CLABEL_FLAG_MASK, 0);
4436 }
4437
4438 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
4439                             GtkWidget *widget)
4440 {
4441         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4442         GList *cur;
4443
4444         START_LONG_OPERATION(summaryview);
4445
4446         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4447                 summary_set_row_colorlabel(summaryview,
4448                                            GTK_CTREE_NODE(cur->data), labelcolor);
4449         END_LONG_OPERATION(summaryview);
4450 }
4451
4452 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
4453                                                           gpointer data)
4454 {
4455         SummaryView *summaryview;
4456         GtkMenuShell *menu;
4457         GtkCheckMenuItem **items;
4458         gint n;
4459         GList *cur, *sel;
4460
4461         summaryview = (SummaryView *)data;
4462         g_return_if_fail(summaryview != NULL);
4463
4464         sel = GTK_CLIST(summaryview->ctree)->selection;
4465         if (!sel) return;
4466
4467         menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
4468         g_return_if_fail(menu != NULL);
4469
4470         Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
4471
4472         /* NOTE: don't return prematurely because we set the "dont_toggle"
4473          * state for check menu items */
4474         g_object_set_data(G_OBJECT(menu), "dont_toggle",
4475                           GINT_TO_POINTER(1));
4476
4477         /* clear items. get item pointers. */
4478         for (n = 0, cur = menu->children; cur != NULL && cur->data != NULL; cur = cur->next) {
4479                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
4480                         gtk_check_menu_item_set_active
4481                                 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
4482                         items[n] = GTK_CHECK_MENU_ITEM(cur->data);
4483                         n++;
4484                 }
4485         }
4486
4487         if (n == (N_COLOR_LABELS + 1)) {
4488                 /* iterate all messages and set the state of the appropriate
4489                  * items */
4490                 for (; sel != NULL; sel = sel->next) {
4491                         MsgInfo *msginfo;
4492                         gint clabel;
4493
4494                         msginfo = gtk_ctree_node_get_row_data
4495                                 (GTK_CTREE(summaryview->ctree),
4496                                  GTK_CTREE_NODE(sel->data));
4497                         if (msginfo) {
4498                                 clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
4499                                 if (!items[clabel]->active)
4500                                         gtk_check_menu_item_set_active
4501                                                 (items[clabel], TRUE);
4502                         }
4503                 }
4504         } else
4505                 g_warning("invalid number of color elements (%d)\n", n);
4506
4507         /* reset "dont_toggle" state */
4508         g_object_set_data(G_OBJECT(menu), "dont_toggle",
4509                           GINT_TO_POINTER(0));
4510 }
4511
4512 static void summary_colorlabel_menu_create(SummaryView *summaryview)
4513 {
4514         GtkWidget *label_menuitem;
4515         GtkWidget *menu;
4516         GtkWidget *item;
4517         gint i;
4518
4519         label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
4520                                                    "/Color label");
4521         g_signal_connect(G_OBJECT(label_menuitem), "activate",
4522                          G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
4523                            summaryview);
4524         gtk_widget_show(label_menuitem);
4525
4526         menu = gtk_menu_new();
4527
4528         /* create sub items. for the menu item activation callback we pass the
4529          * index of label_colors[] as data parameter. for the None color we
4530          * pass an invalid (high) value. also we attach a data pointer so we
4531          * can always get back the SummaryView pointer. */
4532
4533         item = gtk_check_menu_item_new_with_label(_("None"));
4534         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
4535         g_signal_connect(G_OBJECT(item), "activate",
4536                          G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
4537                            GUINT_TO_POINTER(0));
4538         g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
4539         gtk_widget_show(item);
4540
4541         item = gtk_menu_item_new();
4542         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
4543         gtk_widget_show(item);
4544
4545         /* create pixmap/label menu items */
4546         for (i = 0; i < N_COLOR_LABELS; i++) {
4547                 item = colorlabel_create_check_color_menu_item(i);
4548                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
4549                 g_signal_connect(G_OBJECT(item), "activate",
4550                                  G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
4551                                  GUINT_TO_POINTER(i + 1));
4552                 g_object_set_data(G_OBJECT(item), "summaryview",
4553                                   summaryview);
4554                 gtk_widget_show(item);
4555         }
4556
4557         gtk_widget_show(menu);
4558         gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
4559         summaryview->colorlabel_menu = menu;
4560 }
4561
4562 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
4563 {
4564         GtkWidget *ctree;
4565         gint *col_pos = summaryview->col_pos;
4566         SummaryColumnState *col_state;
4567         gchar *titles[N_SUMMARY_COLS];
4568         SummaryColumnType type;
4569         gint pos;
4570
4571         memset(titles, 0, sizeof(titles));
4572
4573         col_state = prefs_summary_column_get_config();
4574         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
4575                 summaryview->col_state[pos] = col_state[pos];
4576                 type = col_state[pos].type;
4577                 col_pos[type] = pos;
4578                 titles[pos] = "dummy";
4579         }
4580         col_state = summaryview->col_state;
4581
4582         ctree = gtk_sctree_new_with_titles
4583                 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
4584
4585         gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_EXTENDED);
4586         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MARK],
4587                                            GTK_JUSTIFY_CENTER);
4588         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_STATUS],
4589                                            GTK_JUSTIFY_CENTER);
4590         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_LOCKED],
4591                                            GTK_JUSTIFY_CENTER);
4592         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MIME],
4593                                            GTK_JUSTIFY_CENTER);
4594         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
4595                                            GTK_JUSTIFY_RIGHT);
4596         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
4597                                            GTK_JUSTIFY_RIGHT);
4598         gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SCORE],
4599                                            GTK_JUSTIFY_RIGHT);
4600         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MARK],
4601                                    SUMMARY_COL_MARK_WIDTH);
4602         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_STATUS],
4603                                    SUMMARY_COL_STATUS_WIDTH);
4604         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_LOCKED],
4605                                    SUMMARY_COL_LOCKED_WIDTH);
4606         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MIME],
4607                                    SUMMARY_COL_MIME_WIDTH);
4608         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SUBJECT],
4609                                    prefs_common.summary_col_size[S_COL_SUBJECT]);
4610         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_FROM],
4611                                    prefs_common.summary_col_size[S_COL_FROM]);
4612         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_TO],
4613                                    prefs_common.summary_col_size[S_COL_TO]);
4614         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_DATE],
4615                                    prefs_common.summary_col_size[S_COL_DATE]);
4616         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
4617                                    prefs_common.summary_col_size[S_COL_SIZE]);
4618         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
4619                                    prefs_common.summary_col_size[S_COL_NUMBER]);
4620         gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SCORE],
4621                                    prefs_common.summary_col_size[S_COL_SCORE]);
4622         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
4623         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
4624                                      GTK_CTREE_EXPANDER_SQUARE);
4625 #if 0
4626         gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
4627         gtk_ctree_set_expander_style(GTK_CTREE(ctree),
4628                                      GTK_CTREE_EXPANDER_TRIANGLE);
4629 #endif
4630         gtk_ctree_set_indent(GTK_CTREE(ctree), 12);
4631         g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 
4632
4633         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
4634                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[pos].button,
4635                                        GTK_CAN_FOCUS);
4636                 gtk_clist_set_column_visibility
4637                         (GTK_CLIST(ctree), pos, col_state[pos].visible);
4638         }
4639
4640         /* connect signal to the buttons for sorting */
4641 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
4642         g_signal_connect \
4643                 (G_OBJECT(GTK_CLIST(ctree)->column[col_pos[col]].button), \
4644                  "clicked", \
4645                  G_CALLBACK(func), \
4646                  summaryview)
4647
4648         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked);
4649         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked);
4650         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked);
4651         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked);
4652         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked);
4653         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked);
4654         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked);
4655         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO     , summary_to_clicked);
4656         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
4657         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE,   summary_score_clicked);
4658         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED,  summary_locked_clicked);
4659
4660 #undef CLIST_BUTTON_SIGNAL_CONNECT
4661
4662         g_signal_connect(G_OBJECT(ctree), "tree_select_row",
4663                          G_CALLBACK(summary_selected), summaryview);
4664         g_signal_connect(G_OBJECT(ctree), "tree_unselect_row",
4665                          G_CALLBACK(summary_unselected), summaryview);
4666         g_signal_connect(G_OBJECT(ctree), "button_press_event",
4667                          G_CALLBACK(summary_button_pressed),
4668                          summaryview);
4669         g_signal_connect(G_OBJECT(ctree), "button_release_event",
4670                          G_CALLBACK(summary_button_released),
4671                          summaryview);
4672         g_signal_connect(G_OBJECT(ctree), "key_press_event",
4673                          G_CALLBACK(summary_key_pressed), summaryview);
4674         g_signal_connect(G_OBJECT(ctree), "resize_column",
4675                          G_CALLBACK(summary_col_resized), summaryview);
4676         g_signal_connect(G_OBJECT(ctree), "open_row",
4677                          G_CALLBACK(summary_open_row), summaryview);
4678
4679         g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
4680                                G_CALLBACK(summary_tree_expanded),
4681                                summaryview);
4682         g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
4683                                G_CALLBACK(summary_tree_collapsed),
4684                                summaryview);
4685
4686         g_signal_connect(G_OBJECT(ctree), "start_drag",
4687                          G_CALLBACK(summary_start_drag),
4688                          summaryview);
4689         g_signal_connect(G_OBJECT(ctree), "drag_data_get",
4690                          G_CALLBACK(summary_drag_data_get),
4691                          summaryview);
4692
4693         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
4694                           summary_drag_types, 2,
4695                           GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
4696
4697         g_signal_connect(G_OBJECT(ctree), "drag_data_received",
4698                          G_CALLBACK(summary_drag_data_received),
4699                          summaryview);
4700
4701         g_signal_connect(G_OBJECT(ctree), "drag_motion",
4702                          G_CALLBACK(summary_drag_motion_cb),
4703                          summaryview);
4704
4705         return ctree;
4706 }
4707
4708 void summary_set_column_order(SummaryView *summaryview)
4709 {
4710         GtkWidget *ctree;
4711         GtkWidget *scrolledwin = summaryview->scrolledwin;
4712         FolderItem *item;
4713         guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected);
4714         guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed);
4715
4716         item = summaryview->folder_item;
4717
4718         summary_clear_all(summaryview);
4719         gtk_widget_destroy(summaryview->ctree);
4720
4721         summaryview->ctree = ctree = summary_ctree_create(summaryview);
4722         summary_set_fonts(summaryview);
4723         summary_set_column_titles(summaryview);
4724         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
4725                                             GTK_CLIST(ctree)->hadjustment);
4726         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
4727                                             GTK_CLIST(ctree)->vadjustment);
4728         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
4729         gtk_widget_show(ctree);
4730
4731         summary_show(summaryview, item);
4732
4733         summary_select_by_msgnum(summaryview, selected_msgnum);
4734
4735         summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum);
4736         if (!summaryview->displayed)
4737                 messageview_clear(summaryview->messageview);
4738         else
4739                 summary_redisplay_msg(summaryview);
4740 }
4741
4742
4743 /* callback functions */
4744
4745 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
4746                                    SummaryView *summaryview)
4747 {
4748         if (event)  
4749                 summary_toggle_view(summaryview);
4750         return TRUE;
4751 }
4752
4753 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
4754                                        SummaryView *summaryview)
4755 {
4756         if (!event) return FALSE;
4757
4758         if (event->button == 3) {
4759                 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
4760                 /* right clicked */
4761                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
4762                                NULL, NULL, event->button, event->time);
4763         } else if (event->button == 2) {
4764                 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
4765         } else if (event->button == 1) {
4766                 if (!prefs_common.emulate_emacs &&
4767                     messageview_is_visible(summaryview->messageview))
4768                         summaryview->display_msg = TRUE;
4769         }
4770
4771         return FALSE;
4772 }
4773
4774 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
4775                                         SummaryView *summaryview)
4776 {
4777         return FALSE;
4778 }
4779
4780 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
4781 {
4782         summary_key_pressed(summaryview->ctree, event, summaryview);
4783 }
4784
4785 #define BREAK_ON_MODIFIER_KEY() \
4786         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
4787
4788 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
4789                                     SummaryView *summaryview)
4790 {
4791         GtkCTree *ctree = GTK_CTREE(widget);
4792         GtkCTreeNode *node;
4793         MessageView *messageview;
4794         TextView *textview;
4795         GtkAdjustment *adj;
4796         gboolean mod_pressed;
4797
4798         if (summary_is_locked(summaryview)) return TRUE;
4799         if (!event) return TRUE;
4800
4801         if (quicksearch_has_focus(summaryview->quicksearch))
4802                 return FALSE;
4803
4804         switch (event->keyval) {
4805         case GDK_Left:          /* Move focus */
4806                 adj = gtk_scrolled_window_get_hadjustment
4807                         (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
4808                 if (adj->lower != adj->value)
4809                         break;
4810                 /* FALLTHROUGH */       
4811         case GDK_Escape:
4812                 gtk_widget_grab_focus(summaryview->folderview->ctree);
4813                 return TRUE;
4814         case GDK_Home:
4815         case GDK_End:
4816                 if ((node = summaryview->selected) != NULL) {
4817                         GtkCTreeNode *next = NULL;
4818                         next = (event->keyval == GDK_Home)
4819                                         ? gtk_ctree_node_nth(ctree, 0)
4820                                         : gtk_ctree_node_nth(ctree, 
4821                                                 g_list_length(GTK_CLIST(ctree)->row_list)-1);
4822                         if (next) {
4823                                 gtk_sctree_select_with_state
4824                                         (GTK_SCTREE(ctree), next, event->state);
4825
4826                                 /* Deprecated - what are the non-deprecated equivalents? */
4827                                 if (gtk_ctree_node_is_visible(GTK_CTREE(ctree), next) != GTK_VISIBILITY_FULL)
4828                                         gtk_ctree_node_moveto(GTK_CTREE(ctree), next, 0, 0, 0);
4829                                 summaryview->selected = next;
4830                         }
4831                 }
4832                 return TRUE;
4833         default:
4834                 break;
4835         }
4836
4837         if (!summaryview->selected) {
4838                 node = gtk_ctree_node_nth(ctree, 0);
4839                 if (node)
4840                         gtk_sctree_select(GTK_SCTREE(ctree), node);
4841                 else
4842                         return TRUE;
4843         }
4844
4845         messageview = summaryview->messageview;
4846         textview = messageview->mimeview->textview;
4847
4848         mod_pressed =
4849                 ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
4850
4851         switch (event->keyval) {
4852         case GDK_space:         /* Page down or go to the next */
4853                 if (event->state & GDK_SHIFT_MASK) 
4854                         textview_scroll_page(textview, TRUE);
4855                 else {
4856                         if (summaryview->displayed != summaryview->selected) {
4857                                 summary_display_msg(summaryview,
4858                                                     summaryview->selected);
4859                                 break;
4860                         }
4861                         if (mod_pressed) {
4862                                 if (!textview_scroll_page(textview, TRUE))
4863                                         summary_select_prev_unread(summaryview);
4864                         } else {
4865                                 if (!textview_scroll_page(textview, FALSE))
4866                                         summary_select_next_unread(summaryview);
4867                         }                               
4868                 }
4869                 break;
4870         case GDK_BackSpace:     /* Page up */
4871                 textview_scroll_page(textview, TRUE);
4872                 break;
4873         case GDK_Return:        /* Scroll up/down one line */
4874                 if (summaryview->displayed != summaryview->selected) {
4875                         summary_display_msg(summaryview,
4876                                             summaryview->selected);
4877                         break;
4878                 }
4879                 textview_scroll_one_line(textview, mod_pressed);
4880                 break;
4881         case GDK_Delete:
4882                 BREAK_ON_MODIFIER_KEY();
4883                 summary_delete_trash(summaryview);
4884                 break;
4885         case GDK_y:
4886         case GDK_t:
4887         case GDK_l:
4888         case GDK_c:
4889                 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) == 0) {
4890                         g_signal_stop_emission_by_name(G_OBJECT(widget), 
4891                                        "key_press_event");
4892                         mimeview_pass_key_press_event(messageview->mimeview,
4893                                                       event);
4894                         break;
4895                 }
4896         default:
4897                 break;
4898         }
4899         return FALSE;
4900 }
4901
4902 static void quicksearch_execute_cb(QuickSearch *quicksearch, gpointer data)
4903 {
4904         SummaryView *summaryview = data;
4905
4906         summary_show(summaryview, summaryview->folder_item);
4907 }
4908
4909 static void tog_searchbar_cb(GtkWidget *w, gpointer data)
4910 {
4911         SummaryView *summaryview = (SummaryView *)data;
4912
4913         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
4914                 prefs_common.show_searchbar = TRUE;
4915                 quicksearch_show(summaryview->quicksearch);
4916         } else {
4917                 prefs_common.show_searchbar = FALSE;
4918                 quicksearch_hide(summaryview->quicksearch);
4919         }
4920 }
4921
4922 void summaryview_activate_quicksearch(SummaryView *summaryview) 
4923 {
4924         gtk_toggle_button_set_active(
4925                 GTK_TOGGLE_BUTTON(summaryview->toggle_search), 
4926                 TRUE);
4927         quicksearch_show(summaryview->quicksearch);
4928 }
4929
4930 static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
4931 {
4932         if (folder_has_parent_of_type(summaryview->folder_item, F_OUTBOX)
4933         ||  folder_has_parent_of_type(summaryview->folder_item, F_DRAFT)
4934         ||  folder_has_parent_of_type(summaryview->folder_item, F_QUEUE))
4935                 summary_reedit(summaryview);
4936         else
4937                 summary_open_msg(summaryview);
4938
4939         summaryview->display_msg = FALSE;
4940 }
4941
4942 static void summary_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
4943                                   SummaryView *summaryview)
4944 {
4945         summary_set_row_marks(summaryview, node);
4946         if (prefs_common.bold_unread) {
4947                 while (node) {
4948                         GtkCTreeNode *next = GTK_CTREE_NODE_NEXT(node);
4949                         if (GTK_CTREE_ROW(node)->children)
4950                                 summary_set_row_marks(summaryview, node);
4951                         node = next;
4952                 }
4953         }
4954 }
4955
4956 static void summary_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
4957                                    SummaryView *summaryview)
4958 {
4959         summary_set_row_marks(summaryview, node);
4960 }
4961
4962 static void summary_unselected(GtkCTree *ctree, GtkCTreeNode *row,
4963                                gint column, SummaryView *summaryview)
4964 {
4965         if (summary_is_locked(summaryview)
4966         ||  GTK_SCTREE(ctree)->selecting_range) {
4967                 return;
4968         }
4969
4970         summary_status_show(summaryview);
4971 }
4972
4973 static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
4974                              gint column, SummaryView *summaryview)
4975 {
4976         MsgInfo *msginfo;
4977         gboolean marked_unread = FALSE;
4978
4979         if (summary_is_locked(summaryview)
4980         ||  GTK_SCTREE(ctree)->selecting_range) {
4981                 return;
4982         }
4983
4984         summary_status_show(summaryview);
4985
4986         if (GTK_CLIST(ctree)->selection &&
4987             GTK_CLIST(ctree)->selection->next) {
4988                 summaryview->display_msg = FALSE;
4989                 summary_set_menu_sensitive(summaryview);
4990                 toolbar_main_set_sensitive(summaryview->mainwin);
4991                 return;
4992         }
4993
4994         summaryview->selected = row;
4995
4996         msginfo = gtk_ctree_node_get_row_data(ctree, row);
4997         g_return_if_fail(msginfo != NULL);
4998
4999         switch (column < 0 ? column : summaryview->col_state[column].type) {
5000         case S_COL_MARK:
5001                 if (!MSG_IS_DELETED(msginfo->flags) &&
5002                     !MSG_IS_MOVE(msginfo->flags) &&
5003                     !MSG_IS_COPY(msginfo->flags)) {
5004                         if (MSG_IS_MARKED(msginfo->flags)) {
5005                                 summary_unmark_row(summaryview, row);
5006                         } else {
5007                                 summary_mark_row(summaryview, row);
5008                         }
5009                 }
5010                 break;
5011         case S_COL_STATUS:
5012                 if (MSG_IS_UNREAD(msginfo->flags)) {
5013                         summary_mark_row_as_read(summaryview, row);
5014                         summary_status_show(summaryview);
5015                 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
5016                          !MSG_IS_FORWARDED(msginfo->flags)) {
5017                         marked_unread = TRUE;
5018                 } else if (MSG_IS_REPLIED(msginfo->flags)) {
5019                         summary_find_answers(summaryview, msginfo);
5020                         return;
5021                 } 
5022                 break;
5023         case S_COL_LOCKED:
5024                 if (MSG_IS_LOCKED(msginfo->flags)) {
5025                         summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
5026                         summary_set_row_marks(summaryview, row);
5027                 }
5028                 else
5029                         summary_lock_row(summaryview, row);
5030                 break;
5031         default:
5032                 break;
5033         }
5034
5035         if (summaryview->display_msg ||
5036             (prefs_common.always_show_msg &&
5037              messageview_is_visible(summaryview->messageview))) {
5038                 summaryview->display_msg = FALSE;
5039                 if (summaryview->displayed != row) {
5040                         summary_display_msg(summaryview, row);
5041                         if (marked_unread) {
5042                                 summary_mark_row_as_unread(summaryview, row);
5043                                 summary_status_show(summaryview);
5044                         } 
5045                         return;
5046                 }
5047         }
5048         
5049         if (marked_unread) {
5050                 summary_mark_row_as_unread(summaryview, row);
5051                 summary_status_show(summaryview);
5052         } 
5053
5054         summary_set_menu_sensitive(summaryview);
5055         toolbar_main_set_sensitive(summaryview->mainwin);
5056 }
5057
5058 static void summary_col_resized(GtkCList *clist, gint column, gint width,
5059                                 SummaryView *summaryview)
5060 {
5061         SummaryColumnType type = summaryview->col_state[column].type;
5062
5063         prefs_common.summary_col_size[type] = width;
5064 }
5065
5066
5067 /*
5068  * \brief get List of msginfo selected in SummaryView
5069  *
5070  * \param summaryview
5071  *
5072  * \return GSList holding MsgInfo
5073  */
5074 GSList *summary_get_selection(SummaryView *summaryview)
5075 {
5076         GList *sel = NULL;
5077         GSList *msginfo_list = NULL;
5078         
5079         g_return_val_if_fail(summaryview != NULL, NULL);
5080
5081         sel = GTK_CLIST(summaryview->ctree)->selection;
5082
5083         g_return_val_if_fail(sel != NULL, NULL);
5084
5085         for ( ; sel != NULL; sel = sel->next)
5086                 msginfo_list = 
5087                         g_slist_append(msginfo_list, 
5088                                        gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
5089                                                                    GTK_CTREE_NODE(sel->data)));
5090         return msginfo_list;
5091 }
5092
5093 static void summary_reply_cb(SummaryView *summaryview, guint action,
5094                              GtkWidget *widget)
5095 {
5096         MessageView *msgview = (MessageView*)summaryview->messageview;
5097         GSList *msginfo_list;
5098
5099         g_return_if_fail(msgview != NULL);
5100
5101         msginfo_list = summary_get_selection(summaryview);
5102         g_return_if_fail(msginfo_list != NULL);
5103         compose_reply_from_messageview(msgview, msginfo_list, action);
5104         g_slist_free(msginfo_list);
5105 }
5106
5107 static void summary_show_all_header_cb(SummaryView *summaryview,
5108                                        guint action, GtkWidget *widget)
5109 {
5110         summaryview->messageview->all_headers =
5111                         GTK_CHECK_MENU_ITEM(widget)->active;
5112         summary_display_msg_selected(summaryview,
5113                                      GTK_CHECK_MENU_ITEM(widget)->active);
5114 }
5115
5116 static void summary_add_address_cb(SummaryView *summaryview,
5117                                    guint action, GtkWidget *widget)
5118 {
5119         summary_add_address(summaryview);
5120 }
5121
5122 static void summary_create_filter_cb(SummaryView *summaryview,
5123                                      guint action, GtkWidget *widget)
5124 {
5125         summary_filter_open(summaryview, (PrefsFilterType)action, 0);
5126 }
5127
5128 static void summary_create_processing_cb(SummaryView *summaryview,
5129                                          guint action, GtkWidget *widget)
5130 {
5131         summary_filter_open(summaryview, (PrefsFilterType)action, 1);
5132 }
5133
5134 static void summary_sort_by_column_click(SummaryView *summaryview,
5135                                          FolderSortKey sort_key)
5136 {
5137         GtkCTreeNode *node = NULL;
5138         START_TIMING("summary_sort_by_column_click");
5139         if (summaryview->sort_key == sort_key)
5140                 summary_sort(summaryview, sort_key,
5141                              summaryview->sort_type == SORT_ASCENDING
5142                              ? SORT_DESCENDING : SORT_ASCENDING);
5143         else
5144                 summary_sort(summaryview, sort_key, SORT_ASCENDING);
5145
5146         node = GTK_CTREE_NODE(GTK_CLIST(summaryview->ctree)->row_list);
5147
5148         if (prefs_common.bold_unread) {
5149                 while (node) {
5150                         GtkCTreeNode *next = GTK_CTREE_NODE_NEXT(node);
5151                         if (GTK_CTREE_ROW(node)->children)
5152                                 summary_set_row_marks(summaryview, node);
5153                         node = next;
5154                 }
5155         }
5156         END_TIMING();
5157 }
5158
5159 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
5160 {
5161         summary_sort_by_column_click(summaryview, SORT_BY_MARK);
5162 }
5163
5164 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview)
5165 {
5166         summary_sort_by_column_click(summaryview, SORT_BY_STATUS);
5167 }
5168
5169 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
5170 {
5171         summary_sort_by_column_click(summaryview, SORT_BY_MIME);
5172 }
5173
5174 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
5175 {
5176         summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
5177 }
5178
5179 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
5180 {
5181         summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
5182 }
5183
5184 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
5185 {
5186         summary_sort_by_column_click(summaryview, SORT_BY_DATE);
5187 }
5188
5189 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
5190 {
5191         if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
5192                 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
5193         else
5194                 summary_sort_by_column_click(summaryview, SORT_BY_TO);
5195 }
5196
5197 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
5198 {
5199         if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
5200                 summary_sort_by_column_click(summaryview, SORT_BY_TO);
5201         else
5202                 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
5203 }
5204
5205 static void summary_subject_clicked(GtkWidget *button,
5206                                     SummaryView *summaryview)
5207 {
5208         summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
5209 }
5210
5211 static void summary_score_clicked(GtkWidget *button,
5212                                   SummaryView *summaryview)
5213 {
5214         summary_sort_by_column_click(summaryview, SORT_BY_SCORE);
5215 }
5216
5217 static void summary_locked_clicked(GtkWidget *button,
5218                                    SummaryView *summaryview)
5219 {
5220         summary_sort_by_column_click(summaryview, SORT_BY_LOCKED);
5221 }
5222
5223 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
5224                                SummaryView *summaryview)
5225 {
5226         GdkDragContext *context;
5227
5228         g_return_if_fail(summaryview != NULL);
5229         g_return_if_fail(summaryview->folder_item != NULL);
5230         g_return_if_fail(summaryview->folder_item->folder != NULL);
5231         if (summaryview->selected == NULL) return;
5232
5233         context = gtk_drag_begin(widget, summaryview->target_list,
5234                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
5235         gtk_drag_set_icon_default(context);
5236 }
5237
5238 static void summary_drag_data_get(GtkWidget        *widget,
5239                                   GdkDragContext   *drag_context,
5240                                   GtkSelectionData *selection_data,
5241                                   guint             info,
5242                                   guint             time,
5243                                   SummaryView      *summaryview)
5244 {
5245         if (info == TARGET_MAIL_URI_LIST) {
5246                 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
5247                 GList *cur;
5248                 MsgInfo *msginfo;
5249                 gchar *mail_list = NULL, *tmp1, *tmp2;
5250
5251                 for (cur = GTK_CLIST(ctree)->selection;
5252                      cur != NULL && cur->data != NULL; cur = cur->next) {
5253                         msginfo = gtk_ctree_node_get_row_data
5254                                 (ctree, GTK_CTREE_NODE(cur->data));
5255                         tmp2 = procmsg_get_message_file(msginfo);
5256                         if (!tmp2) continue;
5257                         if (msginfo->subject) {
5258                                 gchar *san_subject = g_strdup(msginfo->subject);
5259                                 gchar *dest = NULL;
5260                                 subst_for_filename(san_subject);
5261                                 dest = g_strdup_printf("%s%s%s.%d.txt",
5262                                                 get_tmp_dir(),
5263                                                 G_DIR_SEPARATOR_S,
5264                                                 san_subject, msginfo->msgnum);
5265                                 g_free(san_subject);
5266                                 san_subject = g_filename_from_utf8(dest, -1, NULL, NULL, NULL);
5267                                 g_free(dest);
5268                                 dest = san_subject;
5269                                 if (copy_file(tmp2, dest, TRUE) == 0) {
5270                                         g_free(tmp2);
5271                                         tmp2 = dest;
5272                                 }
5273                         } 
5274                         tmp1 = g_strconcat("file://", tmp2, "\r\n", NULL);
5275                         g_free(tmp2);
5276
5277                         if (!mail_list) {
5278                                 mail_list = tmp1;
5279                         } else {
5280                                 tmp2 = g_strconcat(mail_list, tmp1, NULL);
5281                                 g_free(mail_list);
5282                                 g_free(tmp1);
5283                                 mail_list = tmp2;
5284                         }
5285                 }
5286
5287                 if (mail_list != NULL) {
5288                         gtk_selection_data_set(selection_data,
5289                                                selection_data->target, 8,
5290                                                mail_list, strlen(mail_list));
5291                         g_free(mail_list);
5292                 } 
5293         } else if (info == TARGET_DUMMY) {
5294                 if (GTK_CLIST(summaryview->ctree)->selection)
5295                         gtk_selection_data_set(selection_data,
5296                                                selection_data->target, 8,
5297                                                "Dummy-Summaryview", 
5298                                                strlen("Dummy-Summaryview")+1);
5299         }
5300 }
5301
5302 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
5303                                           GdkDragContext *context,
5304                                           gint            x,
5305                                           gint            y,
5306                                           guint           time,
5307                                           SummaryView    *summaryview)
5308 {
5309         FolderItem *item = summaryview->folder_item;
5310         if (!(item && item->folder && folder_item_parent(item) != NULL
5311                     && FOLDER_CLASS(item->folder)->add_msg != NULL)) {
5312                 gdk_drag_status(context, 0, time);
5313                 return FALSE;
5314         } else if (gtk_drag_get_source_widget(context) ==
5315                 mainwindow_get_mainwindow()->folderview->ctree) {
5316                 /* no folders */
5317                 gdk_drag_status(context, 0, time);
5318                 return FALSE;
5319         } else if (gtk_drag_get_source_widget(context) ==
5320                 summaryview->ctree) {
5321                 /* not from same folder */
5322                 gdk_drag_status(context, 0, time);
5323                 return FALSE;
5324         } else {
5325                 gdk_drag_status(context, GDK_ACTION_COPY, time);
5326                 return TRUE;
5327         }
5328 }
5329
5330 static void summary_drag_data_received(GtkWidget        *widget,
5331                                         GdkDragContext   *drag_context,
5332                                         gint              x,
5333                                         gint              y,
5334                                         GtkSelectionData *data,
5335                                         guint             info,
5336                                         guint             time,
5337                                         SummaryView       *summaryview)
5338 {
5339         if (info == TARGET_MAIL_URI_LIST) {
5340                 FolderItem *item = summaryview->folder_item;
5341                 if (!item) {
5342                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
5343                         return;
5344                 } else {
5345                         folderview_finish_dnd(data->data, drag_context, time, item);
5346                 }
5347         }
5348 }
5349
5350
5351 /* custom compare functions for sorting */
5352
5353 #define CMP_FUNC_DEF(func_name, val)                                     \
5354 static gint func_name(GtkCList *clist,                                   \
5355                       gconstpointer ptr1, gconstpointer ptr2)            \
5356 {                                                                        \
5357         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;                 \
5358         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;                 \
5359                                                                          \
5360         if (!msginfo1 || !msginfo2)                                      \
5361                 return -1;                                               \
5362                                                                          \
5363         return (val);                                                    \
5364 }
5365
5366 CMP_FUNC_DEF(summary_cmp_by_mark,
5367              MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
5368 CMP_FUNC_DEF(summary_cmp_by_status,
5369              MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags))
5370 CMP_FUNC_DEF(summary_cmp_by_mime,
5371              MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags))
5372 CMP_FUNC_DEF(summary_cmp_by_label,
5373              MSG_GET_COLORLABEL(msginfo1->flags) -
5374              MSG_GET_COLORLABEL(msginfo2->flags))
5375 CMP_FUNC_DEF(summary_cmp_by_locked,
5376              MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags))
5377
5378 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
5379 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
5380 CMP_FUNC_DEF(summary_cmp_by_date, msginfo1->date_t - msginfo2->date_t)
5381
5382 #undef CMP_FUNC_DEF
5383
5384 static gint summary_cmp_by_subject(GtkCList *clist,
5385                                    gconstpointer ptr1,
5386                                    gconstpointer ptr2)
5387 {
5388         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
5389         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
5390
5391         if (!msginfo1->subject)
5392                 return (msginfo2->subject != NULL);
5393         if (!msginfo2->subject)
5394                 return -1;
5395
5396         return subject_compare_for_sort
5397                 (msginfo1->subject, msginfo2->subject);
5398 }
5399
5400 static gint summary_cmp_by_from(GtkCList *clist, gconstpointer ptr1,
5401                                 gconstpointer ptr2)
5402 {
5403         const gchar *str1, *str2;
5404         const GtkCListRow *r1 = (const GtkCListRow *) ptr1;
5405         const GtkCListRow *r2 = (const GtkCListRow *) ptr2;
5406         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
5407         
5408         g_return_val_if_fail(sv, -1);
5409         
5410         str1 = GTK_CELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text;
5411         str2 = GTK_CELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text;
5412
5413         if (!str1)
5414                 return str2 != NULL;
5415  
5416         if (!str2)
5417                 return -1;
5418  
5419         return g_utf8_collate(str1, str2);
5420 }
5421  
5422 static gint summary_cmp_by_to(GtkCList *clist, gconstpointer ptr1,
5423                                 gconstpointer ptr2)
5424 {
5425         const gchar *str1, *str2;
5426         const GtkCListRow *r1 = (const GtkCListRow *) ptr1;
5427         const GtkCListRow *r2 = (const GtkCListRow *) ptr2;
5428         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
5429         
5430         g_return_val_if_fail(sv, -1);
5431         
5432         str1 = GTK_CELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
5433         str2 = GTK_CELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
5434
5435         if (!str1)
5436                 return str2 != NULL;
5437  
5438         if (!str2)
5439                 return -1;
5440  
5441         return g_utf8_collate(str1, str2);
5442 }
5443  
5444 static gint summary_cmp_by_simplified_subject
5445         (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
5446 {
5447         const FolderItemPrefs *prefs;
5448         const gchar *str1, *str2;
5449         const GtkCListRow *r1 = (const GtkCListRow *) ptr1;
5450         const GtkCListRow *r2 = (const GtkCListRow *) ptr2;
5451         const MsgInfo *msginfo1 = r1->data;
5452         const MsgInfo *msginfo2 = r2->data;
5453         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
5454         
5455         g_return_val_if_fail(sv, -1);
5456         g_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1);
5457         
5458         str1 = GTK_CELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text;
5459         str2 = GTK_CELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text;
5460
5461         if (!str1)
5462                 return str2 != NULL;
5463
5464         if (!str2)
5465                 return -1;
5466
5467         prefs = msginfo1->folder->prefs;
5468         if (!prefs)
5469                 prefs = msginfo2->folder->prefs;
5470         if (!prefs)
5471                 return -1;
5472         
5473         return subject_compare_for_sort(str1, str2);
5474 }
5475
5476 static gint summary_cmp_by_score(GtkCList *clist,
5477                                  gconstpointer ptr1, gconstpointer ptr2)
5478 {
5479         MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
5480         MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
5481         int diff;
5482
5483         /* if score are equal, sort by date */
5484
5485         diff = msginfo1->score - msginfo2->score;
5486         if (diff != 0)
5487                 return diff;
5488         else
5489                 return summary_cmp_by_date(clist, ptr1, ptr2);
5490 }
5491
5492 static void news_flag_crosspost(MsgInfo *msginfo)
5493 {
5494         GString *line;
5495         gpointer key;
5496         gpointer value;
5497         Folder *mff;
5498
5499         g_return_if_fail(msginfo != NULL);
5500         g_return_if_fail(msginfo->folder != NULL);
5501         g_return_if_fail(msginfo->folder->folder != NULL);
5502         mff = msginfo->folder->folder;
5503         g_return_if_fail(mff->klass->type == F_NEWS);
5504
5505         if (mff->account->mark_crosspost_read) {
5506                 line = g_string_sized_new(128);
5507                 g_string_printf(line, "%s:%d", msginfo->folder->path, msginfo->msgnum);
5508                 debug_print("nfcp: checking <%s>", line->str);
5509                 if (mff->newsart && 
5510                     g_hash_table_lookup_extended(mff->newsart, line->str, &key, &value)) {
5511                         debug_print(" <%s>", (gchar *)value);
5512                         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
5513                                 summary_msginfo_change_flags(msginfo, 
5514                                         mff->account->crosspost_col, 0, MSG_NEW | MSG_UNREAD, 0);
5515                         }
5516                         g_hash_table_remove(mff->newsart, key);
5517                         g_free(key);
5518                 }
5519                 g_string_free(line, TRUE);
5520                 debug_print("\n");
5521         }
5522 }
5523
5524 static void summary_ignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
5525 {
5526         SummaryView *summaryview = (SummaryView *) data;
5527         MsgInfo *msginfo;
5528
5529         msginfo = gtk_ctree_node_get_row_data(ctree, row);
5530
5531         summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
5532
5533         summary_set_row_marks(summaryview, row);
5534         debug_print("Message %d is marked as ignore thread\n",
5535             msginfo->msgnum);
5536 }
5537
5538 static void summary_ignore_thread(SummaryView *summaryview)
5539 {
5540         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
5541         GList *cur;
5542
5543         START_LONG_OPERATION(summaryview);
5544         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
5545                 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), 
5546                                         GTK_CTREE_FUNC(summary_ignore_thread_func), 
5547                                         summaryview);
5548
5549         END_LONG_OPERATION(summaryview);
5550
5551         summary_status_show(summaryview);
5552 }
5553
5554 static void summary_unignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
5555 {
5556         SummaryView *summaryview = (SummaryView *) data;
5557         MsgInfo *msginfo;
5558
5559         msginfo = gtk_ctree_node_get_row_data(ctree, row);
5560
5561         summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0);
5562
5563         summary_set_row_marks(summaryview, row);
5564         debug_print("Message %d is marked as unignore thread\n",
5565             msginfo->msgnum);
5566 }
5567
5568 static void summary_unignore_thread(SummaryView *summaryview)
5569 {
5570         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
5571         GList *cur;
5572
5573         START_LONG_OPERATION(summaryview);
5574         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
5575                 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), 
5576                                         GTK_CTREE_FUNC(summary_unignore_thread_func), 
5577                                         summaryview);
5578
5579         END_LONG_OPERATION(summaryview);
5580
5581         summary_status_show(summaryview);
5582 }
5583
5584 static void summary_check_ignore_thread_func
5585                 (GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
5586 {
5587         MsgInfo *msginfo;
5588         gint *found_ignore = (gint *) data;
5589
5590         if (*found_ignore) return;
5591         else {
5592                 msginfo = gtk_ctree_node_get_row_data(ctree, row);
5593                 *found_ignore = MSG_IS_IGNORE_THREAD(msginfo->flags);
5594         }               
5595 }
5596
5597 void summary_toggle_ignore_thread(SummaryView *summaryview)
5598 {
5599         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
5600         GList *cur;
5601         gint found_ignore = 0;
5602
5603         for (cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
5604                 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data),
5605                                         GTK_CTREE_FUNC(summary_check_ignore_thread_func),
5606                                         &found_ignore);
5607
5608         if (found_ignore) 
5609                 summary_unignore_thread(summaryview);
5610         else 
5611                 summary_ignore_thread(summaryview);
5612 }
5613
5614 void summary_toggle_show_read_messages(SummaryView *summaryview)
5615 {
5616         FolderItemUpdateData source;
5617         if (summaryview->folder_item->hide_read_msgs)
5618                 summaryview->folder_item->hide_read_msgs = 0;
5619         else
5620                 summaryview->folder_item->hide_read_msgs = 1;
5621
5622         source.item = summaryview->folder_item;
5623         source.update_flags = F_ITEM_UPDATE_NAME;
5624         source.msg = NULL;
5625         hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
5626         summary_show(summaryview, summaryview->folder_item);
5627 }
5628  
5629 static void summary_set_hide_read_msgs_menu (SummaryView *summaryview,
5630                                              guint action)
5631 {
5632         GtkWidget *widget;
5633  
5634         widget = gtk_item_factory_get_item(gtk_item_factory_from_widget(summaryview->mainwin->menubar),
5635                                            "/View/Hide read messages");
5636         g_object_set_data(G_OBJECT(widget), "dont_toggle",
5637                           GINT_TO_POINTER(1));
5638         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action);
5639         g_object_set_data(G_OBJECT(widget), "dont_toggle",
5640                           GINT_TO_POINTER(0));
5641 }
5642
5643 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
5644 {
5645         GtkWidget *ctree = summaryview->ctree;
5646         GtkWidget *pixmap; 
5647
5648         gtk_widget_destroy(summaryview->folder_pixmap);
5649
5650         stock_pixmap_gdk(ctree, STOCK_PIXMAP_MARK, &markxpm, &markxpmmask);
5651         stock_pixmap_gdk(ctree, STOCK_PIXMAP_DELETED, &deletedxpm, &deletedxpmmask);
5652         stock_pixmap_gdk(ctree, STOCK_PIXMAP_NEW, &newxpm, &newxpmmask);
5653         stock_pixmap_gdk(ctree, STOCK_PIXMAP_UNREAD, &unreadxpm, &unreadxpmmask);
5654         stock_pixmap_gdk(ctree, STOCK_PIXMAP_REPLIED, &repliedxpm, &repliedxpmmask);
5655         stock_pixmap_gdk(ctree, STOCK_PIXMAP_FORWARDED, &forwardedxpm, &forwardedxpmmask);
5656         stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP, &clipxpm, &clipxpmmask);
5657         stock_pixmap_gdk(ctree, STOCK_PIXMAP_LOCKED, &lockedxpm, &lockedxpmmask);
5658         stock_pixmap_gdk(ctree, STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm, &ignorethreadxpmmask);
5659         stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm, &clipkeyxpmmask);
5660         stock_pixmap_gdk(ctree, STOCK_PIXMAP_KEY, &keyxpm, &keyxpmmask);
5661         stock_pixmap_gdk(ctree, STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm, &gpgsignedxpmmask);
5662         stock_pixmap_gdk(ctree, STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm, &clipgpgsignedxpmmask);
5663
5664         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_DIR_OPEN);
5665         gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
5666         gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 1); /* search_toggle before */
5667         gtk_widget_show(pixmap);
5668         summaryview->folder_pixmap = pixmap; 
5669
5670         pixmap = stock_pixmap_widget(summaryview->hbox, STOCK_PIXMAP_QUICKSEARCH);
5671         gtk_container_remove (GTK_CONTAINER(summaryview->toggle_search), 
5672                               summaryview->quick_search_pixmap);
5673         gtk_container_add(GTK_CONTAINER(summaryview->toggle_search), pixmap);
5674         gtk_widget_show(pixmap);
5675         summaryview->quick_search_pixmap = pixmap;
5676
5677         folderview_unselect(summaryview->folderview);
5678         folderview_select(summaryview->folderview, summaryview->folder_item);
5679         summary_set_column_titles(summaryview);
5680 }
5681
5682 /*
5683  * Harvest addresses for selected messages in summary view.
5684  */
5685 void summary_harvest_address(SummaryView *summaryview)
5686 {
5687         GtkCTree *ctree = GTK_CTREE( summaryview->ctree );
5688         GList *cur;
5689         GList *msgList;
5690         MsgInfo *msginfo;
5691
5692         msgList = NULL;
5693         for( cur = GTK_CLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next ) {
5694                 msginfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(cur->data) );
5695                 msgList = g_list_append( msgList, GUINT_TO_POINTER( msginfo->msgnum ) );
5696         }
5697         addressbook_harvest( summaryview->folder_item, TRUE, msgList );
5698         g_list_free( msgList );
5699 }
5700
5701 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp)
5702 {
5703         int err;
5704         gchar buf[BUFFSIZE];
5705         regex_t *preg = NULL;
5706         
5707         preg = g_new0(regex_t, 1);
5708
5709         err = string_match_precompile(simplify_subject_regexp, 
5710                                       preg, REG_EXTENDED);
5711         if (err) {
5712                 regerror(err, preg, buf, BUFFSIZE);
5713                 alertpanel_error(_("Regular expression (regexp) error:\n%s"), buf);
5714                 g_free(preg);
5715                 preg = NULL;
5716         }
5717         
5718         return preg;
5719 }
5720
5721 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item)
5722 {
5723         g_return_if_fail(summaryview != NULL);
5724         g_return_if_fail(item != NULL);
5725
5726         /* Subject simplification */
5727         if(summaryview->simplify_subject_preg) {
5728                 regfree(summaryview->simplify_subject_preg);
5729                 g_free(summaryview->simplify_subject_preg);
5730                 summaryview->simplify_subject_preg = NULL;
5731         }
5732         if(item->prefs && item->prefs->simplify_subject_regexp && 
5733            item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject)
5734                 summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp);
5735
5736         /* Sorting */
5737         summaryview->sort_key = item->sort_key;
5738         summaryview->sort_type = item->sort_type;
5739
5740         /* Threading */
5741         summaryview->threaded = item->threaded;
5742         summaryview->thread_collapsed = item->thread_collapsed;
5743
5744         /* Scoring */
5745 #if 0
5746         if (global_scoring || item->prefs->scoring) {
5747                 summaryview->important_score = prefs_common.important_score;
5748                 if (item->prefs->important_score >
5749                     summaryview->important_score)
5750                         summaryview->important_score =
5751                                 item->prefs->important_score;
5752         }
5753 #endif
5754 }
5755
5756 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item)
5757 {
5758         /* Sorting */
5759         item->sort_key = summaryview->sort_key;
5760         item->sort_type = summaryview->sort_type;
5761
5762         /* Threading */
5763         item->threaded = summaryview->threaded;
5764         item->thread_collapsed = summaryview->thread_collapsed;
5765 }
5766
5767 static gboolean summary_update_msg(gpointer source, gpointer data) 
5768 {
5769         MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source;
5770         SummaryView *summaryview = (SummaryView *)data;
5771         GtkCTreeNode *node;
5772
5773         g_return_val_if_fail(msginfo_update != NULL, TRUE);
5774         g_return_val_if_fail(summaryview != NULL, FALSE);
5775
5776         if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) {
5777                 node = gtk_ctree_find_by_row_data(
5778                                 GTK_CTREE(summaryview->ctree), NULL, 
5779                                 msginfo_update->msginfo);
5780
5781                 if (node) 
5782                         summary_set_row_marks(summaryview, node);
5783         }
5784
5785         return FALSE;
5786 }
5787
5788 /*!
5789  *\brief        change summaryview to display your answer(s) to a message
5790  *
5791  *\param        summaryview The SummaryView ;)
5792  *\param        msginfo The message for which answers are searched
5793  *
5794  */
5795 static void summary_find_answers (SummaryView *summaryview, MsgInfo *msg)
5796 {
5797         FolderItem *sent_folder = NULL;
5798         PrefsAccount *account = NULL;
5799         GtkCTreeNode *node = NULL;
5800         char *buf = NULL;
5801         if (msg == NULL || msg->msgid == NULL)
5802                 return;
5803         
5804         account = account_get_reply_account(msg, prefs_common.reply_account_autosel);
5805         if (account == NULL) 
5806                 return;
5807         sent_folder = account_get_special_folder
5808                                 (account, F_OUTBOX);
5809         
5810         buf = g_strdup_printf("inreplyto matchcase \"%s\"", msg->msgid);
5811
5812         if (sent_folder != summaryview->folder_item) {
5813                 folderview_select(summaryview->mainwin->folderview, sent_folder);
5814         }
5815         
5816         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
5817
5818         quicksearch_set(summaryview->quicksearch, QUICK_SEARCH_EXTENDED, buf);
5819         g_free(buf);
5820
5821         node = gtk_ctree_node_nth(GTK_CTREE(summaryview->ctree), 0);
5822         if (node)
5823                 summary_select_node(summaryview, node, TRUE, TRUE);
5824 }
5825
5826 void summaryview_export_mbox_list(SummaryView *summaryview)
5827 {
5828         GSList *list = summary_get_selected_msg_list(summaryview);
5829         gchar *mbox = filesel_select_file_save(_("Export to mbox file"), NULL);
5830         
5831         if (mbox == NULL || list == NULL)
5832                 return;
5833                 
5834         export_list_to_mbox(list, mbox);
5835         
5836         g_slist_free(list);
5837         g_free(mbox);
5838         
5839 }
5840
5841 void summaryview_lock(SummaryView *summaryview, FolderItem *item)
5842 {
5843         if (!summaryview || !summaryview->folder_item || !item) {
5844                 return;
5845         }
5846
5847         if (summaryview->folder_item->folder == item->folder) {
5848                 gtk_widget_set_sensitive(summaryview->ctree, FALSE);
5849         }
5850 }
5851 void summaryview_unlock(SummaryView *summaryview, FolderItem *item)
5852 {
5853         gtk_widget_set_sensitive(summaryview->ctree, TRUE);
5854 }