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