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