0f7a6bdfcb1239511b9de19707ac79b4127ceb7e
[claws.git] / src / summaryview.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail 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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "defs.h"
20
21 #include <glib.h>
22 #include <glib/gi18n.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtk.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #include "main.h"
32 #include "menu.h"
33 #include "mbox.h"
34 #include "mainwindow.h"
35 #include "folderview.h"
36 #include "summaryview.h"
37 #include "messageview.h"
38 #include "mimeview.h"
39 #include "foldersel.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "sourcewindow.h"
43 #include "prefs_common.h"
44 #include "prefs_summary_column.h"
45 #include "prefs_summary_open.h"
46 #include "prefs_filtering.h"
47 #include "account.h"
48 #include "compose.h"
49 #include "utils.h"
50 #include "gtkutils.h"
51 #include "stock_pixmap.h"
52 #include "filesel.h"
53 #include "alertpanel.h"
54 #include "inputdialog.h"
55 #include "statusbar.h"
56 #include "folder.h"
57 #include "colorlabel.h"
58 #include "inc.h"
59 #include "imap.h"
60 #ifndef USE_ALT_ADDRBOOK
61         #include "addressbook.h"
62 #else
63         #include "addressbook-dbus.h"
64         #include "addressadd.h"
65 #endif
66 #include "addr_compl.h"
67 #include "folder_item_prefs.h"
68 #include "filtering.h"
69 #include "string_match.h"
70 #include "toolbar.h"
71 #include "news.h"
72 #include "hooks.h"
73 #include "description_window.h"
74 #include "folderutils.h"
75 #include "quicksearch.h"
76 #include "partial_download.h"
77 #include "tags.h"
78 #include "timing.h"
79 #include "log.h"
80 #include "edittags.h"
81 #include "manual.h"
82 #include "manage_window.h"
83 #include "avatars.h"
84
85 #define SUMMARY_COL_MARK_WIDTH          10
86 #define SUMMARY_COL_STATUS_WIDTH        13
87 #define SUMMARY_COL_LOCKED_WIDTH        13
88 #define SUMMARY_COL_MIME_WIDTH          11
89
90 static int normal_row_height = -1;
91 static GtkStyle *bold_style;
92 static GtkStyle *bold_marked_style;
93 static GtkStyle *bold_deleted_style;
94 static GtkStyle *small_style;
95 static GtkStyle *small_marked_style;
96 static GtkStyle *small_deleted_style;
97
98 static GdkPixbuf *markxpm;
99 static GdkPixbuf *deletedxpm;
100 static GdkPixbuf *movedxpm;
101 static GdkPixbuf *copiedxpm;
102
103 static GdkPixbuf *newxpm;
104 static GdkPixbuf *unreadxpm;
105 static GdkPixbuf *repliedxpm;
106 static GdkPixbuf *forwardedxpm;
107 static GdkPixbuf *repliedandforwardedxpm;
108 static GdkPixbuf *ignorethreadxpm;
109 static GdkPixbuf *watchthreadxpm;
110 static GdkPixbuf *lockedxpm;
111 static GdkPixbuf *spamxpm;
112
113 static GdkPixbuf *clipxpm;
114 static GdkPixbuf *keyxpm;
115 static GdkPixbuf *clipkeyxpm;
116 static GdkPixbuf *keysignxpm;
117 static GdkPixbuf *gpgsignedxpm;
118 static GdkPixbuf *clipgpgsignedxpm;
119
120 static void summary_free_msginfo_func   (GtkCMCTree             *ctree,
121                                          GtkCMCTreeNode         *node,
122                                          gpointer                data);
123 static void summary_set_marks_func      (GtkCMCTree             *ctree,
124                                          GtkCMCTreeNode         *node,
125                                          gpointer                data);
126
127 void  summary_set_menu_sensitive        (SummaryView            *summaryview);
128 guint summary_get_msgnum                (SummaryView            *summaryview,
129                                          GtkCMCTreeNode         *node);
130
131
132 static void summary_set_hide_menu (SummaryView *summaryview,
133                                    const gchar *menu_item,
134                                    guint action);
135
136 static GtkCMCTreeNode *summary_find_prev_msg
137                                         (SummaryView            *summaryview,
138                                          GtkCMCTreeNode         *current_node,
139                                          gboolean                start_from_prev);
140 static GtkCMCTreeNode *summary_find_next_msg
141                                         (SummaryView            *summaryview,
142                                          GtkCMCTreeNode         *current_node,
143                                          gboolean                start_from_next);
144
145 static GtkCMCTreeNode *summary_find_prev_flagged_msg
146                                         (SummaryView    *summaryview,
147                                          GtkCMCTreeNode *current_node,
148                                          MsgPermFlags    flags,
149                                          gboolean        start_from_prev);
150 static GtkCMCTreeNode *summary_find_next_flagged_msg
151                                         (SummaryView    *summaryview,
152                                          GtkCMCTreeNode *current_node,
153                                          MsgPermFlags    flags,
154                                          gboolean        start_from_next);
155
156 static GtkCMCTreeNode *summary_find_msg_by_msgnum
157                                         (SummaryView            *summaryview,
158                                          guint                   msgnum);
159
160 static void summary_update_status       (SummaryView            *summaryview);
161
162 /* display functions */
163 static void summary_status_show         (SummaryView            *summaryview);
164 static void summary_set_column_titles   (SummaryView            *summaryview);
165 static void summary_set_ctree_from_list (SummaryView            *summaryview,
166                                          GSList                 *mlist,
167                                          guint                  selected_msgnum);
168 static inline void summary_set_header   (SummaryView            *summaryview,
169                                          gchar                  *text[],
170                                          MsgInfo                *msginfo);
171 static void summary_display_msg         (SummaryView            *summaryview,
172                                          GtkCMCTreeNode         *row);
173 static void summary_display_msg_full    (SummaryView            *summaryview,
174                                          GtkCMCTreeNode         *row,
175                                          gboolean                new_window,
176                                          gboolean                all_headers);
177 static void summary_set_row_marks       (SummaryView            *summaryview,
178                                          GtkCMCTreeNode         *row);
179
180 static gboolean summary_set_row_tag     (SummaryView            *summaryview, 
181                                          GtkCMCTreeNode                 *row, 
182                                          gboolean                refresh,
183                                          gboolean                set, 
184                                          gint                    id);
185 /* message handling */
186 static void summary_mark_row            (SummaryView            *summaryview,
187                                          GtkCMCTreeNode         *row);
188 static void summary_lock_row            (SummaryView            *summaryview,
189                                          GtkCMCTreeNode         *row);
190 static void summary_unlock_row          (SummaryView            *summaryview,
191                                          GtkCMCTreeNode         *row);
192 static void summary_mark_row_as_read    (SummaryView            *summaryview,
193                                          GtkCMCTreeNode         *row);
194 static void summary_mark_row_as_unread  (SummaryView            *summaryview,
195                                          GtkCMCTreeNode         *row);
196 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed);
197 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed);
198 static void summary_delete_row          (SummaryView            *summaryview,
199                                          GtkCMCTreeNode         *row);
200 static void summary_unmark_row          (SummaryView            *summaryview,
201                                          GtkCMCTreeNode         *row);
202 static void summary_move_row_to         (SummaryView            *summaryview,
203                                          GtkCMCTreeNode         *row,
204                                          FolderItem             *to_folder);
205 static void summary_copy_row_to         (SummaryView            *summaryview,
206                                          GtkCMCTreeNode         *row,
207                                          FolderItem             *to_folder);
208
209 static gint summary_execute_move        (SummaryView            *summaryview);
210 static void summary_execute_move_func   (GtkCMCTree             *ctree,
211                                          GtkCMCTreeNode         *node,
212                                          gpointer                data);
213 static void summary_execute_copy        (SummaryView            *summaryview);
214 static void summary_execute_copy_func   (GtkCMCTree             *ctree,
215                                          GtkCMCTreeNode         *node,
216                                          gpointer                data);
217 static void summary_execute_delete      (SummaryView            *summaryview);
218 static void summary_execute_delete_func (GtkCMCTree             *ctree,
219                                          GtkCMCTreeNode         *node,
220                                          gpointer                data);
221 static void summary_execute_expunge     (SummaryView            *summaryview);
222
223 static void summary_thread_init         (SummaryView            *summaryview);
224
225 static void summary_unthread_for_exec           (SummaryView    *summaryview);
226 static void summary_unthread_for_exec_func      (GtkCMCTree     *ctree,
227                                                  GtkCMCTreeNode *node,
228                                                  gpointer        data);
229
230 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp,
231                               GSList * mlist);
232
233 static void summary_filter_func         (MsgInfo *msginfo);
234
235 static void summary_colorlabel_menu_item_activate_cb
236                                           (GtkWidget    *widget,
237                                            gpointer      data);
238 static void summary_colorlabel_menu_item_activate_item_cb
239                                           (GtkMenuItem  *label_menu_item,
240                                            gpointer      data);
241 static void summary_colorlabel_menu_create(SummaryView  *summaryview,
242                                            gboolean  refresh);
243 static void summary_tags_menu_item_activate_cb
244                                           (GtkWidget    *widget,
245                                            gpointer      data);
246 static void summary_tags_menu_item_activate_item_cb
247                                           (GtkMenuItem  *label_menu_item,
248                                            gpointer      data);
249 static void summary_tags_menu_create(SummaryView        *summaryview,
250                                            gboolean  refresh);
251
252 static GtkWidget *summary_ctree_create  (SummaryView    *summaryview);
253
254 /* callback functions */
255 static gint summary_toggle_pressed      (GtkWidget              *eventbox,
256                                          GdkEventButton         *event,
257                                          SummaryView            *summaryview);
258 #ifdef GENERIC_UMPC
259 static void summary_toggle_multiple_pressed
260                                         (GtkWidget              *widget,
261                                          SummaryView            *summaryview);
262 #endif
263 static gint summary_folder_eventbox_pressed     
264                                         (GtkWidget              *eventbox,
265                                          GdkEventButton         *event,
266                                          SummaryView            *summaryview);
267 static gboolean summary_button_pressed  (GtkWidget              *ctree,
268                                          GdkEventButton         *event,
269                                          SummaryView            *summaryview);
270 static gboolean summary_button_released (GtkWidget              *ctree,
271                                          GdkEventButton         *event,
272                                          SummaryView            *summaryview);
273 static gboolean summary_key_pressed     (GtkWidget              *ctree,
274                                          GdkEventKey            *event,
275                                          SummaryView            *summaryview);
276 static void summary_tree_expanded       (GtkCMCTree             *ctree,
277                                          GtkCMCTreeNode         *node,
278                                          SummaryView            *summaryview);
279 static void summary_tree_collapsed      (GtkCMCTree             *ctree,
280                                          GtkCMCTreeNode         *node,
281                                          SummaryView            *summaryview);
282 static void summary_selected            (GtkCMCTree             *ctree,
283                                          GtkCMCTreeNode         *row,
284                                          gint                    column,
285                                          SummaryView            *summaryview);
286 static void summary_unselected          (GtkCMCTree             *ctree,
287                                          GtkCMCTreeNode         *row,
288                                          gint                    column,
289                                          SummaryView            *summaryview);
290 static void summary_col_resized         (GtkCMCList             *clist,
291                                          gint                    column,
292                                          gint                    width,
293                                          SummaryView            *summaryview);
294 static void summary_mark_clicked        (GtkWidget              *button,
295                                          SummaryView            *summaryview);
296 static void summary_status_clicked      (GtkWidget              *button,
297                                          SummaryView            *summaryview);
298 static void summary_mime_clicked        (GtkWidget              *button,
299                                          SummaryView            *summaryview);
300 static void summary_num_clicked         (GtkWidget              *button,
301                                          SummaryView            *summaryview);
302 static void summary_score_clicked       (GtkWidget *button,
303                                          SummaryView *summaryview);
304 static void summary_size_clicked        (GtkWidget              *button,
305                                          SummaryView            *summaryview);
306 static void summary_date_clicked        (GtkWidget              *button,
307                                          SummaryView            *summaryview);
308 static void summary_from_clicked        (GtkWidget              *button,
309                                          SummaryView            *summaryview);
310 static void summary_to_clicked          (GtkWidget              *button,
311                                          SummaryView            *summaryview);
312 static void summary_subject_clicked     (GtkWidget              *button,
313                                          SummaryView            *summaryview);
314 static void summary_score_clicked       (GtkWidget              *button,
315                                          SummaryView            *summaryview);
316 static void summary_locked_clicked      (GtkWidget              *button,
317                                          SummaryView            *summaryview);
318 static void summary_tags_clicked        (GtkWidget              *button,
319                                          SummaryView            *summaryview);
320
321 static void summary_start_drag          (GtkWidget        *widget, 
322                                          int button,
323                                          GdkEvent *event,
324                                          SummaryView      *summaryview);
325 static void summary_drag_data_get       (GtkWidget        *widget,
326                                          GdkDragContext   *drag_context,
327                                          GtkSelectionData *selection_data,
328                                          guint             info,
329                                          guint             time,
330                                          SummaryView      *summaryview);
331 static void summary_drag_data_received(GtkWidget        *widget,
332                                         GdkDragContext   *drag_context,
333                                         gint              x,
334                                         gint              y,
335                                         GtkSelectionData *data,
336                                         guint             info,
337                                         guint             time,
338                                         SummaryView       *summaryview);
339 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
340                                           GdkDragContext *context,
341                                           gint            x,
342                                           gint            y,
343                                           guint           time,
344                                           SummaryView    *summaryview);
345 static void summary_drag_end(GtkWidget *widget,
346                                           GdkDragContext *drag_context,
347                                           SummaryView    *summaryview);
348
349 /* custom compare functions for sorting */
350 static gint summary_cmp_by_mark         (GtkCMCList             *clist,
351                                          gconstpointer           ptr1,
352                                          gconstpointer           ptr2);
353 static gint summary_cmp_by_status       (GtkCMCList             *clist,
354                                          gconstpointer           ptr1,
355                                          gconstpointer           ptr2);
356 static gint summary_cmp_by_mime         (GtkCMCList             *clist,
357                                          gconstpointer           ptr1,
358                                          gconstpointer           ptr2);
359 static gint summary_cmp_by_num          (GtkCMCList             *clist,
360                                          gconstpointer           ptr1,
361                                          gconstpointer           ptr2);
362 static gint summary_cmp_by_size         (GtkCMCList             *clist,
363                                          gconstpointer           ptr1,
364                                          gconstpointer           ptr2);
365 static gint summary_cmp_by_date         (GtkCMCList             *clist,
366                                          gconstpointer           ptr1,
367                                          gconstpointer           ptr2);
368 static gint summary_cmp_by_thread_date  (GtkCMCList             *clist,
369                                          gconstpointer           ptr1,
370                                          gconstpointer           ptr2);
371 static gint summary_cmp_by_from         (GtkCMCList             *clist,
372                                          gconstpointer           ptr1,
373                                          gconstpointer           ptr2);
374 static gint summary_cmp_by_simplified_subject
375                                         (GtkCMCList             *clist, 
376                                          gconstpointer           ptr1, 
377                                          gconstpointer           ptr2);
378 static gint summary_cmp_by_score        (GtkCMCList             *clist,
379                                          gconstpointer           ptr1,
380                                          gconstpointer           ptr2);
381 static gint summary_cmp_by_label        (GtkCMCList             *clist,
382                                          gconstpointer           ptr1,
383                                          gconstpointer           ptr2);
384 static gint summary_cmp_by_to           (GtkCMCList             *clist,
385                                          gconstpointer           ptr1,
386                                          gconstpointer           ptr2);
387 static gint summary_cmp_by_subject      (GtkCMCList             *clist,
388                                          gconstpointer           ptr1,
389                                          gconstpointer           ptr2);
390 static gint summary_cmp_by_locked       (GtkCMCList             *clist,
391                                          gconstpointer           ptr1, 
392                                          gconstpointer           ptr2);
393 static gint summary_cmp_by_tags         (GtkCMCList             *clist,
394                                          gconstpointer           ptr1, 
395                                          gconstpointer           ptr2);
396
397 static void quicksearch_execute_cb      (QuickSearch    *quicksearch,
398                                          gpointer        data);
399
400 static void tog_searchbar_cb            (GtkWidget      *w,
401                                          gpointer        data);
402
403 static void summary_find_answers        (SummaryView    *summaryview, 
404                                          MsgInfo        *msg);
405
406 static gboolean summary_update_msg      (gpointer source, gpointer data);
407 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data);
408 static gboolean summary_update_folder_hook(gpointer source, gpointer data);
409 static void summary_set_colorlabel_color (GtkCMCTree            *ctree,
410                                    GtkCMCTreeNode               *node,
411                                    guint                 labelcolor);
412 static void summary_thread_build(SummaryView *summaryview);
413
414 GtkTargetEntry summary_drag_types[3] =
415 {
416         {"text/uri-list", 0, TARGET_MAIL_URI_LIST},
417         {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
418         {"claws-mail/msg-path-list", 0, TARGET_MAIL_CM_PATH_LIST},
419 };
420
421 #define DO_ACTION(name, act) {                                          \
422         if(!strcmp(name, a_name)) {                                     \
423                 act;                                                    \
424         }                                                               \
425 }
426
427 static GtkActionEntry summary_popup_entries[] =
428 {
429         {"SummaryViewPopup",                      NULL, "SummaryViewPopup", NULL, NULL, NULL },
430         {"SummaryViewPopup/ReplyTo",              NULL, N_("Repl_y to"), NULL, NULL, NULL },
431         {"SummaryViewPopup/Mark",                 NULL, N_("_Mark"), NULL, NULL, NULL },
432         {"SummaryViewPopup/ColorLabel",           NULL, N_("Color la_bel"), NULL, NULL, NULL },
433         {"SummaryViewPopup/Tags",                 NULL, N_("Ta_gs"), NULL, NULL, NULL },
434         {"SummaryViewPopup/CreateFilterRule",     NULL, N_("Create _filter rule"), NULL, NULL, NULL },
435 #ifndef GENERIC_UMPC
436         {"SummaryViewPopup/CreateProcessingRule", NULL, N_("Create processing rule"), NULL, NULL, NULL },
437 #endif
438         {"SummaryViewPopup/View",                 NULL, N_("_View"), NULL, NULL, NULL },
439 };
440
441 static const gchar *const col_label[N_SUMMARY_COLS] = {
442         "",            /* S_COL_MARK    */
443         N_("S"),       /* S_COL_STATUS  */
444         "",            /* S_COL_MIME    */
445         N_("Subject"), /* S_COL_SUBJECT */
446         N_("From"),    /* S_COL_FROM    */
447         N_("To"),      /* S_COL_TO      */
448         N_("Date"),    /* S_COL_DATE    */
449         N_("Size"),    /* S_COL_SIZE    */
450         N_("#"),       /* S_COL_NUMBER  */
451         N_("Score"),   /* S_COL_SCORE   */
452         "",            /* S_COL_LOCKED  */
453         N_("Tags"),    /* S_COL_TAGS    */
454 };
455
456 void summary_freeze(SummaryView *summaryview)
457 {
458         if (summaryview)
459                 gtk_cmclist_freeze(GTK_CMCLIST(summaryview->ctree));
460 }
461
462 void summary_thaw(SummaryView *summaryview)
463 {
464         if (summaryview)
465                 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
466 }
467
468 void summary_thaw_with_status(SummaryView *summaryview)
469 {
470         if (summaryview) {
471                 summary_status_show(summaryview);
472                 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
473         }
474 }
475
476 void summary_grab_focus(SummaryView *summaryview)
477 {
478         if (summaryview)
479                 gtk_widget_grab_focus(summaryview->ctree);
480 }
481
482 GtkWidget *summary_get_main_widget(SummaryView *summaryview)
483 {
484         if (summaryview)
485                 return summaryview->ctree;
486         else
487                 return NULL;
488 }
489
490 #define START_LONG_OPERATION(summaryview,force_freeze) {        \
491         summary_lock(summaryview);                              \
492         main_window_cursor_wait(summaryview->mainwin);          \
493         if (force_freeze || sc_g_list_bigger(GTK_CMCLIST(summaryview->ctree)->selection, 1)) {\
494                 froze = TRUE;                                           \
495                 summary_freeze(summaryview);    \
496         }                                                       \
497         folder_item_update_freeze();                            \
498         inc_lock();                                             \
499         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,          \
500                       summaryview->msginfo_update_callback_id); \
501 }
502 #define END_LONG_OPERATION(summaryview) {                       \
503         inc_unlock();                                           \
504         folder_item_update_thaw();                              \
505         if (froze)                                              \
506                 summary_thaw(summaryview);      \
507         main_window_cursor_normal(summaryview->mainwin);        \
508         summary_unlock(summaryview);                            \
509         summaryview->msginfo_update_callback_id =               \
510                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,    \
511                 summary_update_msg, (gpointer) summaryview);    \
512 }
513
514 SummaryView *summary_create(MainWindow *mainwin)
515 {
516         SummaryView *summaryview;
517         GtkWidget *vbox;
518         GtkWidget *scrolledwin;
519         GtkWidget *ctree;
520         GtkWidget *hbox;
521         GtkWidget *hbox_l;
522         GtkWidget *stat_box;
523         GtkWidget *stat_box2;
524         GtkWidget *stat_vbox;
525         GtkWidget *statlabel_folder;
526         GtkWidget *statlabel_select;
527         GtkWidget *statlabel_msgs;
528         GtkWidget *hbox_spc;
529         GtkWidget *toggle_eventbox;
530 #ifdef GENERIC_UMPC
531         GtkWidget *multiple_sel_togbtn;
532 #endif
533         GtkWidget *toggle_arrow;
534         GtkWidget *toggle_search;
535         QuickSearch *quicksearch;
536
537         debug_print("Creating summary view...\n");
538         summaryview = g_new0(SummaryView, 1);
539
540 #define SUMMARY_VBOX_SPACING 3
541         vbox = gtk_vbox_new(FALSE, SUMMARY_VBOX_SPACING);
542         
543         /* create status label */
544         hbox = gtk_hbox_new(FALSE, 0);
545         gtk_widget_show(hbox);
546
547         stat_vbox = gtk_vbox_new(FALSE, 0);
548         gtk_widget_show(stat_vbox);
549
550         stat_box = gtk_hbox_new(FALSE, 0);
551         gtk_widget_show(stat_box);
552         
553         stat_box2 = gtk_hbox_new(FALSE, 0);
554         gtk_widget_show(stat_box2);
555         
556         toggle_search = gtk_toggle_button_new();
557         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_search),
558                                      prefs_common.show_searchbar);
559         gtk_widget_set_can_focus(toggle_search, FALSE);
560         gtk_widget_show(toggle_search);
561
562         CLAWS_SET_TIP(toggle_search, _("Toggle quick search bar"));
563         
564         gtk_box_pack_start(GTK_BOX(hbox), toggle_search, FALSE, FALSE, 2);      
565
566         gtk_box_pack_start(GTK_BOX(hbox), stat_vbox, TRUE, TRUE, 0);    
567         gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box, TRUE, TRUE, 0);        
568         gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box2, TRUE, TRUE, 0);       
569
570         hbox_l = gtk_hbox_new(FALSE, 0);
571         gtk_widget_show(hbox_l);
572         gtk_box_pack_start(GTK_BOX(stat_box), hbox_l, TRUE, TRUE, 0);
573  
574         statlabel_folder = gtk_label_new("");
575         gtk_widget_show(statlabel_folder);
576         gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
577         statlabel_select = gtk_label_new("");
578         gtk_widget_show(statlabel_select);
579         gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
580  
581         /* toggle view button */
582         toggle_eventbox = gtk_event_box_new();
583         gtk_widget_show(toggle_eventbox);
584         
585         gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
586
587         toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
588         gtk_widget_show(toggle_arrow);
589         gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
590         g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
591                          G_CALLBACK(summary_toggle_pressed),
592                          summaryview);
593
594 #ifdef GENERIC_UMPC
595         multiple_sel_togbtn = gtk_toggle_button_new();
596         gtk_widget_show(multiple_sel_togbtn);
597         gtk_box_pack_end(GTK_BOX(hbox), multiple_sel_togbtn, FALSE, FALSE, 4);
598         CLAWS_SET_TIP(multiple_sel_togbtn,
599                              _("Toggle multiple selection"));
600         g_signal_connect(G_OBJECT(multiple_sel_togbtn), "toggled",
601                          G_CALLBACK(summary_toggle_multiple_pressed),
602                          summaryview);
603 #endif
604         
605         statlabel_msgs = gtk_label_new("");
606         gtk_widget_show(statlabel_msgs);
607         gtk_box_pack_end(GTK_BOX(stat_box), statlabel_msgs, FALSE, FALSE, 4);
608
609         hbox_spc = gtk_hbox_new(FALSE, 0);
610         gtk_widget_show(hbox_spc);
611         gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
612
613         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
614         gtk_widget_show(scrolledwin);
615         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
616                                        GTK_POLICY_AUTOMATIC,
617                                        GTK_POLICY_AUTOMATIC);
618         summaryview->mainwidget_book = gtk_notebook_new();
619         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
620         gtk_notebook_set_show_border(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
621 #ifndef GENERIC_UMPC
622         gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
623                 scrolledwin);
624         gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
625 #endif
626         gtk_widget_set_size_request(vbox,
627                              prefs_common.summaryview_width,
628                              prefs_common.summaryview_height);
629
630         ctree = summary_ctree_create(summaryview);
631         gtk_widget_show(ctree);
632
633         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
634                                             GTK_CMCLIST(ctree)->hadjustment);
635         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
636                                             GTK_CMCLIST(ctree)->vadjustment);
637         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
638
639         /* status label */
640         gtk_widget_show_all(stat_vbox);
641         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
642
643         /* quick search */
644         quicksearch = quicksearch_new();
645         gtk_box_pack_start(GTK_BOX(vbox), quicksearch_get_widget(quicksearch), FALSE, FALSE, 0);
646
647 #ifdef GENERIC_UMPC
648         gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
649                 scrolledwin);
650         gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
651 #endif
652         quicksearch_set_execute_callback(quicksearch, quicksearch_execute_cb, summaryview);
653
654         g_signal_connect (G_OBJECT(toggle_search), "toggled",
655                           G_CALLBACK(tog_searchbar_cb), summaryview);
656
657         /* create popup menu */
658         
659         gtk_action_group_add_actions(mainwin->action_group, summary_popup_entries,
660                         G_N_ELEMENTS(summary_popup_entries), (gpointer)summaryview);
661
662         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/", "Menus", "Menus", GTK_UI_MANAGER_MENUBAR)
663         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewPopup", "SummaryViewPopup", GTK_UI_MANAGER_MENU)
664         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reply", "Message/Reply", GTK_UI_MANAGER_MENUITEM)
665 #ifndef GENERIC_UMPC
666         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ReplyTo", "SummaryViewPopup/ReplyTo", GTK_UI_MANAGER_MENU)
667         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
668 #endif
669         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Forward", "Message/Forward", GTK_UI_MANAGER_MENUITEM)
670 #ifndef GENERIC_UMPC
671         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ForwardAtt", "Message/ForwardAtt", GTK_UI_MANAGER_MENUITEM)
672         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Redirect", "Message/Redirect", GTK_UI_MANAGER_MENUITEM)
673 #endif
674         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
675         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Move", "Message/Move", GTK_UI_MANAGER_MENUITEM)
676         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Copy", "Message/Copy", GTK_UI_MANAGER_MENUITEM)
677         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Trash", "Message/Trash", GTK_UI_MANAGER_MENUITEM)
678 #ifndef GENERIC_UMPC
679         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Delete", "Message/Delete", GTK_UI_MANAGER_MENUITEM)
680 #endif
681         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
682         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Mark", "SummaryViewPopup/Mark", GTK_UI_MANAGER_MENU)
683         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ColorLabel", "SummaryViewPopup/ColorLabel", GTK_UI_MANAGER_MENU)
684         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Tags", "SummaryViewPopup/Tags", GTK_UI_MANAGER_MENU)
685
686         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
687 #ifndef GENERIC_UMPC
688         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "AddSenderToAB", "Tools/AddSenderToAB", GTK_UI_MANAGER_MENUITEM)
689 #endif
690         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "CreateFilterRule", "SummaryViewPopup/CreateFilterRule", GTK_UI_MANAGER_MENU)
691 #ifndef GENERIC_UMPC
692         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "CreateProcessingRule", "SummaryViewPopup/CreateProcessingRule", GTK_UI_MANAGER_MENU)
693 #endif
694         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator5", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
695         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "View", "SummaryViewPopup/View", GTK_UI_MANAGER_MENU)
696         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "SaveAs", "File/SaveAs", GTK_UI_MANAGER_MENUITEM)
697 #ifndef GENERIC_UMPC
698         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Print", "File/Print", GTK_UI_MANAGER_MENUITEM)
699 #endif
700         /* last separator, for plugins */
701         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator6", "File/---", GTK_UI_MANAGER_SEPARATOR)
702
703         /* submenus - replyto */
704         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "All", "Message/ReplyTo/All", GTK_UI_MANAGER_MENUITEM)
705         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "Sender", "Message/ReplyTo/Sender", GTK_UI_MANAGER_MENUITEM)
706         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "MailingList", "Message/ReplyTo/List", GTK_UI_MANAGER_MENUITEM)
707
708         /* submenus - mark */
709         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Mark", "Message/Mark/Mark", GTK_UI_MANAGER_MENUITEM)
710         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Unmark", "Message/Mark/Unmark", GTK_UI_MANAGER_MENUITEM)
711         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator1", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
712         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkRead", "Message/Mark/MarkRead", GTK_UI_MANAGER_MENUITEM)
713         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkUnread", "Message/Mark/MarkUnread", GTK_UI_MANAGER_MENUITEM)
714         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator2", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
715         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkAllRead", "Message/Mark/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
716         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkAllUnread", "Message/Mark/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
717         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator3", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
718         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "IgnoreThread", "Message/Mark/IgnoreThread", GTK_UI_MANAGER_MENUITEM)
719         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "UnignoreThread", "Message/Mark/UnignoreThread", GTK_UI_MANAGER_MENUITEM)
720         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "WatchThread", "Message/Mark/WatchThread", GTK_UI_MANAGER_MENUITEM)
721         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "UnwatchThread", "Message/Mark/UnwatchThread", GTK_UI_MANAGER_MENUITEM)
722         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator4", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
723         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkSpam", "Message/Mark/MarkSpam", GTK_UI_MANAGER_MENUITEM)
724         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkHam", "Message/Mark/MarkHam", GTK_UI_MANAGER_MENUITEM)
725         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator5", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
726         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Lock", "Message/Mark/Lock", GTK_UI_MANAGER_MENUITEM)
727         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Unlock", "Message/Mark/Unlock", GTK_UI_MANAGER_MENUITEM)
728
729         /* submenus - colorlabel and tags are dynamic */
730         /* submenus - createfilterrule */
731         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "Automatically", "Tools/CreateFilterRule/Automatically", GTK_UI_MANAGER_MENUITEM)
732         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "ByFrom", "Tools/CreateFilterRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
733         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "ByTo", "Tools/CreateFilterRule/ByTo", GTK_UI_MANAGER_MENUITEM)
734         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "BySubject", "Tools/CreateFilterRule/BySubject", GTK_UI_MANAGER_MENUITEM)
735                 
736 #ifndef GENERIC_UMPC
737         /* submenus - createprocessingrule */
738         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "Automatically", "Tools/CreateProcessingRule/Automatically", GTK_UI_MANAGER_MENUITEM)
739         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "ByFrom", "Tools/CreateProcessingRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
740         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "ByTo", "Tools/CreateProcessingRule/ByTo", GTK_UI_MANAGER_MENUITEM)
741         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "BySubject", "Tools/CreateProcessingRule/BySubject", GTK_UI_MANAGER_MENUITEM)
742 #endif
743                 
744         /* submenus - view */
745         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "OpenNewWindow", "View/OpenNewWindow", GTK_UI_MANAGER_MENUITEM)
746         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "MessageSource", "View/MessageSource", GTK_UI_MANAGER_MENUITEM)
747 #ifndef GENERIC_UMPC
748         MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "AllHeaders", "View/AllHeaders", GTK_UI_MANAGER_MENUITEM)
749 #endif          
750         summaryview->popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
751                                 gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewPopup")) );
752
753
754         summaryview->vbox = vbox;
755         summaryview->scrolledwin = scrolledwin;
756         summaryview->ctree = ctree;
757         summaryview->hbox = hbox;
758         summaryview->hbox_l = hbox_l;
759         summaryview->hbox_spc = hbox_spc;
760         summaryview->stat_box = stat_box;
761         summaryview->stat_box2 = stat_box2;
762         summaryview->statlabel_folder = statlabel_folder;
763         summaryview->statlabel_select = statlabel_select;
764         summaryview->statlabel_msgs = statlabel_msgs;
765         summaryview->toggle_eventbox = toggle_eventbox;
766         summaryview->toggle_arrow = toggle_arrow;
767 #ifdef GENERIC_UMPC
768         summaryview->multiple_sel_togbtn = multiple_sel_togbtn;
769 #endif
770         summaryview->toggle_search = toggle_search;
771         summaryview->lock_count = 0;
772         summaryview->msginfo_update_callback_id =
773                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview);
774         summaryview->folder_item_update_callback_id =
775                 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
776                                 summary_update_folder_item_hook,
777                                 (gpointer) summaryview);
778         summaryview->folder_update_callback_id =
779                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
780                                 summary_update_folder_hook,
781                                 (gpointer) summaryview);
782
783         summaryview->target_list = gtk_target_list_new(summary_drag_types, 3);
784
785         summaryview->quicksearch = quicksearch;
786
787         /* CLAWS: need this to get the SummaryView * from
788          * the CList */
789         g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 
790
791         gtk_widget_show_all(vbox);
792
793         gtk_widget_show(vbox);
794
795         if (prefs_common.show_searchbar)
796                 quicksearch_show(quicksearch);
797         else
798                 quicksearch_hide(quicksearch);
799         
800         if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
801             prefs_common.layout_mode == SMALL_LAYOUT)
802                 gtk_widget_hide(summaryview->toggle_eventbox);
803
804         return summaryview;
805 }
806
807 void summary_relayout(SummaryView *summaryview)
808 {
809         gtk_widget_realize(summaryview->stat_box);
810
811         g_object_ref(summaryview->hbox_l);
812         g_object_ref(summaryview->statlabel_msgs);
813         
814         gtkut_container_remove(GTK_CONTAINER(
815                 gtk_widget_get_parent(summaryview->hbox_l)), summaryview->hbox_l);
816         gtkut_container_remove(GTK_CONTAINER(
817                 gtk_widget_get_parent(summaryview->statlabel_msgs)), summaryview->statlabel_msgs);
818
819         switch (prefs_common.layout_mode) {
820         case NORMAL_LAYOUT:
821         case WIDE_LAYOUT:
822         case WIDE_MSGLIST_LAYOUT:
823                 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
824                 gtk_box_pack_end(GTK_BOX(summaryview->stat_box), summaryview->statlabel_msgs, FALSE, FALSE, 4);
825                 gtk_widget_show_all(summaryview->stat_box);
826                 gtk_widget_show_all(summaryview->stat_box2);
827                 if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
828                     prefs_common.layout_mode == SMALL_LAYOUT)
829                         gtk_widget_hide(summaryview->toggle_eventbox);
830                 else
831                         gtk_widget_show(summaryview->toggle_eventbox);
832                 break;
833         case VERTICAL_LAYOUT:
834         case SMALL_LAYOUT:
835                 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
836                 gtk_box_pack_start(GTK_BOX(summaryview->stat_box2), summaryview->statlabel_msgs, FALSE, FALSE, 4);
837                 gtk_widget_show_all(summaryview->stat_box);
838                 gtk_widget_show_all(summaryview->stat_box2);
839                 if (prefs_common.layout_mode == SMALL_LAYOUT) {
840                         gtk_widget_hide(summaryview->toggle_eventbox);
841                         gtk_widget_hide(summaryview->statlabel_msgs);
842                 } else {
843                         gtk_widget_show(summaryview->toggle_eventbox);
844                         gtk_widget_show(summaryview->statlabel_msgs);
845                 }
846                         
847                 break;
848         }
849         summary_set_column_order(summaryview);
850
851         g_object_unref(summaryview->hbox_l);
852         g_object_unref(summaryview->statlabel_msgs);
853         quicksearch_relayout(summaryview->quicksearch);
854         if (prefs_common.show_searchbar)
855                 quicksearch_show(summaryview->quicksearch);
856         else
857                 quicksearch_hide(summaryview->quicksearch);
858 }
859
860 static void summary_set_fonts(SummaryView *summaryview)
861 {
862         PangoFontDescription *font_desc;
863         gint size;
864
865         font_desc = pango_font_description_from_string(NORMAL_FONT);
866         if (font_desc) {
867                 gtk_widget_modify_font(summaryview->ctree, font_desc);
868                 pango_font_description_free(font_desc);
869         }
870
871         if (!bold_style) {
872                 bold_style = gtk_style_copy
873                         (gtk_widget_get_style(summaryview->ctree));
874
875                 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
876                         font_desc = pango_font_description_from_string(NORMAL_FONT);
877                         if (font_desc) {
878                                 pango_font_description_free(bold_style->font_desc);
879                                 bold_style->font_desc = font_desc;
880                         }
881                         pango_font_description_set_weight
882                                         (bold_style->font_desc, PANGO_WEIGHT_BOLD);
883                 } else {
884                         font_desc = pango_font_description_from_string(BOLD_FONT);
885                         if (font_desc) {
886                                 pango_font_description_free(bold_style->font_desc);
887                                 bold_style->font_desc = font_desc;
888                         }
889                 }
890                 bold_marked_style = gtk_style_copy(bold_style);
891                 bold_marked_style->text[GTK_STATE_NORMAL] =
892                         summaryview->color_marked;
893                 bold_deleted_style = gtk_style_copy(bold_style);
894                 bold_deleted_style->text[GTK_STATE_NORMAL] =
895                         summaryview->color_dim;
896         }
897
898         if (prefs_common.derive_from_normal_font || !SMALL_FONT) {
899                 font_desc = pango_font_description_new();
900                 size = pango_font_description_get_size
901                         (gtk_widget_get_style(summaryview->ctree)->font_desc);
902                 pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
903         } else {
904                 font_desc = pango_font_description_from_string(SMALL_FONT);
905         }
906         if (font_desc) {
907                 gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
908                 gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
909                 gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
910                 pango_font_description_free(font_desc);
911         }
912
913 }
914
915 static void summary_set_folder_pixmap(SummaryView *summaryview, StockPixmap icon)
916 {
917         GtkWidget *pixmap; 
918         if (!summaryview->folder_pixmap_eventbox) {
919                 summaryview->folder_pixmap_eventbox = gtk_event_box_new();
920                 gtk_widget_show(summaryview->folder_pixmap_eventbox);
921                 gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, FALSE, FALSE, 4);
922                 gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, 0); /* search_toggle before */
923                 g_signal_connect(G_OBJECT(summaryview->folder_pixmap_eventbox), "button_press_event",
924                          G_CALLBACK(summary_folder_eventbox_pressed),
925                          summaryview);
926         }
927         if (summaryview->folder_pixmap)
928                 gtk_widget_destroy(summaryview->folder_pixmap);
929
930         pixmap = stock_pixmap_widget(icon);
931         gtk_container_add(GTK_CONTAINER(summaryview->folder_pixmap_eventbox), pixmap);
932         gtk_widget_show(pixmap);
933         summaryview->folder_pixmap = pixmap; 
934 }
935
936 void summary_init(SummaryView *summaryview)
937 {
938         GtkWidget *pixmap;
939
940         gtk_widget_realize(summaryview->ctree);
941         stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
942         stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
943         stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
944         stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
945         stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
946         stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
947         stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
948         stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
949         stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
950         stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
951         stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
952         stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
953         stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
954         stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
955         stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
956         stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
957         stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
958         stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
959         stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
960
961         summary_set_fonts(summaryview);
962
963         summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
964
965         pixmap = stock_pixmap_widget(STOCK_PIXMAP_QUICKSEARCH);
966         gtk_container_add (GTK_CONTAINER(summaryview->toggle_search), pixmap);
967         gtk_widget_show(pixmap);
968         summaryview->quick_search_pixmap = pixmap;
969         
970 #ifdef GENERIC_UMPC
971         pixmap = stock_pixmap_widget(STOCK_PIXMAP_SELECTION);
972         gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
973         gtk_widget_show(pixmap);
974         summaryview->multiple_sel_image = pixmap;
975 #endif
976
977         /* Init summaryview prefs */
978         summaryview->sort_key = SORT_BY_NONE;
979         summaryview->sort_type = SORT_ASCENDING;
980
981         /* Init summaryview extra data */
982         summaryview->simplify_subject_preg = NULL;
983         summary_clear_list(summaryview);
984         summary_set_column_titles(summaryview);
985         summary_colorlabel_menu_create(summaryview, FALSE);
986         summary_tags_menu_create(summaryview, FALSE);
987         main_create_mailing_list_menu (summaryview->mainwin, NULL);     
988         summary_set_menu_sensitive(summaryview);
989
990 }
991
992 #define CURRENTLY_DISPLAYED(m) \
993 ( (m->msgnum == displayed_msgnum) \
994   && (!g_ascii_strcasecmp(m->folder->name,item->name)) )
995
996 #define FOLDER_SHOWS_TO_HDR(i) \
997 ( i && (folder_has_parent_of_type(i, F_OUTBOX) \
998         || folder_has_parent_of_type(i, F_DRAFT) \
999         || folder_has_parent_of_type(i, F_QUEUE)) )
1000   
1001 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
1002 {
1003         gboolean show_from = FALSE, show_to = FALSE;
1004         gboolean showing_from = FALSE, showing_to = FALSE;
1005         gint from_pos = 0, to_pos = 0;
1006         SummaryColumnState *col_state = summaryview->col_state;
1007         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1008         
1009         if (!item || ((prefs_common.layout_mode == VERTICAL_LAYOUT || prefs_common.layout_mode == SMALL_LAYOUT) && prefs_common.two_line_vert) )
1010                 return;
1011         if (FOLDER_SHOWS_TO_HDR(item))
1012                 show_to = TRUE;
1013         else
1014                 show_from = TRUE;
1015         
1016         from_pos = summaryview->col_pos[S_COL_FROM];
1017         to_pos = summaryview->col_pos[S_COL_TO];
1018         showing_from = col_state[from_pos].visible;
1019         showing_to = col_state[to_pos].visible;
1020         
1021         if (showing_from && showing_to) {
1022                 debug_print("showing both\n");
1023                 return;
1024         }
1025
1026         if (!showing_from && !showing_to) {
1027                 debug_print("showing none\n");
1028                 return;
1029         }
1030
1031         debug_print("showing %s %s, must show %s %s\n", 
1032                 showing_from?"From":"",
1033                 showing_to?"To":"",
1034                 show_from?"From":"",
1035                 show_to?"To":"");
1036
1037         if (showing_from == show_from && showing_to == show_to)
1038                 return;
1039         /* else we'll switch both */
1040
1041         debug_print("switching columns\n");
1042         col_state[from_pos].type = S_COL_TO;
1043         col_state[from_pos].visible = show_to;
1044
1045         col_state[to_pos].type = S_COL_FROM;
1046         col_state[to_pos].visible = show_from;
1047
1048         summaryview->col_pos[S_COL_TO] = from_pos;
1049         summaryview->col_pos[S_COL_FROM] = to_pos;
1050
1051         gtk_cmclist_set_column_visibility
1052                 (GTK_CMCLIST(ctree), from_pos, col_state[from_pos].visible);
1053         gtk_cmclist_set_column_visibility
1054                 (GTK_CMCLIST(ctree), to_pos, col_state[to_pos].visible);
1055
1056         summary_set_column_titles(summaryview);
1057 }
1058
1059 static void summaryview_reset_recursive_folder_match(SummaryView *summaryview)
1060 {
1061         GSList *cur;
1062
1063         for (cur = summaryview->recursive_matched_folders; cur != NULL; cur = cur->next) {
1064                 folderview_update_search_icon(cur->data, FALSE);
1065         }
1066
1067         g_slist_free(summaryview->recursive_matched_folders);
1068         summaryview->recursive_matched_folders = NULL;
1069         summaryview->search_root_folder = NULL;
1070 }
1071
1072 static gboolean summaryview_quicksearch_recursive_progress(gpointer data, guint at, guint matched, guint total)
1073 {
1074         QuickSearch *search = (QuickSearch*) data;
1075         gint interval = quicksearch_is_fast(search) ? 5000 : 100;
1076
1077         statusbar_progress_all(at, total, interval);
1078         if (at % interval == 0)
1079                 GTK_EVENTS_FLUSH();
1080
1081         if (matched > 0)
1082                 return FALSE;
1083
1084         return TRUE;
1085 }
1086
1087 static void summaryview_quicksearch_recurse_step(SummaryView *summaryview, FolderItem *item)
1088 {
1089         MsgInfoList *msgs = NULL;
1090         gboolean result = TRUE;
1091
1092         statusbar_print_all(_("Searching in %s... \n"),
1093                 item->path ? item->path : "(null)");
1094         folder_item_update_freeze();
1095
1096         quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_quicksearch_recursive_progress, summaryview->quicksearch);
1097         if (!quicksearch_run_on_folder(summaryview->quicksearch, item, &msgs))
1098                 result = FALSE;
1099
1100         result = result && msgs != NULL;
1101
1102         if (msgs != NULL)
1103                 procmsg_msg_list_free(msgs);
1104
1105         folder_item_update_thaw();
1106         statusbar_progress_all(0, 0, 0);
1107         statusbar_pop_all();
1108
1109         if (result) {
1110                 summaryview->recursive_matched_folders = g_slist_prepend(
1111                                 summaryview->recursive_matched_folders, item);
1112         
1113                 folderview_update_search_icon(item, TRUE);
1114         }
1115 }
1116
1117 static void summaryview_quicksearch_search_subfolders(SummaryView *summaryview, FolderItem *folder_item)
1118 {
1119         FolderItem *cur = NULL;
1120         GNode *node = folder_item->node->children;
1121
1122         if (!prefs_common.summary_quicksearch_recurse
1123                         || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1124                         || quicksearch_is_in_typing(summaryview->quicksearch))
1125                 return;
1126
1127         for (; node != NULL; node = node->next) {
1128                 if (!quicksearch_has_sat_predicate(summaryview->quicksearch))
1129                         return;
1130
1131                 cur = FOLDER_ITEM(node->data);
1132                 summaryview_quicksearch_recurse_step(summaryview, cur);
1133                 if (cur->node->children)
1134                         summaryview_quicksearch_search_subfolders(summaryview, cur);
1135         }
1136 }
1137
1138 static void summaryview_quicksearch_recurse(SummaryView *summaryview)
1139 {
1140         if (!prefs_common.summary_quicksearch_recurse
1141                 || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1142                 || summaryview->folder_item == NULL) {
1143                 return;
1144         }
1145
1146         main_window_cursor_wait(summaryview->mainwin);
1147
1148         summaryview_reset_recursive_folder_match(summaryview);
1149         summaryview->search_root_folder = summaryview->folder_item;
1150
1151         summaryview_quicksearch_search_subfolders(summaryview, summaryview->folder_item);
1152         
1153         main_window_cursor_normal(summaryview->mainwin);
1154 }
1155
1156 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
1157 {
1158         int u = 0, n = 0, m = 0, t = 0, r = 0, f = 0, l = 0, i = 0, w = 0;
1159         GSList *cur;
1160         START_TIMING("");
1161         for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1162                 MsgInfo * msginfo = (MsgInfo *) cur->data;
1163                 t++;
1164                 if (MSG_IS_NEW(msginfo->flags))
1165                         n++;
1166                 if (MSG_IS_UNREAD(msginfo->flags))
1167                         u++;
1168                 if (MSG_IS_MARKED(msginfo->flags))
1169                         m++;            
1170                 if (MSG_IS_REPLIED(msginfo->flags))
1171                         r++;
1172                 if (MSG_IS_FORWARDED(msginfo->flags))
1173                         f++;
1174                 if (MSG_IS_LOCKED(msginfo->flags))
1175                         l++;
1176                 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
1177                         i++;
1178                 if (MSG_IS_WATCH_THREAD(msginfo->flags))
1179                         w++;
1180         }
1181         if (t != item->total_msgs
1182         ||  n != item->new_msgs
1183         ||  u != item->unread_msgs
1184         ||  m != item->marked_msgs
1185         ||  r != item->replied_msgs
1186         ||  f != item->forwarded_msgs
1187         ||  l != item->locked_msgs
1188         ||  i != item->ignored_msgs
1189         ||  w != item->watched_msgs
1190         ||  (m == 0 && item->unreadmarked_msgs != 0)
1191         ||  item->unreadmarked_msgs < 0) {
1192                 debug_print("Inconsistency\n");
1193                 folder_item_scan_full(item, FALSE);
1194                 END_TIMING();
1195                 return FALSE;
1196         } 
1197         END_TIMING();
1198         return TRUE;
1199 }
1200
1201 gboolean summaryview_search_root_progress(gpointer data, guint at, guint matched, guint total)
1202 {
1203         SummaryView *summaryview = (SummaryView*) data;
1204
1205         gint interval = quicksearch_is_fast(summaryview->quicksearch) ? 5000 : 100;
1206         
1207         statusbar_progress_all(at, total, interval);
1208
1209         if (at % interval == 0)
1210                 GTK_EVENTS_FLUSH();
1211
1212         return TRUE;
1213 }
1214
1215 gboolean summary_show(SummaryView *summaryview, FolderItem *item)
1216 {
1217         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1218         GtkCMCTreeNode *node = NULL;
1219         GSList *mlist = NULL;
1220         gchar *buf;
1221         gboolean is_refresh;
1222         guint selected_msgnum = 0;
1223         guint displayed_msgnum = 0;
1224         GSList *cur;
1225         GSList *not_killed;
1226         gboolean hidden_removed = FALSE;
1227
1228         if (summary_is_locked(summaryview)) return FALSE;
1229
1230         if (!summaryview->mainwin)
1231                 return FALSE;
1232         START_TIMING("");
1233         summary_switch_from_to(summaryview, item);
1234
1235         inc_lock();
1236         summary_lock(summaryview);
1237         
1238         menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
1239         
1240         utils_free_regex();
1241
1242         is_refresh = (item == summaryview->folder_item) ? TRUE : FALSE;
1243
1244         if (item && item->folder->klass->item_opened) {
1245                 item->folder->klass->item_opened(item);
1246         }
1247
1248         if (!is_refresh) {
1249                 main_create_mailing_list_menu (summaryview->mainwin, NULL);
1250                 if (prefs_common.layout_mode == SMALL_LAYOUT) {
1251                         if (item) {
1252                                 mainwindow_enter_folder(summaryview->mainwin);
1253                                 gtk_widget_grab_focus(summaryview->ctree);
1254                         }
1255                 }
1256         }
1257         if (!prefs_common.summary_quicksearch_sticky
1258          && (!prefs_common.summary_quicksearch_recurse
1259           || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1260           || (item && !folder_is_child_of(item, summaryview->search_root_folder)))
1261          && !quicksearch_is_running(summaryview->quicksearch)
1262          && !is_refresh) {
1263                 quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
1264         }
1265
1266         /* STATUSBAR_POP(summaryview->mainwin); */
1267
1268         if (is_refresh) {
1269                 selected_msgnum = summary_get_msgnum(summaryview,
1270                                                      summaryview->selected);
1271                 displayed_msgnum = summary_get_msgnum(summaryview,
1272                                                       summaryview->displayed);
1273         }
1274
1275         /* process the marks if any */
1276         if (!is_refresh &&
1277                         (summaryview->mainwin->lock_count == 0 &&
1278                          (summaryview->moved > 0 || summaryview->copied > 0))) {
1279                 AlertValue val;
1280                 gboolean changed = FALSE;
1281
1282                 val = alertpanel(_("Process mark"),
1283                                  _("Some marks are left. Process them?"),
1284                                  GTK_STOCK_NO, GTK_STOCK_YES, GTK_STOCK_CANCEL, ALERTFOCUS_FIRST);
1285                 if (G_ALERTALTERNATE == val) {
1286                         summary_unlock(summaryview);
1287                         summary_execute(summaryview);
1288                         summary_lock(summaryview);
1289                         changed = TRUE;
1290                 } else if (G_ALERTDEFAULT == val) {
1291                         /* DO NOTHING */
1292                 } else {
1293                         summary_unlock(summaryview);
1294                         inc_unlock();
1295                         END_TIMING();
1296                         return FALSE;
1297                 }
1298                 if (changed || !quicksearch_has_sat_predicate(summaryview->quicksearch))
1299                         folder_update_op_count();
1300         }
1301         
1302         summary_freeze(summaryview);
1303
1304         summary_clear_list(summaryview);
1305
1306         buf = NULL;
1307         if (!item || !item->path || !folder_item_parent(item) || item->no_select) {
1308                 g_free(buf);
1309                 debug_print("empty folder (%p %s %p %d)\n",
1310                                 item, 
1311                                 (item && item->path)?item->path:"(null)",
1312                                 item?folder_item_parent(item):0x0,
1313                                 item?item->no_select:FALSE);
1314                 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE);
1315                 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE);
1316                 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE);
1317                 summary_clear_all(summaryview);
1318                 summaryview->folder_item = item;
1319                 summary_thaw(summaryview);
1320                 summary_unlock(summaryview);
1321                 inc_unlock();
1322                 END_TIMING();
1323                 return TRUE;
1324         }
1325         g_free(buf);
1326
1327         if (!is_refresh)
1328                 messageview_clear(summaryview->messageview);
1329
1330         summaryview->folder_item = item;
1331         item->opened = TRUE;
1332
1333         buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
1334         debug_print("%s\n", buf);
1335         STATUSBAR_PUSH(summaryview->mainwin, buf);
1336         g_free(buf);
1337
1338         main_window_cursor_wait(summaryview->mainwin);
1339
1340         mlist = folder_item_get_msg_list(item);
1341
1342         if (!summary_check_consistency(item, mlist)) {
1343                 debug_print("reloading due to inconsistency\n");
1344                 procmsg_msg_list_free(mlist);
1345                 mlist = folder_item_get_msg_list(item);
1346         }
1347
1348         if (quicksearch_has_sat_predicate(summaryview->quicksearch)) {
1349                 procmsg_msg_list_free(mlist);
1350                 mlist = NULL;
1351
1352                 START_TIMING("quicksearch");
1353
1354                 statusbar_print_all(_("Searching in %s... \n"), 
1355                         summaryview->folder_item->path ? 
1356                         summaryview->folder_item->path : "(null)");
1357
1358                 folder_item_update_freeze();
1359
1360                 quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_search_root_progress, summaryview);
1361                 quicksearch_run_on_folder(summaryview->quicksearch, summaryview->folder_item, &mlist);
1362
1363                 folder_item_update_thaw();
1364                 statusbar_progress_all(0, 0, 0);
1365                 statusbar_pop_all();
1366
1367                 if (!quicksearch_has_sat_predicate(summaryview->quicksearch)) {
1368                         debug_print("search cancelled!\n");
1369                         summary_thaw(summaryview);
1370                         STATUSBAR_POP(summaryview->mainwin);
1371                         main_window_cursor_normal(summaryview->mainwin);
1372                         summary_unlock(summaryview);
1373                         inc_unlock();
1374                         summary_show(summaryview, summaryview->folder_item);
1375                         END_TIMING();
1376                         return FALSE;
1377                 }
1378                 END_TIMING();
1379         }
1380
1381         if ((summaryview->folder_item->hide_read_msgs
1382              || summaryview->folder_item->hide_del_msgs
1383              || summaryview->folder_item->hide_read_threads) &&
1384             quicksearch_has_sat_predicate(summaryview->quicksearch) == FALSE) {
1385                 GSList *not_killed;
1386                 
1387                 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
1388                                                 summaryview->folder_item->hide_read_msgs);
1389                 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
1390                                                 summaryview->folder_item->hide_del_msgs);
1391                 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
1392                                                 summaryview->folder_item->hide_read_threads);
1393                 not_killed = NULL;
1394                 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1395                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1396                         
1397                         if (!msginfo->hidden) {
1398                                 if (MSG_IS_DELETED(msginfo->flags) && summaryview->folder_item->hide_del_msgs) {
1399                                         procmsg_msginfo_free(&msginfo);
1400                                         continue;
1401                                 }
1402                                 if (summaryview->folder_item->hide_read_msgs) {
1403                                         if (MSG_IS_UNREAD(msginfo->flags) &&
1404                                             !MSG_IS_IGNORE_THREAD(msginfo->flags))
1405                                                 not_killed = g_slist_prepend(not_killed, msginfo);
1406                                         else if (MSG_IS_MARKED(msginfo->flags) ||
1407                                                  MSG_IS_LOCKED(msginfo->flags))
1408                                                 not_killed = g_slist_prepend(not_killed, msginfo);
1409                                         else if (is_refresh &&
1410                                                 (msginfo->msgnum == selected_msgnum ||
1411                                                  msginfo->msgnum == displayed_msgnum))
1412                                                 not_killed = g_slist_prepend(not_killed, msginfo);
1413                                         else
1414                                                 procmsg_msginfo_free(&msginfo);
1415                                 } else {
1416                                         not_killed = g_slist_prepend(not_killed, msginfo);
1417                                 }
1418                          } else
1419                                 procmsg_msginfo_free(&msginfo);
1420                 }
1421                 hidden_removed = TRUE;
1422                 g_slist_free(mlist);
1423                 mlist = not_killed;
1424         } else {
1425                 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
1426                                                 FALSE);
1427                 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
1428                                                 FALSE);
1429                 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
1430                                                 FALSE);
1431         }
1432
1433         if (!hidden_removed) {
1434                 START_TIMING("removing hidden");
1435                 not_killed = NULL;
1436                 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1437                         MsgInfo * msginfo = (MsgInfo *) cur->data;
1438
1439                         if (!msginfo->hidden)
1440                                 not_killed = g_slist_prepend(not_killed, msginfo);
1441                         else
1442                                 procmsg_msginfo_free(&msginfo);
1443                 }
1444                 g_slist_free(mlist);
1445                 mlist = not_killed;
1446                 END_TIMING();
1447         }
1448
1449         STATUSBAR_POP(summaryview->mainwin);
1450
1451         /* set ctree and hash table from the msginfo list, and
1452            create the thread */
1453         summary_set_ctree_from_list(summaryview, mlist, selected_msgnum);
1454
1455         g_slist_free(mlist);
1456
1457         if (is_refresh) {
1458                 if (!quicksearch_is_in_typing(summaryview->quicksearch)) {
1459                         summaryview->displayed =
1460                                 summary_find_msg_by_msgnum(summaryview,
1461                                                            displayed_msgnum);
1462                         if (!summaryview->displayed)
1463                                 messageview_clear(summaryview->messageview);
1464                         summary_unlock(summaryview);
1465
1466                         if (quicksearch_is_running(summaryview->quicksearch))
1467                                 summary_select_by_msgnum(summaryview, selected_msgnum,
1468                                                 OPEN_SELECTED_ON_SEARCH_RESULTS);
1469                         else
1470                                 summary_select_by_msgnum(summaryview, selected_msgnum,
1471                                                 OPEN_SELECTED_ON_FOLDER_OPEN);
1472
1473                         summary_lock(summaryview);
1474                         if (!summaryview->selected) {
1475                                 /* no selected message - select first unread
1476                                    message, but do not display it */
1477                                 node = summary_find_next_flagged_msg(summaryview, NULL,
1478                                                                      MSG_UNREAD, FALSE);
1479                                 if (node == NULL && GTK_CMCLIST(ctree)->row_list != NULL)
1480                                         node = gtk_cmctree_node_nth
1481                                                 (ctree,
1482                                                  item->sort_type == SORT_DESCENDING
1483                                                  ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1484                                 summary_unlock(summaryview);
1485
1486                                 if (quicksearch_is_running(summaryview->quicksearch))
1487                                         summary_select_node(summaryview, node,
1488                                                         OPEN_SELECTED_ON_SEARCH_RESULTS);
1489                                 else
1490                                         summary_select_node(summaryview, node,
1491                                                         OPEN_SELECTED_ON_FOLDER_OPEN);
1492
1493                                 summary_lock(summaryview);
1494                         }
1495                 } else {
1496                         /* just select first/last */
1497                         if (GTK_CMCLIST(ctree)->row_list != NULL)
1498                                 node = gtk_cmctree_node_nth
1499                                         (ctree,
1500                                          item->sort_type == SORT_DESCENDING
1501                                          ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1502                         summary_select_node(summaryview, node, OPEN_SELECTED_ON_SEARCH_RESULTS);
1503                 }
1504         } else {
1505                 /* backward compat */
1506                 int i = 0;
1507                 gboolean set = FALSE, stop = FALSE;
1508                 for (i = 0; i < 6; i++) {
1509                         EntryAction act = prefs_common.summary_select_prio[i];
1510
1511                         if (act != ACTION_UNSET) {
1512                                 set = TRUE;
1513                                 break;
1514                         }
1515                 }
1516                 if (!set)
1517                         prefs_summary_open_set_defaults();
1518
1519                 for (i = 0; i < 6 && node == NULL; i++) {
1520                         EntryAction act = prefs_common.summary_select_prio[i];
1521                         
1522                         switch(act) {
1523                         case ACTION_OLDEST_MARKED:
1524                                 if (summaryview->sort_type == SORT_ASCENDING)
1525                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1526                                              MSG_MARKED, FALSE);
1527                                 else
1528                                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1529                                              MSG_MARKED, FALSE);
1530                                 break;
1531                         case ACTION_NEWEST_MARKED:
1532                                 if (summaryview->sort_type == SORT_ASCENDING)
1533                                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1534                                              MSG_MARKED, FALSE);
1535                                 else
1536                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1537                                              MSG_MARKED, FALSE);
1538                                 break;
1539                         case ACTION_OLDEST_NEW:
1540                                 if (summaryview->sort_type == SORT_ASCENDING)
1541                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1542                                              MSG_NEW, FALSE);
1543                                 else
1544                                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1545                                              MSG_NEW, FALSE);
1546                                 break;
1547                         case ACTION_NEWEST_NEW:
1548                                 if (summaryview->sort_type == SORT_ASCENDING)
1549                                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1550                                              MSG_NEW, FALSE);
1551                                 else
1552                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1553                                              MSG_NEW, FALSE);
1554                                 break;
1555                         case ACTION_OLDEST_UNREAD:
1556                                 if (summaryview->sort_type == SORT_ASCENDING)
1557                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1558                                              MSG_UNREAD, FALSE);
1559                                 else
1560                                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1561                                              MSG_UNREAD, FALSE);
1562                                 break;
1563                         case ACTION_NEWEST_UNREAD:
1564                                 if (summaryview->sort_type == SORT_ASCENDING)
1565                                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1566                                              MSG_UNREAD, FALSE);
1567                                 else
1568                                         node = summary_find_next_flagged_msg(summaryview, NULL,
1569                                              MSG_UNREAD, FALSE);
1570                                 break;
1571                         case ACTION_LAST_OPENED:
1572                                 if (summaryview->folder_item) {
1573                                         node = summary_find_msg_by_msgnum(summaryview, 
1574                                                         summaryview->folder_item->last_seen);
1575                                 }
1576                                 break;
1577                         case ACTION_NEWEST_LIST:
1578                                 if (GTK_CMCLIST(ctree)->row_list != NULL) {
1579                                         node = gtk_cmctree_node_nth
1580                                                 (ctree,
1581                                                  item->sort_type == SORT_DESCENDING
1582                                                  ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1583                                 }
1584                                 break;
1585                         case ACTION_OLDEST_LIST:
1586                                 if (GTK_CMCLIST(ctree)->row_list != NULL) {
1587                                         node = gtk_cmctree_node_nth
1588                                                 (ctree,
1589                                                  item->sort_type == SORT_ASCENDING
1590                                                  ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1591                                 }
1592                                 break;
1593                         case ACTION_NOTHING:
1594                         case ACTION_UNSET:
1595                                 node = NULL;
1596                                 stop = TRUE;
1597                                 break;
1598                         }
1599                         
1600                         if (stop || node)
1601                                 break;
1602                 }
1603
1604                 summary_unlock(summaryview);
1605
1606                 if (node) {
1607                         gint open_selected = -1;
1608                         if (!is_refresh) {
1609                                 if (OPEN_SELECTED_ON_FOLDER_OPEN)
1610                                         open_selected = 1;
1611                                 else
1612                                         open_selected = 0;
1613                         }
1614                         summary_select_node(summaryview, node, open_selected);
1615                 }
1616
1617                 summary_lock(summaryview);
1618         }
1619
1620         summary_status_show(summaryview);
1621         summary_set_menu_sensitive(summaryview);
1622         toolbar_main_set_sensitive(summaryview->mainwin);
1623         
1624         summary_thaw(summaryview);
1625         debug_print("\n");
1626         STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
1627         STATUSBAR_POP(summaryview->mainwin);
1628         main_window_cursor_normal(summaryview->mainwin);
1629         summary_unlock(summaryview);
1630         inc_unlock();
1631         END_TIMING();
1632         return TRUE;
1633 }
1634
1635 #undef CURRENTLY_DISPLAYED
1636
1637 static void summary_cancel_mark_read_timeout(SummaryView *summaryview) {
1638         if (summaryview->mark_as_read_timeout_tag != 0) {
1639                 g_source_remove(summaryview->mark_as_read_timeout_tag);
1640                 summaryview->mark_as_read_timeout_tag = 0;
1641         }
1642 }
1643
1644 void summary_clear_list(SummaryView *summaryview)
1645 {
1646         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1647         gint optimal_width;
1648
1649         summary_freeze(summaryview);
1650
1651         gtk_cmctree_pre_recursive(GTK_CMCTREE(summaryview->ctree),
1652                                 NULL, summary_free_msginfo_func, NULL);
1653
1654         if (summaryview->folder_item) {
1655                 summaryview->folder_item->opened = FALSE;
1656                 summaryview->folder_item = NULL;
1657         }
1658
1659         summary_cancel_mark_read_timeout(summaryview);
1660
1661         summaryview->display_msg = FALSE;
1662
1663         summaryview->selected = NULL;
1664         summaryview->displayed = NULL;
1665         summaryview->total_size = 0;
1666         summaryview->deleted = summaryview->moved = 0;
1667         summaryview->copied = 0;
1668         if (summaryview->msgid_table) {
1669                 g_hash_table_destroy(summaryview->msgid_table);
1670                 summaryview->msgid_table = NULL;
1671         }
1672         if (summaryview->subject_table) {
1673                 g_hash_table_destroy(summaryview->subject_table);
1674                 summaryview->subject_table = NULL;
1675         }
1676         summaryview->mlist = NULL;
1677
1678         gtk_cmclist_clear(clist);
1679         if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1680                 optimal_width = gtk_cmclist_optimal_column_width
1681                         (clist, summaryview->col_pos[S_COL_SUBJECT]);
1682                 gtk_cmclist_set_column_width
1683                         (clist, summaryview->col_pos[S_COL_SUBJECT],
1684                          optimal_width);
1685         }
1686
1687         summary_thaw(summaryview);
1688 }
1689
1690 void summary_clear_all(SummaryView *summaryview)
1691 {
1692         messageview_clear(summaryview->messageview);
1693         summary_clear_list(summaryview);
1694         summary_set_menu_sensitive(summaryview);
1695         toolbar_main_set_sensitive(summaryview->mainwin);
1696         summary_status_show(summaryview);
1697 }
1698
1699 void summary_lock(SummaryView *summaryview)
1700 {
1701         summaryview->lock_count++;
1702 }
1703
1704 void summary_unlock(SummaryView *summaryview)
1705 {
1706         if (summaryview->lock_count)
1707                 summaryview->lock_count--;
1708 }
1709
1710 gboolean summary_is_locked(SummaryView *summaryview)
1711 {
1712         return summaryview->lock_count > 0;
1713 }
1714
1715 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1716 {
1717         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1718         SummarySelection selection;
1719
1720         if (!clist->row_list)
1721                 selection = SUMMARY_NONE;
1722         else if (!clist->selection)
1723                 selection = SUMMARY_SELECTED_NONE;
1724         else if (!clist->selection->next)
1725                 selection = SUMMARY_SELECTED_SINGLE;
1726         else
1727                 selection = SUMMARY_SELECTED_MULTIPLE;
1728
1729         return selection;
1730 }
1731
1732 /*!
1733  *\return       MsgInfo * Selected message if there's one selected;
1734  *              if multiple selected, or none, return NULL.
1735  */ 
1736 MsgInfo *summary_get_selected_msg(SummaryView *summaryview)
1737 {
1738         /* summaryview->selected may be valid when multiple 
1739          * messages were selected */
1740         GList *sellist = GTK_CMCLIST(summaryview->ctree)->selection;
1741
1742         if (sellist == NULL || sellist->next) 
1743                 return NULL;
1744         
1745         return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data);
1746 }
1747
1748 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1749 {
1750         GSList *mlist = NULL;
1751         GList *cur;
1752         MsgInfo *msginfo;
1753
1754         for (cur = GTK_CMCLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL;
1755              cur = cur->next) {
1756                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
1757                 mlist = g_slist_prepend(mlist, msginfo);
1758         }
1759
1760         mlist = g_slist_reverse(mlist);
1761
1762         return mlist;
1763 }
1764
1765 void summary_set_menu_sensitive(SummaryView *summaryview)
1766 {
1767         SensitiveCondMask state;
1768         gboolean sensitive;
1769         gint i;
1770
1771 #ifndef GENERIC_UMPC
1772 #define N_ENTRIES 39
1773 #else
1774 #define N_ENTRIES 28
1775 #endif
1776         static struct {
1777                 const gchar *entry;
1778                 SensitiveCondMask cond;
1779         } entry[N_ENTRIES];
1780
1781         i = 0;
1782 #define FILL_TABLE(entry_str, ...) \
1783 do { \
1784         entry[i].entry = (const gchar *) entry_str; entry[i++].cond = main_window_get_mask(__VA_ARGS__, -1); \
1785 } while (0)
1786
1787         FILL_TABLE("Menus/SummaryViewPopup/Reply", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1788 #ifndef GENERIC_UMPC
1789         FILL_TABLE("Menus/SummaryViewPopup/ReplyTo", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1790         FILL_TABLE("Menus/SummaryViewPopup/ReplyTo/All", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1791         FILL_TABLE("Menus/SummaryViewPopup/ReplyTo/Sender", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1792         FILL_TABLE("Menus/SummaryViewPopup/ReplyTo/MailingList", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1793 #endif
1794
1795         FILL_TABLE("Menus/SummaryViewPopup/Forward", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1796 #ifndef GENERIC_UMPC
1797         FILL_TABLE("Menus/SummaryViewPopup/ForwardAtt", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1798         FILL_TABLE("Menus/SummaryViewPopup/Redirect", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1799 #endif
1800
1801         FILL_TABLE("Menus/SummaryViewPopup/Move", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS);
1802         FILL_TABLE("Menus/SummaryViewPopup/Copy", M_TARGET_EXIST, M_EXEC);
1803         FILL_TABLE("Menus/SummaryViewPopup/Trash", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS, M_NOT_TRASH);
1804 #ifndef GENERIC_UMPC
1805         FILL_TABLE("Menus/SummaryViewPopup/Delete", M_TARGET_EXIST, M_ALLOW_DELETE);
1806 #endif
1807
1808         FILL_TABLE("Menus/SummaryViewPopup/Mark", M_TARGET_EXIST);
1809         FILL_TABLE("Menus/SummaryViewPopup/Mark/Mark", M_TARGET_EXIST);
1810         FILL_TABLE("Menus/SummaryViewPopup/Mark/Unmark", M_TARGET_EXIST);
1811         FILL_TABLE("Menus/SummaryViewPopup/Mark/MarkRead", M_TARGET_EXIST);
1812         FILL_TABLE("Menus/SummaryViewPopup/Mark/MarkUnread", M_TARGET_EXIST);
1813         FILL_TABLE("Menus/SummaryViewPopup/Mark/MarkAllRead", M_TARGET_EXIST);
1814         FILL_TABLE("Menus/SummaryViewPopup/Mark/MarkAllUnread", M_TARGET_EXIST);
1815         FILL_TABLE("Menus/SummaryViewPopup/Mark/IgnoreThread", M_TARGET_EXIST);
1816         FILL_TABLE("Menus/SummaryViewPopup/Mark/UnignoreThread", M_TARGET_EXIST);
1817         FILL_TABLE("Menus/SummaryViewPopup/Mark/WatchThread", M_TARGET_EXIST);
1818         FILL_TABLE("Menus/SummaryViewPopup/Mark/UnwatchThread", M_TARGET_EXIST);
1819         FILL_TABLE("Menus/SummaryViewPopup/Mark/Lock", M_TARGET_EXIST);
1820         FILL_TABLE("Menus/SummaryViewPopup/Mark/Unlock", M_TARGET_EXIST);
1821         FILL_TABLE("Menus/SummaryViewPopup/Mark/MarkSpam", M_TARGET_EXIST, M_CAN_LEARN_SPAM);
1822         FILL_TABLE("Menus/SummaryViewPopup/Mark/MarkHam", M_TARGET_EXIST, M_CAN_LEARN_SPAM);
1823         FILL_TABLE("Menus/SummaryViewPopup/ColorLabel", M_TARGET_EXIST);
1824         FILL_TABLE("Menus/SummaryViewPopup/Tags", M_TARGET_EXIST);
1825
1826 #ifndef GENERIC_UMPC
1827         FILL_TABLE("Menus/SummaryViewPopup/AddSenderToAB", M_SINGLE_TARGET_EXIST);
1828 #endif
1829         FILL_TABLE("Menus/SummaryViewPopup/CreateFilterRule", M_SINGLE_TARGET_EXIST, M_UNLOCKED);
1830 #ifndef GENERIC_UMPC
1831         FILL_TABLE("Menus/SummaryViewPopup/CreateProcessingRule", M_SINGLE_TARGET_EXIST, M_UNLOCKED);
1832 #endif
1833
1834         FILL_TABLE("Menus/SummaryViewPopup/View", M_SINGLE_TARGET_EXIST);
1835         FILL_TABLE("Menus/SummaryViewPopup/View/OpenNewWindow", M_SINGLE_TARGET_EXIST);
1836         FILL_TABLE("Menus/SummaryViewPopup/View/MessageSource", M_SINGLE_TARGET_EXIST);
1837 #ifndef GENERIC_UMPC
1838         FILL_TABLE("Menus/SummaryViewPopup/View/AllHeaders", M_SINGLE_TARGET_EXIST);
1839 #endif
1840         FILL_TABLE("Menus/SummaryViewPopup/SaveAs", M_TARGET_EXIST);
1841 #ifndef GENERIC_UMPC
1842         FILL_TABLE("Menus/SummaryViewPopup/Print", M_TARGET_EXIST);
1843 #endif
1844         FILL_TABLE(NULL, -1);
1845 #undef FILL_TABLE
1846         if (i != N_ENTRIES)
1847                 g_error("summaryview menu entry table size mismatch (%d/%d)", i, N_ENTRIES);
1848 #undef ENTRIES
1849
1850         main_window_set_menu_sensitive(summaryview->mainwin);
1851
1852         state = main_window_get_current_state(summaryview->mainwin);
1853
1854         for (i = 0; entry[i].entry != NULL; i++) {
1855                 sensitive = ((entry[i].cond & state) == entry[i].cond);
1856                 cm_menu_set_sensitive_full(summaryview->mainwin->ui_manager, entry[i].entry, sensitive);
1857         }
1858
1859         summary_lock(summaryview);
1860 #ifndef GENERIC_UMPC
1861         if (summaryview->messageview 
1862         &&  summaryview->messageview->mimeview
1863         &&  summaryview->messageview->mimeview->textview)
1864                 cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager, "Menus/SummaryViewPopup/View/AllHeaders",
1865                         prefs_common.show_all_headers);
1866 #endif
1867         summary_unlock(summaryview);
1868 }
1869 void summary_select_prev(SummaryView *summaryview)
1870 {
1871         GtkCMCTreeNode *node = summaryview->selected;
1872         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1873
1874         if (summaryview->sort_type == SORT_ASCENDING)
1875                 node = gtkut_ctree_node_prev(ctree, node);
1876         else
1877                 node = gtkut_ctree_node_next(ctree, node);
1878
1879         if (node && node != summaryview->selected)
1880                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1881 }
1882
1883 void summary_select_next(SummaryView *summaryview)
1884 {
1885         GtkCMCTreeNode *node = summaryview->selected;
1886         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1887
1888         if (summaryview->sort_type == SORT_ASCENDING)
1889                 node = gtkut_ctree_node_next(ctree, node);
1890         else
1891                 node = gtkut_ctree_node_prev(ctree, node);
1892
1893         if (node && node != summaryview->selected)
1894                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1895 }
1896
1897 void summary_select_prev_unread(SummaryView *summaryview)
1898 {
1899         GtkCMCTreeNode *node;
1900
1901         if (summaryview->sort_type == SORT_ASCENDING)
1902                 node = summary_find_prev_flagged_msg
1903                         (summaryview, summaryview->selected, MSG_UNREAD, TRUE);
1904         else
1905                 node = summary_find_next_flagged_msg
1906                         (summaryview, summaryview->selected, MSG_UNREAD, TRUE);
1907
1908         if (!node || node == summaryview->selected) {
1909                 AlertValue val = 0;
1910
1911                 switch (prefs_common.next_unread_msg_dialog) {
1912                         case NEXTUNREADMSGDIALOG_ALWAYS:
1913                                 val = alertpanel(_("No more unread messages"),
1914                                                  _("No unread message found. "
1915                                                    "Search from the end?"),
1916                                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
1917                                 break;
1918                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1919                                 val = G_ALERTALTERNATE;
1920                                 break;
1921                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1922                                 val = !G_ALERTALTERNATE;
1923                                 break;
1924                         default:
1925                                 debug_print(
1926                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1927                 }
1928                 if (val != G_ALERTALTERNATE) return;
1929                 if (summaryview->sort_type == SORT_ASCENDING)
1930                         node = summary_find_prev_flagged_msg(summaryview, NULL,
1931                                                              MSG_UNREAD, FALSE);
1932                 else
1933                         node = summary_find_next_flagged_msg(summaryview, NULL,
1934                                                              MSG_UNREAD, FALSE);
1935         }
1936
1937         if (!node)
1938                 alertpanel_notice(_("No unread messages."));
1939         else
1940                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1941 }
1942
1943 void summary_select_next_unread(SummaryView *summaryview)
1944 {
1945         GtkCMCTreeNode *node = summaryview->selected;
1946
1947         if (summaryview->sort_type == SORT_ASCENDING)
1948                 node = summary_find_next_flagged_msg
1949                         (summaryview, node, MSG_UNREAD, TRUE);
1950         else
1951                 node = summary_find_prev_flagged_msg
1952                         (summaryview, node, MSG_UNREAD, TRUE);
1953         
1954         if (node)
1955                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1956         else {
1957                 AlertValue val = 0;
1958
1959                 switch (prefs_common.next_unread_msg_dialog) {
1960                         case NEXTUNREADMSGDIALOG_ALWAYS:
1961                                 val = alertpanel(_("No more unread messages"),
1962                                                  _("No unread message found. "
1963                                                    "Go to next folder?"),
1964                                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
1965                                 break;
1966                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
1967                                 val = G_ALERTALTERNATE;
1968                                 break;
1969                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
1970                                 val = G_ALERTOTHER;
1971                                 break;
1972                         default:
1973                                 debug_print(
1974                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1975                 }
1976
1977                 if (val == G_ALERTALTERNATE)
1978                         folderview_select_next_with_flag(summaryview->folderview, MSG_UNREAD);
1979         }
1980 }
1981
1982 void summary_select_prev_new(SummaryView *summaryview)
1983 {
1984         GtkCMCTreeNode *node;
1985
1986         if (summaryview->sort_type == SORT_ASCENDING)
1987                 node = summary_find_prev_flagged_msg
1988                         (summaryview, summaryview->selected, MSG_NEW, TRUE);
1989         else
1990                 node = summary_find_next_flagged_msg
1991                         (summaryview, summaryview->selected, MSG_NEW, TRUE);
1992
1993         if (!node || node == summaryview->selected) {
1994                 AlertValue val = 0;
1995
1996                 switch (prefs_common.next_unread_msg_dialog) {
1997                         case NEXTUNREADMSGDIALOG_ALWAYS:
1998                                 val = alertpanel(_("No more new messages"),
1999                                                  _("No new message found. "
2000                                                    "Search from the end?"),
2001                                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2002                                 break;
2003                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
2004                                 val = G_ALERTALTERNATE;
2005                                 break;
2006                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
2007                                 val = !G_ALERTALTERNATE;
2008                                 break;
2009                         default:
2010                                 debug_print(
2011                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2012                 }
2013                 if (val != G_ALERTALTERNATE) return;
2014                 if (summaryview->sort_type == SORT_ASCENDING)
2015                         node = summary_find_prev_flagged_msg(summaryview, NULL,
2016                                                              MSG_NEW, FALSE);
2017                 else
2018                         node = summary_find_next_flagged_msg(summaryview, NULL,
2019                                                              MSG_NEW, FALSE);
2020         }
2021
2022         if (!node)
2023                 alertpanel_notice(_("No new messages."));
2024         else
2025                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2026 }
2027
2028 void summary_select_next_new(SummaryView *summaryview)
2029 {
2030         GtkCMCTreeNode *node = summaryview->selected;
2031
2032         if (summaryview->sort_type == SORT_ASCENDING)
2033                 node = summary_find_next_flagged_msg
2034                         (summaryview, node, MSG_NEW, TRUE);
2035         else
2036                 node = summary_find_prev_flagged_msg
2037                         (summaryview, node, MSG_NEW, TRUE);
2038         
2039         if (node)
2040                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2041         else {
2042                 AlertValue val = 0;
2043
2044                 switch (prefs_common.next_unread_msg_dialog) {
2045                         case NEXTUNREADMSGDIALOG_ALWAYS:
2046                                 val = alertpanel(_("No more new messages"),
2047                                                  _("No new message found. "
2048                                                    "Go to next folder?"),
2049                                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2050                                 break;
2051                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
2052                                 val = G_ALERTALTERNATE;
2053                                 break;
2054                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
2055                                 val = G_ALERTOTHER;
2056                                 break;
2057                         default:
2058                                 debug_print(
2059                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2060                 }
2061                 if (val == G_ALERTALTERNATE)
2062                         folderview_select_next_with_flag(summaryview->folderview, MSG_NEW);
2063         }
2064 }
2065
2066 void summary_select_prev_marked(SummaryView *summaryview)
2067 {
2068         GtkCMCTreeNode *node;
2069
2070         if (summaryview->sort_type == SORT_ASCENDING)
2071                 node = summary_find_prev_flagged_msg
2072                         (summaryview, summaryview->selected, MSG_MARKED, TRUE);
2073         else
2074                 node = summary_find_next_flagged_msg
2075                         (summaryview, summaryview->selected, MSG_MARKED, TRUE);
2076
2077         if (!node) {
2078                 AlertValue val;
2079
2080                 val = alertpanel(_("No more marked messages"),
2081                                  _("No marked message found. "
2082                                    "Search from the end?"),
2083                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2084                 if (val != G_ALERTALTERNATE) return;
2085                 node = summary_find_prev_flagged_msg(summaryview, NULL,
2086                                                      MSG_MARKED, TRUE);
2087         }
2088
2089         if (!node)
2090                 alertpanel_notice(_("No marked messages."));
2091         else
2092                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2093 }
2094
2095 void summary_select_next_marked(SummaryView *summaryview)
2096 {
2097         GtkCMCTreeNode *node = summaryview->selected;
2098
2099         if (summaryview->sort_type == SORT_ASCENDING)
2100                 node = summary_find_next_flagged_msg
2101                         (summaryview, node, MSG_MARKED, TRUE);
2102         else
2103                 node = summary_find_prev_flagged_msg
2104                         (summaryview, node, MSG_MARKED, TRUE);
2105         
2106         if (node)
2107                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2108         else {
2109                 AlertValue val = 0;
2110
2111                 switch (prefs_common.next_unread_msg_dialog) {
2112                         case NEXTUNREADMSGDIALOG_ALWAYS:
2113                                 val = alertpanel(_("No more marked messages"),
2114                                                  _("No marked message found. "
2115                                                    "Go to next folder?"),
2116                                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2117                                 break;
2118                         case NEXTUNREADMSGDIALOG_ASSUME_YES:
2119                                 val = G_ALERTALTERNATE;
2120                                 break;
2121                         case NEXTUNREADMSGDIALOG_ASSUME_NO:
2122                                 val = G_ALERTOTHER;
2123                                 break;
2124                         default:
2125                                 debug_print(
2126                                         _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2127                 }
2128                 if (val == G_ALERTALTERNATE)
2129                         folderview_select_next_with_flag(summaryview->folderview, MSG_MARKED);
2130         }
2131 }
2132
2133 void summary_select_prev_labeled(SummaryView *summaryview)
2134 {
2135         GtkCMCTreeNode *node;
2136
2137         if (summaryview->sort_type == SORT_ASCENDING)
2138                 node = summary_find_prev_flagged_msg
2139                         (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2140         else
2141                 node = summary_find_next_flagged_msg
2142                         (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2143
2144         if (!node) {
2145                 AlertValue val;
2146
2147                 val = alertpanel(_("No more labeled messages"),
2148                                  _("No labeled message found. "
2149                                    "Search from the end?"),
2150                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2151                 if (val != G_ALERTALTERNATE) return;
2152                 node = summary_find_prev_flagged_msg(summaryview, NULL,
2153                                                      MSG_CLABEL_FLAG_MASK, TRUE);
2154         }
2155
2156         if (!node)
2157                 alertpanel_notice(_("No labeled messages."));
2158         else
2159                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2160 }
2161
2162 void summary_select_next_labeled(SummaryView *summaryview)
2163 {
2164         GtkCMCTreeNode *node;
2165
2166         if (summaryview->sort_type == SORT_ASCENDING)
2167                 node = summary_find_next_flagged_msg
2168                         (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2169         else
2170                 node = summary_find_prev_flagged_msg
2171                         (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2172
2173         if (!node) {
2174                 AlertValue val;
2175
2176                 val = alertpanel(_("No more labeled messages"),
2177                                  _("No labeled message found. "
2178                                    "Search from the beginning?"),
2179                                  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2180                 if (val != G_ALERTALTERNATE) return;
2181                 if (summaryview->sort_type == SORT_ASCENDING)
2182                         node = summary_find_next_flagged_msg(summaryview, NULL,
2183                                                              MSG_CLABEL_FLAG_MASK, TRUE);
2184                 else
2185                         node = summary_find_prev_flagged_msg(summaryview, NULL,
2186                                                              MSG_CLABEL_FLAG_MASK, TRUE);
2187         }
2188
2189         if (!node)
2190                 alertpanel_notice(_("No labeled messages."));
2191         else
2192                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2193 }
2194
2195 void summary_select_parent(SummaryView *summaryview)
2196 {
2197         GtkCMCTreeNode *node = NULL;
2198
2199         if (summaryview->selected)
2200                 node = GTK_CMCTREE_ROW(summaryview->selected)->parent;
2201         if (node)
2202                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2203 }
2204
2205 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum,
2206                 gboolean show)
2207 {
2208         GtkCMCTreeNode *node;
2209
2210         node = summary_find_msg_by_msgnum(summaryview, msgnum);
2211         summary_select_node(summaryview, node, show);
2212 }
2213
2214 void summary_select_by_msg_list(SummaryView     *summaryview, GSList *msginfos)
2215 {
2216         GtkCMCTree *ctree;
2217         GSList *msgnum_list, *walk;
2218         gboolean froze = FALSE;
2219
2220         ctree = GTK_CMCTREE(summaryview->ctree);
2221
2222         msgnum_list = procmsg_get_number_list_for_msgs(msginfos);
2223
2224         START_LONG_OPERATION(summaryview, FALSE);
2225         for(walk = msgnum_list; walk; walk = walk->next) {
2226                 GtkCMCTreeNode *node;
2227                 node = summary_find_msg_by_msgnum(summaryview, GPOINTER_TO_UINT(walk->data));
2228                 if (node != NULL)
2229                         gtk_cmctree_select(ctree, node);
2230         }
2231         END_LONG_OPERATION(summaryview);
2232         g_slist_free(msgnum_list);
2233 }
2234
2235 typedef struct _PostponedSelectData
2236 {
2237         GtkCMCTree *ctree;
2238         GtkCMCTreeNode *row;
2239         GtkCMCTreeNode *node;
2240         GtkScrollType type;
2241         gint column;
2242         SummaryView *summaryview;
2243         gboolean display_msg;
2244 } PostponedSelectData;
2245
2246 static gboolean summary_select_retry(void *data)
2247 {
2248         PostponedSelectData *psdata = (PostponedSelectData *)data;
2249         debug_print("trying again\n");
2250         if (psdata->row)
2251                 summary_selected(psdata->ctree, psdata->row,
2252                             psdata->column, psdata->summaryview);
2253         else if (psdata->node)
2254                 summary_select_node(psdata->summaryview, psdata->node,
2255                             psdata->display_msg);
2256         g_free(psdata);
2257         return FALSE;
2258 }
2259
2260 /**
2261  * summary_select_node:
2262  * @summaryview: Summary view.
2263  * @node: Summary tree node.
2264  * @display_msg: whether to also display the message
2265  *
2266  * Select @node (bringing it into view by scrolling and expanding its
2267  * thread, if necessary) and unselect all others.
2268  **/
2269
2270 void summary_select_node(SummaryView *summaryview, GtkCMCTreeNode *node,
2271                          gboolean display_msg)
2272 {
2273         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2274
2275         /* If msgview is hidden, we never want to automatically display
2276          * a selected message, since that would unhide the msgview. */
2277         if (!messageview_is_visible(summaryview->messageview))
2278                 display_msg = FALSE;
2279
2280         if (summary_is_locked(summaryview)
2281         && !GTK_SCTREE(ctree)->selecting_range
2282         && summaryview->messageview->mimeview
2283         && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
2284         && summaryview->messageview->mimeview->textview->loading) {
2285                 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2286                 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
2287                 
2288                 data->ctree = ctree;
2289                 data->row = NULL;
2290                 data->node = node;
2291                 data->summaryview = summaryview;
2292                 data->display_msg = display_msg;
2293                 debug_print("postponing open of message till end of load\n");
2294                 g_timeout_add(100, summary_select_retry, data);
2295                 return;
2296         }
2297         if (summary_is_locked(summaryview)) {
2298                 return;
2299         }
2300         if (!summaryview->folder_item)
2301                 return;
2302         if (node) {
2303                 gtkut_ctree_expand_parent_all(ctree, node);
2304
2305                 summary_lock(summaryview);
2306                 GTK_EVENTS_FLUSH();
2307                 summary_unlock(summaryview);
2308                 gtk_widget_grab_focus(GTK_WIDGET(ctree));
2309                 gtkut_ctree_node_move_if_on_the_edge(ctree, node, -1);
2310
2311                 if (display_msg && summaryview->displayed == node)
2312                         summaryview->displayed = NULL;
2313                 summaryview->display_msg = display_msg;
2314                 gtk_sctree_select(GTK_SCTREE(ctree), node);
2315                 if (summaryview->selected == NULL)
2316                         summaryview->selected = node;
2317         }
2318 }
2319
2320 guint summary_get_msgnum(SummaryView *summaryview, GtkCMCTreeNode *node)
2321 {
2322         GtkCMCTree *ctree =NULL;
2323         MsgInfo *msginfo;
2324
2325         if (!summaryview)
2326                 return 0;
2327         ctree = GTK_CMCTREE(summaryview->ctree);
2328         if (!node)
2329                 return 0;
2330         msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2331         if (msginfo)
2332                 return msginfo->msgnum;
2333         else 
2334                 return -1;
2335 }
2336
2337 static GtkCMCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
2338                                              GtkCMCTreeNode *current_node,
2339                                              gboolean start_from_prev)
2340 {
2341         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2342         GtkCMCTreeNode *node;
2343         MsgInfo *msginfo;
2344
2345         if (current_node) {
2346                 if (start_from_prev)
2347                         node = gtkut_ctree_node_prev(ctree, current_node);
2348                 else
2349                         node = current_node;
2350         } else
2351                 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2352
2353         for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
2354                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2355                 if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
2356         }
2357
2358         return node;
2359 }
2360
2361 static GtkCMCTreeNode *summary_find_next_msg(SummaryView *summaryview,
2362                                             GtkCMCTreeNode *current_node,
2363                                             gboolean start_from_next)
2364 {
2365         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2366         GtkCMCTreeNode *node;
2367         MsgInfo *msginfo;
2368
2369         if (current_node) {
2370                 if (start_from_next)
2371                         node = gtkut_ctree_node_next(ctree, current_node);
2372                 else
2373                         node = current_node;
2374         } else
2375                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2376
2377         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2378                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2379                 if (msginfo && !MSG_IS_DELETED(msginfo->flags) 
2380                 && !MSG_IS_MOVE(msginfo->flags)) break;
2381         }
2382
2383         return node;
2384 }
2385
2386 static GtkCMCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
2387                                                    GtkCMCTreeNode *current_node,
2388                                                    MsgPermFlags flags,
2389                                                    gboolean start_from_prev)
2390 {
2391         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2392         GtkCMCTreeNode *node;
2393         MsgInfo *msginfo;
2394
2395         if (current_node) {
2396                 if (start_from_prev)
2397                         node = gtkut_ctree_node_prev(ctree, current_node);
2398                 else
2399                         node = current_node;
2400         } else
2401                 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2402
2403         for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
2404                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2405                 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
2406         }
2407
2408         return node;
2409 }
2410
2411 static GtkCMCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
2412                                                    GtkCMCTreeNode *current_node,
2413                                                    MsgPermFlags flags,
2414                                                    gboolean start_from_next)
2415 {
2416         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2417         GtkCMCTreeNode *node;
2418         MsgInfo *msginfo;
2419
2420         if (current_node) {
2421                 if (start_from_next)
2422                         node = gtkut_ctree_node_next(ctree, current_node);
2423                 else
2424                         node = current_node;
2425         } else
2426                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2427
2428         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2429                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2430                 /* Find msg with matching flags but ignore messages with
2431                    ignore flags, if searching for new or unread messages */
2432                 if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) &&
2433                     !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags)) 
2434                         )
2435                         break;
2436         }
2437
2438         return node;
2439 }
2440
2441 static GtkCMCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
2442                                                 guint msgnum)
2443 {
2444         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2445         GtkCMCTreeNode *node;
2446         MsgInfo *msginfo;
2447
2448         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2449
2450         for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2451                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2452                 if (msginfo && msginfo->msgnum == msgnum) break;
2453         }
2454
2455         return node;
2456 }
2457
2458 static guint attract_hash_func(gconstpointer key)
2459 {
2460         gchar *str;
2461         gchar *p;
2462         guint h;
2463
2464         Xstrdup_a(str, (const gchar *)key, return 0);
2465         trim_subject(str);
2466
2467         p = str;
2468         h = *p;
2469
2470         if (h) {
2471                 for (p += 1; *p != '\0'; p++)
2472                         h = (h << 5) - h + *p;
2473         }
2474
2475         return h;
2476 }
2477
2478 static gint attract_compare_func(gconstpointer a, gconstpointer b)
2479 {
2480         return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2481 }
2482
2483 void summary_attract_by_subject(SummaryView *summaryview)
2484 {
2485         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2486         GtkCMCList *clist = GTK_CMCLIST(ctree);
2487         GtkCMCTreeNode *src_node;
2488         GtkCMCTreeNode *dst_node, *sibling;
2489         GtkCMCTreeNode *tmp;
2490         MsgInfo *src_msginfo, *dst_msginfo;
2491         GHashTable *subject_table;
2492
2493         debug_print("Attracting messages by subject...\n");
2494         STATUSBAR_PUSH(summaryview->mainwin,
2495                        _("Attracting messages by subject..."));
2496
2497         main_window_cursor_wait(summaryview->mainwin);
2498         summary_freeze(summaryview);
2499
2500         subject_table = g_hash_table_new(attract_hash_func,
2501                                          attract_compare_func);
2502
2503         for (src_node = GTK_CMCTREE_NODE(clist->row_list);
2504              src_node != NULL;
2505              src_node = tmp) {
2506                 tmp = GTK_CMCTREE_ROW(src_node)->sibling;
2507                 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
2508                 if (!src_msginfo) continue;
2509                 if (!src_msginfo->subject) continue;
2510
2511                 /* find attracting node */
2512                 dst_node = g_hash_table_lookup(subject_table,
2513                                                src_msginfo->subject);
2514
2515                 if (dst_node) {
2516                         dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
2517
2518                         /* if the time difference is more than 20 days,
2519                            don't attract */
2520                         if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
2521                             > 60 * 60 * 24 * 20)
2522                                 continue;
2523
2524                         sibling = GTK_CMCTREE_ROW(dst_node)->sibling;
2525                         if (src_node != sibling)
2526                                 gtk_cmctree_move(ctree, src_node, NULL, sibling);
2527                 }
2528
2529                 g_hash_table_insert(subject_table,
2530                                     src_msginfo->subject, src_node);
2531         }
2532
2533         g_hash_table_destroy(subject_table);
2534
2535         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
2536
2537         summary_thaw(summaryview);
2538
2539         debug_print("Attracting messages by subject done.\n");
2540         STATUSBAR_POP(summaryview->mainwin);
2541
2542         main_window_cursor_normal(summaryview->mainwin);
2543 }
2544
2545 static void summary_free_msginfo_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2546                                       gpointer data)
2547 {
2548         MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2549
2550         if (msginfo)
2551                 procmsg_msginfo_free(&msginfo);
2552 }
2553
2554 static void summary_set_marks_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2555                                    gpointer data)
2556 {
2557         SummaryView *summaryview = data;
2558         MsgInfo *msginfo;
2559
2560         msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2561
2562         cm_return_if_fail(msginfo != NULL);
2563
2564         if (MSG_IS_DELETED(msginfo->flags))
2565                 summaryview->deleted++;
2566
2567         summaryview->total_size += msginfo->size;
2568
2569         summary_set_row_marks(summaryview, node);
2570 }
2571
2572 static void summary_update_status(SummaryView *summaryview)
2573 {
2574         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2575         GtkCMCTreeNode *node;
2576         MsgInfo *msginfo;
2577
2578         summaryview->total_size =
2579         summaryview->deleted = summaryview->moved = summaryview->copied = 0;
2580
2581         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2582              node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2583                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2584                 
2585                 if (!msginfo)
2586                         continue;
2587
2588                 if (MSG_IS_DELETED(msginfo->flags))
2589                         summaryview->deleted++;
2590                 if (MSG_IS_MOVE(msginfo->flags))
2591                         summaryview->moved++;
2592                 if (MSG_IS_COPY(msginfo->flags))
2593                         summaryview->copied++;
2594                 summaryview->total_size += msginfo->size;
2595         }
2596 }
2597
2598 static void summary_status_show(SummaryView *summaryview)
2599 {
2600         gchar *str;
2601         gchar *del, *mv, *cp;
2602         gchar *sel;
2603         gchar *spc;
2604         gchar *itstr;
2605         GList *rowlist, *cur;
2606         guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0;
2607         guint n_marked = 0, n_replied = 0, n_forwarded = 0, n_locked = 0, n_ignored = 0, n_watched = 0;
2608         goffset sel_size = 0, n_size = 0;
2609         MsgInfo *msginfo;
2610         gchar *name;
2611         gchar *tooltip;
2612         
2613         if (!summaryview->folder_item) {
2614                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), "");
2615                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), "");
2616                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs),   "");
2617                 toolbar_main_set_sensitive(summaryview->mainwin);
2618                 return;
2619         }
2620
2621         rowlist = GTK_CMCLIST(summaryview->ctree)->selection;
2622         for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2623                 msginfo = gtk_cmctree_node_get_row_data
2624                         (GTK_CMCTREE(summaryview->ctree),
2625                          GTK_CMCTREE_NODE(cur->data));
2626                 if (msginfo) {
2627                         sel_size += msginfo->size;
2628                         n_selected++;
2629                 }
2630         }
2631         
2632         if (summaryview->folder_item->hide_read_msgs 
2633         || summaryview->folder_item->hide_del_msgs
2634         || summaryview->folder_item->hide_read_threads
2635         || quicksearch_has_sat_predicate(summaryview->quicksearch)) {
2636                 rowlist = GTK_CMCLIST(summaryview->ctree)->row_list;
2637                 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2638                         msginfo = gtk_cmctree_node_get_row_data
2639                                 (GTK_CMCTREE(summaryview->ctree),
2640                                  GTK_CMCTREE_NODE(cur));
2641                         if (msginfo) {
2642                                 n_size += msginfo->size;
2643                                 n_total++;
2644                                 if (MSG_IS_NEW(msginfo->flags))
2645                                         n_new++;
2646                                 if (MSG_IS_UNREAD(msginfo->flags))
2647                                         n_unread++;
2648                                 if (MSG_IS_MARKED(msginfo->flags))
2649                                         n_marked++;
2650                                 if (MSG_IS_REPLIED(msginfo->flags))
2651                                         n_replied++;
2652                                 if (MSG_IS_FORWARDED(msginfo->flags))
2653                                         n_forwarded++;
2654                                 if (MSG_IS_LOCKED(msginfo->flags))
2655                                         n_locked++;
2656                                 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2657                                         n_ignored++;
2658                                 if (MSG_IS_WATCH_THREAD(msginfo->flags))
2659                                         n_watched++;
2660                         }
2661                 }
2662         } else {
2663                 n_new = summaryview->folder_item->new_msgs;
2664                 n_unread = summaryview->folder_item->unread_msgs;
2665                 n_marked = summaryview->folder_item->marked_msgs;
2666                 n_replied = summaryview->folder_item->replied_msgs;
2667                 n_forwarded = summaryview->folder_item->forwarded_msgs;
2668                 n_locked = summaryview->folder_item->locked_msgs;
2669                 n_ignored = summaryview->folder_item->ignored_msgs;
2670                 n_watched = summaryview->folder_item->watched_msgs;
2671                 n_total = summaryview->folder_item->total_msgs;
2672                 n_size = summaryview->total_size;
2673         }
2674
2675         name = folder_item_get_name(summaryview->folder_item);
2676         gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
2677         g_free(name);
2678
2679         if (summaryview->deleted)
2680                 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
2681         else
2682                 del = g_strdup("");
2683         if (summaryview->moved)
2684                 mv = g_strdup_printf(_("%s%d moved"),
2685                                      summaryview->deleted ? _(", ") : "",
2686                                      summaryview->moved);
2687         else
2688                 mv = g_strdup("");
2689         if (summaryview->copied)
2690                 cp = g_strdup_printf(_("%s%d copied"),
2691                                      summaryview->deleted ||
2692                                      summaryview->moved ? _(", ") : "",
2693                                      summaryview->copied);
2694         else
2695                 cp = g_strdup("");
2696
2697         if (summaryview->deleted || summaryview->moved || summaryview->copied)
2698                 spc = "    ";
2699         else
2700                 spc = "";
2701
2702         if (n_selected) {
2703                 sel = g_strdup_printf(" (%s)", to_human_readable((goffset)sel_size));
2704                 itstr = g_strdup_printf(ngettext(" item selected"," items selected", n_selected));
2705         } else {
2706                 sel = g_strdup("");
2707                 itstr = g_strdup("");
2708         }
2709                 
2710         if (prefs_common.layout_mode != SMALL_LAYOUT) {
2711                 str = g_strconcat(n_selected ? itos(n_selected) : "",
2712                                                 itstr, sel, spc, del, mv, cp, NULL);
2713                 g_free(sel);
2714                 g_free(del);
2715                 g_free(mv);
2716                 g_free(cp);
2717                 g_free(itstr);
2718                 
2719                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2720                 g_free(str);
2721
2722                 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2723                                               n_new, n_unread, n_total,
2724                                               to_human_readable((goffset)n_size));
2725
2726
2727                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str);
2728                 g_free(str);
2729                 tooltip = g_strdup_printf("<b>%s</b>\n"
2730                                         "<b>%s</b> %d\n"
2731                                         "<b>%s</b> %d\n"
2732                                         "<b>%s</b> %d\n"
2733                                         "<b>%s</b> %s\n\n"
2734                                         "<b>%s</b> %d\n"
2735                                         "<b>%s</b> %d\n"
2736                                         "<b>%s</b> %d\n"
2737                                         "<b>%s</b> %d\n"
2738                                         "<b>%s</b> %d\n"
2739                                         "<b>%s</b> %d",
2740                                         _("Message summary"),
2741                                         _("New:"), n_new,
2742                                         _("Unread:"), n_unread,
2743                                         _("Total:"), n_total,
2744                                         _("Size:"), to_human_readable((goffset)n_size),
2745                                         _("Marked:"), n_marked,
2746                                         _("Replied:"), n_replied,
2747                                         _("Forwarded:"), n_forwarded,
2748                                         _("Locked:"), n_locked,
2749                                         _("Ignored:"), n_ignored,
2750                                         _("Watched:"), n_watched);
2751
2752                 gtk_widget_set_tooltip_markup(GTK_WIDGET(summaryview->statlabel_msgs),
2753                                             tooltip); 
2754                 g_free(tooltip);
2755         } else {
2756                 gchar *ssize, *tsize;
2757                 if (n_selected) {
2758                         ssize = g_strdup(to_human_readable((goffset)sel_size));
2759                         tsize = g_strdup(to_human_readable((goffset)n_size));
2760                         str = g_strdup_printf(_("%d/%d selected (%s/%s), %d unread"),
2761                                 n_selected, n_total, ssize, tsize, n_unread);
2762                         g_free(ssize);
2763                         g_free(tsize);
2764                 } else
2765                         str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2766                                 n_new, n_unread, n_total, to_human_readable((goffset)n_size));
2767                 g_free(sel);
2768                 g_free(del);
2769                 g_free(mv);
2770                 g_free(cp);
2771                 g_free(itstr);
2772                 
2773                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2774                 g_free(str);
2775         }
2776
2777         summary_set_menu_sensitive(summaryview);
2778         toolbar_main_set_sensitive(summaryview->mainwin);
2779 }
2780
2781 static void summary_set_column_titles(SummaryView *summaryview)
2782 {
2783         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
2784         FolderItem *item = summaryview->folder_item;
2785         GtkWidget *hbox;
2786         GtkWidget *label;
2787         GtkWidget *arrow;
2788         gint pos;
2789         const gchar *title;
2790         SummaryColumnType type;
2791         GtkJustification justify;
2792
2793         static FolderSortKey sort_by[N_SUMMARY_COLS] = {
2794                 SORT_BY_MARK,
2795                 SORT_BY_STATUS,
2796                 SORT_BY_MIME,
2797                 SORT_BY_SUBJECT,
2798                 SORT_BY_FROM,
2799                 SORT_BY_TO,
2800                 SORT_BY_DATE,
2801                 SORT_BY_SIZE,
2802                 SORT_BY_NUMBER,
2803                 SORT_BY_SCORE,
2804                 SORT_BY_LOCKED,
2805                 SORT_BY_TAGS
2806         };
2807
2808         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
2809                 type = summaryview->col_state[pos].type;
2810
2811                 /* CLAWS: mime and unread are single char headers */
2812                 justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
2813                         ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
2814
2815                 switch (type) {
2816                 case S_COL_SUBJECT:
2817                 case S_COL_FROM:
2818                 case S_COL_TO:
2819                 case S_COL_DATE:
2820                 case S_COL_NUMBER:
2821                         if(type == S_COL_FROM && item != NULL &&
2822                                         FOLDER_SHOWS_TO_HDR(item) &&
2823                                         !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
2824                                 type = S_COL_TO;
2825                         if(type == S_COL_NUMBER)
2826                                 title = gettext(col_label[type]);
2827                         else
2828                                 title = prefs_common_translated_header_name(col_label[type]);
2829                         break;
2830                 default:
2831                         title = gettext(col_label[type]);
2832                 }
2833
2834                 if (type == S_COL_MIME) {
2835                         label = gtk_image_new_from_pixbuf(clipxpm);
2836                         gtk_widget_show(label);
2837                         gtk_cmclist_set_column_widget(clist, pos, label);
2838                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Attachment"));
2839                         continue;
2840                 } else if (type == S_COL_MARK) {
2841                         label = gtk_image_new_from_pixbuf(markxpm);
2842                         gtk_widget_show(label);
2843                         gtk_cmclist_set_column_widget(clist, pos, label);
2844                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Mark"));
2845                         continue;
2846                 } else if (type == S_COL_LOCKED) {
2847                         label = gtk_image_new_from_pixbuf(lockedxpm);
2848                         gtk_widget_show(label);
2849                         gtk_cmclist_set_column_widget(clist, pos, label);
2850                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Locked"));
2851                         continue;
2852                 } else if (type == S_COL_STATUS) {
2853                         gtk_cmclist_set_column_title(clist, pos, title);
2854                         gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Status"));
2855                         continue;
2856                 }
2857
2858                 hbox  = gtk_hbox_new(FALSE, 4);
2859                 label = gtk_label_new(title);
2860 #ifdef GENERIC_UMPC
2861         gtk_widget_set_size_request(hbox, -1, 20);
2862 #endif
2863
2864                 if (justify == GTK_JUSTIFY_RIGHT)
2865                         gtk_box_pack_end(GTK_BOX(hbox), label,
2866                                          FALSE, FALSE, 0);
2867                 else
2868                         gtk_box_pack_start(GTK_BOX(hbox), label,
2869                                            FALSE, FALSE, 0);
2870
2871                 if (summaryview->sort_key == sort_by[type] ||
2872                     (summaryview->sort_key == SORT_BY_THREAD_DATE &&
2873                      sort_by[SORT_BY_DATE] && type == S_COL_DATE)) {
2874                         arrow = gtk_arrow_new
2875                                 (summaryview->sort_type == SORT_ASCENDING
2876                                  ? GTK_ARROW_DOWN : GTK_ARROW_UP,
2877                                  GTK_SHADOW_IN);
2878                         gtk_widget_set_size_request(GTK_WIDGET(arrow), 10, 10);
2879                         if (justify == GTK_JUSTIFY_RIGHT)
2880                                 gtk_box_pack_start(GTK_BOX(hbox), arrow,
2881                                                    FALSE, FALSE, 0);
2882                         else
2883                                 gtk_box_pack_end(GTK_BOX(hbox), arrow,
2884                                                  FALSE, FALSE, 0);
2885                 }
2886
2887                 gtk_widget_show_all(hbox);
2888                 gtk_cmclist_set_column_widget(clist, pos, hbox);
2889         }
2890 }
2891
2892 void summary_reflect_tags_changes(SummaryView *summaryview)
2893 {
2894         GtkMenuShell *menu;
2895         GList *children, *cur;
2896         GtkCMCTreeNode *node;
2897         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2898         gboolean froze = FALSE;
2899         gboolean redisplay = FALSE;
2900
2901         /* re-create colorlabel submenu */
2902         menu = GTK_MENU_SHELL(summaryview->tags_menu);
2903         cm_return_if_fail(menu != NULL);
2904
2905         /* clear items. get item pointers. */
2906         children = gtk_container_get_children(GTK_CONTAINER(menu));
2907         for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
2908                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
2909         }
2910         g_list_free(children);
2911         summary_tags_menu_create(summaryview, TRUE);
2912
2913         START_LONG_OPERATION(summaryview, TRUE);
2914         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
2915              node = gtkut_ctree_node_next(ctree, node)) {
2916                 redisplay |= summary_set_row_tag(summaryview,
2917                                            node, TRUE, FALSE, 0);
2918         }
2919         END_LONG_OPERATION(summaryview);
2920         if (redisplay)
2921                 summary_redisplay_msg(summaryview);
2922 }
2923
2924
2925 void summary_reflect_prefs(void)
2926 {
2927         static gchar *last_smallfont = NULL;
2928         static gchar *last_normalfont = NULL;
2929         static gchar *last_boldfont = NULL;
2930         static gboolean last_derive = 0;
2931         gboolean update_font = FALSE;
2932         SummaryView *summaryview = NULL;
2933
2934         if (!mainwindow_get_mainwindow())
2935                 return;
2936         summaryview = mainwindow_get_mainwindow()->summaryview;
2937
2938         if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2939                         !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2940                         !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2941                         last_derive != prefs_common.derive_from_normal_font)
2942                 update_font = TRUE;
2943
2944         g_free(last_smallfont);
2945         last_smallfont = g_strdup(SMALL_FONT);
2946         g_free(last_normalfont);
2947         last_normalfont = g_strdup(NORMAL_FONT);
2948         g_free(last_boldfont);
2949         last_boldfont = g_strdup(BOLD_FONT);
2950         last_derive = prefs_common.derive_from_normal_font;
2951
2952 #define STYLE_FREE(s)                   \
2953         if (s != NULL) {                \
2954                 g_object_unref(s);      \
2955                 s = NULL;               \
2956         }
2957
2958         if (update_font) {
2959                 STYLE_FREE(bold_style);
2960                 STYLE_FREE(bold_style);
2961                 STYLE_FREE(bold_marked_style);
2962                 STYLE_FREE(bold_deleted_style);
2963                 STYLE_FREE(small_style);
2964                 STYLE_FREE(small_marked_style);
2965                 STYLE_FREE(small_deleted_style);
2966                 summary_set_fonts(summaryview);
2967         }
2968
2969 #undef STYLE_FREE
2970
2971         summary_set_column_titles(summaryview);
2972         summary_relayout(summaryview);
2973         
2974         if (summaryview->folder_item)
2975                 summary_show(summaryview, summaryview->folder_item);
2976 }
2977
2978 void summary_sort(SummaryView *summaryview,
2979                   FolderSortKey sort_key, FolderSortType sort_type)
2980 {
2981         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2982         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
2983         GtkCMCListCompareFunc cmp_func = NULL;
2984         START_TIMING("");
2985         g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
2986                                        G_CALLBACK(summary_tree_expanded), summaryview);
2987         summary_freeze(summaryview);
2988
2989         switch (sort_key) {
2990         case SORT_BY_MARK:
2991                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mark;
2992                 break;
2993         case SORT_BY_STATUS:
2994                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_status;
2995                 break;
2996         case SORT_BY_MIME:
2997                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mime;
2998                 break;
2999         case SORT_BY_NUMBER:
3000                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_num;
3001                 break;
3002         case SORT_BY_SIZE:
3003                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_size;
3004                 break;
3005         case SORT_BY_DATE:
3006                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_date;
3007                 break;
3008         case SORT_BY_THREAD_DATE:
3009                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_thread_date;
3010                 break;
3011         case SORT_BY_FROM:
3012                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_from;
3013                 break;
3014         case SORT_BY_SUBJECT:
3015                 if (summaryview->simplify_subject_preg)
3016                         cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_simplified_subject;
3017                 else
3018                         cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_subject;
3019                 break;
3020         case SORT_BY_SCORE:
3021                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_score;
3022                 break;
3023         case SORT_BY_LABEL:
3024                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_label;
3025                 break;
3026         case SORT_BY_TO:
3027                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_to;
3028                 break;
3029         case SORT_BY_LOCKED:
3030                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_locked;
3031                 break;
3032         case SORT_BY_TAGS:
3033                 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_tags;
3034                 break;
3035         case SORT_BY_NONE:
3036                 break;
3037         default:
3038                 goto unlock;
3039         }
3040
3041         summaryview->sort_key = sort_key;
3042         summaryview->sort_type = sort_type;
3043
3044         summary_set_column_titles(summaryview);
3045         summary_set_menu_sensitive(summaryview);
3046
3047         /* allow fallback to don't sort */
3048         if (summaryview->sort_key == SORT_BY_NONE)
3049                 goto unlock;
3050
3051         if (cmp_func != NULL) {
3052                 debug_print("Sorting summary...\n");
3053                 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
3054
3055                 main_window_cursor_wait(summaryview->mainwin);
3056
3057                 gtk_cmclist_set_compare_func(clist, cmp_func);
3058
3059                 gtk_cmclist_set_sort_type(clist, (GtkSortType)sort_type);
3060                 gtk_sctree_sort_recursive(ctree, NULL);
3061
3062                 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
3063
3064                 main_window_cursor_normal(summaryview->mainwin);
3065
3066                 debug_print("Sorting summary done.\n");
3067                 STATUSBAR_POP(summaryview->mainwin);
3068         }
3069 unlock:
3070         summary_thaw(summaryview);
3071         g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
3072                                        G_CALLBACK(summary_tree_expanded), summaryview);
3073         END_TIMING();
3074 }
3075
3076 static gboolean summary_update_thread_age(GNode *node, gpointer data)
3077 {
3078         MsgInfo *msginfo = node->data;
3079         time_t *most_recent = (time_t *)data;
3080
3081         if (msginfo->date_t > *most_recent) {
3082                 *most_recent = msginfo->date_t;
3083         }
3084         return FALSE;
3085 }       
3086
3087 static void summary_find_thread_age(GNode *gnode)
3088 {
3089         MsgInfo *msginfo = (MsgInfo *)gnode->data;
3090         time_t most_recent;
3091
3092         if (!msginfo)
3093                 return;
3094         most_recent = msginfo->thread_date = msginfo->date_t;
3095
3096         g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_thread_age, &most_recent);
3097
3098         msginfo->thread_date = most_recent;
3099 }
3100
3101 static gboolean summary_update_is_read(GNode *node, gpointer data)
3102 {
3103         MsgInfo *msginfo = node->data;
3104         gboolean *all_read = (gboolean *)data;
3105
3106         if (MSG_IS_UNREAD(msginfo->flags)) {
3107                 *all_read = FALSE;
3108                 return TRUE;
3109         }
3110         return FALSE;
3111 }       
3112
3113 static gboolean summary_thread_is_read(GNode *gnode)
3114 {
3115         MsgInfo *msginfo = (MsgInfo *)gnode->data;
3116         gboolean all_read = TRUE;
3117
3118         if (!msginfo)
3119                 return all_read;
3120
3121         g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_is_read, &all_read);
3122     return all_read;
3123 }
3124
3125 typedef struct  _ThreadSelectedData {
3126         guint msgnum;
3127         gboolean is_selected;
3128 } ThreadSelectedData;
3129
3130 static gboolean summary_update_is_selected(GNode *gnode, gpointer data)
3131 {
3132         ThreadSelectedData *selected = (ThreadSelectedData *)data;
3133         MsgInfo *msginfo = (MsgInfo *)gnode->data;
3134
3135         if (msginfo->msgnum == selected->msgnum) {
3136                 selected->is_selected = TRUE;
3137                 return TRUE;
3138         }
3139
3140         return FALSE;
3141 }
3142
3143 static gboolean summary_thread_is_selected(GNode *gnode, guint selected_msgnum)
3144 {
3145         ThreadSelectedData selected;
3146
3147         selected.msgnum = selected_msgnum;
3148         selected.is_selected = FALSE;
3149         g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1,
3150                         summary_update_is_selected, &selected);
3151         return selected.is_selected;
3152 }
3153
3154 static gboolean summary_insert_gnode_func(GtkCMCTree *ctree, guint depth, GNode *gnode,
3155                                    GtkCMCTreeNode *cnode, gpointer data)
3156 {
3157         SummaryView *summaryview = (SummaryView *)data;
3158         MsgInfo *msginfo = (MsgInfo *)gnode->data;
3159         gchar *text[N_SUMMARY_COLS];
3160         gint *col_pos = summaryview->col_pos;
3161         const gchar *msgid = msginfo->msgid;
3162         GHashTable *msgid_table = summaryview->msgid_table;
3163         gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3164         gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3165
3166         summary_set_header(summaryview, text, msginfo);
3167
3168         gtk_cmctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
3169                                 NULL, NULL, FALSE, summaryview->threaded && !summaryview->thread_collapsed);
3170 #define SET_TEXT(col) {                                         \
3171         gtk_cmctree_node_set_text(ctree, cnode, col_pos[col],   \
3172                                 text[col_pos[col]]);            \
3173 }
3174
3175         if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3176                 SET_TEXT(S_COL_NUMBER);
3177         if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3178                 SET_TEXT(S_COL_SCORE);
3179         if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3180                 SET_TEXT(S_COL_SIZE);
3181         if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible)
3182                 SET_TEXT(S_COL_DATE);
3183         if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
3184                 SET_TEXT(S_COL_FROM);
3185         if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
3186                 SET_TEXT(S_COL_TO);
3187         if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible)
3188                 SET_TEXT(S_COL_TAGS);
3189
3190         if ((vert_layout || small_layout) && prefs_common.two_line_vert)
3191                 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3192
3193 #undef SET_TEXT
3194
3195         GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
3196         summary_set_marks_func(ctree, cnode, summaryview);
3197
3198         if (msgid && msgid[0] != '\0')
3199                 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
3200
3201         return TRUE;
3202 }
3203
3204 static void summary_set_ctree_from_list(SummaryView *summaryview,
3205                                         GSList *mlist, guint selected_msgnum)
3206 {
3207         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3208         MsgInfo *msginfo;
3209         GtkCMCTreeNode *node = NULL;
3210         GHashTable *msgid_table;
3211         GHashTable *subject_table = NULL;
3212         GSList * cur;
3213         gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3214         gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3215         START_TIMING("");
3216         
3217         if (!mlist) return;
3218
3219         debug_print("Setting summary from message data...\n");
3220         STATUSBAR_PUSH(summaryview->mainwin,
3221                        _("Setting summary from message data..."));
3222         gdk_flush();
3223
3224         g_signal_handlers_block_by_func(G_OBJECT(ctree),
3225                                        G_CALLBACK(summary_tree_expanded), summaryview);
3226
3227         msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
3228         summaryview->msgid_table = msgid_table;
3229
3230         if (prefs_common.thread_by_subject) {
3231                 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
3232                 summaryview->subject_table = subject_table;
3233         } else {
3234                 summaryview->subject_table = NULL;
3235         }
3236
3237         if (prefs_common.use_addr_book)
3238                 start_address_completion(NULL);
3239         
3240         if (summaryview->threaded) {
3241                 GNode *root, *gnode;
3242                 START_TIMING("threaded");
3243                 root = procmsg_get_thread_tree(mlist);
3244
3245                 
3246                 for (gnode = root->children; gnode != NULL;
3247                      gnode = gnode->next) {
3248                         if (!summaryview->folder_item->hide_read_threads ||
3249                             !summary_thread_is_read(gnode) ||
3250                             summary_thread_is_selected(gnode, selected_msgnum)) {
3251                                 summary_find_thread_age(gnode);
3252                                 node = gtk_sctree_insert_gnode
3253                                         (ctree, NULL, node, gnode,
3254                                          summary_insert_gnode_func, summaryview);
3255                         }
3256                 }
3257
3258                 g_node_destroy(root);
3259                 
3260                 END_TIMING();
3261         } else {
3262                 gchar *text[N_SUMMARY_COLS];
3263                 START_TIMING("unthreaded");
3264                 cur = mlist;
3265                 for (; mlist != NULL; mlist = mlist->next) {
3266                         msginfo = (MsgInfo *)mlist->data;
3267
3268                         summary_set_header(summaryview, text, msginfo);
3269
3270                         node = gtk_sctree_insert_node
3271                                 (ctree, NULL, node, text, 2,
3272                                  NULL, NULL,
3273                                  FALSE, FALSE);
3274                         if ((vert_layout || small_layout) && prefs_common.two_line_vert)
3275                                 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3276
3277                         GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
3278                         summary_set_marks_func(ctree, node, summaryview);
3279
3280                         if (msginfo->msgid && msginfo->msgid[0] != '\0')
3281                                 g_hash_table_insert(msgid_table,
3282                                                     msginfo->msgid, node);
3283
3284                         if (prefs_common.thread_by_subject)
3285                                 subject_table_insert(subject_table,
3286                                              msginfo->subject,
3287                                              node);
3288                 }
3289                 mlist = cur;
3290                 END_TIMING();
3291         }
3292
3293         if (prefs_common.enable_hscrollbar &&
3294             summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
3295                 gint optimal_width;
3296
3297                 optimal_width = gtk_cmclist_optimal_column_width
3298                         (GTK_CMCLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
3299                 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree),
3300                                            summaryview->col_pos[S_COL_SUBJECT],
3301                                            optimal_width);
3302         }
3303
3304         if (prefs_common.use_addr_book)
3305                 end_address_completion();
3306
3307         debug_print("Setting summary from message data done.\n");
3308         STATUSBAR_POP(summaryview->mainwin);
3309         if (debug_get_mode()) {
3310                 debug_print("\tmsgid hash table size = %d\n",
3311                             g_hash_table_size(msgid_table));
3312                 if (prefs_common.thread_by_subject)
3313                         debug_print("\tsubject hash table size = %d\n",
3314                             g_hash_table_size(subject_table));
3315         }
3316
3317         summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type);
3318
3319         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3320
3321         if (prefs_common.bold_unread) {
3322                 START_TIMING("bold_unread");
3323                 while (node) {
3324                         GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
3325                         if (GTK_CMCTREE_ROW(node)->children)
3326                                 summary_set_row_marks(summaryview, node);
3327                         node = next;
3328                 }
3329                 END_TIMING();
3330         }
3331
3332         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
3333                                        G_CALLBACK(summary_tree_expanded), summaryview);
3334         END_TIMING();
3335 }
3336
3337 static gchar *summary_complete_address(const gchar *addr)
3338 {
3339         gint count;
3340         gchar *res, *tmp, *email_addr;
3341         
3342         if (addr == NULL || !strchr(addr, '@'))
3343                 return NULL;
3344
3345         Xstrdup_a(email_addr, addr, return NULL);
3346         extract_address(email_addr);
3347         if (!*email_addr)
3348                 return NULL;
3349
3350         /*
3351          * completion stuff must be already initialized
3352          */
3353         res = NULL;
3354         if (1 < (count = complete_address(email_addr))) {
3355                 tmp = get_complete_address(1);
3356                 res = procheader_get_fromname(tmp);
3357                 g_free(tmp);
3358         }
3359
3360         return res;
3361 }
3362
3363 static inline void summary_set_header(SummaryView *summaryview, gchar *text[],
3364                                MsgInfo *msginfo)
3365 {
3366         static gchar date_modified[80];
3367         static gchar col_score[11];
3368         static gchar from_buf[BUFFSIZE], to_buf[BUFFSIZE];
3369         static gchar tmp1[BUFFSIZE], tmp2[BUFFSIZE], tmp3[BUFFSIZE];
3370         gint *col_pos = summaryview->col_pos;
3371         gchar *from_text = NULL, *to_text = NULL, *tags_text = NULL;
3372         gboolean should_swap = FALSE;
3373         gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3374         gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3375         static const gchar *color_dim_rgb = NULL;
3376         if (!color_dim_rgb)
3377                 color_dim_rgb = gdk_color_to_string(&summaryview->color_dim);
3378         text[col_pos[S_COL_FROM]]   = "";
3379         text[col_pos[S_COL_TO]]     = "";
3380         text[col_pos[S_COL_SUBJECT]]= "";
3381         text[col_pos[S_COL_MARK]]   = "";
3382         text[col_pos[S_COL_STATUS]] = "";
3383         text[col_pos[S_COL_MIME]]   = "";
3384         text[col_pos[S_COL_LOCKED]] = "";
3385         text[col_pos[S_COL_DATE]]   = "";
3386         text[col_pos[S_COL_TAGS]]   = "";
3387         if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3388                 text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
3389         else
3390                 text[col_pos[S_COL_NUMBER]] = "";
3391
3392         /* slow! */
3393         if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3394                 text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size);
3395         else
3396                 text[col_pos[S_COL_SIZE]] = "";
3397
3398         if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3399                 text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score);
3400         else
3401                 text[col_pos[S_COL_SCORE]] = "";
3402
3403         if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
3404                 tags_text = procmsg_msginfo_get_tags_str(msginfo);
3405                 if (!tags_text) {
3406                         text[col_pos[S_COL_TAGS]] = "-";
3407                 } else {
3408                         strncpy2(tmp1, tags_text, sizeof(tmp1));
3409                         tmp1[sizeof(tmp1)-1]='\0';
3410                         g_free(tags_text);
3411                         text[col_pos[S_COL_TAGS]] = tmp1;
3412                 }
3413         } else
3414                 text[col_pos[S_COL_TAGS]] = "";
3415
3416         /* slow! */
3417         if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible || 
3418             ((vert_layout || small_layout) && prefs_common.two_line_vert)) {
3419                 if (msginfo->date_t && msginfo->date_t > 0) {
3420                         procheader_date_get_localtime(date_modified,
3421                                                       sizeof(date_modified),
3422                                                       msginfo->date_t);
3423                         text[col_pos[S_COL_DATE]] = date_modified;
3424                 } else if (msginfo->date)
3425                         text[col_pos[S_COL_DATE]] = msginfo->date;
3426                 else
3427                         text[col_pos[S_COL_DATE]] = _("(No Date)");
3428         }
3429         
3430         if (prefs_common.swap_from && msginfo->from && msginfo->to
3431         &&  !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) {
3432                 gchar *addr = NULL;
3433                 
3434                 addr = g_strdup(msginfo->from);
3435
3436                 if (addr) {
3437                         extract_address(addr);
3438                         if (account_find_from_address(addr, FALSE)) {
3439                                 should_swap = TRUE;
3440                         }
3441                         g_free(addr);
3442                 }
3443         }
3444
3445         if (!prefs_common.use_addr_book) {
3446                 if (prefs_common.summary_from_show == SHOW_NAME)
3447                         from_text = msginfo->fromname;
3448                 else if (prefs_common.summary_from_show == SHOW_BOTH)
3449                         from_text = msginfo->from;
3450                 else {
3451                         from_text = msginfo->from;
3452                         extract_address(from_text);
3453                 }
3454                 if (!from_text)
3455                         from_text = _("(No From)");
3456         } else {
3457                 gchar *tmp = summary_complete_address(msginfo->from);
3458                 if (tmp) {
3459                         strncpy2(from_buf, tmp, sizeof(from_buf));
3460                         g_free(tmp);
3461                         from_text = from_buf;
3462                 } else {
3463                         if (prefs_common.summary_from_show == SHOW_NAME)
3464                                 from_text = msginfo->fromname;
3465                         else if (prefs_common.summary_from_show == SHOW_BOTH)
3466                                 from_text = msginfo->from;
3467                         else {
3468                                 from_text = msginfo->from;
3469                                 if (from_text)
3470                                         extract_address(from_text);
3471                         }
3472                         if (!from_text)
3473                                 from_text = _("(No From)");
3474                 }
3475         }
3476         
3477         to_text = msginfo->to ? msginfo->to : 
3478                    (msginfo->cc ? msginfo->cc :
3479                      (msginfo->newsgroups ? msginfo->newsgroups : NULL
3480                      )
3481                    );
3482
3483         if (!to_text)
3484                 to_text = _("(No Recipient)");
3485         else {
3486                 if (prefs_common.summary_from_show == SHOW_NAME) {
3487                         gchar *tmp = procheader_get_fromname(to_text);
3488                         /* need to keep to_text pointing to stack, so heap-allocated
3489                          * string from procheader_get_fromname() will be copied to to_buf */
3490                         if (tmp != NULL) {
3491                                 strncpy2(to_buf, tmp, sizeof(to_buf));
3492                                 g_free(tmp);
3493                                 to_text = to_buf;
3494                         }
3495                 } else if (prefs_common.summary_from_show == SHOW_ADDR)
3496                         extract_address(to_text);
3497         }
3498
3499         text[col_pos[S_COL_TO]] = to_text;
3500         if (!should_swap) {
3501                 text[col_pos[S_COL_FROM]] = from_text;
3502         } else {
3503                 if (prefs_common.use_addr_book) {
3504                         gchar *tmp = summary_complete_address(to_text);
3505                         /* need to keep to_text pointing to stack, so heap-allocated
3506                          * string from summary_complete_address() will be copied to to_buf */
3507                         if (tmp) {
3508                                 strncpy2(to_buf, tmp, sizeof(to_buf));
3509                                 g_free(tmp);
3510                                 to_text = to_buf;
3511                         } else {
3512                                 to_text = to_text ? to_text : _("(No From)");
3513                         }
3514                 }
3515                 snprintf(tmp2, BUFFSIZE-1, "âžœ %s", to_text);
3516                 tmp2[BUFFSIZE-1]='\0';
3517                 text[col_pos[S_COL_FROM]] = tmp2;
3518         }
3519         
3520         if (summaryview->simplify_subject_preg != NULL)
3521                 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? 
3522                         string_remove_match(tmp3, BUFFSIZE, msginfo->subject, 
3523                                         summaryview->simplify_subject_preg) : 
3524                         _("(No Subject)");
3525         else 
3526                 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
3527                         _("(No Subject)");
3528         if ((vert_layout || small_layout) && prefs_common.two_line_vert) {
3529                 if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) {
3530                         gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
3531                                                                         "<span color='%s' style='italic'>",
3532                                                                         _("From: %s, on %s"), "</span>", NULL),
3533                                         text[col_pos[S_COL_SUBJECT]],
3534                                         color_dim_rgb,
3535                                         text[col_pos[S_COL_FROM]],
3536                                         text[col_pos[S_COL_DATE]]);
3537                         text[col_pos[S_COL_SUBJECT]] = tmp;
3538                 } else {
3539                         gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
3540                                                                          "<span color='%s' style='italic'>",
3541                                                                         _("To: %s, on %s"), "</span>", NULL),
3542                                         text[col_pos[S_COL_SUBJECT]],
3543                                         color_dim_rgb,
3544                                         text[col_pos[S_COL_TO]],
3545                                         text[col_pos[S_COL_DATE]]);
3546                         text[col_pos[S_COL_SUBJECT]] = tmp;
3547                 }
3548         }
3549 }
3550
3551 static void summary_display_msg(SummaryView *summaryview, GtkCMCTreeNode *row)
3552 {
3553         summary_display_msg_full(summaryview, row, FALSE, FALSE);
3554 }
3555
3556 static gboolean defer_change(gpointer data);
3557 typedef struct _ChangeData {
3558         MsgInfo *info;
3559         gint op; /* 0, 1, 2 for unset, set, change */
3560         MsgPermFlags set_flags;
3561         MsgTmpFlags  set_tmp_flags;
3562         MsgPermFlags unset_flags;
3563         MsgTmpFlags  unset_tmp_flags;
3564 } ChangeData;
3565
3566 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3567 {
3568         if (!msginfo->folder || !msginfo->folder->processing_pending) {
3569                 debug_print("flags: doing unset now\n");
3570                 procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags);
3571         } else {
3572                 ChangeData *unset_data = g_new0(ChangeData, 1);
3573                 unset_data->info = msginfo;
3574                 unset_data->op = 0;
3575                 unset_data->unset_flags = flags;
3576                 unset_data->unset_tmp_flags = tmp_flags;
3577                 debug_print("flags: deferring unset\n");
3578                 g_timeout_add(100, defer_change, unset_data);
3579         }
3580 }
3581
3582 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3583 {
3584         if (!msginfo->folder || !msginfo->folder->processing_pending) {
3585                 debug_print("flags: doing set now\n");
3586                 procmsg_msginfo_set_flags(msginfo, flags, tmp_flags);
3587         } else {
3588                 ChangeData *set_data = g_new0(ChangeData, 1);
3589                 set_data->info = msginfo;
3590                 set_data->op = 1;
3591                 set_data->set_flags = flags;
3592                 set_data->set_tmp_flags = tmp_flags;
3593                 debug_print("flags: deferring set\n");
3594                 g_timeout_add(100, defer_change, set_data);
3595         }
3596 }
3597
3598 static void summary_msginfo_change_flags(MsgInfo *msginfo, 
3599                 MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags,
3600                 MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags)
3601 {
3602         if (!msginfo->folder || !msginfo->folder->processing_pending) {
3603                 debug_print("flags: doing change now\n");
3604                 procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags,
3605                         rem_flags, rem_tmp_flags);
3606         } else {
3607                 ChangeData *change_data = g_new0(ChangeData, 1);
3608                 change_data->info = msginfo;
3609                 change_data->op = 2;
3610                 change_data->set_flags = add_flags;
3611                 change_data->set_tmp_flags = add_tmp_flags;
3612                 change_data->unset_flags = rem_flags;
3613                 change_data->unset_tmp_flags = rem_tmp_flags;
3614                 debug_print("flags: deferring change\n");
3615                 g_timeout_add(100, defer_change, change_data);
3616         }
3617 }
3618
3619 gboolean defer_change(gpointer data)
3620 {
3621         ChangeData *chg = (ChangeData *)data;
3622         if (chg->info->folder && chg->info->folder->processing_pending) {
3623                 debug_print("flags: trying later\n");
3624                 return TRUE; /* try again */
3625         } else {
3626                 debug_print("flags: finally doing it\n");
3627                 switch(chg->op) {
3628                 case 0:
3629                         procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags);
3630                         break;
3631                 case 1:
3632                         procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags);
3633                         break;
3634                 case 2:
3635                         procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags,
3636                                 chg->unset_flags, chg->unset_tmp_flags);
3637                         break;
3638                 default:
3639                         g_warning("unknown change op");
3640                 }
3641                 g_free(chg);
3642         }
3643         return FALSE;
3644 }
3645
3646 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo,
3647                                       GtkCMCTreeNode *row)
3648 {
3649         cm_return_if_fail(summaryview != NULL);
3650         cm_return_if_fail(msginfo != NULL);
3651         cm_return_if_fail(row != NULL);
3652
3653         if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3654                 summary_msginfo_unset_flags
3655                         (msginfo, MSG_NEW | MSG_UNREAD, 0);
3656                 summary_set_row_marks(summaryview, row);
3657                 summary_status_show(summaryview);
3658         }
3659 }
3660
3661 typedef struct  {
3662         MsgInfo *msginfo;
3663         SummaryView *summaryview;
3664 } MarkAsReadData;
3665
3666 static int msginfo_mark_as_read_timeout(void *data)
3667 {
3668         MarkAsReadData *mdata = (MarkAsReadData *)data;
3669         if (!mdata)
3670                 return FALSE;
3671
3672         summary_lock(mdata->summaryview);
3673         if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview))
3674                 msginfo_mark_as_read(mdata->summaryview, mdata->msginfo,
3675                                      mdata->summaryview->selected);
3676         procmsg_msginfo_free(&(mdata->msginfo));
3677
3678         mdata->summaryview->mark_as_read_timeout_tag = 0;
3679         summary_unlock(mdata->summaryview);
3680
3681         g_free(mdata);
3682         return FALSE;
3683 }
3684
3685 static void summary_display_msg_full(SummaryView *summaryview,
3686                                      GtkCMCTreeNode *row,
3687                                      gboolean new_window, gboolean all_headers)
3688 {
3689         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3690         MsgInfo *msginfo;
3691         gint val;
3692         START_TIMING("");
3693         if (!new_window) {
3694                 if (summaryview->displayed == row &&
3695                     messageview_is_visible(summaryview->messageview))
3696                         return;
3697                 else if (summaryview->messageview)
3698                         summaryview->messageview->filtered = FALSE;
3699         }                       
3700         cm_return_if_fail(row != NULL);
3701
3702         if (summary_is_locked(summaryview)) return;
3703         summary_lock(summaryview);
3704
3705         STATUSBAR_POP(summaryview->mainwin);
3706         GTK_EVENTS_FLUSH();
3707
3708         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3709
3710         if (!msginfo) {
3711                 debug_print("NULL msginfo\n");
3712                 summary_unlock(summaryview);
3713                 END_TIMING();
3714                 return;
3715         }       
3716
3717         if (new_window && prefs_common.layout_mode != SMALL_LAYOUT) {
3718                 MessageView *msgview;
3719
3720                 msgview = messageview_create_with_new_window(summaryview->mainwin);
3721                 val = messageview_show(msgview, msginfo, all_headers);
3722         } else {
3723                 MessageView *msgview;
3724
3725                 if (prefs_common.layout_mode == SMALL_LAYOUT) {
3726                         if (summaryview->ext_messageview == NULL)
3727                                 summaryview->ext_messageview = messageview_create_with_new_window(summaryview->mainwin);
3728                         else
3729                                 gtkut_window_popup(summaryview->ext_messageview->window);
3730                         msgview = summaryview->ext_messageview;
3731                         summaryview->displayed = row;
3732                         val = messageview_show(msgview, msginfo, all_headers);
3733                         if (mimeview_tree_is_empty(msgview->mimeview))
3734                                 gtk_widget_grab_focus(summaryview->ctree);
3735                         gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3736                                 GTK_CMCLIST(summaryview->ctree)->focus_row);
3737                 } else {
3738                         msgview = summaryview->messageview;
3739                         summaryview->displayed = row;
3740                         if (!messageview_is_visible(msgview) &&
3741                             gtk_window_is_active(GTK_WINDOW(summaryview->mainwin->window))) {
3742                                 main_window_toggle_message_view(summaryview->mainwin);
3743                                 GTK_EVENTS_FLUSH();
3744                         }
3745                         val = messageview_show(msgview, msginfo, all_headers);
3746                         if (mimeview_tree_is_empty(msgview->mimeview))
3747                                 gtk_widget_grab_focus(summaryview->ctree);
3748                         gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3749                                 GTK_CMCLIST(summaryview->ctree)->focus_row);
3750                 }
3751         }
3752
3753         if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) {
3754                 if (!prefs_common.mark_as_read_on_new_window &&
3755                     prefs_common.mark_as_read_delay) {
3756                         MarkAsReadData *data = g_new0(MarkAsReadData, 1);
3757                         data->summaryview = summaryview;
3758                         data->msginfo = procmsg_msginfo_new_ref(msginfo);
3759                         if (summaryview->mark_as_read_timeout_tag != 0)
3760                                 g_source_remove(summaryview->mark_as_read_timeout_tag);
3761                         summaryview->mark_as_read_timeout_tag = 
3762                                 g_timeout_add_seconds(prefs_common.mark_as_read_delay,
3763                                         msginfo_mark_as_read_timeout, data);
3764                 } else if (new_window || !prefs_common.mark_as_read_on_new_window) {
3765                         msginfo_mark_as_read(summaryview, msginfo, row);
3766                 }
3767         }
3768
3769         summary_set_menu_sensitive(summaryview);
3770         toolbar_main_set_sensitive(summaryview->mainwin);
3771         messageview_set_menu_sensitive(summaryview->messageview);
3772
3773         summary_unlock(summaryview);
3774         END_TIMING();
3775 }
3776
3777 void summary_display_msg_selected(SummaryView *summaryview,
3778                                   gboolean all_headers)
3779 {
3780         if (summary_is_locked(summaryview)) return;
3781         summaryview->displayed = NULL;
3782         summary_display_msg_full(summaryview, summaryview->selected, FALSE,
3783                                  all_headers);
3784 }
3785
3786 void summary_redisplay_msg(SummaryView *summaryview)
3787 {
3788         GtkCMCTreeNode *node;
3789
3790         if (summaryview->displayed) {
3791                 node = summaryview->displayed;
3792                 summaryview->displayed = NULL;
3793                 summary_display_msg(summaryview, node);
3794         }
3795 }
3796
3797 void summary_open_msg(SummaryView *summaryview)
3798 {
3799         if (!summaryview->selected) return;
3800         
3801         /* CLAWS: if separate message view, don't open a new window
3802          * but rather use the current separated message view */
3803         summary_display_msg_full(summaryview, summaryview->selected, 
3804                                  TRUE, FALSE);
3805 }
3806
3807 void summary_view_source(SummaryView * summaryview)
3808 {
3809         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3810         MsgInfo *msginfo;
3811         SourceWindow *srcwin;
3812
3813         if (!summaryview->selected) return;
3814
3815         srcwin = source_window_create();
3816         msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
3817         source_window_show_msg(srcwin, msginfo);
3818         source_window_show(srcwin);
3819 }
3820
3821 void summary_reedit(SummaryView *summaryview)
3822 {
3823         MsgInfo *msginfo;
3824
3825         if (!summaryview->selected) return;
3826         if (!summaryview->folder_item) return;
3827         if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
3828                 return;
3829
3830         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
3831                                               summaryview->selected);
3832         if (!msginfo) return;
3833
3834         compose_reedit(msginfo, FALSE);
3835 }
3836
3837 gboolean summary_is_list(SummaryView *summaryview)
3838 {
3839         return (gtk_notebook_get_current_page(
3840                 GTK_NOTEBOOK(summaryview->mainwidget_book)) == 0);
3841 }
3842
3843 void summary_toggle_view(SummaryView *summaryview)
3844 {
3845         if (prefs_common.layout_mode == SMALL_LAYOUT)
3846                 return;
3847         if (summary_is_locked(summaryview))
3848                 return;
3849         if (!messageview_is_visible(summaryview->messageview) &&
3850             summaryview->selected && summary_is_list(summaryview))
3851                 summary_display_msg(summaryview,
3852                                     summaryview->selected);
3853         else
3854                 main_window_toggle_message_view(summaryview->mainwin);
3855 }
3856
3857 static gboolean summary_search_unread_recursive(GtkCMCTree *ctree,
3858                                                 GtkCMCTreeNode *node)
3859 {
3860         MsgInfo *msginfo;
3861
3862         if (node) {
3863                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
3864                 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3865                         return TRUE;
3866                 node = GTK_CMCTREE_ROW(node)->children;
3867         } else
3868                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3869
3870         while (node) {
3871                 if (summary_search_unread_recursive(ctree, node) == TRUE)
3872                         return TRUE;
3873                 node = GTK_CMCTREE_ROW(node)->sibling;
3874         }
3875
3876         return FALSE;
3877 }
3878
3879 static gboolean summary_have_unread_children(SummaryView *summaryview,
3880                                              GtkCMCTreeNode *node)
3881 {
3882         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3883
3884         if (!node) return FALSE;
3885
3886         node = GTK_CMCTREE_ROW(node)->children;
3887
3888         while (node) {
3889                 if (summary_search_unread_recursive(ctree, node) == TRUE)
3890                         return TRUE;
3891                 node = GTK_CMCTREE_ROW(node)->sibling;
3892         }
3893         return FALSE;
3894 }
3895
3896 static void summary_set_row_marks(SummaryView *summaryview, GtkCMCTreeNode *row)
3897 {
3898         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3899         GtkStyle *style = NULL;
3900         MsgInfo *msginfo;
3901         MsgFlags flags;
3902         gint *col_pos = summaryview->col_pos;
3903
3904         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3905         if (!msginfo) return;
3906
3907         flags = msginfo->flags;
3908
3909         gtk_cmctree_node_set_foreground(ctree, row, NULL);
3910
3911         /* set new/unread column */
3912         if (MSG_IS_IGNORE_THREAD(flags)) {
3913                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3914                                           ignorethreadxpm);
3915         } else if (MSG_IS_WATCH_THREAD(flags)) {
3916                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3917                                           watchthreadxpm);
3918         } else if (MSG_IS_SPAM(flags)) {
3919                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3920                                           spamxpm);
3921         } else if (MSG_IS_NEW(flags)) {
3922                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3923                                           newxpm);
3924         } else if (MSG_IS_UNREAD(flags)) {
3925                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3926                                           unreadxpm);
3927         } else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
3928                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3929                                           repliedandforwardedxpm);
3930         } else if (MSG_IS_REPLIED(flags)) {
3931                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3932                                           repliedxpm);
3933         } else if (MSG_IS_FORWARDED(flags)) {
3934                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3935                                           forwardedxpm);
3936         } else {
3937                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_STATUS],
3938                                         "");
3939         }
3940
3941         if (prefs_common.bold_unread &&
3942             ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
3943              (!GTK_CMCTREE_ROW(row)->expanded &&
3944               GTK_CMCTREE_ROW(row)->children &&
3945               summary_have_unread_children(summaryview, row))))
3946                 style = bold_style;
3947
3948         /* set mark column */
3949         if (MSG_IS_DELETED(flags)) {
3950                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
3951                                           deletedxpm);
3952                 if (style)
3953                         style = bold_deleted_style;
3954                 else {
3955                         style = small_deleted_style;
3956                 }
3957                         gtk_cmctree_node_set_foreground
3958                                 (ctree, row, &summaryview->color_dim);
3959         } else if (MSG_IS_MARKED(flags)) {
3960                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
3961                                           markxpm);
3962         } else if (MSG_IS_MOVE(flags)) {
3963                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
3964                                           movedxpm);
3965                 if (!msginfo->to_folder ||
3966                     !folder_has_parent_of_type(msginfo->to_folder, F_TRASH)) {
3967                         if (style)
3968                                 style = bold_marked_style;
3969                         else {
3970                                 style = small_marked_style;
3971                         }
3972                         gtk_cmctree_node_set_foreground
3973                                 (ctree, row, &summaryview->color_marked);
3974                 } else {
3975                         if (style)
3976                                 style = bold_deleted_style;
3977                         else {
3978                                 style = small_deleted_style;
3979                         }
3980                                 gtk_cmctree_node_set_foreground
3981                                         (ctree, row, &summaryview->color_dim);
3982                 }
3983         } else if (MSG_IS_COPY(flags)) {
3984                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
3985                                           copiedxpm);
3986                 if (style)
3987                         style = bold_marked_style;
3988                 else {
3989                         style = small_marked_style;
3990                 }
3991                         gtk_cmctree_node_set_foreground
3992                                 (ctree, row, &summaryview->color_marked);
3993         } else {
3994                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "");
3995         }
3996
3997         if (MSG_IS_LOCKED(flags)) {
3998                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_LOCKED],
3999                                           lockedxpm);
4000         }
4001         else {
4002                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], "");
4003         }
4004
4005         if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
4006                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4007                                           clipgpgsignedxpm);
4008         } else if (MSG_IS_SIGNED(flags)) {
4009                 if (MSG_IS_ENCRYPTED(flags)) {
4010                         gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4011                                           keysignxpm);
4012                 } else {
4013                         gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4014                                                   gpgsignedxpm);
4015                 }
4016         } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
4017                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4018                                           clipkeyxpm);
4019         } else if (MSG_IS_ENCRYPTED(flags)) {
4020                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4021                                           keyxpm);
4022         } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
4023                 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4024                                           clipxpm);
4025         } else {
4026                 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MIME], "");
4027         }
4028         if (!style)
4029                 style = small_style;
4030
4031         gtk_cmctree_node_set_row_style(ctree, row, style);
4032
4033         if (MSG_GET_COLORLABEL(flags))
4034                 summary_set_colorlabel_color(ctree, row, MSG_GET_COLORLABEL_VALUE(flags));
4035 }
4036
4037 static void summary_mark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4038 {
4039         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4040         MsgInfo *msginfo;
4041
4042         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4043         cm_return_if_fail(msginfo);
4044         if (MSG_IS_DELETED(msginfo->flags))
4045                 summaryview->deleted--;
4046         if (MSG_IS_MOVE(msginfo->flags))
4047                 summaryview->moved--;
4048         if (MSG_IS_COPY(msginfo->flags))
4049                 summaryview->copied--;
4050
4051         procmsg_msginfo_set_to_folder(msginfo, NULL);
4052         summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED, 
4053                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4054         summary_set_row_marks(summaryview, row);
4055         debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum);
4056 }
4057
4058 static void summary_lock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4059 {
4060         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4061         MsgInfo *msginfo;
4062
4063         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4064         cm_return_if_fail(msginfo);
4065         if (MSG_IS_DELETED(msginfo->flags))
4066                 summaryview->deleted--;
4067         if (MSG_IS_MOVE(msginfo->flags)) {
4068                 summaryview->moved--;
4069         }
4070         if (MSG_IS_COPY(msginfo->flags)) {
4071                 summaryview->copied--;
4072         }
4073         procmsg_msginfo_set_to_folder(msginfo, NULL);
4074         summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED, 
4075                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4076         
4077         summary_set_row_marks(summaryview, row);
4078         debug_print("Message %d is locked\n", msginfo->msgnum);
4079 }
4080
4081 static void summary_unlock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4082 {
4083         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4084         MsgInfo *msginfo;
4085
4086         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4087         cm_return_if_fail(msginfo);
4088         if (!MSG_IS_LOCKED(msginfo->flags))
4089                 return;
4090         procmsg_msginfo_set_to_folder(msginfo, NULL);
4091         summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
4092         summary_set_row_marks(summaryview, row);
4093         debug_print("Message %d is unlocked\n", msginfo->msgnum);
4094 }
4095
4096 void summary_mark(SummaryView *summaryview)
4097 {
4098         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4099         GList *cur;
4100         gboolean froze = FALSE;
4101
4102         if (summary_is_locked(summaryview))
4103                 return;
4104         START_LONG_OPERATION(summaryview, FALSE);
4105         folder_item_set_batch(summaryview->folder_item, TRUE);
4106         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4107                 summary_mark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4108         folder_item_set_batch(summaryview->folder_item, FALSE);
4109         END_LONG_OPERATION(summaryview);
4110
4111         summary_status_show(summaryview);
4112 }
4113
4114 static void summary_mark_row_as_read(SummaryView *summaryview,
4115                                      GtkCMCTreeNode *row)
4116 {
4117         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4118         MsgInfo *msginfo;
4119
4120         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4121         cm_return_if_fail(msginfo);
4122
4123         if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
4124                 return;
4125
4126         summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
4127         summary_set_row_marks(summaryview, row);
4128         debug_print("Message %d is marked as read\n",
4129                 msginfo->msgnum);
4130 }
4131
4132 static void summary_mark_row_as_unread(SummaryView *summaryview,
4133                                      GtkCMCTreeNode *row)
4134 {
4135         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4136         MsgInfo *msginfo;
4137
4138         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4139         cm_return_if_fail(msginfo);
4140
4141         if(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))
4142                 return;
4143
4144         summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0);
4145         summary_set_row_marks(summaryview, row);
4146         debug_print("Message %d is marked as unread\n",
4147                 msginfo->msgnum);
4148 }
4149
4150 void summary_mark_as_read(SummaryView *summaryview)
4151 {
4152         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4153         GList *cur;
4154         gboolean froze = FALSE;
4155
4156         if (summary_is_locked(summaryview))
4157                 return;
4158
4159         if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection))
4160                  && !summary_mark_all_read_confirm(TRUE))
4161                 return;
4162
4163         START_LONG_OPERATION(summaryview, FALSE);
4164         folder_item_set_batch(summaryview->folder_item, TRUE);
4165         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4166                 summary_mark_row_as_read(summaryview,
4167                                          GTK_CMCTREE_NODE(cur->data));
4168         folder_item_set_batch(summaryview->folder_item, FALSE);
4169         END_LONG_OPERATION(summaryview);
4170         
4171         summary_status_show(summaryview);
4172 }
4173
4174 void summary_mark_as_unread(SummaryView *summaryview)
4175 {
4176         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4177         GList *cur;
4178         gboolean froze = FALSE;
4179
4180         if (summary_is_locked(summaryview))
4181                 return;
4182
4183         if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection))
4184                  && !summary_mark_all_unread_confirm(TRUE))
4185                 return;
4186
4187         START_LONG_OPERATION(summaryview, FALSE);
4188         folder_item_set_batch(summaryview->folder_item, TRUE);
4189         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4190                 summary_mark_row_as_unread(summaryview,
4191                                          GTK_CMCTREE_NODE(cur->data));
4192         folder_item_set_batch(summaryview->folder_item, FALSE);
4193         END_LONG_OPERATION(summaryview);
4194         
4195         summary_status_show(summaryview);
4196 }
4197
4198 void summary_msgs_lock(SummaryView *summaryview)
4199 {
4200         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4201         GList *cur;
4202         gboolean froze = FALSE;
4203
4204         if (summary_is_locked(summaryview))
4205                 return;
4206         START_LONG_OPERATION(summaryview, FALSE);
4207         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4208                 summary_lock_row(summaryview,
4209                                          GTK_CMCTREE_NODE(cur->data));
4210         END_LONG_OPERATION(summaryview);
4211         
4212         summary_status_show(summaryview);
4213 }
4214
4215 void summary_msgs_unlock(SummaryView *summaryview)
4216 {
4217         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4218         GList *cur;
4219         gboolean froze = FALSE;
4220
4221         if (summary_is_locked(summaryview))
4222                 return;
4223         START_LONG_OPERATION(summaryview, FALSE);
4224         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4225                 summary_unlock_row(summaryview,
4226                                    GTK_CMCTREE_NODE(cur->data));
4227         END_LONG_OPERATION(summaryview);
4228         
4229         summary_status_show(summaryview);
4230 }
4231
4232 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed)
4233 {
4234         /* ask_if_needed is FALSE when user-asking is performed by caller,
4235            commonly when the caller is a mark-as-read-recursive func */
4236         if (ask_if_needed && prefs_common.ask_mark_all_read) {
4237                 AlertValue val = alertpanel_full(_("Mark all as read"),
4238                           _("Do you really want to mark all mails in this folder as read?"),
4239                           GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
4240                           TRUE, NULL, ALERT_QUESTION);
4241
4242                 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4243                         return FALSE;
4244                 else if (val & G_ALERTDISABLE)
4245                         prefs_common.ask_mark_all_read = FALSE;
4246         }
4247         return TRUE;
4248 }
4249
4250 void summary_mark_all_read(SummaryView *summaryview, gboolean ask_if_needed)
4251 {
4252         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4253         GtkCMCTreeNode *node;
4254         gboolean froze = FALSE;
4255
4256         if (summary_is_locked(summaryview))
4257                 return;
4258
4259         if (!summary_mark_all_read_confirm(ask_if_needed))
4260                 return;
4261
4262         START_LONG_OPERATION(summaryview, TRUE);
4263         folder_item_set_batch(summaryview->folder_item, TRUE);
4264         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4265                 node = gtkut_ctree_node_next(ctree, node))
4266                 summary_mark_row_as_read(summaryview, node);
4267         folder_item_set_batch(summaryview->folder_item, FALSE);
4268         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4269                 node = gtkut_ctree_node_next(ctree, node)) {
4270                 if (!GTK_CMCTREE_ROW(node)->expanded)
4271                         summary_set_row_marks(summaryview, node);
4272         }
4273         END_LONG_OPERATION(summaryview);
4274
4275         summary_status_show(summaryview);
4276 }
4277
4278 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed)
4279 {
4280         /* ask_if_needed is FALSE when user-asking is performed by caller,
4281            commonly when the caller is a mark-as-unread-recursive func */
4282         if (ask_if_needed && prefs_common.ask_mark_all_read) {
4283                 AlertValue val = alertpanel_full(_("Mark all as unread"),
4284                           _("Do you really want to mark all mails in this folder as unread?"),
4285                           GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
4286                           TRUE, NULL, ALERT_QUESTION);
4287
4288                 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4289                         return FALSE;
4290                 else if (val & G_ALERTDISABLE)
4291                         prefs_common.ask_mark_all_read = FALSE;
4292         }
4293         return TRUE;
4294 }
4295
4296 void summary_mark_all_unread(SummaryView *summaryview, gboolean ask_if_needed)
4297 {
4298         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4299         GtkCMCTreeNode *node;
4300         gboolean froze = FALSE;
4301
4302         if (summary_is_locked(summaryview))
4303                 return;
4304
4305         if (!summary_mark_all_unread_confirm(ask_if_needed))
4306                 return;
4307
4308         START_LONG_OPERATION(summaryview, TRUE);
4309         folder_item_set_batch(summaryview->folder_item, TRUE);
4310         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4311                 node = gtkut_ctree_node_next(ctree, node))
4312                 summary_mark_row_as_unread(summaryview, node);
4313         folder_item_set_batch(summaryview->folder_item, FALSE);
4314         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4315                 node = gtkut_ctree_node_next(ctree, node)) {
4316                 if (!GTK_CMCTREE_ROW(node)->expanded)
4317                         summary_set_row_marks(summaryview, node);
4318         }
4319         END_LONG_OPERATION(summaryview);
4320
4321         summary_status_show(summaryview);
4322 }
4323
4324 void summary_mark_as_spam(SummaryView *summaryview, guint action, GtkWidget *widget)
4325 {
4326         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4327         GList *cur;
4328         gboolean is_spam = action;
4329         GSList *msgs = NULL;
4330         gboolean immediate_exec = prefs_common.immediate_exec;
4331         gboolean moved = FALSE;
4332         gboolean froze = FALSE;
4333
4334
4335         if (summary_is_locked(summaryview))
4336                 return;
4337
4338         prefs_common.immediate_exec = FALSE;
4339         START_LONG_OPERATION(summaryview, FALSE);
4340         folder_item_set_batch(summaryview->folder_item, TRUE);
4341         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4342                 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4343                 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4344                 if (msginfo)
4345                         msgs = g_slist_prepend(msgs, msginfo);
4346         }
4347         
4348         if (procmsg_spam_learner_learn(NULL, msgs, is_spam) == 0) {
4349                 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4350                         GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4351                         MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4352                         if (!msginfo)
4353                                 continue;
4354                         if (is_spam) {
4355                                 summary_msginfo_change_flags(msginfo, MSG_SPAM, 0, MSG_NEW|MSG_UNREAD, 0);
4356                                 if (procmsg_spam_get_folder(msginfo) != summaryview->folder_item) {
4357                                         summary_move_row_to(summaryview, row,
4358                                                         procmsg_spam_get_folder(msginfo));
4359                                         moved = TRUE;
4360                                 }
4361                         } else {
4362                                 summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
4363                         }
4364                         summaryview->display_msg = prefs_common.always_show_msg;
4365         
4366                         summary_set_row_marks(summaryview, row);
4367                 }
4368         } else {
4369                 log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
4370         }
4371
4372         prefs_common.immediate_exec = immediate_exec;
4373         folder_item_set_batch(summaryview->folder_item, FALSE);
4374         END_LONG_OPERATION(summaryview);
4375
4376         if (prefs_common.immediate_exec && moved) {
4377                 summary_execute(summaryview);
4378         }
4379
4380         if (!moved && msgs) {
4381                 MsgInfo *msginfo = (MsgInfo *)msgs->data;
4382                 toolbar_set_learn_button
4383                         (summaryview->mainwin->toolbar,
4384                          MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
4385         }
4386         g_slist_free(msgs);
4387         
4388         summary_status_show(summaryview);       
4389 }
4390
4391 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo)
4392 {
4393         GList * cur;
4394         gboolean found;
4395
4396         switch (FOLDER_TYPE(summaryview->folder_item->folder)) {
4397
4398         case F_NEWS:
4399
4400                 /*
4401                   security : checks if one the accounts correspond to
4402                   the author of the post
4403                 */
4404
4405                 found = FALSE;
4406                 for(cur = account_get_list() ; cur != NULL ; cur = cur->next) {
4407                         PrefsAccount * account;
4408                         gchar * from_name;
4409                         
4410                         account = cur->data;
4411                         if (account->name && *account->name)
4412                                 from_name =
4413                                         g_strdup_printf("%s <%s>",
4414                                                         account->name,
4415                                                         account->address);
4416                         else
4417                                 from_name =
4418                                         g_strdup_printf("%s",
4419                                                         account->address);
4420                         
4421                         if (g_utf8_collate(from_name, msginfo->from) == 0) {
4422                                 g_free(from_name);
4423                                 found = TRUE;
4424                                 break;
4425                         }
4426                         g_free(from_name);
4427                 }
4428
4429                 if (!found) {
4430                         alertpanel_error(_("You're not the author of the article."));
4431                 }
4432                 
4433                 return found;
4434
4435         default:
4436                 return TRUE;
4437         }
4438 }
4439
4440 static void summary_delete_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4441 {
4442         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4443         MsgInfo *msginfo;
4444
4445         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4446         cm_return_if_fail(msginfo);
4447
4448         if (MSG_IS_LOCKED(msginfo->flags)) return;
4449
4450         if (MSG_IS_DELETED(msginfo->flags)) return;
4451
4452         if (MSG_IS_MOVE(msginfo->flags))
4453                 summaryview->moved--;
4454         if (MSG_IS_COPY(msginfo->flags))
4455                 summaryview->copied--;
4456
4457         procmsg_msginfo_set_to_folder(msginfo, NULL);
4458         summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED, 
4459                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4460         summaryview->deleted++;
4461
4462         if (!prefs_common.immediate_exec && 
4463             !folder_has_parent_of_type(summaryview->folder_item, F_TRASH)) {
4464                 summary_set_row_marks(summaryview, row);
4465         }
4466         debug_print("Message %s/%d is set to delete\n",
4467                     msginfo->folder->path, msginfo->msgnum);
4468 }
4469
4470 void summary_cancel(SummaryView *summaryview)
4471 {
4472         MsgInfo * msginfo;
4473
4474         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4475                                               summaryview->selected);
4476         if (!msginfo) return;
4477
4478         if (!check_permission(summaryview, msginfo))
4479                 return;
4480
4481         news_cancel_article(summaryview->folder_item->folder, msginfo);
4482         
4483         if (summary_is_locked(summaryview)) return;
4484
4485         summary_lock(summaryview);
4486
4487         summary_freeze(summaryview);
4488
4489         summary_update_status(summaryview);
4490         summary_status_show(summaryview);
4491
4492         summary_thaw(summaryview);
4493
4494         summary_unlock(summaryview);
4495 }
4496
4497 void summary_delete(SummaryView *summaryview)
4498 {
4499         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4500         FolderItem *item = summaryview->folder_item;
4501         GList *cur;
4502         GtkCMCTreeNode *sel_last = NULL;
4503         GtkCMCTreeNode *node;
4504         AlertValue aval;
4505         MsgInfo *msginfo;
4506         gboolean froze = FALSE;
4507
4508         if (!item) return;
4509
4510         if (summary_is_locked(summaryview)) return;
4511
4512         if (!summaryview->folder_item) return;
4513         
4514         START_LONG_OPERATION(summaryview, FALSE);
4515
4516         if (!prefs_common.live_dangerously) {
4517                 gchar *buf = NULL;
4518                 guint num = g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
4519                 buf = g_strdup_printf(ngettext(
4520                         "Do you really want to delete the selected message?",
4521                         "Do you really want to delete the %d selected messages?", num),
4522                         num);
4523                 aval = alertpanel(ngettext("Delete message", "Delete messages", num),
4524                                   buf,
4525                                   GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND);
4526                 g_free(buf);
4527                 if (aval != G_ALERTALTERNATE) {
4528                         END_LONG_OPERATION(summaryview);
4529                         return;
4530                 }
4531         }
4532
4533         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; 
4534              cur = cur->next) {
4535                 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4536                 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4537                 if (msginfo && msginfo->total_size != 0 && 
4538                     msginfo->size != (goffset)msginfo->total_size)
4539                         partial_mark_for_delete(msginfo);
4540         }
4541
4542         main_window_cursor_wait(summaryview->mainwin);
4543
4544         /* next code sets current row focus right. We need to find a row
4545          * that is not deleted. */
4546         folder_item_set_batch(summaryview->folder_item, TRUE);
4547         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4548                 sel_last = GTK_CMCTREE_NODE(cur->data);
4549                 summary_delete_row(summaryview, sel_last);
4550         }
4551         folder_item_set_batch(summaryview->folder_item, FALSE);
4552         END_LONG_OPERATION(summaryview);
4553
4554         if (summaryview->sort_type == SORT_ASCENDING) {
4555                 node = summary_find_next_msg(summaryview, sel_last, TRUE);
4556                 if (!node || prefs_common.next_on_delete == FALSE)
4557                         node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4558         } else {
4559                 node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4560                 if (!node || prefs_common.next_on_delete == FALSE)
4561                         node = summary_find_next_msg(summaryview, sel_last,TRUE);
4562         }
4563         summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4564         
4565         if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) {
4566                 summary_execute(summaryview);
4567                 /* after deleting, the anchor may be at an invalid row
4568                  * so reset it to the node we found earlier */
4569                 gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node);
4570         } else
4571                 summary_status_show(summaryview);
4572
4573                 
4574         main_window_cursor_normal(summaryview->mainwin);
4575 }
4576
4577 void summary_delete_trash(SummaryView *summaryview)
4578 {
4579         FolderItem *to_folder = NULL;
4580         PrefsAccount *ac;
4581         if (!summaryview->folder_item ||
4582             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4583         
4584         if (NULL != (ac = account_find_from_item(summaryview->folder_item)))
4585                 to_folder = account_get_special_folder(ac, F_TRASH);
4586
4587         if (to_folder == NULL)
4588                 to_folder = summaryview->folder_item->folder->trash;
4589         
4590         if (to_folder == NULL || to_folder == summaryview->folder_item
4591             || folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
4592                 summary_delete(summaryview);
4593         else
4594                 summary_move_selected_to(summaryview, to_folder);
4595 }
4596
4597
4598 static void summary_unmark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4599 {
4600         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4601         MsgInfo *msginfo;
4602
4603         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4604         cm_return_if_fail(msginfo);
4605         if (MSG_IS_DELETED(msginfo->flags))
4606                 summaryview->deleted--;
4607         if (MSG_IS_MOVE(msginfo->flags))
4608                 summaryview->moved--;
4609         if (MSG_IS_COPY(msginfo->flags))
4610                 summaryview->copied--;
4611
4612         procmsg_msginfo_set_to_folder(msginfo, NULL);
4613         summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED, 
4614                 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4615         summary_set_row_marks(summaryview, row);
4616
4617         debug_print("Message %s/%d is unmarked\n",
4618                     msginfo->folder->path, msginfo->msgnum);
4619 }
4620
4621 void summary_unmark(SummaryView *summaryview)
4622 {
4623         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4624         GList *cur;
4625         gboolean froze = FALSE;
4626
4627         if (summary_is_locked(summaryview))
4628                 return;
4629         START_LONG_OPERATION(summaryview, FALSE);
4630         folder_item_set_batch(summaryview->folder_item, TRUE);
4631         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4632                 summary_unmark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4633         folder_item_set_batch(summaryview->folder_item, FALSE);
4634         END_LONG_OPERATION(summaryview);
4635         
4636         summary_status_show(summaryview);
4637 }
4638
4639 static void summary_move_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4640                                 FolderItem *to_folder)
4641 {
4642         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4643         MsgInfo *msginfo;
4644
4645         cm_return_if_fail(to_folder != NULL);
4646
4647         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4648         cm_return_if_fail(msginfo);
4649         if (MSG_IS_LOCKED(msginfo->flags))
4650                 return;
4651
4652         procmsg_msginfo_set_to_folder(msginfo, to_folder);
4653         if (MSG_IS_DELETED(msginfo->flags))
4654                 summaryview->deleted--;
4655         if (MSG_IS_COPY(msginfo->flags)) {
4656                 summaryview->copied--;
4657         }
4658         if (!MSG_IS_MOVE(msginfo->flags)) {
4659                 summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED, 
4660                         MSG_COPY | MSG_MOVE_DONE);
4661                 summaryview->moved++;
4662         } else {
4663                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY);
4664         }
4665         
4666         if (!prefs_common.immediate_exec) {
4667                 summary_set_row_marks(summaryview, row);
4668         }
4669
4670         debug_print("Message %d is set to move to %s\n",
4671                     msginfo->msgnum, to_folder->path);
4672 }
4673
4674 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4675 {
4676         GList *cur;
4677         GtkCMCTreeNode *sel_last = NULL;
4678         gboolean froze = FALSE;
4679
4680         if (!to_folder) return;
4681         if (!summaryview->folder_item ||
4682             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4683
4684         if (summary_is_locked(summaryview)) return;
4685
4686         if (summaryview->folder_item == to_folder) {
4687                 alertpanel_error(_("Destination is same as current folder."));
4688                 return;
4689         }
4690
4691         if (to_folder->no_select) {
4692                 alertpanel_error(_("The destination folder can only be used to "
4693                                    "store subfolders."));
4694                 return;
4695         }
4696
4697         START_LONG_OPERATION(summaryview, FALSE); 
4698
4699         for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4700              cur != NULL && cur->data != NULL; cur = cur->next) {
4701                 sel_last = GTK_CMCTREE_NODE(cur->data);
4702                 summary_move_row_to
4703                         (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4704         }
4705         END_LONG_OPERATION(summaryview);
4706
4707         if (prefs_common.immediate_exec) {
4708                 summary_execute(summaryview);
4709         } else {
4710                 GtkCMCTreeNode *node = NULL;
4711                 if (summaryview->sort_type == SORT_ASCENDING) {
4712                         node = summary_find_next_msg(summaryview, sel_last,TRUE);
4713                         if (!node || prefs_common.next_on_delete == FALSE)
4714                                 node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4715                 } else {
4716                         node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4717                         if (!node || prefs_common.next_on_delete == FALSE)
4718                                 node = summary_find_next_msg(summaryview, sel_last,TRUE);
4719                 }
4720                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4721                 summary_status_show(summaryview);
4722         }
4723         
4724         if (!summaryview->selected) { /* this was the last message */
4725                 GtkCMCTreeNode *node = gtk_cmctree_node_nth (GTK_CMCTREE(summaryview->ctree), 
4726                                                          GTK_CMCLIST(summaryview->ctree)->rows - 1);
4727                 if (node)
4728                         summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4729         }
4730
4731 }
4732
4733 void summary_move_to(SummaryView *summaryview)
4734 {
4735         FolderItem *to_folder;
4736
4737         if (!summaryview->folder_item ||
4738             FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4739
4740         to_folder = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE,
4741                         ngettext(
4742                                 "Select folder to move selected message to",
4743                                 "Select folder to move selected messages to",
4744                                 g_list_length(GTK_CMCLIST(summaryview->ctree)->selection))
4745         );
4746         summary_move_selected_to(summaryview, to_folder);
4747 }
4748
4749 static void summary_copy_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4750                                 FolderItem *to_folder)
4751 {
4752         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4753         MsgInfo *msginfo;
4754
4755         cm_return_if_fail(to_folder != NULL);
4756
4757         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4758         cm_return_if_fail(msginfo);
4759         procmsg_msginfo_set_to_folder(msginfo, to_folder);
4760         if (MSG_IS_DELETED(msginfo->flags))
4761                 summaryview->deleted--;
4762         if (MSG_IS_MOVE(msginfo->flags)) {
4763                 summaryview->moved--;
4764         }
4765         
4766         if (!MSG_IS_COPY(msginfo->flags)) {
4767                 summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED, 
4768                         MSG_MOVE | MSG_MOVE_DONE);
4769                 summaryview->copied++;
4770         } else {
4771                 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE);
4772         }
4773         if (!prefs_common.immediate_exec) {
4774                 summary_set_row_marks(summaryview, row);
4775         }
4776
4777         debug_print("Message %d is set to copy to %s\n",
4778                     msginfo->msgnum, to_folder->path);
4779 }
4780
4781 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4782 {
4783         GList *cur;
4784         gboolean froze = FALSE;
4785
4786         if (!to_folder) return;
4787         if (!summaryview->folder_item) return;
4788
4789         if (summary_is_locked(summaryview)) return;
4790
4791         if (summaryview->folder_item == to_folder) {
4792                 alertpanel_error
4793                         (_("Destination to copy is same as current folder."));
4794                 return;
4795         }
4796
4797         if (to_folder->no_select) {
4798                 alertpanel_error(_("The destination folder can only be used to "
4799                                    "store subfolders."));
4800                 return;
4801         }
4802
4803         START_LONG_OPERATION(summaryview, FALSE);
4804
4805         for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4806              cur != NULL && cur->data != NULL; cur = cur->next)
4807                 summary_copy_row_to
4808                         (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4809
4810         END_LONG_OPERATION(summaryview);
4811
4812         if (prefs_common.immediate_exec)
4813                 summary_execute(summaryview);
4814         else {
4815                 summary_status_show(summaryview);
4816         }
4817 }
4818
4819 void summary_copy_to(SummaryView *summaryview)
4820 {
4821         FolderItem *to_folder;
4822
4823         if (!summaryview->folder_item) return;
4824
4825         to_folder = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
4826                         ngettext(
4827                                 "Select folder to copy selected message to",
4828                                 "Select folder to copy selected messages to",
4829                                 g_list_length(GTK_CMCLIST(summaryview->ctree)->selection))
4830         );
4831         summary_copy_selected_to(summaryview, to_folder);
4832 }
4833
4834 void summary_add_address(SummaryView *summaryview)
4835 {
4836         MsgInfo *msginfo, *full_msginfo;
4837         gchar *from;
4838         GdkPixbuf *picture = NULL;
4839         AvatarRender *avatarr;
4840
4841         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4842                                               summaryview->selected);
4843         if (!msginfo || !msginfo->from) 
4844                 return;
4845
4846         Xstrdup_a(from, msginfo->from, return);
4847         eliminate_address_comment(from);
4848         extract_address(from);
4849         
4850         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
4851
4852         avatarr = avatars_avatarrender_new(full_msginfo);
4853         hooks_invoke(AVATAR_IMAGE_RENDER_HOOKLIST, avatarr);
4854
4855         procmsg_msginfo_free(&full_msginfo);
4856
4857         if (avatarr->image)
4858                 picture = gtk_image_get_pixbuf(GTK_IMAGE(avatarr->image));
4859
4860 #ifndef USE_ALT_ADDRBOOK
4861         addressbook_add_contact(msginfo->fromname, from, NULL, picture);
4862 #else
4863         if (addressadd_selection(msginfo->fromname, from, NULL, picture)) {
4864                 debug_print( "addressbook_add_contact - added\n" );
4865         }
4866 #endif
4867         avatars_avatarrender_free(avatarr);
4868 }
4869
4870 void summary_select_all(SummaryView *summaryview)
4871 {
4872         GtkCMCTreeNode *node;
4873
4874         if (!summaryview->folder_item) return;
4875
4876         if (GTK_CMCLIST(summaryview->ctree)->focus_row < 0) {
4877                 /* If no row is selected, select (but do not open) the first
4878                  * row, to get summaryview into correct state for selecting all. */
4879                 debug_print("summary_select_all: no row selected, selecting first one\n");
4880                 if (GTK_CMCLIST(summaryview->ctree)->row_list != NULL) {
4881                         node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
4882                         summary_select_node(summaryview, node, FALSE);
4883                 }
4884         }
4885
4886         /* Now select all rows while locking the summaryview for
4887          * faster performance. */
4888         summary_lock(summaryview);
4889         gtk_cmclist_select_all(GTK_CMCLIST(summaryview->ctree));
4890         summary_unlock(summaryview);
4891
4892         summary_status_show(summaryview);
4893 }
4894
4895 void summary_unselect_all(SummaryView *summaryview)
4896 {
4897         summary_lock(summaryview);
4898         gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
4899         summary_unlock(summaryview);
4900         summary_status_show(summaryview);
4901 }
4902
4903 void summary_select_thread(SummaryView *summaryview, gboolean delete_thread,
4904                            gboolean trash_thread)
4905 {
4906         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4907         GtkCMCTreeNode *node = NULL;
4908         gboolean froze = FALSE;
4909         GList *cur = NULL;
4910         GList *copy = NULL;
4911         if (!GTK_CMCLIST(summaryview->ctree)->selection) 
4912                 return;
4913
4914
4915         START_LONG_OPERATION(summaryview, FALSE);
4916         copy = g_list_copy(GTK_CMCLIST(summaryview->ctree)->selection);
4917         for (cur = copy; cur != NULL && cur->data != NULL;
4918              cur = cur->next) {
4919                 node = GTK_CMCTREE_NODE(cur->data);
4920                 if (!node)
4921                         continue;
4922                 while (GTK_CMCTREE_ROW(node)->parent != NULL)
4923                         node = GTK_CMCTREE_ROW(node)->parent;
4924
4925                 gtk_cmctree_select_recursive(ctree, node);
4926         }
4927         g_list_free(copy);
4928         END_LONG_OPERATION(summaryview);
4929
4930         if (trash_thread) {
4931                 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
4932                         summary_delete(summaryview);
4933                 else
4934                         summary_delete_trash(summaryview);
4935         } else if (delete_thread)
4936                 summary_delete(summaryview);
4937
4938         summary_status_show(summaryview);
4939 }
4940
4941 void summary_save_as(SummaryView *summaryview)
4942 {
4943         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4944         MsgInfo *msginfo;
4945         gchar *filename = NULL;
4946         gchar *src, *dest;
4947         gchar *tmp;
4948
4949         AlertValue aval = 0;
4950
4951         if (!summaryview->selected) return;
4952         msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
4953         if (!msginfo) return;
4954
4955         if (msginfo->subject) {
4956                 Xstrdup_a(filename, msginfo->subject, return);
4957                 subst_for_filename(filename);
4958         }
4959
4960         manage_window_focus_in(summaryview->window, NULL, NULL);
4961
4962         if (filename && !g_utf8_validate(filename, -1, NULL)) {
4963                 gchar *oldstr = filename;
4964                 filename = conv_codeset_strdup(filename,
4965                                                conv_get_locale_charset_str(),
4966                                                CS_UTF_8);
4967                 if (!filename) {
4968                         g_warning("summary_save_as(): failed to convert character set.");
4969                         filename = g_strdup(oldstr);
4970                 }
4971                 dest = filesel_select_file_save(_("Save as"), filename);
4972                 g_free(filename);
4973         } else
4974                 dest = filesel_select_file_save(_("Save as"), filename);
4975         filename = NULL;
4976         if (!dest) return;
4977         if (is_file_exist(dest)) {
4978                 aval = alertpanel(_("Append or Overwrite"),
4979                                   _("Append or overwrite existing file?"),
4980                                   _("_Append"), _("_Overwrite"), GTK_STOCK_CANCEL,
4981                                         ALERTFOCUS_FIRST);
4982                 if (aval != 0 && aval != 1)
4983                         return;
4984         }
4985
4986         src = procmsg_get_message_file(msginfo);
4987         tmp = g_path_get_basename(dest);
4988
4989         if ( aval==0 ) { /* append */
4990                 if (append_file(src, dest, TRUE) < 0) 
4991                         alertpanel_error(_("Couldn't save the file '%s'."), tmp);
4992         } else { /* overwrite */
4993                 if (copy_file(src, dest, TRUE) < 0)
4994                         alertpanel_error(_("Couldn't save the file '%s'."), tmp);
4995         }
4996         g_free(src);
4997         
4998         /*
4999          * If two or more msgs are selected,
5000          * append them to the output file.
5001          */
5002         if (GTK_CMCLIST(ctree)->selection->next) {
5003                 GList *item;
5004                 for (item = GTK_CMCLIST(ctree)->selection->next; item != NULL; item=item->next) {
5005                         msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(item->data));
5006                         if (!msginfo) break;
5007                         src = procmsg_get_message_file(msginfo);
5008                         if (append_file(src, dest, TRUE) < 0)
5009                                 alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5010                 }
5011                 g_free(src);
5012         }
5013         g_free(dest);
5014         g_free(tmp);
5015 }
5016
5017 void summary_print(SummaryView *summaryview)
5018 {
5019         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5020         GList *cur;
5021         gchar *msg = g_strdup_printf(_("You are about to print %d "
5022                                        "messages, one by one. Do you "
5023                                        "want to continue?"), 
5024                                        g_list_length(clist->selection));
5025         if (g_list_length(clist->selection) > 9
5026         &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_YES,
5027                 NULL, ALERTFOCUS_SECOND) != G_ALERTALTERNATE) {
5028                 g_free(msg);
5029                 return;
5030         }
5031         g_free(msg);
5032
5033         if (clist->selection == NULL) return;
5034         for (cur = clist->selection; 
5035              cur != NULL && cur->data != NULL; 
5036              cur = cur->next) {
5037                 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(cur->data);
5038                 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(
5039                                         GTK_CMCTREE(summaryview->ctree),
5040                                         node);
5041                 gint sel_start = -1, sel_end = -1, partnum = 0;
5042                 
5043                 if (node == summaryview->displayed) {
5044                         partnum = mimeview_get_selected_part_num(summaryview->messageview->mimeview);
5045                         textview_get_selection_offsets(summaryview->messageview->mimeview->textview,
5046                                 &sel_start, &sel_end);
5047                 } 
5048                 messageview_print(msginfo, summaryview->messageview->all_headers,
5049                         sel_start, sel_end, partnum);
5050         }
5051 }
5052
5053 gboolean summary_execute(SummaryView *summaryview)
5054 {
5055         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5056         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5057         GtkCMCTreeNode *node, *next;
5058         GtkCMCTreeNode *new_selected = NULL;
5059         gint move_val = -1;
5060
5061         if (!summaryview->folder_item) return FALSE;
5062
5063         if (summary_is_locked(summaryview)) return FALSE;
5064         summary_lock(summaryview);
5065
5066         summary_freeze(summaryview);
5067
5068         main_window_cursor_wait(summaryview->mainwin);
5069
5070         if (summaryview->threaded)
5071                 summary_unthread_for_exec(summaryview);
5072
5073         folder_item_update_freeze();
5074         move_val = summary_execute_move(summaryview);
5075         summary_execute_copy(summaryview);
5076         summary_execute_delete(summaryview);
5077         
5078         node = GTK_CMCTREE_NODE(clist->row_list);
5079         for (; node != NULL; node = next) {
5080                 next = gtkut_ctree_node_next(ctree, node);
5081                 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
5082
5083                 if (node == summaryview->displayed) {
5084                         messageview_clear(summaryview->messageview);
5085                         summary_cancel_mark_read_timeout(summaryview);
5086                         summaryview->displayed = NULL;
5087                 }
5088                 if (GTK_CMCTREE_ROW(node)->children != NULL) {
5089                         next = NULL;
5090                         if (GTK_CMCTREE_ROW(node)->sibling) {
5091                                 next = GTK_CMCTREE_ROW(node)->sibling;
5092                         } else {
5093                                 GtkCMCTreeNode *parent = NULL;
5094                                 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
5095                                      parent = GTK_CMCTREE_ROW(parent)->parent) {
5096                                         if (GTK_CMCTREE_ROW(parent)->sibling) {
5097                                                 next = GTK_CMCTREE_ROW(parent)->sibling;
5098                                         }
5099                                 }
5100                         }
5101                 }
5102
5103                 if (!new_selected &&
5104                     gtkut_ctree_node_is_selected(ctree, node)) {
5105                         summary_unselect_all(summaryview);
5106                         if (summaryview->sort_type == SORT_ASCENDING) {
5107                                 new_selected = summary_find_next_msg(summaryview, node,TRUE);
5108                                 if (!new_selected || prefs_common.next_on_delete == FALSE)
5109                                         new_selected = summary_find_prev_msg(summaryview, node,TRUE);
5110                         } else {
5111                                 new_selected = summary_find_prev_msg(summaryview, node,TRUE);
5112                                 if (!new_selected || prefs_common.next_on_delete == FALSE)
5113                                         new_selected = summary_find_next_msg(summaryview, node,TRUE);
5114                         }
5115                 }
5116
5117                 gtk_sctree_remove_node((GtkSCTree *)ctree, node);
5118         }
5119
5120         folder_item_update_thaw();
5121
5122         if (new_selected) {
5123                 summary_unlock(summaryview);
5124                 summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
5125                 summary_lock(summaryview);
5126         }
5127
5128         if (summaryview->threaded) {
5129                 summary_thread_build(summaryview);
5130                 summary_thread_init(summaryview);
5131         }
5132
5133         summary_thaw(summaryview);
5134
5135         summaryview->selected = clist->selection ?
5136                 GTK_CMCTREE_NODE(clist->selection->data) : NULL;
5137
5138         if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
5139                 menu_set_insensitive_all
5140                         (GTK_MENU_SHELL(summaryview->popupmenu));
5141                 folderview_grab_focus(summaryview->folderview);
5142         } else {
5143                 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
5144                 gtk_widget_grab_focus(summaryview->ctree);
5145         }
5146         summary_update_status(summaryview);
5147         summary_status_show(summaryview);
5148
5149         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5150
5151         summary_unlock(summaryview);
5152
5153         main_window_cursor_normal(summaryview->mainwin);
5154
5155         if (move_val < 0) 
5156                 summary_show(summaryview, summaryview->folder_item);
5157         return TRUE;
5158 }
5159
5160 gboolean summary_expunge(SummaryView *summaryview)
5161 {
5162         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5163         GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5164         GtkCMCTreeNode *node, *next;
5165         GtkCMCTreeNode *new_selected = NULL;
5166
5167         if (!summaryview->folder_item) return FALSE;
5168
5169         if (summary_is_locked(summaryview)) return FALSE;
5170
5171         summary_lock(summaryview);
5172
5173         summary_freeze(summaryview);
5174
5175         main_window_cursor_wait(summaryview->mainwin);
5176
5177         if (summaryview->threaded)
5178                 summary_unthread_for_exec(summaryview);
5179
5180         folder_item_update_freeze();
5181         summary_execute_expunge(summaryview);
5182         
5183         node = GTK_CMCTREE_NODE(clist->row_list);
5184         for (; node != NULL; node = next) {
5185                 next = gtkut_ctree_node_next(ctree, node);
5186                 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
5187
5188                 if (node == summaryview->displayed) {
5189                         messageview_clear(summaryview->messageview);
5190                         summary_cancel_mark_read_timeout(summaryview);
5191                         summaryview->displayed = NULL;
5192                 }
5193                 if (GTK_CMCTREE_ROW(node)->children != NULL) {
5194                         next = NULL;
5195                         if (GTK_CMCTREE_ROW(node)->sibling) {
5196                                 next = GTK_CMCTREE_ROW(node)->sibling;
5197                         } else {
5198                                 GtkCMCTreeNode *parent = NULL;
5199                                 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
5200                                      parent = GTK_CMCTREE_ROW(parent)->parent) {
5201                                         if (GTK_CMCTREE_ROW(parent)->sibling) {
5202                                                 next = GTK_CMCTREE_ROW(parent)->sibling;
5203                                         }
5204                                 }
5205                         }
5206                 }
5207
5208                 if (!new_selected &&
5209                     gtkut_ctree_node_is_selected(ctree, node)) {
5210                         summary_unselect_all(summaryview);
5211                         new_selected = summary_find_next_msg(summaryview, node,TRUE);
5212                         if (!new_selected)
5213                                 new_selected = summary_find_prev_msg
5214                                         (summaryview, node,TRUE);
5215                 }
5216
5217                 gtk_sctree_remove_node((GtkSCTree *)ctree, node);
5218         }
5219
5220         folder_item_update_thaw();
5221
5222         if (new_selected) {
5223                 summary_unlock(summaryview);
5224                 summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
5225                 summary_lock(summaryview);
5226         }
5227
5228         if (summaryview->threaded) {
5229                 summary_thread_build(summaryview);
5230                 summary_thread_init(summaryview);
5231         }
5232
5233         summary_thaw(summaryview);
5234
5235         summaryview->selected = clist->selection ?
5236                 GTK_CMCTREE_NODE(clist->selection->data) : NULL;
5237
5238         if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
5239                 menu_set_insensitive_all
5240                         (GTK_MENU_SHELL(summaryview->popupmenu));
5241                 folderview_grab_focus(summaryview->folderview);
5242         } else {
5243                 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
5244                 gtk_widget_grab_focus(summaryview->ctree);
5245         }
5246
5247         summary_update_status(summaryview);
5248         summary_status_show(summaryview);
5249
5250         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5251
5252         summary_unlock(summaryview);
5253
5254         main_window_cursor_normal(summaryview->mainwin);
5255
5256         return TRUE;
5257 }
5258
5259 static gint summary_execute_move(SummaryView *summaryview)
5260 {
5261         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5262         GSList *cur;
5263         gint val = -1;
5264         /* search moving messages and execute */
5265         
5266         gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_move_func,
5267                         summaryview);
5268
5269         if (summaryview->mlist) {
5270                 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5271                         summaryview->msginfo_update_callback_id);
5272                 val = procmsg_move_messages(summaryview->mlist);
5273                 summaryview->msginfo_update_callback_id =
5274                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 
5275                         summary_update_msg, (gpointer) summaryview);
5276
5277                 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
5278                         procmsg_msginfo_free((MsgInfo **)&(cur->data));
5279                 }
5280
5281                 g_slist_free(summaryview->mlist);
5282                 summaryview->mlist = NULL;
5283                 return val;
5284         }
5285         return 0;
5286 }
5287
5288 static void summary_execute_move_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5289                                       gpointer data)
5290 {
5291         SummaryView *summaryview = data;
5292         MsgInfo *msginfo;
5293
5294         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5295
5296         if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
5297                 summaryview->mlist =
5298                         g_slist_prepend(summaryview->mlist, msginfo);
5299                 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5300
5301                 if (msginfo->msgid && *msginfo->msgid &&
5302                     node == g_hash_table_lookup(summaryview->msgid_table,
5303                                                 msginfo->msgid))
5304                         g_hash_table_remove(summaryview->msgid_table,
5305                                             msginfo->msgid);
5306                 if (prefs_common.thread_by_subject &&
5307                     msginfo->subject && *msginfo->subject &&
5308                     node == subject_table_lookup(summaryview->subject_table,
5309                                                  msginfo->subject)) {
5310                         subject_table_remove(summaryview->subject_table,
5311                                              msginfo->subject);
5312                 }
5313         }
5314 }
5315
5316 static void summary_execute_copy(SummaryView *summaryview)
5317 {
5318         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5319
5320         /* search copying messages and execute */
5321         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5322                 summaryview->msginfo_update_callback_id);
5323         gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
5324                                 summaryview);
5325
5326         if (summaryview->mlist) {
5327                 summaryview->mlist = g_slist_reverse(summaryview->mlist);
5328                 procmsg_copy_messages(summaryview->mlist);
5329
5330                 g_slist_free(summaryview->mlist);
5331                 summaryview->mlist = NULL;
5332         }
5333         summaryview->msginfo_update_callback_id =
5334                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5335                         summary_update_msg, (gpointer) summaryview);
5336 }
5337
5338 static void summary_execute_copy_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5339                                       gpointer data)
5340 {
5341         SummaryView *summaryview = data;
5342         MsgInfo *msginfo;
5343
5344         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5345
5346         if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
5347                 summaryview->mlist =
5348                         g_slist_prepend(summaryview->mlist, msginfo);
5349
5350                 summary_msginfo_unset_flags(msginfo, 0, MSG_COPY);
5351                 summary_set_row_marks(summaryview, node);
5352         }
5353 }
5354
5355 static void summary_execute_delete(SummaryView *summaryview)
5356 {
5357         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5358         GSList *cur;
5359
5360         /* search deleting messages and execute */
5361         gtk_cmctree_pre_recursive
5362                 (ctree, NULL, summary_execute_delete_func, summaryview);
5363
5364         if (!summaryview->mlist) return;
5365
5366         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5367                 summaryview->msginfo_update_callback_id);
5368
5369         folder_item_remove_msgs(summaryview->folder_item,
5370                                 summaryview->mlist);
5371
5372         summaryview->msginfo_update_callback_id =
5373                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 
5374                         summary_update_msg, (gpointer) summaryview);
5375
5376         for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
5377                         procmsg_msginfo_free((MsgInfo **)&(cur->data));
5378         }
5379
5380         g_slist_free(summaryview->mlist);
5381         summaryview->mlist = NULL;
5382 }
5383
5384 static void summary_execute_delete_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5385                                         gpointer data)
5386 {
5387         SummaryView *summaryview = data;
5388         MsgInfo *msginfo;
5389
5390         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5391
5392         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5393                 summaryview->mlist =
5394                         g_slist_prepend(summaryview->mlist, msginfo);
5395                 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5396
5397                 if (msginfo->msgid && *msginfo->msgid &&
5398                     node == g_hash_table_lookup(summaryview->msgid_table,
5399                                                 msginfo->msgid)) {
5400                         g_hash_table_remove(summaryview->msgid_table,
5401                                             msginfo->msgid);
5402                 }       
5403                 if (prefs_common.thread_by_subject &&
5404                     msginfo->subject && *msginfo->subject && 
5405                     node == subject_table_lookup(summaryview->subject_table,
5406                                                  msginfo->subject)) {
5407                         subject_table_remove(summaryview->subject_table,
5408                                              msginfo->subject);
5409                 }                                           
5410         }
5411 }
5412
5413 static void summary_execute_expunge_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5414                                         gpointer data)
5415 {
5416         SummaryView *summaryview = data;
5417         MsgInfo *msginfo;
5418
5419         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5420
5421         if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5422                 summaryview->mlist =
5423                         g_slist_prepend(summaryview->mlist, msginfo);
5424                 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5425
5426                 if (msginfo->msgid && *msginfo->msgid &&
5427                     node == g_hash_table_lookup(summaryview->msgid_table,
5428                                                 msginfo->msgid)) {
5429                         g_hash_table_remove(summaryview->msgid_table,
5430                                             msginfo->msgid);
5431                 }       
5432                 if (prefs_common.thread_by_subject &&
5433                     msginfo->subject && *msginfo->subject && 
5434                     node == subject_table_lookup(summaryview->subject_table,
5435                                                  msginfo->subject)) {
5436                         subject_table_remove(summaryview->subject_table,
5437                                              msginfo->subject);
5438                 }                                           
5439         }
5440 }
5441
5442 static void summary_execute_expunge(SummaryView *summaryview)
5443 {
5444         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5445         GSList *cur;
5446
5447         gtk_cmctree_pre_recursive
5448                 (ctree, NULL, summary_execute_expunge_func, summaryview);
5449
5450         hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5451                 summaryview->msginfo_update_callback_id);
5452
5453         folder_item_expunge(summaryview->folder_item);
5454
5455         summaryview->msginfo_update_callback_id =
5456                 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 
5457                         summary_update_msg, (gpointer) summaryview);
5458         for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
5459                 procmsg_msginfo_free((MsgInfo **)&(cur->data));
5460
5461         g_slist_free(summaryview->mlist);
5462         summaryview->mlist = NULL;
5463 }
5464
5465 /* thread functions */
5466
5467 static void summary_thread_build(SummaryView *summaryview)
5468 {
5469         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5470         GtkCMCTreeNode *node;
5471         GtkCMCTreeNode *next;
5472         GtkCMCTreeNode *parent;
5473         MsgInfo *msginfo;
5474         GSList *reflist;
5475
5476         summary_lock(summaryview);
5477
5478         debug_print("Building threads...\n");
5479         STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
5480         main_window_cursor_wait(summaryview->mainwin);
5481
5482         g_signal_handlers_block_by_func(G_OBJECT(ctree),
5483                                        G_CALLBACK(summary_tree_expanded), summaryview);
5484         summary_freeze(summaryview);
5485
5486         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5487         while (node) {
5488                 next = GTK_CMCTREE_ROW(node)->sibling;
5489
5490                 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5491
5492                 parent = NULL;
5493
5494                 if (msginfo && msginfo->inreplyto) {
5495                         parent = g_hash_table_lookup(summaryview->msgid_table,
5496                                                      msginfo->inreplyto);
5497                                                      
5498                         if (!parent && msginfo->references) {
5499                                 for (reflist = msginfo->references;
5500                                      reflist != NULL; reflist = reflist->next)
5501                                         if ((parent = g_hash_table_lookup
5502                                                 (summaryview->msgid_table,
5503                                                  reflist->data)))
5504                                                 break;
5505                         }
5506                 }
5507
5508                 if (msginfo && prefs_common.thread_by_subject && parent == NULL) {
5509                         parent = subject_table_lookup
5510                                 (summaryview->subject_table,
5511                                  msginfo->subject);
5512                 }
5513
5514                 if (parent && parent != node && parent != GTK_CMCTREE_ROW(node)->parent) {
5515                         gtk_cmctree_move(ctree, node, parent, NULL);
5516                 }
5517
5518                 node = next;
5519         }
5520
5521         gtkut_ctree_set_focus_row(ctree, summaryview->selected);
5522
5523         summary_thaw(summaryview);
5524         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5525                                          G_CALLBACK(summary_tree_expanded), summaryview);
5526
5527         debug_print("Building threads done.\n");
5528         STATUSBAR_POP(summaryview->mainwin);
5529         main_window_cursor_normal(summaryview->mainwin);
5530
5531         summaryview->threaded = TRUE;
5532
5533         summary_unlock(summaryview);
5534 }
5535
5536 static void summary_thread_init(SummaryView *summaryview)
5537 {
5538         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5539         GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5540         GtkCMCTreeNode *next;
5541         START_TIMING("");
5542         if (!summaryview->thread_collapsed) {
5543                 g_signal_handlers_block_by_func(G_OBJECT(ctree),
5544                                        G_CALLBACK(summary_tree_expanded), summaryview);
5545                 while (node) {
5546                         next = GTK_CMCTREE_ROW(node)->sibling;
5547                         if (GTK_CMCTREE_ROW(node)->children)
5548                                 gtk_cmctree_expand_recursive(ctree, node);
5549                         node = next;
5550                 }
5551                 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5552                                        G_CALLBACK(summary_tree_expanded), summaryview);
5553         } 
5554         END_TIMING();
5555 }
5556
5557 static void summary_unthread_for_exec(SummaryView *summaryview)
5558 {
5559         GtkCMCTreeNode *node;
5560         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5561         gboolean froze = FALSE;
5562
5563         debug_print("Unthreading for execution...\n");
5564
5565         START_LONG_OPERATION(summaryview, TRUE);
5566         for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5567              node != NULL; node = GTK_CMCTREE_NODE_NEXT(node)) {
5568                 summary_unthread_for_exec_func(ctree, node, summaryview);
5569         }
5570
5571         END_LONG_OPERATION(summaryview);
5572
5573         debug_print("Unthreading for execution done.\n");
5574 }
5575
5576 static void summary_unthread_for_exec_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5577                                            gpointer data)
5578 {
5579         MsgInfo *msginfo;
5580         GtkCMCTreeNode *top_parent;
5581         GtkCMCTreeNode *child;
5582         GtkCMCTreeNode *sibling;
5583         SummaryView * summaryview = (SummaryView *)data;
5584         msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5585
5586         if (!msginfo ||
5587             (!MSG_IS_MOVE(msginfo->flags) &&
5588              !MSG_IS_DELETED(msginfo->flags)))
5589                 return;
5590         child = GTK_CMCTREE_ROW(node)->children;
5591         if (!child) return;
5592
5593         if (node == summaryview->selected)
5594                 summaryview->selected = NULL;
5595         if (node == summaryview->displayed)
5596                 summaryview->displayed = NULL;
5597
5598         summary_cancel_mark_read_timeout(summaryview);
5599         
5600         for (top_parent = node;
5601              GTK_CMCTREE_ROW(top_parent)->parent != NULL;
5602              top_parent = GTK_CMCTREE_ROW(top_parent)->parent)
5603                 ;
5604         sibling = GTK_CMCTREE_ROW(top_parent)->sibling;
5605
5606         GTK_SCTREE(ctree)->sorting = TRUE;
5607         while (child != NULL) {
5608                 GtkCMCTreeNode *next_child;
5609                 MsgInfo *cinfo = GTKUT_CTREE_NODE_GET_ROW_DATA(child);
5610                 
5611                 next_child = GTK_CMCTREE_ROW(child)->sibling;
5612                 
5613                 if (!MSG_IS_MOVE(cinfo->flags) && !MSG_IS_DELETED(cinfo->flags)) {
5614                         gtk_cmctree_move(ctree, child, 
5615                                 NULL, 
5616                                 sibling); 
5617                 } else {
5618                         if (child == summaryview->displayed) {
5619                                 messageview_clear(summaryview->messageview);
5620                                 summaryview->displayed = NULL;
5621                         }
5622                         if (child == summaryview->selected) {
5623                                 messageview_clear(summaryview->messageview);
5624                                 summaryview->selected = NULL;
5625                         }
5626                 }
5627                 child = next_child;
5628         }
5629         GTK_SCTREE(ctree)->sorting = FALSE;
5630 }
5631
5632 void summary_expand_threads(SummaryView *summaryview)
5633 {
5634         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5635         GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5636         GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
5637
5638         g_signal_handlers_block_by_func(G_OBJECT(ctree),
5639                                        G_CALLBACK(summary_tree_expanded), summaryview);
5640         summary_freeze(summaryview);
5641         GTK_SCTREE(ctree)->sorting = TRUE;
5642
5643         while (node) {
5644                 if (GTK_CMCTREE_ROW(node)->children) {
5645                         gtk_cmctree_expand(ctree, node);
5646                         summary_set_row_marks(summaryview, node);
5647                 }
5648                 node = GTK_CMCTREE_NODE_NEXT(node);
5649         }
5650
5651         GTK_SCTREE(ctree)->sorting = FALSE;
5652         if (focus_node) {
5653                 GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
5654         }
5655         summary_thaw(summaryview);
5656
5657         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5658                                          G_CALLBACK(summary_tree_expanded), summaryview);
5659
5660         summaryview->thread_collapsed = FALSE;
5661
5662         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5663 }
5664
5665 void summary_collapse_threads(SummaryView *summaryview)
5666 {
5667         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5668         GtkCMCTreeNode *node = NULL;
5669         GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
5670
5671         g_signal_handlers_block_by_func(G_OBJECT(ctree),
5672                                        G_CALLBACK(summary_tree_collapsed), summaryview);
5673         summary_freeze(summaryview);
5674         GTK_SCTREE(ctree)->sorting = TRUE;
5675
5676         node = focus_node;
5677         while (node && GTK_CMCTREE_ROW(node)->parent) {
5678                 focus_node = node = GTK_CMCTREE_ROW(node)->parent;
5679         }
5680         summary_select_node(summaryview, node, OPEN_SELECTED_ON_DIRECTIONAL);
5681         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5682         while (node) {
5683                 if (GTK_CMCTREE_ROW(node)->children) {
5684                         gtk_cmctree_collapse(ctree, node);
5685                         summary_set_row_marks(summaryview, node);
5686                 }
5687                 node = GTK_CMCTREE_ROW(node)->sibling;
5688         }
5689
5690         GTK_SCTREE(ctree)->sorting = FALSE;
5691         if (focus_node) {
5692                 GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
5693         }
5694         GTK_SCTREE(ctree)->anchor_row =
5695                         gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row);
5696         summary_thaw(summaryview);
5697         g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5698                                          G_CALLBACK(summary_tree_collapsed), summaryview);
5699         
5700         summaryview->thread_collapsed = TRUE;
5701
5702         gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5703 }
5704
5705 static void account_rules_radio_button_toggled_cb(GtkToggleButton *btn, gpointer data)
5706 {
5707         prefs_common.apply_per_account_filtering_rules = GPOINTER_TO_INT(data);
5708 }
5709
5710 static gboolean summary_filter_get_mode(void)
5711 /* ask what to do w/ them: skip them, apply them regardless to the account,
5712    use the current account */
5713 {
5714         /* TODO: eventually also propose to use the current folder's default account,
5715            if it is set */
5716         /* TODO: eventually allow to select the account to use from a optmenu */
5717
5718         GtkWidget *vbox;
5719         GtkWidget *account_rules_skip;
5720         GtkWidget *account_rules_force;
5721         GtkWidget *account_rules_user_current;
5722         AlertValue val;
5723
5724         vbox = gtk_vbox_new (FALSE, 0);
5725
5726         account_rules_skip = gtk_radio_button_new_with_label
5727                                                         (NULL, _("Skip these rules"));
5728         account_rules_force = gtk_radio_button_new_with_label_from_widget
5729                                                         (GTK_RADIO_BUTTON(account_rules_skip),
5730                                                         _("Apply these rules regardless of the account they belong to"));
5731         account_rules_user_current = gtk_radio_button_new_with_label_from_widget
5732                                                         (GTK_RADIO_BUTTON(account_rules_skip),
5733                                                         _("Apply these rules if they apply to the current account"));
5734         gtk_box_pack_start (GTK_BOX (vbox), account_rules_skip, FALSE, FALSE, 0);
5735         gtk_box_pack_start (GTK_BOX (vbox), account_rules_force, FALSE, FALSE, 0);
5736         gtk_box_pack_start (GTK_BOX (vbox), account_rules_user_current, FALSE, FALSE, 0);
5737         g_signal_connect(G_OBJECT(account_rules_skip), "toggled",
5738                         G_CALLBACK(account_rules_radio_button_toggled_cb),
5739                         GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_SKIP));
5740         g_signal_connect(G_OBJECT(account_rules_force), "toggled",
5741                         G_CALLBACK(account_rules_radio_button_toggled_cb),
5742                         GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_FORCE));
5743         g_signal_connect(G_OBJECT(account_rules_user_current), "toggled",
5744                         G_CALLBACK(account_rules_radio_button_toggled_cb),
5745                         GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_USE_CURRENT));
5746         switch (prefs_common.apply_per_account_filtering_rules) {
5747         case FILTERING_ACCOUNT_RULES_SKIP:
5748                 gtk_toggle_button_set_active(
5749                                 GTK_TOGGLE_BUTTON(account_rules_skip), TRUE);
5750                 break;
5751         case FILTERING_ACCOUNT_RULES_FORCE:
5752                 gtk_toggle_button_set_active(
5753                                 GTK_TOGGLE_BUTTON(account_rules_force), TRUE);
5754                 break;
5755         case FILTERING_ACCOUNT_RULES_USE_CURRENT:
5756                 gtk_toggle_button_set_active(
5757                                 GTK_TOGGLE_BUTTON(account_rules_user_current), TRUE);
5758                 break;
5759         }
5760
5761         val = alertpanel_with_widget(
5762                         _("Filtering"),
5763                         _("There are some filtering rules that belong to an account.\n"
5764                           "Please choose what to do with these rules:"),
5765                         GTK_STOCK_CANCEL, _("_Filter"), NULL, ALERTFOCUS_SECOND,
5766                         TRUE, vbox);
5767
5768         if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) {
5769                 return FALSE;
5770         } else if (val & G_ALERTDISABLE)
5771                 prefs_common.ask_apply_per_account_filtering_rules = FALSE;
5772
5773         return TRUE;
5774 }
5775
5776 void summary_filter(SummaryView *summaryview, gboolean selected_only)
5777 {
5778         GSList *mlist = NULL, *cur_list;
5779         summary_lock(summaryview);
5780
5781         /* are there any per-account filtering rules? */
5782         if (prefs_common.ask_apply_per_account_filtering_rules == TRUE &&
5783                 filtering_peek_per_account_rules(filtering_rules)) {
5784
5785                 if (summary_filter_get_mode() == FALSE) {
5786                         summary_unlock(summaryview);
5787                         return;
5788                 }
5789         }
5790
5791         folder_item_update_freeze();
5792         
5793         debug_print("filtering...\n");
5794         STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
5795         main_window_cursor_wait(summaryview->mainwin);
5796
5797         summary_freeze(summaryview);
5798
5799         if (selected_only) {
5800                 GList *cur;
5801
5802                 for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
5803                      cur != NULL && cur->data != NULL; cur = cur->next) {
5804                         mlist = g_slist_prepend(mlist, 
5805                                  procmsg_msginfo_new_ref(
5806                                   GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data)));
5807                 }
5808                 mlist = g_slist_reverse(mlist);
5809         } else {
5810                 mlist = folder_item_get_msg_list(summaryview->folder_item);
5811         }
5812         
5813         folder_item_set_batch(summaryview->folder_item, TRUE);
5814         for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5815                 summary_filter_func((MsgInfo *)cur_list->data);
5816         }
5817         folder_item_set_batch(summaryview->folder_item, FALSE);
5818         
5819         filtering_move_and_copy_msgs(mlist);
5820         
5821         for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5822                 procmsg_msginfo_free((MsgInfo **)&(cur_list->data));
5823         }
5824         g_slist_free(mlist);
5825
5826         summary_thaw(summaryview);
5827
5828         folder_item_update_thaw();
5829         debug_print("filtering done.\n");
5830         STATUSBAR_POP(summaryview->mainwin);
5831         main_window_cursor_normal(summaryview->mainwin);
5832
5833         summary_unlock(summaryview);
5834
5835         /* 
5836          * CLAWS: summary_show() only valid after having a lock. ideally
5837          * we want the lock to be context aware...  
5838          */
5839         summary_show(summaryview, summaryview->folder_item);
5840 }
5841
5842 static void summary_filter_func(MsgInfo *msginfo)
5843 {
5844         MailFilteringData mail_filtering_data;
5845
5846         mail_filtering_data.msginfo = msginfo;
5847         mail_filtering_data.msglist = NULL;                     
5848         mail_filtering_data.filtered = NULL;                    
5849         mail_filtering_data.unfiltered = NULL;                  
5850         if (hooks_invoke(MAIL_MANUAL_FILTERING_HOOKLIST, &mail_filtering_data))
5851                 return;
5852
5853         filter_message_by_msginfo(filtering_rules, msginfo, NULL,
5854                         FILTERING_MANUALLY, NULL);
5855 }
5856
5857 void summary_msginfo_filter_open(FolderItem * item, MsgInfo *msginfo,
5858                                  PrefsFilterType type, gint processing_rule)
5859 {
5860         gchar *header = NULL;
5861         gchar *key = NULL;
5862
5863         procmsg_get_filter_keyword(msginfo, &header, &key, type);
5864         
5865         if (processing_rule) {
5866                 if (item == NULL)
5867                         prefs_filtering_open(&pre_global_processing,
5868                                              _("Processing rules to apply before folder rules"),
5869                                              MANUAL_ANCHOR_PROCESSING,
5870                                              header, key, FALSE);
5871                 else
5872                         prefs_filtering_open(&item->prefs->processing,
5873                                              _("Processing configuration"),
5874                                              MANUAL_ANCHOR_PROCESSING,
5875                                              header, key, FALSE);
5876         }
5877         else {
5878                 prefs_filtering_open(&filtering_rules,
5879                                 _("Filtering configuration"),
5880                                 MANUAL_ANCHOR_FILTERING,
5881                                 header, key, TRUE);
5882         }
5883         
5884         g_free(header);
5885         g_free(key);
5886 }
5887
5888 void summary_filter_open(SummaryView *summaryview, PrefsFilterType type,
5889                          gint processing_rule)
5890 {
5891         MsgInfo *msginfo;
5892         FolderItem * item;
5893         
5894         if (!summaryview->selected) return;
5895
5896         msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
5897                                               summaryview->selected);
5898         if (!msginfo) return;
5899         
5900         item = summaryview->folder_item;
5901         summary_msginfo_filter_open(item, msginfo, type, processing_rule);
5902 }
5903
5904 /* color label */
5905
5906 #define N_COLOR_LABELS colorlabel_get_color_count()
5907
5908 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
5909                                                      gpointer data)
5910 {
5911         guint color = GPOINTER_TO_UINT(data);
5912         SummaryView *summaryview;
5913
5914         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5915         cm_return_if_fail(summaryview != NULL);
5916
5917         /* "dont_toggle" state set? */
5918         if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
5919                                 "dont_toggle"))
5920                 return;
5921
5922         summary_set_colorlabel(summaryview, color, NULL);
5923 }
5924
5925 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
5926  * for the message; not the color index */
5927 void summary_set_colorlabel_color(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5928                                   guint labelcolor)
5929 {
5930         GdkColor color;
5931         GtkStyle *style, *prev_style, *ctree_style;
5932         MsgInfo *msginfo;
5933         gint color_index;
5934
5935         msginfo = gtk_cmctree_node_get_row_data(ctree, node);
5936         cm_return_if_fail(msginfo);
5937
5938         color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
5939         ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
5940         prev_style = gtk_cmctree_node_get_row_style(ctree, node);
5941
5942         if (color_index < 0 || color_index >= N_COLOR_LABELS) {
5943                 if (!prev_style) return;
5944                 style = gtk_style_copy(prev_style);
5945                 color = ctree_style->text[GTK_STATE_NORMAL];
5946                 style->text[GTK_STATE_NORMAL] = color;
5947                 color = ctree_style->text[GTK_STATE_SELECTED];
5948                 style->text[GTK_STATE_SELECTED] = color;
5949         } else {
5950                 if (prev_style)
5951                         style = gtk_style_copy(prev_style);
5952                 else
5953                         style = gtk_style_copy(ctree_style);
5954                 color = colorlabel_get_color(color_index);
5955                 style->text[GTK_STATE_NORMAL] = color;
5956                 /* get the average of label color and selected fg color
5957                    for visibility */
5958                 style->text[GTK_STATE_SELECTED].red   = (color.red   + 3*ctree_style->text[GTK_STATE_SELECTED].red  ) / 4;
5959                 style->text[GTK_STATE_SELECTED].green = (color.green + 3*ctree_style->text[GTK_STATE_SELECTED].green) / 4;
5960                 style->text[GTK_STATE_SELECTED].blue  = (color.blue  + 3*ctree_style->text[GTK_STATE_SELECTED].blue ) / 4;
5961         }
5962
5963         gtk_cmctree_node_set_row_style(ctree, node, style);
5964         g_object_unref(style);
5965 }
5966
5967 static void summary_set_row_colorlabel(SummaryView *summaryview, GtkCMCTreeNode *row, guint labelcolor)
5968 {
5969         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5970         MsgInfo *msginfo;
5971
5972         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
5973         cm_return_if_fail(msginfo);
5974
5975         summary_msginfo_change_flags(msginfo, MSG_COLORLABEL_TO_FLAGS(labelcolor), 0, 
5976                                         MSG_CLABEL_FLAG_MASK, 0);
5977         summary_set_row_marks(summaryview, row);
5978 }
5979
5980 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
5981                             GtkWidget *widget)
5982 {
5983         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5984         GList *cur;
5985         gboolean froze = FALSE;
5986
5987         if (prefs_common.ask_override_colorlabel) {
5988                 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5989                 gboolean ask = FALSE;
5990                 AlertValue val;
5991                 guint color;
5992                 gboolean already_this_color_everywhere = TRUE;
5993
5994                 /* if clearing color labels (applying 'none', 0):
5995                     - ask if at least one message has a non-0 color label set
5996                    if applying a non-0 color label:
5997                     - ask if at least one of the selected messages has a non-0 color label different
5998                           from the one we want to apply.
5999                     - don't ask if all messages have the same color label as the one we're applying
6000                 */
6001                 for (cur = GTK_CMCLIST(ctree)->selection;
6002                          !ask && cur != NULL && cur->data != NULL;
6003                          cur = cur->next) {
6004                         MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(cur->data));
6005                         if (msginfo) {
6006                                 color = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
6007                                 if (labelcolor == 0) {
6008                                         /* clearing color labels */
6009                                         ask = (color != 0);
6010                                 } else {
6011                                         already_this_color_everywhere &= (color == labelcolor);
6012                                         ask = ((color != 0) && (color != labelcolor)) && !already_this_color_everywhere;
6013                                 }
6014                         }
6015                 }
6016
6017                 if (ask) {
6018                         gchar *msg;
6019
6020                         if (labelcolor == 0)
6021                                 msg = _("Do you really want to reset the color label of all selected messages?");
6022                         else
6023                                 msg = _("Do you really want to apply this color label to all selected messages?");
6024                         val = alertpanel_full(_("Set color label"), msg,
6025                                   GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
6026                                   TRUE, NULL, ALERT_QUESTION);
6027
6028                         if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
6029                                 return;
6030                         else if (val & G_ALERTDISABLE)
6031                                 prefs_common.ask_override_colorlabel = FALSE;
6032                 }
6033         }
6034
6035         START_LONG_OPERATION(summaryview, FALSE);
6036         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
6037                 summary_set_row_colorlabel(summaryview,
6038                                            GTK_CMCTREE_NODE(cur->data), labelcolor);
6039         END_LONG_OPERATION(summaryview);
6040 }
6041
6042 static gboolean summary_set_row_tag(SummaryView *summaryview, GtkCMCTreeNode *row, gboolean refresh, gboolean set, gint id)
6043 {
6044         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6045         MsgInfo *msginfo;
6046         gchar *tags_str = NULL;
6047         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
6048         cm_return_val_if_fail(msginfo, FALSE);
6049
6050         procmsg_msginfo_update_tags(msginfo, set, id);
6051         
6052         if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
6053                 tags_str = procmsg_msginfo_get_tags_str(msginfo);
6054                 gtk_cmctree_node_set_text(ctree, row, 
6055                                 summaryview->col_pos[S_COL_TAGS],
6056                                 tags_str?tags_str:"-");
6057                 g_free(tags_str);
6058         }
6059
6060         summary_set_row_marks(summaryview, row);
6061         if (row == summaryview->displayed) {
6062                 return TRUE;
6063         }
6064         return FALSE;
6065 }
6066
6067 void summary_set_tag(SummaryView *summaryview, gint tag_id,
6068                             GtkWidget *widget)
6069 {
6070         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6071         GList *cur;
6072         gboolean set = tag_id > 0;
6073         gint real_id = set? tag_id:-tag_id;
6074         gboolean froze = FALSE;
6075         gboolean redisplay = FALSE;
6076
6077         if (summary_is_locked(summaryview))
6078                 return;
6079         START_LONG_OPERATION(summaryview, FALSE);
6080         folder_item_set_batch(summaryview->folder_item, TRUE);
6081         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
6082                 redisplay |= summary_set_row_tag(summaryview,
6083                                            GTK_CMCTREE_NODE(cur->data), FALSE, set, real_id);
6084         }
6085         folder_item_set_batch(summaryview->folder_item, FALSE);
6086         END_LONG_OPERATION(summaryview);
6087         if (redisplay)
6088                 summary_redisplay_msg(summaryview);
6089 }
6090
6091 static void summary_tags_menu_item_activate_cb(GtkWidget *widget,
6092                                                      gpointer data)
6093 {
6094         gint id = GPOINTER_TO_INT(data);
6095         gboolean set = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
6096         SummaryView *summaryview;
6097
6098         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
6099         cm_return_if_fail(summaryview != NULL);
6100
6101         /* "dont_toggle" state set? */
6102         if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
6103                                 "dont_toggle"))
6104                 return;
6105
6106         if (!set)
6107                 id = -id;
6108         summary_set_tag(summaryview, id, NULL);
6109 }
6110
6111 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
6112                                                           gpointer data)
6113 {
6114         SummaryView *summaryview;
6115         GtkMenuShell *menu;
6116         GtkCheckMenuItem **items;
6117         gint n;
6118         GList *children, *cur, *sel;
6119
6120         summaryview = (SummaryView *)data;
6121         cm_return_if_fail(summaryview != NULL);
6122
6123         sel = GTK_CMCLIST(summaryview->ctree)->selection;
6124         if (!sel) return;
6125
6126         menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
6127         
6128         cm_return_if_fail(menu != NULL);
6129
6130         Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
6131
6132         /* NOTE: don't return prematurely because we set the "dont_toggle"
6133          * state for check menu items */
6134         g_object_set_data(G_OBJECT(menu), "dont_toggle",
6135                           GINT_TO_POINTER(1));
6136
6137         /* clear items. get item pointers. */
6138         children = gtk_container_get_children(GTK_CONTAINER(menu));
6139         for (n = 0, cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6140                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6141                         gtk_check_menu_item_set_active
6142                                 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6143                         items[n] = GTK_CHECK_MENU_ITEM(cur->data);
6144                         n++;
6145                 }
6146         }
6147
6148         g_list_free(children);
6149
6150         if (n == (N_COLOR_LABELS + 1)) {
6151                 /* iterate all messages and set the state of the appropriate
6152                  * items */
6153                 for (; sel != NULL; sel = sel->next) {
6154                         MsgInfo *msginfo;
6155                         gint clabel;
6156
6157                         msginfo = gtk_cmctree_node_get_row_data
6158                                 (GTK_CMCTREE(summaryview->ctree),
6159                                  GTK_CMCTREE_NODE(sel->data));
6160                         if (msginfo) {
6161                                 clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
6162                                 if (!gtk_check_menu_item_get_active(items[clabel]))
6163                                         gtk_check_menu_item_set_active
6164                                                 (items[clabel], TRUE);
6165                         }
6166                 }
6167         } else
6168                 g_warning("invalid number of color elements (%d)", n);
6169
6170         /* reset "dont_toggle" state */
6171         g_object_set_data(G_OBJECT(menu), "dont_toggle",
6172                           GINT_TO_POINTER(0));
6173 }
6174
6175 static void summary_colorlabel_menu_create(SummaryView *summaryview, gboolean refresh)
6176 {
6177         GtkWidget *label_menuitem;
6178         GtkWidget *menu;
6179         GtkWidget *item;
6180         gint i;
6181         gchar *accel_path = NULL;
6182
6183         label_menuitem = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menus/SummaryViewPopup/ColorLabel");
6184         g_signal_connect(G_OBJECT(label_menuitem), "activate",
6185                          G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
6186                            summaryview);
6187         gtk_widget_show(label_menuitem);
6188
6189         menu = gtk_menu_new();
6190
6191         gtk_menu_set_accel_group (GTK_MENU (menu), 
6192                 gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager));
6193
6194         /* create sub items. for the menu item activation callback we pass the
6195          * index of label_colors[] as data parameter. for the None color we
6196          * pass an invalid (high) value. also we attach a data pointer so we
6197          * can always get back the SummaryView pointer. */
6198
6199         item = gtk_check_menu_item_new_with_label(_("None"));
6200         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6201         g_signal_connect(G_OBJECT(item), "activate",
6202                          G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
6203                            GUINT_TO_POINTER(0));
6204         g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
6205         gtk_widget_show(item);
6206
6207         accel_path = g_strdup_printf("<ClawsColorLabels>/None");
6208         gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6209         g_free(accel_path);
6210         gtk_accel_map_add_entry("<ClawsColorLabels>/None", GDK_KEY_0, GDK_CONTROL_MASK);
6211
6212         item = gtk_menu_item_new();
6213         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6214         gtk_widget_show(item);
6215
6216         /* create pixmap/label menu items */
6217         for (i = 0; i < N_COLOR_LABELS; i++) {
6218                 item = colorlabel_create_check_color_menu_item(
6219                         i, refresh, SUMMARY_COLORMENU);
6220                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6221                 g_signal_connect(G_OBJECT(item), "activate",
6222                                  G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
6223                                  GUINT_TO_POINTER(i + 1));
6224                 g_object_set_data(G_OBJECT(item), "summaryview",
6225                                   summaryview);
6226                 gtk_widget_show(item);
6227                 accel_path = g_strdup_printf("<ClawsColorLabels>/%d", i+1);
6228                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6229                 if (i < 9)
6230                         gtk_accel_map_add_entry(accel_path, GDK_KEY_1+i, GDK_CONTROL_MASK);
6231                 g_free(accel_path);
6232                 g_signal_connect (gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager), 
6233                         "accel-changed", G_CALLBACK (mainwin_accel_changed_cb), item);
6234         }
6235
6236         gtk_widget_show(menu);
6237         gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6238         summaryview->colorlabel_menu = menu;
6239 }
6240
6241 static void summary_tags_menu_item_activate_item_cb(GtkMenuItem *menu_item,
6242                                                           gpointer data)
6243 {
6244         GtkMenuShell *menu;
6245         GList *children, *cur;
6246         GList *sel;
6247         GHashTable *menu_table = g_hash_table_new_full(
6248                                         g_direct_hash,
6249                                         g_direct_equal,
6250                                         NULL, NULL);
6251         GHashTable *menu_allsel_table = g_hash_table_new_full(
6252                                         g_direct_hash,
6253                                         g_direct_equal,
6254                                         NULL, NULL);
6255         gint sel_len;
6256         SummaryView *summaryview = (SummaryView *)data;
6257         cm_return_if_fail(summaryview != NULL);
6258
6259         sel = GTK_CMCLIST(summaryview->ctree)->selection;
6260         if (!sel) return;
6261
6262         menu = GTK_MENU_SHELL(summaryview->tags_menu);
6263         cm_return_if_fail(menu != NULL);
6264
6265         /* NOTE: don't return prematurely because we set the "dont_toggle"
6266          * state for check menu items */
6267         g_object_set_data(G_OBJECT(menu), "dont_toggle",
6268                           GINT_TO_POINTER(1));
6269
6270         /* clear items. get item pointers. */
6271         children = gtk_container_get_children(GTK_CONTAINER(menu));
6272         for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6273                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6274                         gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
6275                                 "tag_id"));
6276                         gtk_check_menu_item_set_active
6277                                 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6278                                 
6279                         g_hash_table_insert(menu_table, GINT_TO_POINTER(id), GTK_CHECK_MENU_ITEM(cur->data));
6280                         g_hash_table_insert(menu_allsel_table, GINT_TO_POINTER(id), GINT_TO_POINTER(0));
6281                 }
6282         }
6283
6284         g_list_free(children);
6285
6286         /* iterate all messages and set the state of the appropriate
6287          * items */
6288         sel_len = 0;
6289         for (; sel != NULL; sel = sel->next) {
6290                 MsgInfo *msginfo;
6291                 GSList *tags = NULL;
6292                 GtkCheckMenuItem *item;
6293                 msginfo = gtk_cmctree_node_get_row_data
6294                         (GTK_CMCTREE(summaryview->ctree),
6295                          GTK_CMCTREE_NODE(sel->data));
6296                 sel_len++;
6297                 if (msginfo) {
6298                         tags =  msginfo->tags;
6299                         if (!tags)
6300                                 continue;
6301
6302                         for (; tags; tags = tags->next) {
6303                                 gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, tags->data));
6304                                 item = g_hash_table_lookup(menu_table, GINT_TO_POINTER(tags->data));
6305                                 if (item && !gtk_check_menu_item_get_active(item)) {
6306                                         gtk_check_menu_item_set_active
6307                                                 (item, TRUE);
6308                                 }
6309                                 num_checked++;
6310                                 g_hash_table_replace(menu_allsel_table, tags->data, GINT_TO_POINTER(num_checked));
6311                         }
6312                 }
6313         }
6314
6315         children = gtk_container_get_children(GTK_CONTAINER(menu));
6316         for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6317                 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6318                         gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
6319                                 "tag_id"));
6320                         gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, GINT_TO_POINTER(id)));
6321                         if (num_checked < sel_len && num_checked > 0)
6322                                 gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), TRUE);
6323                         else
6324                                 gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6325                 }
6326         }
6327         g_list_free(children);
6328         g_hash_table_destroy(menu_table);
6329         g_hash_table_destroy(menu_allsel_table);
6330         /* reset "dont_toggle" state */
6331         g_object_set_data(G_OBJECT(menu), "dont_toggle",
6332                           GINT_TO_POINTER(0));
6333 }
6334
6335 void summaryview_destroy(SummaryView *summaryview)
6336 {
6337         if(summaryview->simplify_subject_preg) {
6338                 regfree(summaryview->simplify_subject_preg);
6339                 g_free(summaryview->simplify_subject_preg);
6340                 summaryview->simplify_subject_preg = NULL;
6341         }
6342 }
6343 static void summary_tags_menu_item_apply_tags_activate_cb(GtkWidget *widget,
6344                                                      gpointer data)
6345 {
6346         SummaryView *summaryview;
6347
6348         summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
6349         cm_return_if_fail(summaryview != NULL);
6350
6351         /* "dont_toggle" state set? */
6352         if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
6353                                 "dont_toggle"))
6354                 return;
6355         
6356         tag_apply_open(summary_get_selection(summaryview));     
6357 }
6358
6359 static gint summary_tag_cmp_list(gconstpointer a, gconstpointer b)
6360 {
6361         gint id_a = GPOINTER_TO_INT(a);
6362         gint id_b = GPOINTER_TO_INT(b);
6363         const gchar *tag_a = tags_get_tag(id_a);
6364         const gchar *tag_b = tags_get_tag(id_b);
6365         
6366         if (tag_a == NULL)
6367                 return tag_b == NULL ? 0:1;
6368         
6369         if (tag_b == NULL)
6370                 return 1;
6371
6372         return g_utf8_collate(tag_a, tag_b);
6373 }
6374
6375 static void summary_tags_menu_create(SummaryView *summaryview, gboolean refresh)
6376 {
6377
6378         GtkWidget *label_menuitem;
6379         GtkWidget *menu;
6380         GtkWidget *item;
6381         GSList *cur = tags_get_list();
6382         GSList *orig = NULL;
6383         gboolean existing_tags = FALSE;
6384         gchar *accel_path = NULL;
6385
6386         cur = orig = g_slist_sort(cur, summary_tag_cmp_list);
6387         label_menuitem = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menus/SummaryViewPopup/Tags");
6388         g_signal_connect(G_OBJECT(label_menuitem), "activate",
6389                          G_CALLBACK(summary_tags_menu_item_activate_item_cb),
6390                            summaryview);
6391
6392         gtk_widget_show(label_menuitem);
6393
6394         menu = gtk_menu_new();
6395
6396         gtk_menu_set_accel_group (GTK_MENU (menu), 
6397                 gtk_ui_manager_get_accel_group(summaryview->mainwin->ui_manager));
6398
6399         /* create tags menu items */
6400         for (; cur; cur = cur->next) {
6401                 gint id = GPOINTER_TO_INT(cur->data);
6402                 const gchar *tag = tags_get_tag(id);
6403                 item = gtk_check_menu_item_new_with_label(tag);
6404                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6405                 g_signal_connect(G_OBJECT(item), "activate",
6406                                  G_CALLBACK(summary_tags_menu_item_activate_cb),
6407                                  GINT_TO_POINTER(id));
6408                 g_object_set_data(G_OBJECT(item), "summaryview",
6409                                   summaryview);
6410                 g_object_set_data(G_OBJECT(item), "tag_id",
6411                                   GINT_TO_POINTER(id));
6412                 gtk_widget_show(item);
6413                 accel_path = g_strconcat("<ClawsTags>/",tag, NULL);
6414                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6415                 g_free(accel_path);
6416                 existing_tags = TRUE;
6417         }
6418         if (existing_tags) {
6419                 /* separator */
6420                 item = gtk_menu_item_new();
6421                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6422                 gtk_widget_show(item);
6423         }
6424
6425         item = gtk_menu_item_new_with_label(_("Apply tags..."));
6426         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6427         g_signal_connect(G_OBJECT(item), "activate",
6428                          G_CALLBACK(summary_tags_menu_item_apply_tags_activate_cb),
6429                          NULL);
6430         g_object_set_data(G_OBJECT(item), "summaryview",
6431                           summaryview);
6432         gtk_widget_show(item);
6433         accel_path = g_strdup_printf("<ClawsTags>/ApplyTags");
6434         gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6435         g_free(accel_path);
6436
6437         g_slist_free(orig);
6438         gtk_widget_show(menu);
6439         gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6440         summaryview->tags_menu = menu;
6441 }
6442
6443 static gboolean summary_popup_menu(GtkWidget *widget, gpointer data)
6444 {
6445         SummaryView *summaryview = (SummaryView *)data;
6446         summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6447
6448         gtk_menu_popup(GTK_MENU(summaryview->popupmenu), 
6449                        NULL, NULL, NULL, NULL, 
6450                        3, gtk_get_current_event_time());
6451
6452         return TRUE;
6453 }
6454
6455 #if !GENERIC_UMPC
6456 static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column)
6457 {
6458         MsgFlags flags;
6459         if (!info)
6460                 return NULL;
6461
6462         flags = info->flags;
6463
6464         switch(summaryview->col_state[column].type) {
6465                 case S_COL_STATUS:
6466                         if (MSG_IS_IGNORE_THREAD(flags)) {
6467                                 return _("Ignored thread");
6468                         } else if (MSG_IS_WATCH_THREAD(flags)) {
6469                                 return _("Watched thread");
6470                         } else if (MSG_IS_SPAM(flags)) {
6471                                 return _("Spam");
6472                         } else if (MSG_IS_NEW(flags)) {
6473                                 return _("New");
6474                         } else if (MSG_IS_UNREAD(flags)) {
6475                                 return _("Unread");
6476                         } else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
6477                                 return _("Replied but also forwarded - click to see reply");
6478                         } else if (MSG_IS_REPLIED(flags)) {
6479                                 return _("Replied - click to see reply");
6480                         } else if (MSG_IS_FORWARDED(flags)) {
6481                                 return _("Forwarded");
6482                         } else {
6483                                 return NULL;
6484                         }
6485                 case S_COL_MARK:
6486                         if (MSG_IS_DELETED(flags)) {
6487                                 return _("Deleted");
6488                         } else if (MSG_IS_MARKED(flags)) {
6489                                 return _("Marked");
6490                         } else if (MSG_IS_MOVE(flags)) {
6491                                 return _("To be moved");
6492                         } else if (MSG_IS_COPY(flags)) {
6493                                 return _("To be copied");
6494                         } else {
6495                                 return NULL;
6496                         }
6497                 case S_COL_LOCKED:
6498                         if (MSG_IS_LOCKED(flags)) {
6499                                 return _("Locked");
6500                         } else {
6501                                 return NULL;
6502                         }
6503                 case S_COL_MIME:
6504                         if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
6505                                 return _("Signed, has attachment(s)");
6506                         } else if (MSG_IS_SIGNED(flags)) {
6507                                 return _("Signed");
6508                         } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
6509                                 return _("Encrypted, has attachment(s)");
6510                         } else if (MSG_IS_ENCRYPTED(flags)) {
6511                                 return _("Encrypted");
6512                         } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
6513                                 return _("Has attachment(s)");
6514                         } else {
6515                                 return NULL;
6516                         }
6517                 default:
6518                         return NULL;
6519         }
6520 }
6521 static gboolean tooltip_cb (GtkWidget  *widget,
6522                             gint        x,
6523                             gint        y,
6524                             gboolean    keyboard_mode,
6525                             GtkTooltip *tooltip,
6526                             gpointer    user_data) 
6527 {
6528         GtkCMCTree *ctree = GTK_CMCTREE(widget);
6529         SummaryView *summaryview = (SummaryView *)user_data;
6530         gint row = -1, column = -1;
6531         int offset = prefs_common.show_col_headers ? 24:0;
6532         GtkCMCTreeNode *node = NULL;
6533         gchar *text = NULL;
6534         gchar *formatted = NULL;
6535         MsgInfo *info = NULL;
6536         GdkRectangle rect;
6537         gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6538         gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
6539         if (!prefs_common.show_tooltips)
6540                 return FALSE;
6541
6542         if (y - offset < 0)
6543                 return FALSE;
6544
6545         if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), x, y - offset,
6546                                           &row, &column))
6547                 return FALSE;
6548
6549         if ((node = gtk_cmctree_node_nth(ctree, row)) == NULL)
6550                 return FALSE;
6551
6552         if ((info = gtk_cmctree_node_get_row_data(ctree, node)) == NULL)
6553                 return FALSE;
6554
6555         switch (gtk_cmctree_node_get_cell_type(ctree, node, column)) {
6556                 case GTK_CMCELL_TEXT:
6557                         if (gtk_cmctree_node_get_text(ctree, node, column, &text) != TRUE)
6558                                 return FALSE;
6559                         break;
6560                 case GTK_CMCELL_PIXTEXT:
6561                         if (gtk_cmctree_node_get_pixtext(ctree, node, column, &text, 
6562                                 NULL, NULL) != TRUE)
6563                                 return FALSE;
6564                         break;
6565                 default: 
6566                         if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL)
6567                                 return FALSE;
6568         }
6569         
6570         if (!text || !*text)
6571                 return FALSE;
6572
6573         formatted = g_strdup(text);
6574         g_strstrip(formatted);
6575
6576         if ((vert_layout || small_layout) && prefs_common.two_line_vert)
6577                 gtk_tooltip_set_markup (tooltip, formatted);
6578         else 
6579                         gtk_tooltip_set_text (tooltip, formatted);
6580         g_free(formatted);
6581         
6582         rect.x = x - 2;
6583         rect.y = y - 2;
6584         rect.width = 12;
6585         rect.height= 12;        
6586         gtk_tooltip_set_tip_area(tooltip, &rect);
6587         
6588         return TRUE;
6589 }
6590 #endif
6591 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
6592 {
6593         GtkWidget *ctree;
6594         gint *col_pos = summaryview->col_pos;
6595         SummaryColumnState *col_state;
6596         gchar *titles[N_SUMMARY_COLS];
6597         SummaryColumnType type;
6598         gint pos;
6599         gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6600         gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
6601         memset(titles, 0, sizeof(titles));
6602
6603         col_state = prefs_summary_column_get_config();
6604         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6605                 summaryview->col_state[pos] = col_state[pos];
6606                 type = col_state[pos].type;
6607                 col_pos[type] = pos;
6608                 titles[pos] = "dummy";
6609         }
6610         col_state = summaryview->col_state;
6611
6612         ctree = gtk_sctree_new_with_titles
6613                 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
6614
6615         if (prefs_common.show_col_headers == FALSE)
6616                 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
6617
6618         gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_MULTIPLE);
6619         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6620                                            GTK_JUSTIFY_CENTER);
6621         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6622                                            GTK_JUSTIFY_CENTER);
6623         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6624                                            GTK_JUSTIFY_CENTER);
6625         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6626                                            GTK_JUSTIFY_CENTER);
6627         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6628                                            GTK_JUSTIFY_RIGHT);
6629         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6630                                            GTK_JUSTIFY_RIGHT);
6631         gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6632                                            GTK_JUSTIFY_RIGHT);
6633         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6634                                    prefs_common.summary_col_size[S_COL_MARK]);
6635         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6636                                    prefs_common.summary_col_size[S_COL_STATUS]);
6637         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6638                                    prefs_common.summary_col_size[S_COL_LOCKED]);
6639         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6640                                    prefs_common.summary_col_size[S_COL_MIME]);
6641         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SUBJECT],
6642                                    prefs_common.summary_col_size[S_COL_SUBJECT]);
6643         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_FROM],
6644                                    prefs_common.summary_col_size[S_COL_FROM]);
6645         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TO],
6646                                    prefs_common.summary_col_size[S_COL_TO]);
6647         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_DATE],
6648                                    prefs_common.summary_col_size[S_COL_DATE]);
6649         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6650                                    prefs_common.summary_col_size[S_COL_SIZE]);
6651         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6652                                    prefs_common.summary_col_size[S_COL_NUMBER]);
6653         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6654                                    prefs_common.summary_col_size[S_COL_SCORE]);
6655         gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TAGS],
6656                                    prefs_common.summary_col_size[S_COL_TAGS]);
6657
6658         gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
6659         gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
6660                              GTK_CMCTREE_EXPANDER_TRIANGLE);
6661
6662         gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
6663
6664         gtk_cmctree_set_indent(GTK_CMCTREE(ctree), 12);
6665         g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 
6666
6667         for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6668                 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[pos].button,
6669                                        FALSE);
6670                 if (((pos == summaryview->col_pos[S_COL_FROM] && !FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6671                      (pos == summaryview->col_pos[S_COL_TO] && FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6672                      pos == summaryview->col_pos[S_COL_DATE]) && (vert_layout || small_layout) &&
6673                             prefs_common.two_line_vert)
6674                         gtk_cmclist_set_column_visibility
6675                                 (GTK_CMCLIST(ctree), pos, FALSE);
6676                 else
6677                         gtk_cmclist_set_column_visibility
6678                                 (GTK_CMCLIST(ctree), pos, col_state[pos].visible);
6679         }
6680         if (prefs_common.two_line_vert)
6681                 gtk_sctree_set_use_markup(GTK_SCTREE(ctree), summaryview->col_pos[S_COL_SUBJECT], vert_layout||small_layout);
6682
6683         /* connect signal to the buttons for sorting */
6684 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
6685         g_signal_connect \
6686                 (G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
6687                  "clicked", \
6688                  G_CALLBACK(func), \
6689                  summaryview)
6690
6691         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked);
6692         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked);
6693         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked);
6694         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked);
6695         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked);
6696         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked);
6697         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked);
6698         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO     , summary_to_clicked);
6699         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
6700         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE,   summary_score_clicked);
6701         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED,  summary_locked_clicked);
6702         CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TAGS,    summary_tags_clicked);
6703         
6704 #undef CLIST_BUTTON_SIGNAL_CONNECT
6705
6706         g_signal_connect(G_OBJECT(ctree), "tree_select_row",
6707                          G_CALLBACK(summary_selected), summaryview);
6708         g_signal_connect(G_OBJECT(ctree), "tree_unselect_row",
6709                          G_CALLBACK(summary_unselected), summaryview);
6710         g_signal_connect(G_OBJECT(ctree), "button_press_event",
6711                          G_CALLBACK(summary_button_pressed),
6712                          summaryview);
6713         g_signal_connect(G_OBJECT(ctree), "popup-menu",
6714                          G_CALLBACK(summary_popup_menu), summaryview);
6715         g_signal_connect(G_OBJECT(ctree), "button_release_event",
6716                          G_CALLBACK(summary_button_released),
6717                          summaryview);
6718         g_signal_connect(G_OBJECT(ctree), "key_press_event",
6719                          G_CALLBACK(summary_key_pressed), summaryview);
6720         g_signal_connect(G_OBJECT(ctree), "resize_column",
6721                          G_CALLBACK(summary_col_resized), summaryview);
6722         g_signal_connect(G_OBJECT(ctree), "open_row",
6723                          G_CALLBACK(summary_open_row), summaryview);
6724
6725         g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
6726                                G_CALLBACK(summary_tree_expanded),
6727                                summaryview);
6728         g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
6729                                G_CALLBACK(summary_tree_collapsed),
6730                                summaryview);
6731
6732         g_signal_connect(G_OBJECT(ctree), "start_drag",
6733                          G_CALLBACK(summary_start_drag),
6734                          summaryview);
6735         g_signal_connect(G_OBJECT(ctree), "drag_data_get",
6736                          G_CALLBACK(summary_drag_data_get),
6737                          summaryview);
6738         g_signal_connect(G_OBJECT(ctree), "drag_end",
6739                          G_CALLBACK(summary_drag_end),
6740                          summaryview);
6741
6742         gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
6743                           summary_drag_types, 3,
6744                           GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
6745
6746         g_signal_connect(G_OBJECT(ctree), "drag_data_received",
6747                          G_CALLBACK(summary_drag_data_received),
6748                          summaryview);
6749
6750         g_signal_connect(G_OBJECT(ctree), "drag_motion",
6751                          G_CALLBACK(summary_drag_motion_cb),
6752                          summaryview);
6753
6754 #if !GENERIC_UMPC
6755         g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL);
6756         g_signal_connect(G_OBJECT(ctree), "query-tooltip", 
6757                          G_CALLBACK(tooltip_cb),
6758                         summaryview);
6759 #endif
6760         return ctree;
6761 }
6762
6763 void summary_set_column_order(SummaryView *summaryview)
6764 {
6765         GtkWidget *ctree;
6766         GtkWidget *scrolledwin = summaryview->scrolledwin;
6767         FolderItem *item;
6768         guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected);
6769         guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed);
6770
6771         item = summaryview->folder_item;
6772
6773         summary_clear_all(summaryview);
6774         gtk_widget_destroy(summaryview->ctree);
6775
6776         summaryview->ctree = ctree = summary_ctree_create(summaryview);
6777         summary_set_fonts(summaryview);
6778         summary_set_column_titles(summaryview);
6779         gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6780                                             GTK_CMCLIST(ctree)->hadjustment);
6781         gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6782                                             GTK_CMCLIST(ctree)->vadjustment);
6783         gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
6784         gtk_widget_show(ctree);
6785
6786         summary_show(summaryview, item);
6787
6788         summary_select_by_msgnum(summaryview, selected_msgnum, FALSE);
6789
6790         summaryview->selected = summary_find_msg_by_msgnum(summaryview, selected_msgnum);
6791         summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum);
6792         if (!summaryview->displayed)
6793                 messageview_clear(summaryview->messageview);
6794         else
6795                 summary_redisplay_msg(summaryview);
6796
6797         /* get normal row height */
6798         gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
6799         normal_row_height = GTK_CMCLIST(ctree)->row_height;             
6800                 
6801         if ((prefs_common.layout_mode == SMALL_LAYOUT || prefs_common.layout_mode == VERTICAL_LAYOUT) &&
6802             prefs_common.two_line_vert) {
6803                 gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 2*normal_row_height + 2);           
6804         }
6805 }
6806
6807
6808 /* callback functions */
6809
6810 static gint summary_folder_eventbox_pressed(GtkWidget *eventbox, GdkEventButton *event,
6811                                             SummaryView *summaryview)
6812 {
6813         if (event) {
6814                 folderview_grab_focus(summaryview->folderview);
6815                 mainwindow_exit_folder(summaryview->mainwin);
6816         }
6817         return TRUE;
6818 }
6819
6820 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
6821                                    SummaryView *summaryview)
6822 {
6823         if (event)  
6824                 summary_toggle_view(summaryview);
6825         return TRUE;
6826 }
6827 #ifdef GENERIC_UMPC
6828 static void summary_toggle_multiple_pressed(GtkWidget *widget,
6829                                    SummaryView *summaryview)
6830 {
6831         GTK_SCTREE(summaryview->ctree)->force_additive_sel = 
6832                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6833 }
6834 #endif
6835 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
6836                                        SummaryView *summaryview)
6837 {
6838         if (!event) return FALSE;
6839
6840         if (event->button == 3) {
6841                 /* right clicked */
6842                 summary_set_menu_sensitive(summaryview);
6843                 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
6844                                NULL, NULL, event->button, event->time);
6845         } else if (event->button == 2) {
6846                 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6847         } else if (event->button == 1) {
6848                 if (!prefs_common.emulate_emacs &&
6849                     messageview_is_visible(summaryview->messageview))
6850                         summaryview->display_msg = TRUE;
6851         }
6852
6853         return FALSE;
6854 }
6855
6856 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
6857                                         SummaryView *summaryview)
6858 {
6859         return FALSE;
6860 }
6861
6862 gboolean summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
6863 {
6864         if (!summaryview)
6865                 return FALSE;
6866         if (summary_is_list(summaryview))
6867                 return summary_key_pressed(summaryview->ctree, event, summaryview);
6868         else
6869                 return FALSE;
6870 }
6871
6872 #define BREAK_ON_MODIFIER_KEY() \
6873         if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
6874
6875 /* Copied from gtkcmclist.c, if it changes there, it has to change
6876  * here as well. This is an ugly hack, there must be a better way to
6877  * find out how much to move for page up/down. */
6878 #define CELL_SPACING 1
6879 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
6880                                     SummaryView *summaryview)
6881 {
6882         GtkCMCTree *ctree = GTK_CMCTREE(widget);
6883         GtkCMCTreeNode *node;
6884         MessageView *messageview;
6885         GtkAdjustment *adj;
6886         gboolean mod_pressed;
6887
6888         if (!event) 
6889                 return TRUE;
6890
6891         if (quicksearch_has_focus(summaryview->quicksearch))
6892                 return FALSE;
6893
6894         messageview = summaryview->messageview;
6895
6896         mod_pressed =
6897                 ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
6898
6899         if (summaryview->selected) {
6900                 gboolean handled = FALSE;
6901                 switch (event->keyval) {
6902                 case GDK_KEY_space:             /* Page down or go to the next */
6903                         handled = TRUE;
6904                         if (event->state & GDK_CONTROL_MASK) {
6905                                 handled = FALSE;
6906                                 break;
6907                         }
6908                         if (event->state & GDK_SHIFT_MASK) 
6909                                 mimeview_scroll_page(messageview->mimeview, TRUE);
6910                         else {
6911                                 if (summaryview->displayed != summaryview->selected) {
6912                                         summary_display_msg(summaryview,
6913                                                             summaryview->selected);
6914                                         break;
6915                                 }
6916                                 if (mod_pressed) {
6917                                         if (!mimeview_scroll_page(messageview->mimeview, TRUE))
6918                                                 summary_select_prev_unread(summaryview);
6919                                 } else {
6920                                         if (!mimeview_scroll_page(messageview->mimeview, FALSE))
6921                                                 summary_select_next_unread(summaryview);
6922                                 }                               
6923                         }
6924                         break;
6925                 case GDK_KEY_BackSpace: /* Page up */
6926                         handled = TRUE;
6927                         mimeview_scroll_page(messageview->mimeview, TRUE);
6928                         break;
6929                 case GDK_KEY_Return:    /* Scroll up/down one line */
6930                 case GDK_KEY_KP_Enter:
6931                         handled = TRUE;
6932                         if (summaryview->displayed != summaryview->selected) {
6933 #ifndef GENERIC_UMPC
6934                                 summary_display_msg(summaryview,
6935                                                     summaryview->selected);
6936 #else
6937                                 summary_open_row(NULL, summaryview);
6938 #endif
6939                                 break;
6940                         }
6941                         mimeview_scroll_one_line(messageview->mimeview, mod_pressed);
6942                         break;
6943                 }
6944                 
6945                 if (handled)
6946                         return FALSE;
6947         }
6948         if (summary_is_locked(summaryview)) 
6949                 return TRUE;
6950
6951         switch (event->keyval) {
6952         case GDK_KEY_Left:              /* Move focus */
6953                 adj = gtk_scrolled_window_get_hadjustment
6954                         (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
6955                 if (gtk_adjustment_get_lower(adj) != gtk_adjustment_get_value(adj))
6956                         break;
6957                 /* FALLTHROUGH */       
6958         case GDK_KEY_Escape:
6959                 folderview_grab_focus(summaryview->folderview);
6960                 mainwindow_exit_folder(summaryview->mainwin);
6961                 return TRUE;
6962         case GDK_KEY_Home: case GDK_KEY_KP_Home:
6963         case GDK_KEY_End: case GDK_KEY_KP_End:
6964         case GDK_KEY_Up: case GDK_KEY_KP_Up:
6965         case GDK_KEY_Down: case GDK_KEY_KP_Down:
6966         case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
6967         case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
6968                 if ((node = summaryview->selected) != NULL) {
6969                         GtkCMCTreeNode *next = NULL;
6970                         switch (event->keyval) {
6971                                 case GDK_KEY_Home: case GDK_KEY_KP_Home:
6972                                         next = gtk_cmctree_node_nth(ctree, 0);
6973                                         break;
6974                                 case GDK_KEY_End: case GDK_KEY_KP_End:
6975                                         next = gtk_cmctree_node_nth(ctree,
6976                                                         g_list_length(GTK_CMCLIST(ctree)->row_list)-1);
6977                                         break;
6978                                 case GDK_KEY_Up: case GDK_KEY_KP_Up:
6979                                         next = gtk_cmctree_node_nth(ctree,
6980                                                         MAX(0, GTK_CMCLIST(ctree)->focus_row - 1));
6981                                         break;
6982                                 case GDK_KEY_Down: case GDK_KEY_KP_Down:
6983                                         next = gtk_cmctree_node_nth(ctree,
6984                                                         MIN(GTK_CMCLIST(ctree)->focus_row + 1, GTK_CMCLIST(ctree)->rows));
6985                                         break;
6986                                 case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
6987                                         next = gtk_cmctree_node_nth(ctree,
6988                                                         MAX(0, GTK_CMCLIST(ctree)->focus_row -
6989                                                                 (2 * GTK_CMCLIST(ctree)->clist_window_height -
6990                                                                  GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
6991                                                                 (2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
6992                                         break;
6993                                 case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
6994                                         next = gtk_cmctree_node_nth(ctree,
6995                                                         MIN(GTK_CMCLIST(ctree)->rows - 1, GTK_CMCLIST(ctree)->focus_row +
6996                                                                 (2 * GTK_CMCLIST(ctree)->clist_window_height -
6997                                                                  GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
6998                                                                 (2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
6999                                         break;
7000                         }
7001
7002                         if (next) {
7003                                 gtk_sctree_select_with_state
7004                                         (GTK_SCTREE(ctree), next, (event->state & ~GDK_CONTROL_MASK) );
7005
7006                                 /* Deprecated - what are the non-deprecated equivalents? */
7007                                 if (gtk_cmctree_node_is_visible(GTK_CMCTREE(ctree), next) != GTK_VISIBILITY_FULL)
7008                                         gtkut_ctree_node_move_if_on_the_edge(GTK_CMCTREE(ctree), next, -1);
7009                                 if (!mod_pressed)
7010                                         summary_select_node(summaryview, summaryview->selected, OPEN_SELECTED_ON_DIRECTIONAL);
7011                                 summaryview->selected = next;
7012                         }
7013                 }
7014                 return TRUE;
7015         default:
7016                 break;
7017         }
7018
7019         if (!summaryview->selected) {
7020                 node = gtk_cmctree_node_nth(ctree, 0);
7021                 if (node)
7022                         gtk_sctree_select(GTK_SCTREE(ctree), node);
7023                 else
7024                         return TRUE;
7025         }
7026
7027         switch (event->keyval) {
7028         case GDK_KEY_Delete:
7029                 BREAK_ON_MODIFIER_KEY();
7030                 summary_delete_trash(summaryview);
7031                 break;
7032         default:
7033                 break;
7034         }
7035         return FALSE;
7036 }
7037 #undef CELL_SPACING
7038
7039 static void quicksearch_execute_cb(QuickSearch *quicksearch, gpointer data)
7040 {
7041         SummaryView *summaryview = data;
7042
7043         summaryview_reset_recursive_folder_match(summaryview);
7044         if (summary_show(summaryview, summaryview->folder_item))
7045                 summaryview_quicksearch_recurse(summaryview);
7046         else
7047                 summaryview_reset_recursive_folder_match(summaryview);
7048 }
7049
7050 static void tog_searchbar_cb(GtkWidget *w, gpointer data)
7051 {
7052         SummaryView *summaryview = (SummaryView *)data;
7053
7054         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
7055                 prefs_common.show_searchbar = TRUE;
7056                 quicksearch_show(summaryview->quicksearch);
7057         } else {
7058                 prefs_common.show_searchbar = FALSE;
7059                 quicksearch_hide(summaryview->quicksearch);
7060         }
7061 }
7062
7063 void summaryview_activate_quicksearch(SummaryView *summaryview, gboolean show) 
7064 {
7065         prefs_common.show_searchbar = show;
7066         gtk_toggle_button_set_active(
7067                 GTK_TOGGLE_BUTTON(summaryview->toggle_search), 
7068                 show);
7069         if (show) {
7070                 quicksearch_show(summaryview->quicksearch);
7071         } else {
7072                 quicksearch_hide(summaryview->quicksearch);
7073                 summary_grab_focus(summaryview);
7074         }
7075 }
7076
7077 void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
7078 {
7079         if (FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
7080                 summary_reedit(summaryview);
7081         else
7082                 summary_open_msg(summaryview);
7083
7084         summaryview->display_msg = FALSE;
7085 }
7086
7087 static void summary_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
7088                                   SummaryView *summaryview)
7089 {
7090         summary_set_row_marks(summaryview, node);
7091         if (prefs_common.bold_unread) {
7092                 while (node) {
7093                         GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
7094                         if (GTK_CMCTREE_ROW(node)->children)
7095                                 summary_set_row_marks(summaryview, node);
7096                         node = next;
7097                 }
7098         }
7099 }
7100
7101 static void summary_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
7102                                    SummaryView *summaryview)
7103 {
7104         gtk_sctree_select(GTK_SCTREE(ctree), node);
7105         summary_set_row_marks(summaryview, node);
7106 }
7107
7108 static void summary_unselected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
7109                                gint column, SummaryView *summaryview)
7110 {
7111         if (summary_is_locked(summaryview)
7112         ||  GTK_SCTREE(ctree)->selecting_range) {
7113                 return;
7114         }
7115
7116         summary_status_show(summaryview);
7117 }
7118
7119 static void summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
7120                              gint column, SummaryView *summaryview)
7121 {
7122         const GList *list, *cur;
7123         MessageView *msgview;
7124         MsgInfo *msginfo;
7125         gboolean marked_unread = FALSE;
7126
7127         if (summary_is_locked(summaryview)
7128         && !GTK_SCTREE(ctree)->selecting_range
7129         && summaryview->messageview
7130         && summaryview->messageview->mimeview
7131         && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
7132         && summaryview->messageview->mimeview->textview->loading) {
7133                 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
7134                 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
7135                 
7136                 data->ctree = ctree;
7137                 data->row = row;
7138                 data->node = NULL;
7139                 data->column = column;
7140                 data->summaryview = summaryview;
7141                 debug_print("postponing open of message till end of load\n");
7142                 g_timeout_add(100, summary_select_retry, data);
7143                 return;
7144         }
7145         if (summary_is_locked(summaryview)
7146         ||  GTK_SCTREE(ctree)->selecting_range) {
7147                 return;
7148         }
7149
7150         summary_status_show(summaryview);
7151
7152         if (GTK_CMCLIST(ctree)->selection &&
7153             GTK_CMCLIST(ctree)->selection->next) {
7154                 summaryview->display_msg = FALSE;
7155                 summary_set_menu_sensitive(summaryview);
7156                 toolbar_main_set_sensitive(summaryview->mainwin);
7157                 return;
7158         }
7159
7160         summaryview->selected = row;
7161
7162         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7163         cm_return_if_fail(msginfo != NULL);
7164
7165         main_create_mailing_list_menu (summaryview->mainwin, msginfo);
7166         toolbar_set_learn_button
7167                 (summaryview->mainwin->toolbar,
7168                  MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
7169
7170         switch (column < 0 ? column : summaryview->col_state[column].type) {
7171         case S_COL_MARK:
7172                 if (!MSG_IS_DELETED(msginfo->flags) &&
7173                     !MSG_IS_MOVE(msginfo->flags) &&
7174                     !MSG_IS_COPY(msginfo->flags)) {
7175                         if (MSG_IS_MARKED(msginfo->flags)) {
7176                                 summary_unmark_row(summaryview, row);
7177                                 summary_status_show(summaryview);
7178                         } else {
7179                                 summary_mark_row(summaryview, row);
7180                                 summary_status_show(summaryview);
7181                         }
7182                 }
7183                 break;
7184         case S_COL_STATUS:
7185                 if (MSG_IS_UNREAD(msginfo->flags)) {
7186                         summary_mark_row_as_read(summaryview, row);
7187                         summary_status_show(summaryview);
7188                 } else if (MSG_IS_SPAM(msginfo->flags)) {
7189                                 if (procmsg_spam_learner_learn(msginfo, NULL, FALSE) == 0)
7190                                         summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
7191                                 else
7192                                         log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
7193                 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
7194                          !MSG_IS_FORWARDED(msginfo->flags)) {
7195                         marked_unread = TRUE;
7196                 } else if (MSG_IS_REPLIED(msginfo->flags)) {
7197                         summary_find_answers(summaryview, msginfo);
7198                         return;
7199                 } 
7200                 break;
7201         case S_COL_LOCKED:
7202                 if (MSG_IS_LOCKED(msginfo->flags)) {
7203                         summary_unlock_row(summaryview, row);
7204                         summary_status_show(summaryview);
7205                 }
7206                 else {
7207                         summary_lock_row(summaryview, row);
7208                         summary_status_show(summaryview);
7209                 }
7210                 break;
7211         default:
7212                 break;
7213         }
7214
7215         list = messageview_get_msgview_list();
7216         for (cur = list; cur != NULL; cur = cur->next) {
7217                 msgview = (MessageView *) cur->data;
7218                 
7219                 if (msgview->new_window && msgview->update_needed) {
7220                         MsgInfo *new_msginfo = summary_get_selected_msg(summaryview);
7221                         messageview_show(msgview, new_msginfo, msgview->all_headers);
7222                         msgview->update_needed = FALSE;         
7223                 }
7224         }
7225
7226         if (summaryview->display_msg) {
7227                 summaryview->display_msg = FALSE;
7228                 if (summaryview->displayed != row) {
7229                         summary_display_msg(summaryview, row);
7230                         if (marked_unread) {
7231                                 summary_mark_row_as_unread(summaryview, row);
7232                                 summary_status_show(summaryview);
7233                         } 
7234                         return;
7235                 }
7236         }
7237         
7238         if (marked_unread) {
7239                 summary_mark_row_as_unread(summaryview, row);
7240                 summary_status_show(summaryview);
7241         } 
7242
7243         summary_set_menu_sensitive(summaryview);
7244         toolbar_main_set_sensitive(summaryview->mainwin);
7245 }
7246
7247 static void summary_col_resized(GtkCMCList *clist, gint column, gint width,
7248                                 SummaryView *summaryview)
7249 {
7250         SummaryColumnType type = summaryview->col_state[column].type;
7251
7252         prefs_common.summary_col_size[type] = width;
7253 }
7254
7255
7256 /*
7257  * \brief get List of msginfo selected in SummaryView
7258  *
7259  * \param summaryview
7260  *
7261  * \return GSList holding MsgInfo
7262  */
7263 GSList *summary_get_selection(SummaryView *summaryview)
7264 {
7265         GList *sel = NULL;
7266         GSList *msginfo_list = NULL;
7267         
7268         cm_return_val_if_fail(summaryview != NULL, NULL);
7269
7270         sel = GTK_CMCLIST(summaryview->ctree)->selection;
7271
7272         cm_return_val_if_fail(sel != NULL, NULL);
7273
7274         for ( ; sel != NULL; sel = sel->next)
7275                 msginfo_list = 
7276                         g_slist_prepend(msginfo_list, 
7277                                        gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
7278                                                                    GTK_CMCTREE_NODE(sel->data)));
7279         return g_slist_reverse(msginfo_list);
7280 }
7281
7282 /*
7283  * \brief get number of messages currently selected in SummaryView
7284  *
7285  * \param summaryview
7286  *
7287  * \return number of messages currently selected
7288  */
7289 guint summary_get_selection_count(SummaryView *summaryview)
7290 {
7291         cm_return_val_if_fail(summaryview != NULL, 0);
7292         return g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
7293 }
7294
7295 static void summary_sort_by_column_click(SummaryView *summaryview,
7296                                          FolderSortKey sort_key)
7297 {
7298         GtkCMCTreeNode *node = NULL;
7299         START_TIMING("");
7300         if (summaryview->sort_key == sort_key)
7301                 summary_sort(summaryview, sort_key,
7302                              summaryview->sort_type == SORT_ASCENDING
7303                              ? SORT_DESCENDING : SORT_ASCENDING);
7304         else
7305                 summary_sort(summaryview, sort_key, summaryview->sort_type);
7306
7307         node = GTK_CMCTREE_NODE(GTK_CMCLIST(summaryview->ctree)->row_list);
7308
7309         summary_freeze(summaryview);
7310         if (prefs_common.bold_unread) {
7311                 while (node) {
7312                         GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
7313                         if (GTK_CMCTREE_ROW(node)->children)
7314                                 summary_set_row_marks(summaryview, node);
7315                         node = next;
7316                 }
7317         }
7318         summary_thaw(summaryview);
7319         END_TIMING();
7320 }
7321
7322 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
7323 {
7324         summary_sort_by_column_click(summaryview, SORT_BY_MARK);
7325 }
7326
7327 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview)
7328 {
7329         summary_sort_by_column_click(summaryview, SORT_BY_STATUS);
7330 }
7331
7332 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
7333 {
7334         summary_sort_by_column_click(summaryview, SORT_BY_MIME);
7335 }
7336
7337 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
7338 {
7339         summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
7340 }
7341
7342 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
7343 {
7344         summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
7345 }
7346
7347 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
7348 {
7349         if (summaryview->sort_key == SORT_BY_THREAD_DATE)
7350                 summary_sort_by_column_click(summaryview, SORT_BY_THREAD_DATE);
7351         else
7352                 summary_sort_by_column_click(summaryview, SORT_BY_DATE);
7353 }
7354
7355 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
7356 {
7357         if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
7358                 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
7359         else
7360                 summary_sort_by_column_click(summaryview, SORT_BY_TO);
7361 }
7362
7363 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
7364 {
7365         if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
7366                 summary_sort_by_column_click(summaryview, SORT_BY_TO);
7367         else
7368                 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
7369 }
7370
7371 static void summary_subject_clicked(GtkWidget *button,
7372                                     SummaryView *summaryview)
7373 {
7374         summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
7375 }
7376
7377 static void summary_score_clicked(GtkWidget *button,
7378                                   SummaryView *summaryview)
7379 {
7380         summary_sort_by_column_click(summaryview, SORT_BY_SCORE);
7381 }
7382
7383 static void summary_locked_clicked(GtkWidget *button,
7384                                    SummaryView *summaryview)
7385 {
7386         summary_sort_by_column_click(summaryview, SORT_BY_LOCKED);
7387 }
7388
7389 static void summary_tags_clicked(GtkWidget *button,
7390                                    SummaryView *summaryview)
7391 {
7392         summary_sort_by_column_click(summaryview, SORT_BY_TAGS);
7393 }
7394
7395 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
7396                                SummaryView *summaryview)
7397 {
7398         GdkDragContext *context;
7399
7400         cm_return_if_fail(summaryview != NULL);
7401         cm_return_if_fail(summaryview->folder_item != NULL);
7402         cm_return_if_fail(summaryview->folder_item->folder != NULL);
7403
7404         if (summaryview->selected == NULL) return;
7405
7406         context = gtk_drag_begin(widget, summaryview->target_list,
7407                                  GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
7408         gtk_drag_set_icon_default(context);
7409         if (prefs_common.layout_mode == SMALL_LAYOUT) {
7410                 GtkWidget *paned = gtk_widget_get_parent(GTK_WIDGET_PTR(summaryview));
7411                 if (paned && GTK_IS_PANED(paned)) {
7412                         mainwindow_reset_paned(GTK_PANED(paned));
7413                 }
7414         }
7415 }
7416
7417 static gboolean summary_return_to_list(void *data)
7418 {
7419         SummaryView *summaryview = (SummaryView *)data;
7420         mainwindow_enter_folder(summaryview->mainwin);
7421         return FALSE;
7422 }
7423
7424 static void summary_drag_end       (GtkWidget       *widget, 
7425                                    GdkDragContext   *drag_context,
7426                                    SummaryView      *summaryview)
7427 {
7428         if (prefs_common.layout_mode == SMALL_LAYOUT) {
7429                 g_timeout_add(250, summary_return_to_list, summaryview);
7430         }
7431 }
7432
7433 static void summary_drag_data_get(GtkWidget        *widget,
7434                                   GdkDragContext   *drag_context,
7435                                   GtkSelectionData *selection_data,
7436                                   guint             info,
7437                                   guint             time,
7438                                   SummaryView      *summaryview)
7439 {
7440         if (info == TARGET_MAIL_URI_LIST) {
7441                 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7442                 GList *cur;
7443                 MsgInfo *msginfo;
7444                 gchar *mail_list = NULL, *tmp1, *tmp2;
7445
7446                 for (cur = GTK_CMCLIST(ctree)->selection;
7447                      cur != NULL && cur->data != NULL; cur = cur->next) {
7448                         msginfo = gtk_cmctree_node_get_row_data
7449                                 (ctree, GTK_CMCTREE_NODE(cur->data));
7450                         tmp2 = procmsg_get_message_file(msginfo);
7451                         if (!tmp2) continue;
7452                         if (msginfo->subject) {
7453                                 gchar *san_subject = g_strdup(msginfo->subject);
7454                                 gchar *dest = NULL;
7455                                 subst_for_filename(san_subject);
7456                                 dest = g_strdup_printf("%s%s%s.%d.txt",
7457                                                 get_tmp_dir(),
7458                                                 G_DIR_SEPARATOR_S,
7459                                                 san_subject, msginfo->msgnum);
7460                                 g_free(san_subject);
7461                                 san_subject = g_filename_from_utf8(dest, -1, NULL, NULL, NULL);
7462                                 g_free(dest);
7463                                 dest = san_subject;
7464                                 if (copy_file(tmp2, dest, TRUE) == 0) {
7465                                         g_free(tmp2);
7466                                         tmp2 = dest;
7467                                 }
7468                         } 
7469                         tmp1 = g_filename_to_uri(tmp2, NULL, NULL);
7470                         g_free(tmp2);
7471                         tmp2 = g_strconcat(tmp1, "\r\n", NULL);
7472                         g_free(tmp1);
7473                         tmp1 = tmp2;
7474
7475                         if (!mail_list) {
7476                                 mail_list = tmp1;
7477                         } else {
7478                                 tmp2 = g_strconcat(mail_list, tmp1, NULL);
7479                                 g_free(mail_list);
7480                                 g_free(tmp1);
7481                                 mail_list = tmp2;
7482                         }
7483                 }
7484
7485                 if (mail_list != NULL) {
7486                         gtk_selection_data_set(selection_data,
7487                                                gtk_selection_data_get_target(selection_data), 8,
7488                                                mail_list, strlen(mail_list));
7489                         g_free(mail_list);
7490                 } 
7491         } else if (info == TARGET_DUMMY) {
7492                 if (GTK_CMCLIST(summaryview->ctree)->selection)
7493                         gtk_selection_data_set(selection_data,
7494                                                gtk_selection_data_get_target(selection_data), 8,
7495                                                "Dummy-Summaryview", 
7496                                                strlen("Dummy-Summaryview")+1);
7497         } else if (info == TARGET_MAIL_CM_PATH_LIST) {
7498                 /* content: folder_item_identifier\nmsgid1\nmsgid2\nmsgid3 */
7499
7500                 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7501                 GList *cur;
7502                 MsgInfo *msginfo;
7503                 gchar *path_list = NULL;
7504
7505                 /* identifier */
7506                 if(GTK_CMCLIST(ctree)->selection != NULL) {
7507                         msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->selection->data));
7508             if(msginfo && msginfo->folder)
7509               path_list = folder_item_get_identifier(msginfo->folder);
7510                 }
7511
7512                 for (cur = GTK_CMCLIST(ctree)->selection;
7513                      cur != NULL && cur->data != NULL; cur = cur->next) {
7514                         gchar *tmp;
7515
7516                         msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(cur->data));
7517             if(!msginfo)
7518               continue;
7519                         tmp = path_list;
7520                         path_list = g_strconcat(path_list, "\n", (msginfo->msgid ? msginfo->msgid : "unknown"), NULL);
7521                         g_free(tmp);
7522                 }
7523
7524                 if (path_list != NULL) {
7525                         gtk_selection_data_set(selection_data,
7526                                                gtk_selection_data_get_target(selection_data), 8,
7527                                                path_list, strlen(path_list));
7528                         g_free(path_list);
7529                 }
7530     }
7531 }
7532
7533 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
7534                                           GdkDragContext *context,
7535                                           gint            x,
7536                                           gint            y,
7537                                           guint           time,
7538                                           SummaryView    *summaryview)
7539 {
7540         FolderItem *item = summaryview->folder_item;
7541         if (!(item && item->folder && folder_item_parent(item) != NULL
7542                     && FOLDER_CLASS(item->folder)->add_msg != NULL)) {
7543                 gdk_drag_status(context, 0, time);
7544                 return FALSE;
7545         } else if (gtk_drag_get_source_widget(context) ==
7546                 mainwindow_get_mainwindow()->folderview->ctree) {
7547                 /* no folders */
7548                 gdk_drag_status(context, 0, time);
7549                 return FALSE;
7550         } else if (gtk_drag_get_source_widget(context) ==
7551                 summaryview->ctree) {
7552                 /* not from same folder */
7553                 gdk_drag_status(context, 0, time);
7554                 return FALSE;
7555         } else {
7556                 gdk_drag_status(context, GDK_ACTION_COPY, time);
7557                 return TRUE;
7558         }
7559 }
7560
7561 static void summary_drag_data_received(GtkWidget        *widget,
7562                                         GdkDragContext   *drag_context,
7563                                         gint              x,
7564                                         gint              y,
7565                                         GtkSelectionData *data,
7566                                         guint             info,
7567                                         guint             time,
7568                                         SummaryView       *summaryview)
7569 {
7570         if (info == TARGET_MAIL_URI_LIST) {
7571                 FolderItem *item = summaryview->folder_item;
7572                 if (!item) {
7573                         gtk_drag_finish(drag_context, FALSE, FALSE, time);                      
7574                         return;
7575                 } else {
7576                         folderview_finish_dnd(gtk_selection_data_get_data(data),
7577                                 drag_context, time, item);
7578                 }
7579         }
7580 }
7581
7582
7583 /* custom compare functions for sorting */
7584
7585 static gint summary_cmp_by_date(GtkCMCList *clist,
7586                       gconstpointer ptr1, gconstpointer ptr2)
7587 {
7588         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7589         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7590         gint res;
7591         if (!msginfo1 || !msginfo2)
7592                 return -1;
7593
7594         res = (msginfo1->date_t - msginfo2->date_t);
7595         if (res == 0)
7596                 res = msginfo1->msgnum - msginfo2->msgnum;
7597         return res;
7598 }
7599
7600 #define CMP_FUNC_DEF(func_name, val)                                     \
7601 static gint func_name(GtkCMCList *clist,                                 \
7602                       gconstpointer ptr1, gconstpointer ptr2)            \
7603 {                                                                        \
7604         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;               \
7605         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;               \
7606         gint res;                                                        \
7607         if (!msginfo1 || !msginfo2)                                      \
7608                 return -1;                                               \
7609                                                                          \
7610         res = (val);                                                     \
7611         return (res != 0) ? res:summary_cmp_by_date(clist, ptr1, ptr2);  \
7612 }
7613
7614 CMP_FUNC_DEF(summary_cmp_by_mark,
7615              MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
7616 CMP_FUNC_DEF(summary_cmp_by_status,
7617              (-(MSG_IS_SPAM(msginfo1->flags))+(MSG_IS_UNREAD(msginfo1->flags)<<1)+(MSG_IS_NEW(msginfo1->flags)<<2)) 
7618              - (-(MSG_IS_SPAM(msginfo2->flags))+(MSG_IS_UNREAD(msginfo2->flags)<<1)+(MSG_IS_NEW(msginfo2->flags)<<2)) )
7619 CMP_FUNC_DEF(summary_cmp_by_mime,
7620              MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags))
7621 CMP_FUNC_DEF(summary_cmp_by_label,
7622              MSG_GET_COLORLABEL(msginfo1->flags) -
7623              MSG_GET_COLORLABEL(msginfo2->flags))
7624 CMP_FUNC_DEF(summary_cmp_by_locked,
7625              MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags))
7626
7627 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
7628 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
7629
7630 #undef CMP_FUNC_DEF
7631
7632 static gint summary_cmp_by_subject(GtkCMCList *clist,
7633                                    gconstpointer ptr1,
7634                                    gconstpointer ptr2)
7635 {
7636         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7637         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7638         gint res;
7639
7640         if (!msginfo1->subject)
7641                 return (msginfo2->subject != NULL);
7642         if (!msginfo2->subject)
7643                 return -1;
7644
7645         res = subject_compare_for_sort
7646                 (msginfo1->subject, msginfo2->subject);
7647         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7648 }
7649
7650 static gint summary_cmp_by_thread_date(GtkCMCList *clist,
7651                                    gconstpointer ptr1,
7652                                    gconstpointer ptr2)
7653 {
7654         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7655         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7656         gint thread_diff = msginfo1->thread_date - msginfo2->thread_date;
7657         
7658         if (msginfo1->thread_date > 0 && msginfo2->thread_date > 0)
7659                 return thread_diff;
7660         else 
7661                 return msginfo1->date_t - msginfo2->date_t;
7662 }
7663
7664 static gint summary_cmp_by_from(GtkCMCList *clist, gconstpointer ptr1,
7665                                 gconstpointer ptr2)
7666 {
7667         const gchar *str1, *str2;
7668         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7669         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7670         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7671         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7672         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7673         gint res;
7674
7675         cm_return_val_if_fail(sv, -1);
7676         if (sv->col_state[sv->col_pos[S_COL_FROM]].visible) {
7677                 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text;
7678                 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text;
7679         } else {
7680                 str1 = msginfo1->from;
7681                 str2 = msginfo2->from;
7682         }
7683
7684         if (!str1)
7685                 return str2 != NULL;
7686  
7687         if (!str2)
7688                 return -1;
7689  
7690         res = g_utf8_collate(str1, str2);
7691         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7692 }
7693  
7694 static gint summary_cmp_by_to(GtkCMCList *clist, gconstpointer ptr1,
7695                                 gconstpointer ptr2)
7696 {
7697         const gchar *str1, *str2;
7698         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7699         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7700         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7701         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7702         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7703         gint res;
7704         cm_return_val_if_fail(sv, -1);
7705         
7706         if (sv->col_state[sv->col_pos[S_COL_TO]].visible) {
7707                 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
7708                 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
7709         } else {
7710                 str1 = msginfo1->to;
7711                 str2 = msginfo2->to;
7712         }
7713
7714         if (!str1)
7715                 return str2 != NULL;
7716  
7717         if (!str2)
7718                 return -1;
7719  
7720         res = g_utf8_collate(str1, str2);
7721         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7722 }
7723  
7724 static gint summary_cmp_by_tags(GtkCMCList *clist, gconstpointer ptr1,
7725                                 gconstpointer ptr2)
7726 {
7727         gchar *str1, *str2;
7728         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7729         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7730         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7731         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7732         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7733         gint res;
7734         cm_return_val_if_fail(sv, -1);
7735         
7736         if (sv->col_state[sv->col_pos[S_COL_TAGS]].visible) {
7737                 str1 = g_strdup(GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TAGS]])->text);
7738                 str2 = g_strdup(GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TAGS]])->text);
7739         } else {
7740                 str1 = procmsg_msginfo_get_tags_str(msginfo1);
7741                 str2 = procmsg_msginfo_get_tags_str(msginfo2);
7742         }
7743
7744         if (!str1) {
7745                 res = (str2 != NULL);
7746                 g_free(str2);
7747                 return res;
7748         }
7749         if (!str2) {
7750                 g_free(str1);
7751                 return -1;
7752         }
7753  
7754         res = g_utf8_collate(str1, str2);
7755         g_free(str1);
7756         g_free(str2);
7757         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7758 }
7759  
7760 static gint summary_cmp_by_simplified_subject
7761         (GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2)
7762 {
7763         const FolderItemPrefs *prefs;
7764         const gchar *str1, *str2;
7765         const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7766         const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7767         const MsgInfo *msginfo1 = r1->data;
7768         const MsgInfo *msginfo2 = r2->data;
7769         const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7770         gint res;
7771
7772         cm_return_val_if_fail(sv, -1);
7773         cm_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1);
7774         
7775         if (sv->col_state[sv->col_pos[S_COL_SUBJECT]].visible) {
7776                 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7777                 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7778         } else {
7779                 str1 = msginfo1->subject;
7780                 str2 = msginfo2->subject;
7781         }
7782
7783         if (!str1)
7784                 return str2 != NULL;
7785
7786         if (!str2)
7787                 return -1;
7788
7789         prefs = msginfo1->folder->prefs;
7790         if (!prefs)
7791                 prefs = msginfo2->folder->prefs;
7792         if (!prefs)
7793                 return -1;
7794         
7795         res = subject_compare_for_sort(str1, str2);
7796         return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7797 }
7798
7799 static gint summary_cmp_by_score(GtkCMCList *clist,
7800                                  gconstpointer ptr1, gconstpointer ptr2)
7801 {
7802         MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7803         MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7804         int diff;
7805
7806         /* if score are equal, sort by date */
7807
7808         diff = msginfo1->score - msginfo2->score;
7809         if (diff != 0)
7810                 return diff;
7811         else
7812                 return summary_cmp_by_date(clist, ptr1, ptr2);
7813 }
7814
7815 static void summary_ignore_thread_func_mark_unread(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7816 {
7817         MsgInfo *msginfo;
7818
7819         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7820         cm_return_if_fail(msginfo);
7821
7822         summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
7823         summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
7824
7825         debug_print("Message %d is marked as ignore thread\n", msginfo->msgnum);
7826 }
7827
7828 static void summary_ignore_thread_func_set_row(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7829 {
7830         SummaryView *summaryview = (SummaryView *) data;
7831         MsgInfo *msginfo;
7832
7833         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7834         cm_return_if_fail(msginfo);
7835
7836         summary_set_row_marks(summaryview, row);
7837         debug_print("Message %d update in row view\n", msginfo->msgnum);
7838 }
7839
7840 void summary_ignore_thread(SummaryView *summaryview)
7841 {
7842         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7843         GList *cur;
7844         gboolean froze = FALSE;
7845
7846         START_LONG_OPERATION(summaryview, FALSE);
7847         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7848                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7849                                         GTK_CMCTREE_FUNC(summary_ignore_thread_func_mark_unread),
7850                                         summaryview);
7851
7852         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7853                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7854                                         GTK_CMCTREE_FUNC(summary_ignore_thread_func_set_row),
7855                                         summaryview);
7856
7857         END_LONG_OPERATION(summaryview);
7858
7859         summary_status_show(summaryview);
7860 }
7861
7862 static void summary_unignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7863 {
7864         SummaryView *summaryview = (SummaryView *) data;
7865         MsgInfo *msginfo;
7866
7867         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7868         cm_return_if_fail(msginfo);
7869
7870         summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0);
7871
7872         summary_set_row_marks(summaryview, row);
7873         debug_print("Message %d is marked as unignore thread\n",
7874             msginfo->msgnum);
7875 }
7876
7877 void summary_unignore_thread(SummaryView *summaryview)
7878 {
7879         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7880         GList *cur;
7881         gboolean froze = FALSE;
7882
7883         START_LONG_OPERATION(summaryview, FALSE);
7884         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7885                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7886                                         GTK_CMCTREE_FUNC(summary_unignore_thread_func), 
7887                                         summaryview);
7888
7889         END_LONG_OPERATION(summaryview);
7890
7891         summary_status_show(summaryview);
7892 }
7893
7894 static void summary_check_ignore_thread_func
7895                 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7896 {
7897         MsgInfo *msginfo;
7898         gint *found_ignore = (gint *) data;
7899
7900         if (*found_ignore) return;
7901         else {
7902                 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7903                 *found_ignore = msginfo && MSG_IS_IGNORE_THREAD(msginfo->flags);
7904         }               
7905 }
7906
7907 void summary_toggle_ignore_thread(SummaryView *summaryview)
7908 {
7909         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7910         GList *cur;
7911         gint found_ignore = 0;
7912
7913         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7914                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7915                                         GTK_CMCTREE_FUNC(summary_check_ignore_thread_func),
7916                                         &found_ignore);
7917
7918         if (found_ignore) 
7919                 summary_unignore_thread(summaryview);
7920         else 
7921                 summary_ignore_thread(summaryview);
7922 }
7923
7924 static void summary_watch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7925 {
7926         SummaryView *summaryview = (SummaryView *) data;
7927         MsgInfo *msginfo;
7928
7929         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7930         cm_return_if_fail(msginfo);
7931
7932         summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0);
7933
7934         summary_set_row_marks(summaryview, row);
7935         debug_print("Message %d is marked as watch thread\n",
7936             msginfo->msgnum);
7937 }
7938
7939 void summary_watch_thread(SummaryView *summaryview)
7940 {
7941         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7942         GList *cur;
7943         gboolean froze = FALSE;
7944
7945         START_LONG_OPERATION(summaryview, FALSE);
7946         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7947                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7948                                         GTK_CMCTREE_FUNC(summary_watch_thread_func), 
7949                                         summaryview);
7950
7951         END_LONG_OPERATION(summaryview);
7952
7953         summary_status_show(summaryview);
7954 }
7955
7956 static void summary_unwatch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7957 {
7958         SummaryView *summaryview = (SummaryView *) data;
7959         MsgInfo *msginfo;
7960
7961         msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7962         cm_return_if_fail(msginfo);
7963
7964         summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
7965
7966         summary_set_row_marks(summaryview, row);
7967         debug_print("Message %d is marked as unwatch thread\n",
7968             msginfo->msgnum);
7969 }
7970
7971 void summary_unwatch_thread(SummaryView *summaryview)
7972 {
7973         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7974         GList *cur;
7975         gboolean froze = FALSE;
7976
7977         START_LONG_OPERATION(summaryview, FALSE);
7978         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7979                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 
7980                                         GTK_CMCTREE_FUNC(summary_unwatch_thread_func), 
7981                                         summaryview);
7982
7983         END_LONG_OPERATION(summaryview);
7984
7985         summary_status_show(summaryview);
7986 }
7987
7988 static void summary_check_watch_thread_func
7989                 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7990 {
7991         MsgInfo *msginfo;
7992         gint *found_watch = (gint *) data;
7993
7994         if (*found_watch) return;
7995         else {
7996                 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7997                 *found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags);
7998         }               
7999 }
8000
8001 void summary_toggle_watch_thread(SummaryView *summaryview)
8002 {
8003         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8004         GList *cur;
8005         gint found_watch = 0;
8006
8007         for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8008                 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8009                                         GTK_CMCTREE_FUNC(summary_check_watch_thread_func),
8010                                         &found_watch);
8011
8012         if (found_watch) 
8013                 summary_unwatch_thread(summaryview);
8014         else 
8015                 summary_watch_thread(summaryview);
8016 }
8017
8018
8019 void summary_toggle_show_read_messages(SummaryView *summaryview)
8020 {
8021         FolderItemUpdateData source;
8022         if (summaryview->folder_item->hide_read_msgs)
8023                 summaryview->folder_item->hide_read_msgs = 0;
8024         else
8025                 summaryview->folder_item->hide_read_msgs = 1;
8026
8027         source.item = summaryview->folder_item;
8028         source.update_flags = F_ITEM_UPDATE_NAME;
8029         source.msg = NULL;
8030         hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8031         summary_show(summaryview, summaryview->folder_item);
8032 }
8033  
8034 void summary_toggle_show_del_messages(SummaryView *summaryview)
8035 {
8036         FolderItemUpdateData source;
8037         if (summaryview->folder_item->hide_del_msgs)
8038                 summaryview->folder_item->hide_del_msgs = 0;
8039         else
8040                 summaryview->folder_item->hide_del_msgs = 1;
8041
8042         source.item = summaryview->folder_item;
8043         source.update_flags = F_ITEM_UPDATE_NAME;
8044         source.msg = NULL;
8045         hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8046         summary_show(summaryview, summaryview->folder_item);
8047 }
8048  
8049 void summary_toggle_show_read_threads(SummaryView *summaryview)
8050 {
8051         FolderItemUpdateData source;
8052         if (summaryview->folder_item->hide_read_threads)
8053                 summaryview->folder_item->hide_read_threads = 0;
8054         else
8055                 summaryview->folder_item->hide_read_threads = 1;
8056
8057         source.item = summaryview->folder_item;
8058         source.update_flags = F_ITEM_UPDATE_NAME;
8059         source.msg = NULL;
8060         hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8061         summary_show(summaryview, summaryview->folder_item);
8062 }
8063  
8064 static void summary_set_hide_menu (SummaryView *summaryview,
8065                                    const gchar *menu_item,
8066                                    guint action)
8067 {
8068         GtkWidget *widget;
8069
8070         widget = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, menu_item);
8071         cm_return_if_fail(widget != NULL);
8072
8073         g_object_set_data(G_OBJECT(widget), "dont_toggle",
8074                           GINT_TO_POINTER(1));
8075         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action);
8076         g_object_set_data(G_OBJECT(widget), "dont_toggle",
8077                           GINT_TO_POINTER(0));
8078 }
8079
8080 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
8081 {
8082         GtkWidget *pixmap; 
8083
8084         stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
8085         stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
8086         stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
8087         stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
8088         stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
8089         stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
8090         stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
8091         stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
8092         stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
8093         stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
8094         stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
8095         stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
8096         stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
8097         stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
8098         stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
8099         stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
8100         stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
8101         stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
8102         stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
8103
8104         summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8105
8106         pixmap = stock_pixmap_widget(STOCK_PIXMAP_QUICKSEARCH);
8107         gtk_container_remove (GTK_CONTAINER(summaryview->toggle_search), 
8108                               summaryview->quick_search_pixmap);
8109         gtk_container_add(GTK_CONTAINER(summaryview->toggle_search), pixmap);
8110         gtk_widget_show(pixmap);
8111         summaryview->quick_search_pixmap = pixmap;
8112
8113 #ifdef GENERIC_UMPC
8114         pixmap = stock_pixmap_widget(STOCK_PIXMAP_SELECTION);
8115         gtk_container_remove (GTK_CONTAINER(summaryview->multiple_sel_togbtn), 
8116                               summaryview->multiple_sel_image);
8117         gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
8118         gtk_widget_show(pixmap);
8119         summaryview->multiple_sel_togbtn = pixmap;
8120 #endif
8121
8122         folderview_unselect(summaryview->folderview);
8123         folderview_select(summaryview->folderview, summaryview->folder_item);
8124         summary_set_column_titles(summaryview);
8125 }
8126
8127 void summary_reflect_prefs_custom_colors(SummaryView *summaryview)
8128 {
8129         GtkMenuShell *menu;
8130         GList *children, *cur;
8131
8132         /* re-create colorlabel submenu */
8133         menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
8134         cm_return_if_fail(menu != NULL);
8135
8136         /* clear items. get item pointers. */
8137         children = gtk_container_get_children(GTK_CONTAINER(menu));
8138         for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
8139                 g_signal_handlers_disconnect_matched
8140                          (gtk_ui_manager_get_accel_group(summaryview->mainwin->ui_manager), 
8141                          G_SIGNAL_MATCH_DATA|G_SIGNAL_MATCH_FUNC,
8142                          0, 0, NULL, mainwin_accel_changed_cb, cur->data);
8143                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
8144         }
8145         g_list_free(children);
8146         summary_colorlabel_menu_create(summaryview, TRUE);
8147 }
8148
8149 /*
8150  * Harvest addresses for selected messages in summary view.
8151  */
8152 void summary_harvest_address(SummaryView *summaryview)
8153 {
8154         GtkCMCTree *ctree = GTK_CMCTREE( summaryview->ctree );
8155         GList *cur;
8156         GList *msgList;
8157         MsgInfo *msginfo;
8158
8159         msgList = NULL;
8160         for( cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next ) {
8161                 msginfo = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(cur->data) );
8162                 if (!msginfo)
8163                         continue;
8164                 msgList = g_list_append( msgList, GUINT_TO_POINTER( msginfo->msgnum ) );
8165         }
8166
8167         addressbook_harvest( summaryview->folder_item, TRUE, msgList );
8168
8169         g_list_free( msgList );
8170 }
8171
8172 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp)
8173 {
8174         int err;
8175         gchar buf[BUFFSIZE];
8176         regex_t *preg = NULL;
8177         
8178         preg = g_new0(regex_t, 1);
8179
8180         err = string_match_precompile(simplify_subject_regexp, 
8181                                       preg, REG_EXTENDED);
8182         if (err) {
8183                 regerror(err, preg, buf, BUFFSIZE);
8184                 alertpanel_error(_("Regular expression (regexp) error:\n%s"), buf);
8185                 g_free(preg);
8186                 preg = NULL;
8187         }
8188         
8189         return preg;
8190 }
8191 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item)
8192 {
8193         FolderSortKey sort_key;
8194         FolderSortType sort_type;
8195         cm_return_if_fail(summaryview != NULL);
8196         cm_return_if_fail(item != NULL);
8197
8198         /* Subject simplification */
8199         if(summaryview->simplify_subject_preg) {
8200                 regfree(summaryview->simplify_subject_preg);
8201                 g_free(summaryview->simplify_subject_preg);
8202                 summaryview->simplify_subject_preg = NULL;
8203         }
8204         if(item->prefs && item->prefs->simplify_subject_regexp && 
8205            item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject)
8206                 summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp);
8207
8208         /* Sorting */
8209         sort_key = item->sort_key;
8210         sort_type = item->sort_type;
8211
8212         folder_get_sort_type(item->folder, &sort_key, &sort_type);
8213
8214         summaryview->sort_key = sort_key;
8215         summaryview->sort_type = sort_type;
8216
8217         /* Threading */
8218         summaryview->threaded = item->threaded;
8219         summaryview->thread_collapsed = item->thread_collapsed;
8220
8221         /* Scoring */
8222 }
8223
8224 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item)
8225 {
8226         /* Sorting */
8227         item->sort_key = summaryview->sort_key;
8228         item->sort_type = summaryview->sort_type;
8229
8230         /* Threading */
8231         item->threaded = summaryview->threaded;
8232         item->thread_collapsed = summaryview->thread_collapsed;
8233 }
8234
8235 static gboolean summary_update_msg(gpointer source, gpointer data) 
8236 {
8237         MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source;
8238         SummaryView *summaryview = (SummaryView *)data;
8239         GtkCMCTreeNode *node;
8240
8241         cm_return_val_if_fail(msginfo_update != NULL, TRUE);
8242         cm_return_val_if_fail(summaryview != NULL, FALSE);
8243
8244         if (msginfo_update->msginfo->folder != summaryview->folder_item)
8245                 return FALSE;
8246
8247         if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) {
8248                 node = gtk_cmctree_find_by_row_data(
8249                                 GTK_CMCTREE(summaryview->ctree), NULL, 
8250                                 msginfo_update->msginfo);
8251
8252                 if (node) 
8253                         summary_set_row_marks(summaryview, node);
8254         }
8255
8256         return FALSE;
8257 }
8258
8259 void summary_update_unread(SummaryView *summaryview, FolderItem *removed_item)
8260 {
8261         guint new, unread, unreadmarked, marked, total;
8262         guint replied, forwarded, locked, ignored, watched;
8263         static gboolean tips_initialized = FALSE;
8264
8265         if (prefs_common.layout_mode != SMALL_LAYOUT) {
8266                 if (tips_initialized) {
8267                         summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8268                         CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8269                              NULL);
8270                         tips_initialized = FALSE;
8271                 } 
8272                 return;
8273         }
8274         folder_count_total_msgs(&new, &unread, &unreadmarked, &marked, &total,
8275                                 &replied, &forwarded, &locked, &ignored,
8276                                 &watched);
8277         if (removed_item) {
8278                 total -= removed_item->total_msgs;
8279                 new -= removed_item->new_msgs;
8280                 unread -= removed_item->unread_msgs;
8281         }
8282         
8283         if (new > 0 || unread > 0) {
8284                 tips_initialized = TRUE;
8285                 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN_HRM);
8286                 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8287                              _("Go back to the folder list (You have unread messages)"));
8288         } else {
8289                 tips_initialized = TRUE;
8290                 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8291                 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8292                              _("Go back to the folder list"));
8293         }
8294 }
8295
8296 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data)
8297 {
8298         FolderItemUpdateData *hookdata = (FolderItemUpdateData *)source;
8299         SummaryView *summaryview = (SummaryView *)data;
8300
8301         cm_return_val_if_fail(hookdata != NULL, FALSE);
8302         cm_return_val_if_fail(hookdata->item != NULL, FALSE);
8303         cm_return_val_if_fail(summaryview != NULL, FALSE);
8304
8305         if (hookdata->item == summaryview->folder_item &&
8306             hookdata->update_flags & F_ITEM_UPDATE_NAME) {
8307                 gchar *name = folder_item_get_name(hookdata->item);
8308                 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
8309                 g_free(name);
8310         }
8311         summary_update_unread(summaryview, NULL);
8312
8313         return FALSE;
8314 }
8315
8316 static gboolean summary_update_folder_hook(gpointer source, gpointer data)
8317 {
8318         FolderUpdateData *hookdata;
8319         SummaryView *summaryview = (SummaryView *)data;
8320         hookdata = source;
8321         if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
8322                 summary_update_unread(summaryview, hookdata->item);
8323         } else
8324                 summary_update_unread(summaryview, NULL);
8325
8326         return FALSE;
8327 }
8328
8329 /*!
8330  *\brief        change summaryview to display your answer(s) to a message
8331  *
8332  *\param        summaryview The SummaryView ;)
8333  *\param        msginfo The message for which answers are searched
8334  *
8335  */
8336 static void summary_find_answers (SummaryView *summaryview, MsgInfo *msg)
8337 {
8338         FolderItem *sent_folder = NULL;
8339         PrefsAccount *account = NULL;
8340         GtkCMCTreeNode *node = NULL;
8341         char *buf = NULL;
8342         if (msg == NULL || msg->msgid == NULL)
8343                 return;
8344         
8345         account = account_get_reply_account(msg, prefs_common.reply_account_autosel);
8346         if (account == NULL) 
8347                 return;
8348         sent_folder = account_get_special_folder
8349                                 (account, F_OUTBOX);
8350         
8351         buf = g_strdup_printf("inreplyto matchcase \"%s\"", msg->msgid);
8352
8353         if (sent_folder != summaryview->folder_item) {
8354                 folderview_select(summaryview->mainwin->folderview, sent_folder);
8355         }
8356         
8357         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
8358
8359         quicksearch_set(summaryview->quicksearch, ADVANCED_SEARCH_EXTENDED, buf);
8360         g_free(buf);
8361
8362         node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
8363         if (node)
8364                 summary_select_node(summaryview, node, OPEN_SELECTED_ON_SEARCH_RESULTS);
8365 }
8366
8367 gint summaryview_export_mbox_list(SummaryView *summaryview)
8368 /* return values: -2 skipped, -1 error, 0 OK */
8369 {
8370         GSList *list = summary_get_selected_msg_list(summaryview);
8371         gchar *mbox = filesel_select_file_save(_("Export to mbox file"), NULL);
8372         gint ret;
8373         
8374         if (mbox == NULL)
8375                 return -2;
8376         if (list == NULL)
8377                 return -1;
8378                 
8379         ret = export_list_to_mbox(list, mbox);
8380         
8381         g_slist_free(list);
8382         g_free(mbox);
8383         
8384         return ret;
8385 }
8386
8387 void summaryview_lock(SummaryView *summaryview, FolderItem *item)
8388 {
8389         if (!summaryview || !summaryview->folder_item || !item) {
8390                 return;
8391         }
8392
8393         if (summaryview->folder_item->folder == item->folder) {
8394                 gtk_widget_set_sensitive(summaryview->ctree, FALSE);
8395         }
8396 }
8397 void summaryview_unlock(SummaryView *summaryview, FolderItem *item)
8398 {
8399         gtk_widget_set_sensitive(summaryview->ctree, TRUE);
8400 }