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