Statistics
| Revision:

root / src / summaryview.c @ 1

History | View | Annotate | Download (117 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 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/gtkctree.h>
27
#include <gtk/gtkcontainer.h>
28
#include <gtk/gtksignal.h>
29
#include <gtk/gtktext.h>
30
#include <gtk/gtkmenu.h>
31
#include <gtk/gtkmenuitem.h>
32
#include <gtk/gtkitemfactory.h>
33
#include <gtk/gtkvbox.h>
34
#include <gtk/gtkhbox.h>
35
#include <gtk/gtkwindow.h>
36
#include <gtk/gtkstyle.h>
37
#include <gtk/gtkarrow.h>
38
#include <gtk/gtkeventbox.h>
39
#include <gtk/gtkstatusbar.h>
40

    
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <ctype.h>
45
#include <unistd.h>
46

    
47
#include "intl.h"
48
#include "main.h"
49
#include "menu.h"
50
#include "mainwindow.h"
51
#include "folderview.h"
52
#include "summaryview.h"
53
#include "messageview.h"
54
#include "foldersel.h"
55
#include "procmsg.h"
56
#include "procheader.h"
57
#include "sourcewindow.h"
58
#include "prefs_common.h"
59
#include "prefs_summary_column.h"
60
#include "prefs_filter.h"
61
#include "account.h"
62
#include "compose.h"
63
#include "utils.h"
64
#include "gtkutils.h"
65
#include "stock_pixmap.h"
66
#include "filesel.h"
67
#include "alertpanel.h"
68
#include "inputdialog.h"
69
#include "statusbar.h"
70
#include "filter.h"
71
#include "folder.h"
72
#include "colorlabel.h"
73
#include "inc.h"
74
#include "imap.h"
75

    
76
#define STATUSBAR_PUSH(mainwin, str) \
77
{ \
78
        gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
79
                           mainwin->summaryview_cid, str); \
80
        gtkut_widget_wait_for_draw(mainwin->hbox_stat); \
81
}
82

    
83
#define STATUSBAR_POP(mainwin) \
84
{ \
85
        gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
86
                          mainwin->summaryview_cid); \
87
}
88

    
89
#define SUMMARY_COL_MARK_WIDTH                10
90
#define SUMMARY_COL_UNREAD_WIDTH        13
91
#define SUMMARY_COL_MIME_WIDTH                10
92

    
93
static GtkStyle *bold_style;
94
static GtkStyle *bold_marked_style;
95
static GtkStyle *bold_deleted_style;
96

    
97
static GdkPixmap *markxpm;
98
static GdkBitmap *markxpmmask;
99
static GdkPixmap *deletedxpm;
100
static GdkBitmap *deletedxpmmask;
101

    
102
static GdkPixmap *mailxpm;
103
static GdkBitmap *mailxpmmask;
104
static GdkPixmap *newxpm;
105
static GdkBitmap *newxpmmask;
106
static GdkPixmap *unreadxpm;
107
static GdkBitmap *unreadxpmmask;
108
static GdkPixmap *repliedxpm;
109
static GdkBitmap *repliedxpmmask;
110
static GdkPixmap *forwardedxpm;
111
static GdkBitmap *forwardedxpmmask;
112

    
113
static GdkPixmap *clipxpm;
114
static GdkBitmap *clipxpmmask;
115

    
116
static void summary_free_msginfo_func        (GtkCTree                *ctree,
117
                                         GtkCTreeNode                *node,
118
                                         gpointer                 data);
119
static void summary_set_marks_func        (GtkCTree                *ctree,
120
                                         GtkCTreeNode                *node,
121
                                         gpointer                 data);
122
static void summary_write_cache_func        (GtkCTree                *ctree,
123
                                         GtkCTreeNode                *node,
124
                                         gpointer                 data);
125

    
126
static void summary_set_menu_sensitive        (SummaryView                *summaryview);
127

    
128
static guint summary_get_msgnum                (SummaryView                *summaryview,
129
                                         GtkCTreeNode                *node);
130

    
131
static GtkCTreeNode *summary_find_prev_msg
132
                                        (SummaryView                *summaryview,
133
                                         GtkCTreeNode                *current_node);
134
static GtkCTreeNode *summary_find_next_msg
135
                                        (SummaryView                *summaryview,
136
                                         GtkCTreeNode                *current_node);
137

    
138
static GtkCTreeNode *summary_find_prev_flagged_msg
139
                                        (SummaryView        *summaryview,
140
                                         GtkCTreeNode        *current_node,
141
                                         MsgPermFlags         flags,
142
                                         gboolean         start_from_prev);
143
static GtkCTreeNode *summary_find_next_flagged_msg
144
                                        (SummaryView        *summaryview,
145
                                         GtkCTreeNode        *current_node,
146
                                         MsgPermFlags         flags,
147
                                         gboolean         start_from_next);
148

    
149
static GtkCTreeNode *summary_find_msg_by_msgnum
150
                                        (SummaryView                *summaryview,
151
                                         guint                         msgnum);
152

    
153
static void summary_update_status        (SummaryView                *summaryview);
154

    
155
/* display functions */
156
static void summary_status_show                (SummaryView                *summaryview);
157
static void summary_set_column_titles        (SummaryView                *summaryview);
158
static void summary_set_ctree_from_list        (SummaryView                *summaryview,
159
                                         GSList                        *mlist);
160
static void summary_set_header                (SummaryView                *summaryview,
161
                                         gchar                        *text[],
162
                                         MsgInfo                *msginfo);
163
static void summary_display_msg                (SummaryView                *summaryview,
164
                                         GtkCTreeNode                *row);
165
static void summary_display_msg_full        (SummaryView                *summaryview,
166
                                         GtkCTreeNode                *row,
167
                                         gboolean                 new_window,
168
                                         gboolean                 all_headers);
169
static void summary_set_row_marks        (SummaryView                *summaryview,
170
                                         GtkCTreeNode                *row);
171

    
172
/* message handling */
173
static void summary_mark_row                (SummaryView                *summaryview,
174
                                         GtkCTreeNode                *row);
175
static void summary_mark_row_as_read        (SummaryView                *summaryview,
176
                                         GtkCTreeNode                *row);
177
static void summary_mark_row_as_unread        (SummaryView                *summaryview,
178
                                         GtkCTreeNode                *row);
179
static void summary_delete_row                (SummaryView                *summaryview,
180
                                         GtkCTreeNode                *row);
181
static void summary_unmark_row                (SummaryView                *summaryview,
182
                                         GtkCTreeNode                *row);
183
static void summary_move_row_to                (SummaryView                *summaryview,
184
                                         GtkCTreeNode                *row,
185
                                         FolderItem                *to_folder);
186
static void summary_copy_row_to                (SummaryView                *summaryview,
187
                                         GtkCTreeNode                *row,
188
                                         FolderItem                *to_folder);
189

    
190
static void summary_delete_duplicated_func
191
                                        (GtkCTree                *ctree,
192
                                         GtkCTreeNode                *node,
193
                                         SummaryView                *summaryview);
194

    
195
static void summary_remove_invalid_messages
196
                                        (SummaryView                *summaryview);
197

    
198
static gint summary_execute_move        (SummaryView                *summaryview);
199
static void summary_execute_move_func        (GtkCTree                *ctree,
200
                                         GtkCTreeNode                *node,
201
                                         gpointer                 data);
202
static gint summary_execute_copy        (SummaryView                *summaryview);
203
static void summary_execute_copy_func        (GtkCTree                *ctree,
204
                                         GtkCTreeNode                *node,
205
                                         gpointer                 data);
206
static gint summary_execute_delete        (SummaryView                *summaryview);
207
static void summary_execute_delete_func        (GtkCTree                *ctree,
208
                                         GtkCTreeNode                *node,
209
                                         gpointer                 data);
210

    
211
static void summary_thread_init                (SummaryView                *summaryview);
212

    
213
static void summary_unthread_for_exec                (SummaryView        *summaryview);
214
static void summary_unthread_for_exec_func        (GtkCTree        *ctree,
215
                                                 GtkCTreeNode        *node,
216
                                                 gpointer         data);
217

    
218
static void summary_filter_func                (GtkCTree                *ctree,
219
                                         GtkCTreeNode                *node,
220
                                         gpointer                 data);
221

    
222
static void summary_colorlabel_menu_item_activate_cb
223
                                          (GtkWidget        *widget,
224
                                           gpointer         data);
225
static void summary_colorlabel_menu_item_activate_item_cb
226
                                          (GtkMenuItem        *label_menu_item,
227
                                           gpointer         data);
228
static void summary_colorlabel_menu_create(SummaryView        *summaryview);
229

    
230
static GtkWidget *summary_ctree_create        (SummaryView        *summaryview);
231

    
232
/* callback functions */
233
static gboolean summary_toggle_pressed        (GtkWidget                *eventbox,
234
                                         GdkEventButton                *event,
235
                                         SummaryView                *summaryview);
236
static gboolean summary_button_pressed        (GtkWidget                *ctree,
237
                                         GdkEventButton                *event,
238
                                         SummaryView                *summaryview);
239
static gboolean summary_button_released        (GtkWidget                *ctree,
240
                                         GdkEventButton                *event,
241
                                         SummaryView                *summaryview);
242
static gboolean summary_key_pressed        (GtkWidget                *ctree,
243
                                         GdkEventKey                *event,
244
                                         SummaryView                *summaryview);
245
static void summary_open_row                (GtkSCTree                *sctree,
246
                                         SummaryView                *summaryview);
247
static void summary_tree_expanded        (GtkCTree                *ctree,
248
                                         GtkCTreeNode                *node,
249
                                         SummaryView                *summaryview);
250
static void summary_tree_collapsed        (GtkCTree                *ctree,
251
                                         GtkCTreeNode                *node,
252
                                         SummaryView                *summaryview);
253
static void summary_selected                (GtkCTree                *ctree,
254
                                         GtkCTreeNode                *row,
255
                                         gint                         column,
256
                                         SummaryView                *summaryview);
257
static void summary_col_resized                (GtkCList                *clist,
258
                                         gint                         column,
259
                                         gint                         width,
260
                                         SummaryView                *summaryview);
261
static void summary_reply_cb                (SummaryView                *summaryview,
262
                                         guint                         action,
263
                                         GtkWidget                *widget);
264
static void summary_show_all_header_cb        (SummaryView                *summaryview,
265
                                         guint                         action,
266
                                         GtkWidget                *widget);
267

    
268
static void summary_add_address_cb        (SummaryView                *summaryview,
269
                                         guint                         action,
270
                                         GtkWidget                *widget);
271

    
272
static void summary_mark_clicked        (GtkWidget                *button,
273
                                         SummaryView                *summaryview);
274
static void summary_unread_clicked        (GtkWidget                *button,
275
                                         SummaryView                *summaryview);
276
static void summary_mime_clicked        (GtkWidget                *button,
277
                                         SummaryView                *summaryview);
278
static void summary_num_clicked                (GtkWidget                *button,
279
                                         SummaryView                *summaryview);
280
static void summary_size_clicked        (GtkWidget                *button,
281
                                         SummaryView                *summaryview);
282
static void summary_date_clicked        (GtkWidget                *button,
283
                                         SummaryView                *summaryview);
284
static void summary_from_clicked        (GtkWidget                *button,
285
                                         SummaryView                *summaryview);
286
static void summary_subject_clicked        (GtkWidget                *button,
287
                                         SummaryView                *summaryview);
288

    
289
static void summary_start_drag                (GtkWidget        *widget, 
290
                                         int button,
291
                                         GdkEvent *event,
292
                                         SummaryView      *summaryview);
293
static void summary_drag_data_get       (GtkWidget        *widget,
294
                                         GdkDragContext   *drag_context,
295
                                         GtkSelectionData *selection_data,
296
                                         guint             info,
297
                                         guint             time,
298
                                         SummaryView      *summaryview);
299

    
300
/* custom compare functions for sorting */
301

    
302
static gint summary_cmp_by_mark                (GtkCList                *clist,
303
                                         gconstpointer                 ptr1,
304
                                         gconstpointer                 ptr2);
305
static gint summary_cmp_by_unread        (GtkCList                *clist,
306
                                         gconstpointer                 ptr1,
307
                                         gconstpointer                 ptr2);
308
static gint summary_cmp_by_mime                (GtkCList                *clist,
309
                                         gconstpointer                 ptr1,
310
                                         gconstpointer                 ptr2);
311
static gint summary_cmp_by_num                (GtkCList                *clist,
312
                                         gconstpointer                 ptr1,
313
                                         gconstpointer                 ptr2);
314
static gint summary_cmp_by_size                (GtkCList                *clist,
315
                                         gconstpointer                 ptr1,
316
                                         gconstpointer                 ptr2);
317
static gint summary_cmp_by_date                (GtkCList                *clist,
318
                                         gconstpointer                 ptr1,
319
                                         gconstpointer                 ptr2);
320
static gint summary_cmp_by_from                (GtkCList                *clist,
321
                                         gconstpointer                 ptr1,
322
                                         gconstpointer                 ptr2);
323
static gint summary_cmp_by_label        (GtkCList                *clist,
324
                                         gconstpointer                 ptr1,
325
                                         gconstpointer                 ptr2);
326
static gint summary_cmp_by_to                (GtkCList                *clist,
327
                                         gconstpointer                 ptr1,
328
                                         gconstpointer                 ptr2);
329
static gint summary_cmp_by_subject        (GtkCList                *clist,
330
                                         gconstpointer                 ptr1,
331
                                         gconstpointer                 ptr2);
332

    
333
GtkTargetEntry summary_drag_types[1] =
334
{
335
        {"text/plain", GTK_TARGET_SAME_APP, TARGET_DUMMY}
336
};
337

    
338
static GtkItemFactoryEntry summary_popup_entries[] =
339
{
340
        {N_("/_Reply"),                        NULL, summary_reply_cb,        COMPOSE_REPLY, NULL},
341
        {N_("/Repl_y to"),                NULL, NULL,                0, "<Branch>"},
342
        {N_("/Repl_y to/_all"),                NULL, summary_reply_cb,        COMPOSE_REPLY_TO_ALL, NULL},
343
        {N_("/Repl_y to/_sender"),        NULL, summary_reply_cb,        COMPOSE_REPLY_TO_SENDER, NULL},
344
        {N_("/Repl_y to/mailing _list"),
345
                                        NULL, summary_reply_cb,        COMPOSE_REPLY_TO_LIST, NULL},
346
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
347
        {N_("/_Forward"),                NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
348
        {N_("/For_ward as attachment"),        NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
349
        {N_("/Redirec_t"),                NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL},
350
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
351
        {N_("/M_ove..."),                NULL, summary_move_to,        0, NULL},
352
        {N_("/_Copy..."),                NULL, summary_copy_to,        0, NULL},
353
        {N_("/_Delete"),                NULL, summary_delete,        0, NULL},
354
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
355
        {N_("/_Mark"),                        NULL, NULL,                0, "<Branch>"},
356
        {N_("/_Mark/_Mark"),                NULL, summary_mark,        0, NULL},
357
        {N_("/_Mark/_Unmark"),                NULL, summary_unmark,        0, NULL},
358
        {N_("/_Mark/---"),                NULL, NULL,                0, "<Separator>"},
359
        {N_("/_Mark/Mark as unr_ead"),        NULL, summary_mark_as_unread, 0, NULL},
360
        {N_("/_Mark/Mark as rea_d"),
361
                                        NULL, summary_mark_as_read, 0, NULL},
362
        {N_("/_Mark/Mark all _read"),        NULL, summary_mark_all_read, 0, NULL},
363
        {N_("/Color la_bel"),                NULL, NULL,                0, NULL},
364
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
365
        {N_("/Re-_edit"),                NULL, summary_reedit,   0, NULL},
366
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
367
        {N_("/Add sender to address boo_k"),
368
                                        NULL, summary_add_address_cb, 0, NULL},
369
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
370
        {N_("/_View"),                        NULL, NULL,                0, "<Branch>"},
371
        {N_("/_View/Open in new _window"),
372
                                        NULL, summary_open_msg,        0, NULL},
373
        {N_("/_View/_Source"),                NULL, summary_view_source, 0, NULL},
374
        {N_("/_View/All _header"),        NULL, summary_show_all_header_cb, 0, "<ToggleItem>"},
375
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
376
        {N_("/_Print..."),                NULL, summary_print,        0, NULL}
377
};
378

    
379
static const gchar *const col_label[N_SUMMARY_COLS] = {
380
        N_("M"),        /* S_COL_MARK    */
381
        N_("U"),        /* S_COL_UNREAD  */
382
        "",                /* S_COL_MIME    */
383
        N_("Subject"),        /* S_COL_SUBJECT */
384
        N_("From"),        /* S_COL_FROM    */
385
        N_("Date"),        /* S_COL_DATE    */
386
        N_("Size"),        /* S_COL_SIZE    */
387
        N_("No."),        /* S_COL_NUMBER  */
388
};
389

    
390
SummaryView *summary_create(void)
391
{
392
        SummaryView *summaryview;
393
        GtkWidget *vbox;
394
        GtkWidget *scrolledwin;
395
        GtkWidget *ctree;
396
        GtkWidget *hbox;
397
        GtkWidget *hbox_l;
398
        GtkWidget *statlabel_folder;
399
        GtkWidget *statlabel_select;
400
        GtkWidget *statlabel_msgs;
401
        GtkWidget *hbox_spc;
402
        GtkWidget *toggle_eventbox;
403
        GtkWidget *toggle_arrow;
404
        GtkWidget *popupmenu;
405
        GtkItemFactory *popupfactory;
406
        gint n_entries;
407
        GList *child;
408

    
409
        debug_print(_("Creating summary view...\n"));
410
        summaryview = g_new0(SummaryView, 1);
411

    
412
        vbox = gtk_vbox_new(FALSE, 2);
413

    
414
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
415
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
416
                                       GTK_POLICY_AUTOMATIC,
417
                                       GTK_POLICY_ALWAYS);
418
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
419
        gtk_widget_set_size_request(vbox,
420
                                    prefs_common.summaryview_width,
421
                                    prefs_common.summaryview_height);
422

    
423
        ctree = summary_ctree_create(summaryview);
424

    
425
        gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
426
                                            GTK_CLIST(ctree)->hadjustment);
427
        gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
428
                                            GTK_CLIST(ctree)->vadjustment);
429
        gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
430

    
431
        /* create status label */
432
        hbox = gtk_hbox_new(FALSE, 0);
433
        gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
434

    
435
        hbox_l = gtk_hbox_new(FALSE, 0);
436
        gtk_box_pack_start(GTK_BOX(hbox), hbox_l, TRUE, TRUE, 0);
437

    
438
        statlabel_folder = gtk_label_new("");
439
        gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
440
        statlabel_select = gtk_label_new("");
441
        gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
442

    
443
        /* toggle view button */
444
        toggle_eventbox = gtk_event_box_new();
445
        gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
446
        toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
447
        gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
448
        g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
449
                         G_CALLBACK(summary_toggle_pressed), summaryview);
450

    
451
        statlabel_msgs = gtk_label_new("");
452
        gtk_box_pack_end(GTK_BOX(hbox), statlabel_msgs, FALSE, FALSE, 4);
453

    
454
        hbox_spc = gtk_hbox_new(FALSE, 0);
455
        gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
456

    
457
        /* create popup menu */
458
        n_entries = sizeof(summary_popup_entries) /
459
                sizeof(summary_popup_entries[0]);
460
        popupmenu = menu_create_items(summary_popup_entries, n_entries,
461
                                      "<SummaryView>", &popupfactory,
462
                                      summaryview);
463

    
464
        summaryview->vbox = vbox;
465
        summaryview->scrolledwin = scrolledwin;
466
        summaryview->ctree = ctree;
467
        summaryview->hbox = hbox;
468
        summaryview->hbox_l = hbox_l;
469
        summaryview->statlabel_folder = statlabel_folder;
470
        summaryview->statlabel_select = statlabel_select;
471
        summaryview->statlabel_msgs = statlabel_msgs;
472
        summaryview->toggle_eventbox = toggle_eventbox;
473
        summaryview->toggle_arrow = toggle_arrow;
474
        summaryview->popupmenu = popupmenu;
475
        summaryview->popupfactory = popupfactory;
476
        summaryview->lock_count = 0;
477

    
478
        summaryview->reedit_menuitem =
479
                gtk_item_factory_get_widget(popupfactory, "/Re-edit");
480
        child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
481
                            summaryview->reedit_menuitem);
482
        summaryview->reedit_separator = GTK_WIDGET(child->next->data);
483

    
484
        gtk_widget_show_all(vbox);
485

    
486
        return summaryview;
487
}
488

    
489
void summary_init(SummaryView *summaryview)
490
{
491
        GtkStyle *style;
492
        GtkWidget *pixmap;
493

    
494
        gtk_widget_realize(summaryview->ctree);
495
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_MARK,
496
                         &markxpm, &markxpmmask);
497
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_DELETED,
498
                         &deletedxpm, &deletedxpmmask);
499
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_MAIL_SMALL,
500
                         &mailxpm, &mailxpmmask);
501
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_NEW,
502
                         &newxpm, &newxpmmask);
503
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_UNREAD,
504
                         &unreadxpm, &unreadxpmmask);
505
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_REPLIED,
506
                         &repliedxpm, &repliedxpmmask);
507
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_FORWARDED,
508
                         &forwardedxpm, &forwardedxpmmask);
509
        stock_pixmap_gdk(summaryview->ctree, STOCK_PIXMAP_CLIP,
510
                         &clipxpm, &clipxpmmask);
511

    
512
        if (!bold_style) {
513
                PangoFontDescription *font_desc = NULL;
514

    
515
                bold_style = gtk_style_copy
516
                        (gtk_widget_get_style(summaryview->ctree));
517
                if (prefs_common.boldfont)
518
                        font_desc = pango_font_description_from_string
519
                                (prefs_common.boldfont);
520
                if (font_desc) {
521
                        if (bold_style->font_desc)
522
                                pango_font_description_free
523
                                        (bold_style->font_desc);
524
                        bold_style->font_desc = font_desc;
525
                }
526
                bold_marked_style = gtk_style_copy(bold_style);
527
                bold_marked_style->fg[GTK_STATE_NORMAL] =
528
                        summaryview->color_marked;
529
                bold_deleted_style = gtk_style_copy(bold_style);
530
                bold_deleted_style->fg[GTK_STATE_NORMAL] =
531
                        summaryview->color_dim;
532
        }
533

    
534
        style = gtk_style_copy(gtk_widget_get_style
535
                                (summaryview->statlabel_folder));
536
        if (prefs_common.smallfont) {
537
                PangoFontDescription *font_desc = NULL;
538

    
539
                if (prefs_common.smallfont)
540
                        font_desc = pango_font_description_from_string
541
                                (prefs_common.smallfont);
542
                if (font_desc) {
543
                        if (style->font_desc)
544
                                pango_font_description_free(style->font_desc);
545
                        style->font_desc = font_desc;
546
                }
547
        }
548
        gtk_widget_set_style(summaryview->statlabel_folder, style);
549
        gtk_widget_set_style(summaryview->statlabel_select, style);
550
        gtk_widget_set_style(summaryview->statlabel_msgs, style);
551

    
552
        pixmap = stock_pixmap_widget(summaryview->hbox_l, STOCK_PIXMAP_DIR_OPEN);
553
        gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), pixmap, FALSE, FALSE, 4);
554
        gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), pixmap, 0);
555
        gtk_widget_show(pixmap);
556

    
557
        summary_clear_list(summaryview);
558
        summary_set_column_titles(summaryview);
559
        summary_colorlabel_menu_create(summaryview);
560
        summary_set_menu_sensitive(summaryview);
561
}
562

    
563
gboolean summary_show(SummaryView *summaryview, FolderItem *item,
564
                      gboolean update_cache)
565
{
566
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
567
        GtkCTreeNode *node;
568
        GSList *mlist = NULL;
569
        gchar *buf;
570
        gboolean is_refresh;
571
        guint selected_msgnum = 0;
572
        guint displayed_msgnum = 0;
573
        GtkCTreeNode *selected_node = summaryview->folderview->selected;
574

    
575
        if (summary_is_locked(summaryview)) return FALSE;
576

    
577
        inc_lock();
578
        summary_lock(summaryview);
579

    
580
        STATUSBAR_POP(summaryview->mainwin);
581

    
582
        is_refresh = (item == summaryview->folder_item &&
583
                      update_cache == FALSE) ? TRUE : FALSE;
584
        if (is_refresh) {
585
                selected_msgnum = summary_get_msgnum(summaryview,
586
                                                     summaryview->selected);
587
                displayed_msgnum = summary_get_msgnum(summaryview,
588
                                                      summaryview->displayed);
589
        }
590

    
591
        /* process the marks if any */
592
        if (summaryview->mainwin->lock_count == 0 &&
593
            (summaryview->moved > 0 || summaryview->copied > 0)) {
594
                AlertValue val;
595

    
596
                val = alertpanel(_("Process mark"),
597
                                 _("Some marks are left. Process it?"),
598
                                 _("Yes"), _("No"), _("Cancel"));
599
                if (G_ALERTDEFAULT == val) {
600
                        summary_unlock(summaryview);
601
                        summary_execute(summaryview);
602
                        summary_lock(summaryview);
603
                } else if (G_ALERTALTERNATE == val)
604
                        summary_write_cache(summaryview);
605
                else {
606
                        summary_unlock(summaryview);
607
                        inc_unlock();
608
                        return FALSE;
609
                }
610
        } else
611
                summary_write_cache(summaryview);
612

    
613
        summaryview->folderview->opened = selected_node;
614

    
615
        gtk_clist_freeze(GTK_CLIST(ctree));
616

    
617
        summary_clear_list(summaryview);
618
        summary_set_column_titles(summaryview);
619

    
620
        buf = NULL;
621
        if (!item || !item->path || !item->parent || item->no_select ||
622
            (FOLDER_TYPE(item->folder) == F_MH &&
623
             ((buf = folder_item_get_path(item)) == NULL ||
624
              change_dir(buf) < 0))) {
625
                g_free(buf);
626
                debug_print("empty folder\n\n");
627
                summary_clear_all(summaryview);
628
                summaryview->folder_item = item;
629
                gtk_clist_thaw(GTK_CLIST(ctree));
630
                summary_unlock(summaryview);
631
                inc_unlock();
632
                return TRUE;
633
        }
634
        g_free(buf);
635

    
636
        if (!is_refresh)
637
                messageview_clear(summaryview->messageview);
638

    
639
        summaryview->folder_item = item;
640

    
641
        g_signal_handlers_block_matched(G_OBJECT(ctree),
642
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
643
                                        0, 0, NULL, NULL, summaryview);
644

    
645
        buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
646
        debug_print("%s\n", buf);
647
        STATUSBAR_PUSH(summaryview->mainwin, buf);
648
        g_free(buf);
649

    
650
        main_window_cursor_wait(summaryview->mainwin);
651

    
652
        mlist = folder_item_get_msg_list(item, !update_cache);
653

    
654
        statusbar_pop_all();
655
        STATUSBAR_POP(summaryview->mainwin);
656

    
657
        /* set ctree and hash table from the msginfo list, and
658
           create the thread */
659
        summary_set_ctree_from_list(summaryview, mlist);
660

    
661
        g_slist_free(mlist);
662

    
663
        summary_write_cache(summaryview);
664

    
665
        item->opened = TRUE;
666

    
667
        g_signal_handlers_unblock_matched(G_OBJECT(ctree),
668
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
669
                                          0, 0, NULL, NULL, summaryview);
670

    
671
        gtk_clist_thaw(GTK_CLIST(ctree));
672

    
673
        if (is_refresh) {
674
                summaryview->displayed =
675
                        summary_find_msg_by_msgnum(summaryview,
676
                                                   displayed_msgnum);
677
                if (!summaryview->displayed)
678
                        messageview_clear(summaryview->messageview);
679
                summary_select_by_msgnum(summaryview, selected_msgnum);
680
                if (!summaryview->selected) {
681
                        /* no selected message - select first unread
682
                           message, but do not display it */
683
                        node = summary_find_next_flagged_msg(summaryview, NULL,
684
                                                             MSG_UNREAD, FALSE);
685
                        if (node == NULL && GTK_CLIST(ctree)->row_list != NULL)
686
                                node = gtk_ctree_node_nth
687
                                        (ctree,
688
                                         item->sort_type == SORT_DESCENDING
689
                                         ? 0 : GTK_CLIST(ctree)->rows - 1);
690
                        summary_select_node(summaryview, node, FALSE, TRUE);
691
                }
692
        } else {
693
                /* select first unread message */
694
                node = summary_find_next_flagged_msg(summaryview, NULL,
695
                                                     MSG_UNREAD, FALSE);
696
                if (node == NULL && GTK_CLIST(ctree)->row_list != NULL) {
697
                        node = gtk_ctree_node_nth
698
                                (ctree,
699
                                 item->sort_type == SORT_DESCENDING
700
                                 ? 0 : GTK_CLIST(ctree)->rows - 1);
701
                }
702
                if (prefs_common.open_unread_on_enter ||
703
                    prefs_common.always_show_msg) {
704
                        summary_unlock(summaryview);
705
                        summary_select_node(summaryview, node, TRUE, TRUE);
706
                        summary_lock(summaryview);
707
                } else
708
                        summary_select_node(summaryview, node, FALSE, TRUE);
709
        }
710

    
711
        summary_set_column_titles(summaryview);
712
        summary_status_show(summaryview);
713
        summary_set_menu_sensitive(summaryview);
714
        main_window_set_toolbar_sensitive(summaryview->mainwin);
715

    
716
        debug_print("\n");
717
        STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
718

    
719
        main_window_cursor_normal(summaryview->mainwin);
720
        summary_unlock(summaryview);
721
        inc_unlock();
722

    
723
        return TRUE;
724
}
725

    
726
void summary_clear_list(SummaryView *summaryview)
727
{
728
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
729
        gint optimal_width;
730

    
731
        gtk_clist_freeze(clist);
732

    
733
        gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree),
734
                                NULL, summary_free_msginfo_func, NULL);
735

    
736
        if (summaryview->folder_item) {
737
                folder_item_close(summaryview->folder_item);
738
                summaryview->folder_item = NULL;
739
        }
740

    
741
        summaryview->display_msg = FALSE;
742

    
743
        summaryview->selected = NULL;
744
        summaryview->displayed = NULL;
745
        summaryview->total_size = 0;
746
        summaryview->deleted = summaryview->moved = 0;
747
        summaryview->copied = 0;
748
        if (summaryview->msgid_table) {
749
                g_hash_table_destroy(summaryview->msgid_table);
750
                summaryview->msgid_table = NULL;
751
        }
752
        summaryview->mlist = NULL;
753
        if (summaryview->folder_table) {
754
                g_hash_table_destroy(summaryview->folder_table);
755
                summaryview->folder_table = NULL;
756
        }
757

    
758
        gtk_clist_clear(clist);
759
        if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
760
                optimal_width = gtk_clist_optimal_column_width
761
                        (clist, summaryview->col_pos[S_COL_SUBJECT]);
762
                gtk_clist_set_column_width
763
                        (clist, summaryview->col_pos[S_COL_SUBJECT],
764
                         optimal_width);
765
        }
766

    
767
        gtk_clist_thaw(clist);
768
}
769

    
770
void summary_clear_all(SummaryView *summaryview)
771
{
772
        messageview_clear(summaryview->messageview);
773
        summary_clear_list(summaryview);
774
        summary_set_menu_sensitive(summaryview);
775
        main_window_set_toolbar_sensitive(summaryview->mainwin);
776
        summary_status_show(summaryview);
777
}
778

    
779
void summary_lock(SummaryView *summaryview)
780
{
781
        summaryview->lock_count++;
782
}
783

    
784
void summary_unlock(SummaryView *summaryview)
785
{
786
        if (summaryview->lock_count)
787
                summaryview->lock_count--;
788
}
789

    
790
gboolean summary_is_locked(SummaryView *summaryview)
791
{
792
        return summaryview->lock_count > 0;
793
}
794

    
795
SummarySelection summary_get_selection_type(SummaryView *summaryview)
796
{
797
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
798
        SummarySelection selection;
799

    
800
        if (!clist->row_list)
801
                selection = SUMMARY_NONE;
802
        else if (!clist->selection)
803
                selection = SUMMARY_SELECTED_NONE;
804
        else if (!clist->selection->next)
805
                selection = SUMMARY_SELECTED_SINGLE;
806
        else
807
                selection = SUMMARY_SELECTED_MULTIPLE;
808

    
809
        return selection;
810
}
811

    
812
GSList *summary_get_selected_msg_list(SummaryView *summaryview)
813
{
814
        GSList *mlist = NULL;
815
        GList *cur;
816
        MsgInfo *msginfo;
817

    
818
        for (cur = GTK_CLIST(summaryview->ctree)->selection; cur != NULL;
819
             cur = cur->next) {
820
                msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
821
                mlist = g_slist_prepend(mlist, msginfo);
822
        }
823

    
824
        mlist = g_slist_reverse(mlist);
825

    
826
        return mlist;
827
}
828

    
829
GSList *summary_get_msg_list(SummaryView *summaryview)
830
{
831
        GSList *mlist = NULL;
832
        GtkCTree *ctree;
833
        GtkCTreeNode *node;
834
        MsgInfo *msginfo;
835

    
836
        ctree = GTK_CTREE(summaryview->ctree);
837

    
838
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
839
             node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
840
                msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
841
                mlist = g_slist_prepend(mlist, msginfo);
842
        }
843

    
844
        mlist = g_slist_reverse(mlist);
845

    
846
        return mlist;
847
}
848

    
849
static void summary_set_menu_sensitive(SummaryView *summaryview)
850
{
851
        GtkItemFactory *ifactory = summaryview->popupfactory;
852
        SummarySelection selection;
853
        GtkWidget *menuitem;
854
        gboolean sens;
855

    
856
        selection = summary_get_selection_type(summaryview);
857
        sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
858

    
859
        main_window_set_menu_sensitive(summaryview->mainwin);
860

    
861
        if (summaryview->folder_item &&
862
            (summaryview->folder_item->stype == F_OUTBOX ||
863
             summaryview->folder_item->stype == F_DRAFT  ||
864
             summaryview->folder_item->stype == F_QUEUE)) {
865
                gtk_widget_show(summaryview->reedit_menuitem);
866
                gtk_widget_show(summaryview->reedit_separator);
867
                menu_set_sensitive(ifactory, "/Re-edit", sens);
868
        } else {
869
                gtk_widget_hide(summaryview->reedit_menuitem);
870
                gtk_widget_hide(summaryview->reedit_separator);
871
                menu_set_sensitive(ifactory, "/Re-edit", FALSE);
872
        }
873

    
874
        if (selection == SUMMARY_NONE) {
875
                menu_set_insensitive_all
876
                        (GTK_MENU_SHELL(summaryview->popupmenu));
877
                return;
878
        }
879

    
880
        if (FOLDER_TYPE(summaryview->folder_item->folder) != F_NEWS) {
881
                menu_set_sensitive(ifactory, "/Move...", TRUE);
882
                menu_set_sensitive(ifactory, "/Delete", TRUE);
883
        } else {
884
                menu_set_sensitive(ifactory, "/Move...", FALSE);
885
                menu_set_sensitive(ifactory, "/Delete", FALSE);
886
        }
887

    
888
        menu_set_sensitive(ifactory, "/Copy...", TRUE);
889

    
890
        menu_set_sensitive(ifactory, "/Mark", TRUE);
891
        menu_set_sensitive(ifactory, "/Mark/Mark",   TRUE);
892
        menu_set_sensitive(ifactory, "/Mark/Unmark", TRUE);
893

    
894
        menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
895
        menu_set_sensitive(ifactory, "/Mark/Mark as read",   TRUE);
896
        menu_set_sensitive(ifactory, "/Mark/Mark all read",  TRUE);
897

    
898
        menu_set_sensitive(ifactory, "/Color label", TRUE);
899

    
900
        menu_set_sensitive(ifactory, "/Reply",                          sens);
901
        menu_set_sensitive(ifactory, "/Reply to",                  sens);
902
        menu_set_sensitive(ifactory, "/Reply to/all",                  sens);
903
        menu_set_sensitive(ifactory, "/Reply to/sender",          sens);
904
        menu_set_sensitive(ifactory, "/Reply to/mailing list",          sens);
905
        menu_set_sensitive(ifactory, "/Forward",                  TRUE);
906
        menu_set_sensitive(ifactory, "/Forward as attachment",          TRUE);
907
        menu_set_sensitive(ifactory, "/Redirect",                  sens);
908

    
909
        menu_set_sensitive(ifactory, "/Add sender to address book", sens);
910

    
911
        menu_set_sensitive(ifactory, "/View", sens);
912
        menu_set_sensitive(ifactory, "/View/Open in new window", sens);
913
        menu_set_sensitive(ifactory, "/View/Source", sens);
914
        menu_set_sensitive(ifactory, "/View/All header", sens);
915

    
916
        menu_set_sensitive(ifactory, "/Print...",   TRUE);
917

    
918
        summary_lock(summaryview);
919
        menuitem = gtk_item_factory_get_widget(ifactory, "/View/All header");
920
        gtk_check_menu_item_set_active
921
                (GTK_CHECK_MENU_ITEM(menuitem),
922
                 summaryview->messageview->textview->show_all_headers);
923
        summary_unlock(summaryview);
924
}
925

    
926
void summary_select_prev_unread(SummaryView *summaryview)
927
{
928
        GtkCTreeNode *node;
929

    
930
        node = summary_find_prev_flagged_msg
931
                (summaryview, summaryview->selected, MSG_UNREAD, FALSE);
932

    
933
        if (!node) {
934
                AlertValue val;
935

    
936
                val = alertpanel(_("No more unread messages"),
937
                                 _("No unread message found. "
938
                                   "Search from the end?"),
939
                                 _("Yes"), _("No"), NULL);
940
                if (val != G_ALERTDEFAULT) return;
941
                node = summary_find_prev_flagged_msg(summaryview, NULL,
942
                                                     MSG_UNREAD, FALSE);
943
        }
944

    
945
        if (!node)
946
                alertpanel_notice(_("No unread messages."));
947
        else
948
                summary_select_node(summaryview, node, TRUE, FALSE);
949
}
950

    
951
void summary_select_next_unread(SummaryView *summaryview)
952
{
953
        GtkCTreeNode *node = summaryview->selected;
954
        //GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
955

    
956
        while ((node = summary_find_next_flagged_msg
957
                (summaryview, node, MSG_UNREAD, FALSE)) == NULL) {
958
                AlertValue val;
959

    
960
                val = alertpanel(_("No more unread messages"),
961
                                 _("No unread message found. "
962
                                   "Go to next folder?"),
963
                                 _("Yes"), _("Search again"), _("No"));
964
                if (val == G_ALERTDEFAULT) {
965
#if 0
966
                        if (gtk_signal_n_emissions_by_name
967
                                (G_OBJECT(ctree), "key_press_event") > 0)
968
                                        gtk_signal_emit_stop_by_name
969
                                                (G_OBJECT(ctree),
970
                                                 "key_press_event");
971
#endif
972
                        folderview_select_next_unread(summaryview->folderview);
973
                        return;
974
                } else if (val == G_ALERTALTERNATE)
975
                        node = NULL;
976
                else
977
                        return;
978
        }
979

    
980
        if (node)
981
                summary_select_node(summaryview, node, TRUE, FALSE);
982
}
983

    
984
void summary_select_prev_new(SummaryView *summaryview)
985
{
986
        GtkCTreeNode *node;
987

    
988
        node = summary_find_prev_flagged_msg
989
                (summaryview, summaryview->selected, MSG_NEW, FALSE);
990

    
991
        if (!node) {
992
                AlertValue val;
993

    
994
                val = alertpanel(_("No more new messages"),
995
                                 _("No new message found. "
996
                                   "Search from the end?"),
997
                                 _("Yes"), _("No"), NULL);
998
                if (val != G_ALERTDEFAULT) return;
999
                node = summary_find_prev_flagged_msg(summaryview, NULL,
1000
                                                     MSG_NEW, FALSE);
1001
        }
1002

    
1003
        if (!node)
1004
                alertpanel_notice(_("No new messages."));
1005
        else
1006
                summary_select_node(summaryview, node, TRUE, FALSE);
1007
}
1008

    
1009
void summary_select_next_new(SummaryView *summaryview)
1010
{
1011
        GtkCTreeNode *node = summaryview->selected;
1012
        //GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1013

    
1014
        while ((node = summary_find_next_flagged_msg
1015
                (summaryview, node, MSG_NEW, FALSE)) == NULL) {
1016
                AlertValue val;
1017

    
1018
                val = alertpanel(_("No more new messages"),
1019
                                 _("No new message found. "
1020
                                   "Go to next folder?"),
1021
                                 _("Yes"), _("Search again"), _("No"));
1022
                if (val == G_ALERTDEFAULT) {
1023
#if 0
1024
                        if (gtk_signal_n_emissions_by_name
1025
                                (G_OBJECT(ctree), "key_press_event") > 0)
1026
                                        gtk_signal_emit_stop_by_name
1027
                                                (G_OBJECT(ctree),
1028
                                                 "key_press_event");
1029
#endif
1030
                        folderview_select_next_unread(summaryview->folderview);
1031
                        return;
1032
                } else if (val == G_ALERTALTERNATE)
1033
                        node = NULL;
1034
                else
1035
                        return;
1036
        }
1037

    
1038
        if (node)
1039
                summary_select_node(summaryview, node, TRUE, FALSE);
1040
}
1041

    
1042
void summary_select_prev_marked(SummaryView *summaryview)
1043
{
1044
        GtkCTreeNode *node;
1045

    
1046
        node = summary_find_prev_flagged_msg
1047
                (summaryview, summaryview->selected, MSG_MARKED, TRUE);
1048

    
1049
        if (!node) {
1050
                AlertValue val;
1051

    
1052
                val = alertpanel(_("No more marked messages"),
1053
                                 _("No marked message found. "
1054
                                   "Search from the end?"),
1055
                                 _("Yes"), _("No"), NULL);
1056
                if (val != G_ALERTDEFAULT) return;
1057
                node = summary_find_prev_flagged_msg(summaryview, NULL,
1058
                                                     MSG_MARKED, TRUE);
1059
        }
1060

    
1061
        if (!node)
1062
                alertpanel_notice(_("No marked messages."));
1063
        else
1064
                summary_select_node(summaryview, node, TRUE, FALSE);
1065
}
1066

    
1067
void summary_select_next_marked(SummaryView *summaryview)
1068
{
1069
        GtkCTreeNode *node;
1070

    
1071
        node = summary_find_next_flagged_msg
1072
                (summaryview, summaryview->selected, MSG_MARKED, TRUE);
1073

    
1074
        if (!node) {
1075
                AlertValue val;
1076

    
1077
                val = alertpanel(_("No more marked messages"),
1078
                                 _("No marked message found. "
1079
                                   "Search from the beginning?"),
1080
                                 _("Yes"), _("No"), NULL);
1081
                if (val != G_ALERTDEFAULT) return;
1082
                node = summary_find_next_flagged_msg(summaryview, NULL,
1083
                                                     MSG_MARKED, TRUE);
1084
        }
1085

    
1086
        if (!node)
1087
                alertpanel_notice(_("No marked messages."));
1088
        else
1089
                summary_select_node(summaryview, node, TRUE, FALSE);
1090
}
1091

    
1092
void summary_select_prev_labeled(SummaryView *summaryview)
1093
{
1094
        GtkCTreeNode *node;
1095

    
1096
        node = summary_find_prev_flagged_msg
1097
                (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
1098

    
1099
        if (!node) {
1100
                AlertValue val;
1101

    
1102
                val = alertpanel(_("No more labeled messages"),
1103
                                 _("No labeled message found. "
1104
                                   "Search from the end?"),
1105
                                 _("Yes"), _("No"), NULL);
1106
                if (val != G_ALERTDEFAULT) return;
1107
                node = summary_find_prev_flagged_msg(summaryview, NULL,
1108
                                                     MSG_CLABEL_FLAG_MASK, TRUE);
1109
        }
1110

    
1111
        if (!node)
1112
                alertpanel_notice(_("No labeled messages."));
1113
        else
1114
                summary_select_node(summaryview, node, TRUE, FALSE);
1115
}
1116

    
1117
void summary_select_next_labeled(SummaryView *summaryview)
1118
{
1119
        GtkCTreeNode *node;
1120

    
1121
        node = summary_find_next_flagged_msg
1122
                (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
1123

    
1124
        if (!node) {
1125
                AlertValue val;
1126

    
1127
                val = alertpanel(_("No more labeled messages"),
1128
                                 _("No labeled message found. "
1129
                                   "Search from the beginning?"),
1130
                                 _("Yes"), _("No"), NULL);
1131
                if (val != G_ALERTDEFAULT) return;
1132
                node = summary_find_next_flagged_msg(summaryview, NULL,
1133
                                                     MSG_CLABEL_FLAG_MASK, TRUE);
1134
        }
1135

    
1136
        if (!node)
1137
                alertpanel_notice(_("No labeled messages."));
1138
        else
1139
                summary_select_node(summaryview, node, TRUE, FALSE);
1140
}
1141

    
1142
void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1143
{
1144
        GtkCTreeNode *node;
1145

    
1146
        node = summary_find_msg_by_msgnum(summaryview, msgnum);
1147
        summary_select_node(summaryview, node, FALSE, TRUE);
1148
}
1149

    
1150
/**
1151
 * summary_select_node:
1152
 * @summaryview: Summary view.
1153
 * @node: Summary tree node.
1154
 * @display_msg: TRUE to display the selected message.
1155
 * @do_refresh: TRUE to refresh the widget.
1156
 *
1157
 * Select @node (bringing it into view by scrolling and expanding its
1158
 * thread, if necessary) and unselect all others.  If @display_msg is
1159
 * TRUE, display the corresponding message in the message view.
1160
 * If @do_refresh is TRUE, the widget is refreshed.
1161
 **/
1162
void summary_select_node(SummaryView *summaryview, GtkCTreeNode *node,
1163
                         gboolean display_msg, gboolean do_refresh)
1164
{
1165
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1166

    
1167
        if (node) {
1168
                gtkut_ctree_expand_parent_all(ctree, node);
1169
                if (do_refresh) {
1170
                        GTK_EVENTS_FLUSH();
1171
                        gtk_widget_grab_focus(GTK_WIDGET(ctree));
1172
                        gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
1173
                }
1174
                gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1175
                if (display_msg && summaryview->displayed == node)
1176
                        summaryview->displayed = NULL;
1177
                summaryview->display_msg = display_msg;
1178
                gtk_sctree_select(GTK_SCTREE(ctree), node);
1179
        }
1180
}
1181

    
1182
static guint summary_get_msgnum(SummaryView *summaryview, GtkCTreeNode *node)
1183
{
1184
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1185
        MsgInfo *msginfo;
1186

    
1187
        if (!node)
1188
                return 0;
1189
        msginfo = gtk_ctree_node_get_row_data(ctree, node);
1190
        return msginfo->msgnum;
1191
}
1192

    
1193
static GtkCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
1194
                                           GtkCTreeNode *current_node)
1195
{
1196
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1197
        GtkCTreeNode *node;
1198
        MsgInfo *msginfo;
1199

    
1200
        if (current_node)
1201
                node = current_node;
1202
        else
1203
                node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1204

    
1205
        for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1206
                msginfo = gtk_ctree_node_get_row_data(ctree, node);
1207
                if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1208
                    !MSG_IS_DELETED(msginfo->flags)) break;
1209
        }
1210

    
1211
        return node;
1212
}
1213

    
1214
static GtkCTreeNode *summary_find_next_msg(SummaryView *summaryview,
1215
                                           GtkCTreeNode *current_node)
1216
{
1217
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1218
        GtkCTreeNode *node;
1219
        MsgInfo *msginfo;
1220

    
1221
        if (current_node)
1222
                node = current_node;
1223
        else
1224
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1225

    
1226
        for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1227
                msginfo = gtk_ctree_node_get_row_data(ctree, node);
1228
                if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1229
                    !MSG_IS_DELETED(msginfo->flags)) break;
1230
        }
1231

    
1232
        return node;
1233
}
1234

    
1235
static GtkCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
1236
                                                   GtkCTreeNode *current_node,
1237
                                                   MsgPermFlags flags,
1238
                                                   gboolean start_from_prev)
1239
{
1240
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1241
        GtkCTreeNode *node;
1242
        MsgInfo *msginfo;
1243

    
1244
        if (current_node) {
1245
                if (start_from_prev)
1246
                        node = GTK_CTREE_NODE_PREV(current_node);
1247
                else
1248
                        node = current_node;
1249
        } else
1250
                node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1251

    
1252
        for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1253
                msginfo = gtk_ctree_node_get_row_data(ctree, node);
1254
                if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
1255
        }
1256

    
1257
        return node;
1258
}
1259

    
1260
static GtkCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
1261
                                                   GtkCTreeNode *current_node,
1262
                                                   MsgPermFlags flags,
1263
                                                   gboolean start_from_next)
1264
{
1265
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1266
        GtkCTreeNode *node;
1267
        MsgInfo *msginfo;
1268

    
1269
        if (current_node) {
1270
                if (start_from_next)
1271
                        node = gtkut_ctree_node_next(ctree, current_node);
1272
                else
1273
                        node = current_node;
1274
        } else
1275
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1276

    
1277
        for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1278
                msginfo = gtk_ctree_node_get_row_data(ctree, node);
1279
                if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
1280
        }
1281

    
1282
        return node;
1283
}
1284

    
1285
static GtkCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
1286
                                                guint msgnum)
1287
{
1288
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1289
        GtkCTreeNode *node;
1290
        MsgInfo *msginfo;
1291

    
1292
        node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1293

    
1294
        for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1295
                msginfo = gtk_ctree_node_get_row_data(ctree, node);
1296
                if (msginfo && msginfo->msgnum == msgnum) break;
1297
        }
1298

    
1299
        return node;
1300
}
1301

    
1302
static guint attract_hash_func(gconstpointer key)
1303
{
1304
        gchar *str;
1305
        gchar *p;
1306
        guint h;
1307

    
1308
        Xstrdup_a(str, (const gchar *)key, return 0);
1309
        trim_subject_for_compare(str);
1310

    
1311
        p = str;
1312
        h = *p;
1313

    
1314
        if (h) {
1315
                for (p += 1; *p != '\0'; p++)
1316
                        h = (h << 5) - h + *p;
1317
        }
1318

    
1319
        return h;
1320
}
1321

    
1322
static gint attract_compare_func(gconstpointer a, gconstpointer b)
1323
{
1324
        return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1325
}
1326

    
1327
void summary_attract_by_subject(SummaryView *summaryview)
1328
{
1329
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1330
        GtkCList *clist = GTK_CLIST(ctree);
1331
        GtkCTreeNode *src_node;
1332
        GtkCTreeNode *dst_node, *sibling;
1333
        GtkCTreeNode *tmp;
1334
        MsgInfo *src_msginfo, *dst_msginfo;
1335
        GHashTable *subject_table;
1336

    
1337
        debug_print(_("Attracting messages by subject..."));
1338
        STATUSBAR_PUSH(summaryview->mainwin,
1339
                       _("Attracting messages by subject..."));
1340

    
1341
        main_window_cursor_wait(summaryview->mainwin);
1342
        gtk_clist_freeze(clist);
1343

    
1344
        subject_table = g_hash_table_new(attract_hash_func,
1345
                                         attract_compare_func);
1346

    
1347
        for (src_node = GTK_CTREE_NODE(clist->row_list);
1348
             src_node != NULL;
1349
             src_node = tmp) {
1350
                tmp = GTK_CTREE_ROW(src_node)->sibling;
1351
                src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
1352
                if (!src_msginfo) continue;
1353
                if (!src_msginfo->subject) continue;
1354

    
1355
                /* find attracting node */
1356
                dst_node = g_hash_table_lookup(subject_table,
1357
                                               src_msginfo->subject);
1358

    
1359
                if (dst_node) {
1360
                        dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
1361

    
1362
                        /* if the time difference is more than 20 days,
1363
                           don't attract */
1364
                        if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
1365
                            > 60 * 60 * 24 * 20)
1366
                                continue;
1367

    
1368
                        sibling = GTK_CTREE_ROW(dst_node)->sibling;
1369
                        if (src_node != sibling)
1370
                                gtk_ctree_move(ctree, src_node, NULL, sibling);
1371
                }
1372

    
1373
                g_hash_table_insert(subject_table,
1374
                                    src_msginfo->subject, src_node);
1375
        }
1376

    
1377
        g_hash_table_destroy(subject_table);
1378

    
1379
        gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1380

    
1381
        gtk_clist_thaw(clist);
1382

    
1383
        debug_print(_("done.\n"));
1384
        STATUSBAR_POP(summaryview->mainwin);
1385

    
1386
        main_window_cursor_normal(summaryview->mainwin);
1387
}
1388

    
1389
static void summary_free_msginfo_func(GtkCTree *ctree, GtkCTreeNode *node,
1390
                                      gpointer data)
1391
{
1392
        MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1393

    
1394
        if (msginfo)
1395
                procmsg_msginfo_free(msginfo);
1396
}
1397

    
1398
static void summary_set_marks_func(GtkCTree *ctree, GtkCTreeNode *node,
1399
                                   gpointer data)
1400
{
1401
        SummaryView *summaryview = data;
1402
        MsgInfo *msginfo;
1403

    
1404
        msginfo = gtk_ctree_node_get_row_data(ctree, node);
1405

    
1406
        if (MSG_IS_DELETED(msginfo->flags))
1407
                summaryview->deleted++;
1408

    
1409
        summaryview->total_size += msginfo->size;
1410

    
1411
        summary_set_row_marks(summaryview, node);
1412
}
1413

    
1414
static void summary_update_status(SummaryView *summaryview)
1415
{
1416
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1417
        GtkCTreeNode *node;
1418
        MsgInfo *msginfo;
1419

    
1420
        summaryview->total_size =
1421
        summaryview->deleted = summaryview->moved = summaryview->copied = 0;
1422

    
1423
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1424
             node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1425
                msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1426

    
1427
                if (MSG_IS_DELETED(msginfo->flags))
1428
                        summaryview->deleted++;
1429
                if (MSG_IS_MOVE(msginfo->flags))
1430
                        summaryview->moved++;
1431
                if (MSG_IS_COPY(msginfo->flags))
1432
                        summaryview->copied++;
1433
                summaryview->total_size += msginfo->size;
1434
        }
1435
}
1436

    
1437
static void summary_status_show(SummaryView *summaryview)
1438
{
1439
        gchar *str;
1440
        gchar *del, *mv, *cp;
1441
        gchar *sel;
1442
        gchar *spc;
1443
        GList *rowlist, *cur;
1444
        guint n_selected = 0;
1445
        off_t sel_size = 0;
1446
        MsgInfo *msginfo;
1447

    
1448
        if (!summaryview->folder_item) {
1449
                gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
1450
                gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
1451
                gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
1452
                return;
1453
        }
1454

    
1455
        rowlist = GTK_CLIST(summaryview->ctree)->selection;
1456
        for (cur = rowlist; cur != NULL; cur = cur->next) {
1457
                msginfo = gtk_ctree_node_get_row_data
1458
                        (GTK_CTREE(summaryview->ctree),
1459
                         GTK_CTREE_NODE(cur->data));
1460
                if (!msginfo)
1461
                        g_warning("summary_status_show(): msginfo == NULL\n");
1462
                else {
1463
                        sel_size += msginfo->size;
1464
                        n_selected++;
1465
                }
1466
        }
1467

    
1468
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) {
1469
                gchar *group;
1470
                group = get_abbrev_newsgroup_name
1471
                        (g_basename(summaryview->folder_item->path),
1472
                         prefs_common.ng_abbrev_len);
1473
                str = trim_string_before(group, 32);
1474
                g_free(group);
1475
        } else
1476
                str = trim_string_before(summaryview->folder_item->path, 32);
1477
        gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), str);
1478
        g_free(str);
1479

    
1480
        if (summaryview->deleted)
1481
                del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
1482
        else
1483
                del = g_strdup("");
1484
        if (summaryview->moved)
1485
                mv = g_strdup_printf(_("%s%d moved"),
1486
                                     summaryview->deleted ? _(", ") : "",
1487
                                     summaryview->moved);
1488
        else
1489
                mv = g_strdup("");
1490
        if (summaryview->copied)
1491
                cp = g_strdup_printf(_("%s%d copied"),
1492
                                     summaryview->deleted ||
1493
                                     summaryview->moved ? _(", ") : "",
1494
                                     summaryview->copied);
1495
        else
1496
                cp = g_strdup("");
1497

    
1498
        if (summaryview->deleted || summaryview->moved || summaryview->copied)
1499
                spc = "    ";
1500
        else
1501
                spc = "";
1502

    
1503
        if (n_selected)
1504
                sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
1505
        else
1506
                sel = g_strdup("");
1507
        str = g_strconcat(n_selected ? itos(n_selected) : "",
1508
                          n_selected ? _(" item(s) selected") : "",
1509
                          sel, spc, del, mv, cp, NULL);
1510
        gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str);
1511
        g_free(str);
1512
        g_free(sel);
1513
        g_free(del);
1514
        g_free(mv);
1515
        g_free(cp);
1516

    
1517
        if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
1518
                str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
1519
                                      summaryview->folder_item->new,
1520
                                      summaryview->folder_item->unread,
1521
                                      summaryview->folder_item->total,
1522
                                      to_human_readable(summaryview->total_size));
1523
        } else {
1524
                str = g_strdup_printf(_("%d new, %d unread, %d total"),
1525
                                      summaryview->folder_item->new,
1526
                                      summaryview->folder_item->unread,
1527
                                      summaryview->folder_item->total);
1528
        }
1529
        gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str);
1530
        g_free(str);
1531

    
1532
        folderview_update_msg_num(summaryview->folderview,
1533
                                  summaryview->folderview->opened);
1534
}
1535

    
1536
static void summary_set_column_titles(SummaryView *summaryview)
1537
{
1538
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
1539
        GtkWidget *hbox;
1540
        GtkWidget *label;
1541
        GtkWidget *arrow;
1542
        gint pos;
1543
        const gchar *title;
1544
        SummaryColumnType type;
1545
        GtkJustification justify;
1546
        FolderItem *item = summaryview->folder_item;
1547

    
1548
        static FolderSortKey sort_by[N_SUMMARY_COLS] = {
1549
                SORT_BY_MARK,
1550
                SORT_BY_UNREAD,
1551
                SORT_BY_MIME,
1552
                SORT_BY_SUBJECT,
1553
                SORT_BY_FROM,
1554
                SORT_BY_DATE,
1555
                SORT_BY_SIZE,
1556
                SORT_BY_NUMBER
1557
        };
1558

    
1559
        for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
1560
                type = summaryview->col_state[pos].type;
1561

    
1562
                justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
1563
                        ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
1564

    
1565
                switch (type) {
1566
                case S_COL_SUBJECT:
1567
                case S_COL_FROM:
1568
                case S_COL_DATE:
1569
                case S_COL_NUMBER:
1570
                        if (prefs_common.trans_hdr)
1571
                                title = gettext(col_label[type]);
1572
                        else
1573
                                title = col_label[type];
1574
                        break;
1575
                default:
1576
                        title = gettext(col_label[type]);
1577
                }
1578

    
1579
                if (type == S_COL_MARK) {
1580
                        label = gtk_pixmap_new(markxpm, markxpmmask);
1581
                        gtk_widget_show(label);
1582
                        gtk_clist_set_column_widget(clist, pos, label);
1583
                        continue;
1584
                } else if (type == S_COL_UNREAD) {
1585
                        label = gtk_pixmap_new(mailxpm, mailxpmmask);
1586
                        gtk_widget_show(label);
1587
                        gtk_clist_set_column_widget(clist, pos, label);
1588
                        continue;
1589
                } else if (type == S_COL_MIME) {
1590
                        label = gtk_pixmap_new(clipxpm, clipxpmmask);
1591
                        gtk_widget_show(label);
1592
                        gtk_clist_set_column_widget(clist, pos, label);
1593
                        continue;
1594
                }
1595

    
1596
                hbox = gtk_hbox_new(FALSE, 4);
1597
                label = gtk_label_new(title);
1598
                if (justify == GTK_JUSTIFY_RIGHT)
1599
                        gtk_box_pack_end(GTK_BOX(hbox), label,
1600
                                         FALSE, FALSE, 0);
1601
                else
1602
                        gtk_box_pack_start(GTK_BOX(hbox), label,
1603
                                           FALSE, FALSE, 0);
1604

    
1605
                if (item && item->sort_key == sort_by[type]) {
1606
                        arrow = gtk_arrow_new
1607
                                (item->sort_type == SORT_ASCENDING
1608
                                 ? GTK_ARROW_UP : GTK_ARROW_DOWN,
1609
                                 GTK_SHADOW_IN);
1610
                        if (justify == GTK_JUSTIFY_RIGHT)
1611
                                gtk_box_pack_start(GTK_BOX(hbox), arrow,
1612
                                                   FALSE, FALSE, 0);
1613
                        else
1614
                                gtk_box_pack_end(GTK_BOX(hbox), arrow,
1615
                                                 FALSE, FALSE, 0);
1616
                }
1617

    
1618
                gtk_widget_show_all(hbox);
1619
                gtk_clist_set_column_widget(clist, pos, hbox);
1620
        }
1621
}
1622

    
1623
void summary_sort(SummaryView *summaryview,
1624
                  FolderSortKey sort_key, FolderSortType sort_type)
1625
{
1626
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1627
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
1628
        GtkCListCompareFunc cmp_func;
1629
        FolderItem *item = summaryview->folder_item;
1630

    
1631
        if (!item || !item->path || !item->parent || item->no_select) return;
1632

    
1633
        switch (sort_key) {
1634
        case SORT_BY_MARK:
1635
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_mark;
1636
                break;
1637
        case SORT_BY_UNREAD:
1638
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_unread;
1639
                break;
1640
        case SORT_BY_MIME:
1641
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_mime;
1642
                break;
1643
        case SORT_BY_NUMBER:
1644
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_num;
1645
                break;
1646
        case SORT_BY_SIZE:
1647
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_size;
1648
                break;
1649
        case SORT_BY_DATE:
1650
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_date;
1651
                break;
1652
        case SORT_BY_FROM:
1653
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_from;
1654
                break;
1655
        case SORT_BY_SUBJECT:
1656
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
1657
                break;
1658
        case SORT_BY_LABEL:
1659
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_label;
1660
                break;
1661
        case SORT_BY_TO:
1662
                cmp_func = (GtkCListCompareFunc)summary_cmp_by_to;
1663
                break;
1664
        case SORT_BY_NONE:
1665
                item->sort_key = sort_key;
1666
                item->sort_type = SORT_ASCENDING;
1667
                summary_set_column_titles(summaryview);
1668
                summary_set_menu_sensitive(summaryview);
1669
                return;
1670
        default:
1671
                return;
1672
        }
1673

    
1674
        debug_print(_("Sorting summary..."));
1675
        STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
1676

    
1677
        main_window_cursor_wait(summaryview->mainwin);
1678

    
1679
        gtk_clist_set_compare_func(clist, cmp_func);
1680

    
1681
        gtk_clist_set_sort_type(clist, (GtkSortType)sort_type);
1682
        item->sort_key = sort_key;
1683
        item->sort_type = sort_type;
1684

    
1685
        summary_set_column_titles(summaryview);
1686
        summary_set_menu_sensitive(summaryview);
1687

    
1688
        gtk_ctree_sort_recursive(ctree, NULL);
1689

    
1690
        gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1691

    
1692
        debug_print(_("done.\n"));
1693
        STATUSBAR_POP(summaryview->mainwin);
1694

    
1695
        main_window_cursor_normal(summaryview->mainwin);
1696
}
1697

    
1698
gboolean summary_insert_gnode_func(GtkCTree *ctree, guint depth, GNode *gnode,
1699
                                   GtkCTreeNode *cnode, gpointer data)
1700
{
1701
        SummaryView *summaryview = (SummaryView *)data;
1702
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
1703
        gchar *text[N_SUMMARY_COLS];
1704
        gint *col_pos = summaryview->col_pos;
1705
        const gchar *msgid = msginfo->msgid;
1706
        GHashTable *msgid_table = summaryview->msgid_table;
1707

    
1708
        summary_set_header(summaryview, text, msginfo);
1709

    
1710
        gtk_ctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
1711
                                NULL, NULL, NULL, NULL, FALSE,
1712
                                gnode->parent->parent ? TRUE : FALSE);
1713
#define SET_TEXT(col) \
1714
        gtk_ctree_node_set_text(ctree, cnode, col_pos[col], \
1715
                                text[col_pos[col]])
1716

    
1717
        SET_TEXT(S_COL_NUMBER);
1718
        SET_TEXT(S_COL_SIZE);
1719
        SET_TEXT(S_COL_DATE);
1720
        SET_TEXT(S_COL_FROM);
1721
        SET_TEXT(S_COL_SUBJECT);
1722

    
1723
#undef SET_TEXT
1724

    
1725
        GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
1726
        summary_set_marks_func(ctree, cnode, summaryview);
1727

    
1728
        if (msgid && msgid[0] != '\0')
1729
                g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
1730

    
1731
        return TRUE;
1732
}
1733

    
1734
static void summary_set_ctree_from_list(SummaryView *summaryview,
1735
                                        GSList *mlist)
1736
{
1737
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1738
        MsgInfo *msginfo;
1739
        GtkCTreeNode *node = NULL;
1740
        GHashTable *msgid_table;
1741

    
1742
        if (!mlist) return;
1743

    
1744
        debug_print(_("\tSetting summary from message data..."));
1745
        STATUSBAR_PUSH(summaryview->mainwin,
1746
                       _("Setting summary from message data..."));
1747
        gdk_flush();
1748

    
1749
        msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
1750
        summaryview->msgid_table = msgid_table;
1751

    
1752
        if (summaryview->folder_item->threaded) {
1753
                GNode *root, *gnode;
1754

    
1755
                root = procmsg_get_thread_tree(mlist);
1756

    
1757
                for (gnode = root->children; gnode != NULL;
1758
                     gnode = gnode->next) {
1759
                        node = gtk_ctree_insert_gnode
1760
                                (ctree, NULL, node, gnode,
1761
                                 summary_insert_gnode_func, summaryview);
1762
                }
1763

    
1764
                g_node_destroy(root);
1765

    
1766
                summary_thread_init(summaryview);
1767
        } else {
1768
                gchar *text[N_SUMMARY_COLS];
1769

    
1770
                mlist = g_slist_reverse(mlist);
1771
                for (; mlist != NULL; mlist = mlist->next) {
1772
                        msginfo = (MsgInfo *)mlist->data;
1773

    
1774
                        summary_set_header(summaryview, text, msginfo);
1775

    
1776
                        node = gtk_ctree_insert_node
1777
                                (ctree, NULL, node, text, 2,
1778
                                 NULL, NULL, NULL, NULL, FALSE, FALSE);
1779
                        GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
1780
                        summary_set_marks_func(ctree, node, summaryview);
1781

    
1782
                        if (msginfo->msgid && msginfo->msgid[0] != '\0')
1783
                                g_hash_table_insert(msgid_table,
1784
                                                    msginfo->msgid, node);
1785
                }
1786
                mlist = g_slist_reverse(mlist);
1787
        }
1788

    
1789
        if (prefs_common.enable_hscrollbar &&
1790
            summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1791
                gint optimal_width;
1792

    
1793
                optimal_width = gtk_clist_optimal_column_width
1794
                        (GTK_CLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
1795
                gtk_clist_set_column_width(GTK_CLIST(ctree),
1796
                                           summaryview->col_pos[S_COL_SUBJECT],
1797
                                           optimal_width);
1798
        }
1799

    
1800
        debug_print(_("done.\n"));
1801
        STATUSBAR_POP(summaryview->mainwin);
1802
        if (debug_mode)
1803
                debug_print("\tmsgid hash table size = %d\n",
1804
                            g_hash_table_size(msgid_table));
1805
}
1806

    
1807
struct wcachefp
1808
{
1809
        FILE *cache_fp;
1810
        FILE *mark_fp;
1811
};
1812

    
1813
gint summary_write_cache(SummaryView *summaryview)
1814
{
1815
        struct wcachefp fps;
1816
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1817
        FolderItem *item;
1818
        gchar *buf;
1819

    
1820
        item = summaryview->folder_item;
1821
        if (!item || !item->path)
1822
                return -1;
1823

    
1824
        fps.cache_fp = procmsg_open_cache_file(item, DATA_WRITE);
1825
        if (fps.cache_fp == NULL)
1826
                return -1;
1827
        fps.mark_fp = procmsg_open_mark_file(item, DATA_WRITE);
1828
        if (fps.mark_fp == NULL) {
1829
                fclose(fps.cache_fp);
1830
                return -1;
1831
        }
1832

    
1833
        buf = g_strdup_printf(_("Writing summary cache (%s)..."), item->path);
1834
        debug_print(buf);
1835
        STATUSBAR_PUSH(summaryview->mainwin, buf);
1836
        g_free(buf);
1837

    
1838
        gtk_ctree_pre_recursive(ctree, NULL, summary_write_cache_func, &fps);
1839

    
1840
        procmsg_flush_mark_queue(item, fps.mark_fp);
1841
        item->unmarked_num = 0;
1842

    
1843
        fclose(fps.cache_fp);
1844
        fclose(fps.mark_fp);
1845

    
1846
        debug_print(_("done.\n"));
1847
        STATUSBAR_POP(summaryview->mainwin);
1848

    
1849
        return 0;
1850
}
1851

    
1852
static void summary_write_cache_func(GtkCTree *ctree, GtkCTreeNode *node,
1853
                                     gpointer data)
1854
{
1855
        struct wcachefp *fps = data;
1856
        MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1857

    
1858
        if (msginfo == NULL) return;
1859

    
1860
        if (msginfo->folder->mark_queue != NULL) {
1861
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
1862
        }
1863

    
1864
        procmsg_write_cache(msginfo, fps->cache_fp);
1865
        procmsg_write_flags(msginfo, fps->mark_fp);
1866
}
1867

    
1868
static void summary_set_header(SummaryView *summaryview, gchar *text[],
1869
                               MsgInfo *msginfo)
1870
{
1871
        static gchar date_modified[80];
1872
        static gchar *to = NULL;
1873
        static gchar *subject = NULL;
1874
        gint *col_pos = summaryview->col_pos;
1875

    
1876
        text[col_pos[S_COL_MARK]]   = NULL;
1877
        text[col_pos[S_COL_UNREAD]] = NULL;
1878
        text[col_pos[S_COL_MIME]]   = NULL;
1879
        text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
1880
        text[col_pos[S_COL_SIZE]]   = to_human_readable(msginfo->size);
1881

    
1882
        if (msginfo->date_t) {
1883
                procheader_date_get_localtime(date_modified,
1884
                                              sizeof(date_modified),
1885
                                              msginfo->date_t);
1886
                text[col_pos[S_COL_DATE]] = date_modified;
1887
        } else if (msginfo->date)
1888
                text[col_pos[S_COL_DATE]] = msginfo->date;
1889
        else
1890
                text[col_pos[S_COL_DATE]] = _("(No Date)");
1891

    
1892
        text[col_pos[S_COL_FROM]] = msginfo->fromname ? msginfo->fromname :
1893
                _("(No From)");
1894
        if (prefs_common.swap_from && msginfo->from && msginfo->to &&
1895
            cur_account && cur_account->address) {
1896
                gchar *from;
1897

    
1898
                Xstrdup_a(from, msginfo->from, return);
1899
                extract_address(from);
1900
                if (!strcmp(from, cur_account->address)) {
1901
                        g_free(to);
1902
                        to = g_strconcat("-->", msginfo->to, NULL);
1903
                        text[col_pos[S_COL_FROM]] = to;
1904
                }
1905
        }
1906

    
1907
        if (msginfo->subject) {
1908
                if (msginfo->folder && msginfo->folder->trim_summary_subject) {
1909
                        g_free(subject);
1910
                        subject = g_strdup(msginfo->subject);
1911
                        trim_subject(subject);
1912
                        text[col_pos[S_COL_SUBJECT]] = subject;
1913
                } else
1914
                        text[col_pos[S_COL_SUBJECT]] = msginfo->subject;
1915
        } else
1916
                text[col_pos[S_COL_SUBJECT]] = _("(No Subject)");
1917
}
1918

    
1919
static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row)
1920
{
1921
        summary_display_msg_full(summaryview, row, FALSE, FALSE);
1922
}
1923

    
1924
static void summary_display_msg_full(SummaryView *summaryview,
1925
                                     GtkCTreeNode *row,
1926
                                     gboolean new_window, gboolean all_headers)
1927
{
1928
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1929
        MsgInfo *msginfo;
1930
        gint val;
1931

    
1932
        if (!new_window && summaryview->displayed == row) return;
1933
        g_return_if_fail(row != NULL);
1934

    
1935
        if (summary_is_locked(summaryview)) return;
1936
        summary_lock(summaryview);
1937

    
1938
        STATUSBAR_POP(summaryview->mainwin);
1939
        GTK_EVENTS_FLUSH();
1940

    
1941
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
1942

    
1943
        if (new_window) {
1944
                MessageView *msgview;
1945

    
1946
                msgview = messageview_create_with_new_window();
1947
                val = messageview_show(msgview, msginfo, all_headers);
1948
        } else {
1949
                MessageView *msgview;
1950

    
1951
                msgview = summaryview->messageview;
1952

    
1953
                summaryview->displayed = row;
1954
                if (!messageview_is_visible(msgview))
1955
                        main_window_toggle_message_view(summaryview->mainwin);
1956
                val = messageview_show(msgview, msginfo, all_headers);
1957
                if (msgview->type == MVIEW_TEXT ||
1958
                    (msgview->type == MVIEW_MIME &&
1959
                     (GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL ||
1960
                      gtk_notebook_get_current_page
1961
                        (GTK_NOTEBOOK(msgview->mimeview->notebook)) == 0)))
1962
                        gtk_widget_grab_focus(summaryview->ctree);
1963
                GTK_EVENTS_FLUSH();
1964
                gtkut_ctree_node_move_if_on_the_edge(ctree, row);
1965
        }
1966

    
1967
        if (val == 0 &&
1968
            (new_window || !prefs_common.mark_as_read_on_new_window)) {
1969
                if (MSG_IS_NEW(msginfo->flags))
1970
                        summaryview->folder_item->new--;
1971
                if (MSG_IS_UNREAD(msginfo->flags))
1972
                        summaryview->folder_item->unread--;
1973
                if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
1974
                        MSG_UNSET_PERM_FLAGS
1975
                                (msginfo->flags, MSG_NEW | MSG_UNREAD);
1976
                        if (MSG_IS_IMAP(msginfo->flags))
1977
                                imap_msg_unset_perm_flags
1978
                                        (msginfo, MSG_NEW | MSG_UNREAD);
1979
                        summary_set_row_marks(summaryview, row);
1980
                        gtk_clist_thaw(GTK_CLIST(ctree));
1981
                        summary_status_show(summaryview);
1982
                }
1983
        }
1984

    
1985
        summary_set_menu_sensitive(summaryview);
1986
        main_window_set_toolbar_sensitive(summaryview->mainwin);
1987

    
1988
        statusbar_pop_all();
1989

    
1990
        summary_unlock(summaryview);
1991
}
1992

    
1993
void summary_display_msg_selected(SummaryView *summaryview,
1994
                                  gboolean all_headers)
1995
{
1996
        if (summary_is_locked(summaryview)) return;
1997
        summaryview->displayed = NULL;
1998
        summary_display_msg_full(summaryview, summaryview->selected, FALSE,
1999
                                 all_headers);
2000
}
2001

    
2002
void summary_redisplay_msg(SummaryView *summaryview)
2003
{
2004
        GtkCTreeNode *node;
2005

    
2006
        if (summaryview->displayed) {
2007
                node = summaryview->displayed;
2008
                summaryview->displayed = NULL;
2009
                summary_display_msg(summaryview, node);
2010
        }
2011
}
2012

    
2013
void summary_open_msg(SummaryView *summaryview)
2014
{
2015
        if (!summaryview->selected) return;
2016

    
2017
        summary_display_msg_full(summaryview, summaryview->selected,
2018
                                 TRUE, FALSE);
2019
}
2020

    
2021
void summary_view_source(SummaryView * summaryview)
2022
{
2023
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2024
        MsgInfo *msginfo;
2025
        SourceWindow *srcwin;
2026

    
2027
        if (!summaryview->selected) return;
2028

    
2029
        srcwin = source_window_create();
2030
        msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2031
        source_window_show_msg(srcwin, msginfo);
2032
        source_window_show(srcwin);
2033
}
2034

    
2035
void summary_reedit(SummaryView *summaryview)
2036
{
2037
        MsgInfo *msginfo;
2038

    
2039
        if (!summaryview->selected) return;
2040
        if (!summaryview->folder_item) return;
2041
        if (summaryview->folder_item->stype != F_OUTBOX &&
2042
            summaryview->folder_item->stype != F_DRAFT  &&
2043
            summaryview->folder_item->stype != F_QUEUE) return;
2044

    
2045
        msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2046
                                              summaryview->selected);
2047
        if (!msginfo) return;
2048

    
2049
        compose_reedit(msginfo);
2050
}
2051

    
2052
void summary_step(SummaryView *summaryview, GtkScrollType type)
2053
{
2054
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2055
        GtkCTreeNode *node;
2056

    
2057
        if (summary_is_locked(summaryview)) return;
2058

    
2059
        if (type == GTK_SCROLL_STEP_FORWARD) {
2060
                node = gtkut_ctree_node_next(ctree, summaryview->selected);
2061
                if (node)
2062
                        gtkut_ctree_expand_parent_all(ctree, node);
2063
                else
2064
                        return;
2065
        } else {
2066
                if (summaryview->selected) {
2067
                        node = GTK_CTREE_NODE_PREV(summaryview->selected);
2068
                        if (!node) return;
2069
                }
2070
        }
2071

    
2072
        if (messageview_is_visible(summaryview->messageview))
2073
                summaryview->display_msg = TRUE;
2074

    
2075
        g_signal_emit_by_name(G_OBJECT(ctree), "scroll_vertical", type, 0.0);
2076

    
2077
        if (GTK_CLIST(ctree)->selection)
2078
                gtk_sctree_set_anchor_row
2079
                        (GTK_SCTREE(ctree),
2080
                         GTK_CTREE_NODE(GTK_CLIST(ctree)->selection->data));
2081
}
2082

    
2083
void summary_toggle_view(SummaryView *summaryview)
2084
{
2085
        if (!messageview_is_visible(summaryview->messageview) &&
2086
            summaryview->selected)
2087
                summary_display_msg(summaryview,
2088
                                    summaryview->selected);
2089
        else
2090
                main_window_toggle_message_view(summaryview->mainwin);
2091
}
2092

    
2093
static gboolean summary_search_unread_recursive(GtkCTree *ctree,
2094
                                                GtkCTreeNode *node)
2095
{
2096
        MsgInfo *msginfo;
2097

    
2098
        if (node) {
2099
                msginfo = gtk_ctree_node_get_row_data(ctree, node);
2100
                if (msginfo && MSG_IS_UNREAD(msginfo->flags))
2101
                        return TRUE;
2102
                node = GTK_CTREE_ROW(node)->children;
2103
        } else
2104
                node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2105

    
2106
        while (node) {
2107
                if (summary_search_unread_recursive(ctree, node) == TRUE)
2108
                        return TRUE;
2109
                node = GTK_CTREE_ROW(node)->sibling;
2110
        }
2111

    
2112
        return FALSE;
2113
}
2114

    
2115
static gboolean summary_have_unread_children(SummaryView *summaryview,
2116
                                             GtkCTreeNode *node)
2117
{
2118
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2119

    
2120
        if (!node) return FALSE;
2121

    
2122
        node = GTK_CTREE_ROW(node)->children;
2123

    
2124
        while (node) {
2125
                if (summary_search_unread_recursive(ctree, node) == TRUE)
2126
                        return TRUE;
2127
                node = GTK_CTREE_ROW(node)->sibling;
2128
        }
2129

    
2130
        return FALSE;
2131
}
2132

    
2133
static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2134
{
2135
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2136
        GtkStyle *style = NULL;
2137
        MsgInfo *msginfo;
2138
        MsgFlags flags;
2139
        gint *col_pos = summaryview->col_pos;
2140

    
2141
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2142
        if (!msginfo) return;
2143

    
2144
        flags = msginfo->flags;
2145

    
2146
        gtk_ctree_node_set_foreground(ctree, row, NULL);
2147

    
2148
        /* set new/unread column */
2149
        if (MSG_IS_NEW(flags)) {
2150
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2151
                                          newxpm, newxpmmask);
2152
        } else if (MSG_IS_UNREAD(flags)) {
2153
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2154
                                          unreadxpm, unreadxpmmask);
2155
        } else if (MSG_IS_REPLIED(flags)) {
2156
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2157
                                          repliedxpm, repliedxpmmask);
2158
        } else if (MSG_IS_FORWARDED(flags)) {
2159
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2160
                                          forwardedxpm, forwardedxpmmask);
2161
        } else {
2162
                gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_UNREAD],
2163
                                        NULL);
2164
        }
2165

    
2166
        if (prefs_common.bold_unread &&
2167
            (MSG_IS_UNREAD(flags) ||
2168
             (!GTK_CTREE_ROW(row)->expanded &&
2169
              GTK_CTREE_ROW(row)->children &&
2170
              summary_have_unread_children(summaryview, row))))
2171
                style = bold_style;
2172

    
2173
        /* set mark column */
2174
        if (MSG_IS_DELETED(flags)) {
2175
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2176
                                          deletedxpm, deletedxpmmask);
2177
                if (style)
2178
                        style = bold_deleted_style;
2179
                else
2180
                        gtk_ctree_node_set_foreground
2181
                                (ctree, row, &summaryview->color_dim);
2182
        } else if (MSG_IS_MOVE(flags)) {
2183
                gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "o");
2184
                if (style)
2185
                        style = bold_marked_style;
2186
                else
2187
                        gtk_ctree_node_set_foreground
2188
                                (ctree, row, &summaryview->color_marked);
2189
        } else if (MSG_IS_COPY(flags)) {
2190
                gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "O");
2191
                if (style)
2192
                        style = bold_marked_style;
2193
                else
2194
                        gtk_ctree_node_set_foreground
2195
                                (ctree, row, &summaryview->color_marked);
2196
        } else if (MSG_IS_MARKED(flags)) {
2197
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2198
                                          markxpm, markxpmmask);
2199
        } else {
2200
                gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], NULL);
2201
        }
2202

    
2203
        if (MSG_IS_MIME(flags)) {
2204
                gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
2205
                                          clipxpm, clipxpmmask);
2206
        } else {
2207
                gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MIME], NULL);
2208
        }
2209

    
2210
        gtk_ctree_node_set_row_style(ctree, row, style);
2211

    
2212
        if (MSG_GET_COLORLABEL(flags))
2213
                summary_set_colorlabel_color(ctree, row,
2214
                                             MSG_GET_COLORLABEL_VALUE(flags));
2215
}
2216

    
2217
void summary_set_marks_selected(SummaryView *summaryview)
2218
{
2219
        GList *cur;
2220

    
2221
        for (cur = GTK_CLIST(summaryview->ctree)->selection; cur != NULL;
2222
             cur = cur->next)
2223
                summary_set_row_marks(summaryview, GTK_CTREE_NODE(cur->data));
2224
}
2225

    
2226
static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
2227
{
2228
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2229
        MsgInfo *msginfo;
2230

    
2231
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2232
        msginfo->to_folder = NULL;
2233
        if (MSG_IS_DELETED(msginfo->flags))
2234
                summaryview->deleted--;
2235
        if (MSG_IS_MOVE(msginfo->flags))
2236
                summaryview->moved--;
2237
        if (MSG_IS_COPY(msginfo->flags))
2238
                summaryview->copied--;
2239
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2240
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2241
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2242
        summary_set_row_marks(summaryview, row);
2243
        debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2244
}
2245

    
2246
void summary_mark(SummaryView *summaryview)
2247
{
2248
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2249
        GList *cur;
2250

    
2251
        for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2252
                summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
2253
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2254
                GSList *msglist;
2255
                msglist = summary_get_selected_msg_list(summaryview);
2256
                imap_msg_list_set_perm_flags(msglist, MSG_MARKED);
2257
                g_slist_free(msglist);
2258
        }
2259

    
2260
        /* summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); */
2261
}
2262

    
2263
static void summary_mark_row_as_read(SummaryView *summaryview,
2264
                                     GtkCTreeNode *row)
2265
{
2266
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2267
        MsgInfo *msginfo;
2268

    
2269
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2270
        if (MSG_IS_NEW(msginfo->flags))
2271
                summaryview->folder_item->new--;
2272
        if (MSG_IS_UNREAD(msginfo->flags))
2273
                summaryview->folder_item->unread--;
2274
        if (MSG_IS_NEW(msginfo->flags) ||
2275
            MSG_IS_UNREAD(msginfo->flags)) {
2276
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2277
                summary_set_row_marks(summaryview, row);
2278
                debug_print(_("Message %d is marked as being read\n"),
2279
                            msginfo->msgnum);
2280
        }
2281
}
2282

    
2283
void summary_mark_as_read(SummaryView *summaryview)
2284
{
2285
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2286
        GList *cur;
2287

    
2288
        for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2289
                summary_mark_row_as_read(summaryview,
2290
                                         GTK_CTREE_NODE(cur->data));
2291
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2292
                GSList *msglist;
2293
                msglist = summary_get_selected_msg_list(summaryview);
2294
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
2295
                g_slist_free(msglist);
2296
        }
2297

    
2298
        summary_status_show(summaryview);
2299
}
2300

    
2301
void summary_mark_all_read(SummaryView *summaryview)
2302
{
2303
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2304
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
2305
        GtkCTreeNode *node;
2306

    
2307
        gtk_clist_freeze(clist);
2308
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2309
             node = gtkut_ctree_node_next(ctree, node))
2310
                summary_mark_row_as_read(summaryview, node);
2311
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2312
             node = gtkut_ctree_node_next(ctree, node)) {
2313
                if (!GTK_CTREE_ROW(node)->expanded)
2314
                        summary_set_row_marks(summaryview, node);
2315
        }
2316
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2317
                GSList *msglist;
2318
                msglist = summary_get_msg_list(summaryview);
2319
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
2320
                g_slist_free(msglist);
2321
        }
2322
        gtk_clist_thaw(clist);
2323

    
2324
        summary_status_show(summaryview);
2325
}
2326

    
2327
static void summary_mark_row_as_unread(SummaryView *summaryview,
2328
                                       GtkCTreeNode *row)
2329
{
2330
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2331
        MsgInfo *msginfo;
2332

    
2333
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2334
        if (MSG_IS_DELETED(msginfo->flags)) {
2335
                msginfo->to_folder = NULL;
2336
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2337
                summaryview->deleted--;
2338
        }
2339
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
2340
        if (!MSG_IS_UNREAD(msginfo->flags)) {
2341
                MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
2342
                summaryview->folder_item->unread++;
2343
                debug_print(_("Message %d is marked as unread\n"),
2344
                            msginfo->msgnum);
2345
        }
2346
        summary_set_row_marks(summaryview, row);
2347
}
2348

    
2349
void summary_mark_as_unread(SummaryView *summaryview)
2350
{
2351
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2352
        GList *cur;
2353

    
2354
        for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2355
                summary_mark_row_as_unread(summaryview,
2356
                                           GTK_CTREE_NODE(cur->data));
2357
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2358
                GSList *msglist;
2359
                msglist = summary_get_selected_msg_list(summaryview);
2360
                imap_msg_list_unset_perm_flags(msglist, MSG_REPLIED);
2361
                imap_msg_list_set_perm_flags(msglist, MSG_UNREAD);
2362
                g_slist_free(msglist);
2363
        }
2364

    
2365
        summary_status_show(summaryview);
2366
}
2367

    
2368
static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
2369
{
2370
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2371
        MsgInfo *msginfo;
2372

    
2373
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2374

    
2375
        if (MSG_IS_DELETED(msginfo->flags)) return;
2376

    
2377
        msginfo->to_folder = NULL;
2378
        if (MSG_IS_MOVE(msginfo->flags))
2379
                summaryview->moved--;
2380
        if (MSG_IS_COPY(msginfo->flags))
2381
                summaryview->copied--;
2382
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2383
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2384
        summaryview->deleted++;
2385

    
2386
        if (!prefs_common.immediate_exec && 
2387
            summaryview->folder_item->stype != F_TRASH)
2388
                summary_set_row_marks(summaryview, row);
2389

    
2390
        debug_print(_("Message %s/%d is set to delete\n"),
2391
                    msginfo->folder->path, msginfo->msgnum);
2392
}
2393

    
2394
void summary_delete(SummaryView *summaryview)
2395
{
2396
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2397
        FolderItem *item = summaryview->folder_item;
2398
        GList *cur;
2399
        GtkCTreeNode *sel_last = NULL;
2400
        GtkCTreeNode *node;
2401

    
2402
        if (!item || FOLDER_TYPE(item->folder) == F_NEWS) return;
2403

    
2404
        if (summary_is_locked(summaryview)) return;
2405

    
2406
        /* if current folder is trash, ask for confirmation */
2407
        if (item->stype == F_TRASH) {
2408
                AlertValue aval;
2409

    
2410
                aval = alertpanel(_("Delete message(s)"),
2411
                                  _("Do you really want to delete message(s) from the trash?"),
2412
                                  _("Yes"), _("No"), NULL);
2413
                if (aval != G_ALERTDEFAULT) return;
2414
        }
2415

    
2416
        /* next code sets current row focus right. We need to find a row
2417
         * that is not deleted. */
2418
        for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
2419
                sel_last = GTK_CTREE_NODE(cur->data);
2420
                summary_delete_row(summaryview, sel_last);
2421
        }
2422

    
2423
        node = summary_find_next_msg(summaryview, sel_last);
2424
        if (!node)
2425
                node = summary_find_prev_msg(summaryview, sel_last);
2426

    
2427
        if (node) {
2428
                if (sel_last && node == gtkut_ctree_node_next(ctree, sel_last))
2429
                        summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2430
                else if (sel_last && node == GTK_CTREE_NODE_PREV(sel_last))
2431
                        summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
2432
                else
2433
                        summary_select_node
2434
                                (summaryview, node,
2435
                                 messageview_is_visible(summaryview->messageview),
2436
                                 FALSE);
2437
        }
2438

    
2439
        if (prefs_common.immediate_exec || item->stype == F_TRASH)
2440
                summary_execute(summaryview);
2441
        else
2442
                summary_status_show(summaryview);
2443
}
2444

    
2445
void summary_delete_duplicated(SummaryView *summaryview)
2446
{
2447
        if (!summaryview->folder_item ||
2448
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
2449
        if (summaryview->folder_item->stype == F_TRASH) return;
2450

    
2451
        main_window_cursor_wait(summaryview->mainwin);
2452
        debug_print(_("Deleting duplicated messages..."));
2453
        STATUSBAR_PUSH(summaryview->mainwin,
2454
                       _("Deleting duplicated messages..."));
2455

    
2456
        gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2457
                                GTK_CTREE_FUNC(summary_delete_duplicated_func),
2458
                                summaryview);
2459

    
2460
        if (prefs_common.immediate_exec)
2461
                summary_execute(summaryview);
2462
        else
2463
                summary_status_show(summaryview);
2464

    
2465
        debug_print(_("done.\n"));
2466
        STATUSBAR_POP(summaryview->mainwin);
2467
        main_window_cursor_normal(summaryview->mainwin);
2468
}
2469

    
2470
static void summary_delete_duplicated_func(GtkCTree *ctree, GtkCTreeNode *node,
2471
                                           SummaryView *summaryview)
2472
{
2473
        GtkCTreeNode *found;
2474
        MsgInfo *msginfo;
2475

    
2476
        msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2477

    
2478
        if (!msginfo || !msginfo->msgid || !*msginfo->msgid) return;
2479

    
2480
        found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
2481

    
2482
        if (found && found != node)
2483
                summary_delete_row(summaryview, node);
2484
}
2485

    
2486
static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
2487
{
2488
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2489
        MsgInfo *msginfo;
2490

    
2491
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2492
        msginfo->to_folder = NULL;
2493
        if (MSG_IS_DELETED(msginfo->flags))
2494
                summaryview->deleted--;
2495
        if (MSG_IS_MOVE(msginfo->flags))
2496
                summaryview->moved--;
2497
        if (MSG_IS_COPY(msginfo->flags))
2498
                summaryview->copied--;
2499
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2500
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2501
        summary_set_row_marks(summaryview, row);
2502

    
2503
        debug_print(_("Message %s/%d is unmarked\n"),
2504
                    msginfo->folder->path, msginfo->msgnum);
2505
}
2506

    
2507
void summary_unmark(SummaryView *summaryview)
2508
{
2509
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2510
        GList *cur;
2511

    
2512
        for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2513
                summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
2514
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2515
                GSList *msglist;
2516
                msglist = summary_get_selected_msg_list(summaryview);
2517
                imap_msg_list_unset_perm_flags(msglist, MSG_MARKED);
2518
                g_slist_free(msglist);
2519
        }
2520

    
2521
        summary_status_show(summaryview);
2522
}
2523

    
2524
static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2525
                                FolderItem *to_folder)
2526
{
2527
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2528
        MsgInfo *msginfo;
2529

    
2530
        g_return_if_fail(to_folder != NULL);
2531

    
2532
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2533
        msginfo->to_folder = to_folder;
2534
        if (MSG_IS_DELETED(msginfo->flags))
2535
                summaryview->deleted--;
2536
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2537
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2538
        if (!MSG_IS_MOVE(msginfo->flags)) {
2539
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2540
                summaryview->moved++;
2541
        }
2542
        if (!prefs_common.immediate_exec)
2543
                summary_set_row_marks(summaryview, row);
2544

    
2545
        debug_print(_("Message %d is set to move to %s\n"),
2546
                    msginfo->msgnum, to_folder->path);
2547
}
2548

    
2549
void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2550
{
2551
        GList *cur;
2552

    
2553
        if (!to_folder) return;
2554
        if (!summaryview->folder_item ||
2555
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
2556

    
2557
        if (summary_is_locked(summaryview)) return;
2558

    
2559
        if (summaryview->folder_item == to_folder) {
2560
                alertpanel_warning(_("Destination is same as current folder."));
2561
                return;
2562
        }
2563

    
2564
        for (cur = GTK_CLIST(summaryview->ctree)->selection;
2565
             cur != NULL; cur = cur->next)
2566
                summary_move_row_to
2567
                        (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2568

    
2569
        summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2570

    
2571
        if (prefs_common.immediate_exec)
2572
                summary_execute(summaryview);
2573
        else
2574
                summary_status_show(summaryview);
2575
}
2576

    
2577
void summary_move_to(SummaryView *summaryview)
2578
{
2579
        FolderItem *to_folder;
2580

    
2581
        if (!summaryview->folder_item ||
2582
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
2583

    
2584
        to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
2585
                                         FOLDER_SEL_MOVE, NULL);
2586
        summary_move_selected_to(summaryview, to_folder);
2587
}
2588

    
2589
static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2590
                                FolderItem *to_folder)
2591
{
2592
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2593
        MsgInfo *msginfo;
2594

    
2595
        g_return_if_fail(to_folder != NULL);
2596

    
2597
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
2598
        msginfo->to_folder = to_folder;
2599
        if (MSG_IS_DELETED(msginfo->flags))
2600
                summaryview->deleted--;
2601
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2602
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2603
        if (!MSG_IS_COPY(msginfo->flags)) {
2604
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2605
                summaryview->copied++;
2606
        }
2607
        if (!prefs_common.immediate_exec)
2608
                summary_set_row_marks(summaryview, row);
2609

    
2610
        debug_print(_("Message %d is set to copy to %s\n"),
2611
                    msginfo->msgnum, to_folder->path);
2612
}
2613

    
2614
void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2615
{
2616
        GList *cur;
2617

    
2618
        if (!to_folder) return;
2619
        if (!summaryview->folder_item) return;
2620

    
2621
        if (summary_is_locked(summaryview)) return;
2622

    
2623
        if (summaryview->folder_item == to_folder) {
2624
                alertpanel_warning
2625
                        (_("Destination for copy is same as current folder."));
2626
                return;
2627
        }
2628

    
2629
        for (cur = GTK_CLIST(summaryview->ctree)->selection;
2630
             cur != NULL; cur = cur->next)
2631
                summary_copy_row_to
2632
                        (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2633

    
2634
        summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2635

    
2636
        if (prefs_common.immediate_exec)
2637
                summary_execute(summaryview);
2638
        else
2639
                summary_status_show(summaryview);
2640
}
2641

    
2642
void summary_copy_to(SummaryView *summaryview)
2643
{
2644
        FolderItem *to_folder;
2645

    
2646
        if (!summaryview->folder_item) return;
2647

    
2648
        to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
2649
                                         FOLDER_SEL_COPY, NULL);
2650
        summary_copy_selected_to(summaryview, to_folder);
2651
}
2652

    
2653
void summary_add_address(SummaryView *summaryview)
2654
{
2655
        MsgInfo *msginfo;
2656
        gchar *from;
2657

    
2658
        msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2659
                                              summaryview->selected);
2660
        if (!msginfo) return;
2661

    
2662
        Xstrdup_a(from, msginfo->from, return);
2663
        eliminate_address_comment(from);
2664
        extract_address(from);
2665
        addressbook_add_contact(msginfo->fromname, from, NULL);
2666
}
2667

    
2668
void summary_select_all(SummaryView *summaryview)
2669
{
2670
        if (!summaryview->folder_item) return;
2671

    
2672
        if (summaryview->folder_item->total >= 500) {
2673
                STATUSBAR_PUSH(summaryview->mainwin,
2674
                               _("Selecting all messages..."));
2675
                main_window_cursor_wait(summaryview->mainwin);
2676
        }
2677

    
2678
        gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
2679

    
2680
        if (summaryview->folder_item->total >= 500) {
2681
                STATUSBAR_POP(summaryview->mainwin);
2682
                main_window_cursor_normal(summaryview->mainwin);
2683
        }
2684
}
2685

    
2686
void summary_unselect_all(SummaryView *summaryview)
2687
{
2688
        gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
2689
}
2690

    
2691
void summary_select_thread(SummaryView *summaryview)
2692
{
2693
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2694
        GtkCTreeNode *node = summaryview->selected;
2695

    
2696
        if (!node) return;
2697

    
2698
        while (GTK_CTREE_ROW(node)->parent != NULL)
2699
                node = GTK_CTREE_ROW(node)->parent;
2700

    
2701
        if (node != summaryview->selected)
2702
                summary_select_node
2703
                        (summaryview, node,
2704
                         messageview_is_visible(summaryview->messageview),
2705
                         FALSE);
2706

    
2707
        gtk_ctree_select_recursive(ctree, node);
2708

    
2709
        summary_status_show(summaryview);
2710
}
2711

    
2712
void summary_save_as(SummaryView *summaryview)
2713
{
2714
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2715
        MsgInfo *msginfo;
2716
        gchar *filename = NULL;
2717
        gchar *src, *dest;
2718

    
2719
        if (!summaryview->selected) return;
2720
        msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2721
        if (!msginfo) return;
2722

    
2723
        if (msginfo->subject) {
2724
                Xstrdup_a(filename, msginfo->subject, return);
2725
                subst_for_filename(filename);
2726
        }
2727
        dest = filesel_select_file(_("Save as"), filename);
2728
        if (!dest) return;
2729
        if (is_file_exist(dest)) {
2730
                AlertValue aval;
2731

    
2732
                aval = alertpanel(_("Overwrite"),
2733
                                  _("Overwrite existing file?"),
2734
                                  _("OK"), _("Cancel"), NULL);
2735
                if (G_ALERTDEFAULT != aval) return;
2736
        }
2737

    
2738
        src = procmsg_get_message_file(msginfo);
2739
        if (copy_file(src, dest, TRUE) < 0) {
2740
                alertpanel_error(_("Can't save the file `%s'."),
2741
                                 g_basename(dest));
2742
        }
2743
        g_free(src);
2744
}
2745

    
2746
void summary_print(SummaryView *summaryview)
2747
{
2748
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2749
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
2750
        MsgInfo *msginfo;
2751
        GList *cur;
2752
        gchar *cmdline;
2753
        gchar *p;
2754

    
2755
        if (clist->selection == NULL) return;
2756

    
2757
        cmdline = input_dialog(_("Print"),
2758
                               _("Enter the print command line:\n"
2759
                                 "(`%s' will be replaced with file name)"),
2760
                               prefs_common.print_cmd);
2761
        if (!cmdline) return;
2762
        if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
2763
            strchr(p + 2, '%')) {
2764
                alertpanel_error(_("Print command line is invalid:\n`%s'"),
2765
                                 cmdline);
2766
                g_free(cmdline);
2767
                return;
2768
        }
2769

    
2770
        for (cur = clist->selection; cur != NULL; cur = cur->next) {
2771
                msginfo = gtk_ctree_node_get_row_data
2772
                        (ctree, GTK_CTREE_NODE(cur->data));
2773
                if (msginfo) procmsg_print_message(msginfo, cmdline);
2774
        }
2775

    
2776
        g_free(cmdline);
2777
}
2778

    
2779
gboolean summary_execute(SummaryView *summaryview)
2780
{
2781
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
2782
        gint val = 0;
2783

    
2784
        if (!summaryview->folder_item) return FALSE;
2785

    
2786
        if (summary_is_locked(summaryview)) return FALSE;
2787
        summary_lock(summaryview);
2788

    
2789
        gtk_clist_freeze(clist);
2790

    
2791
        val |= summary_execute_move(summaryview);
2792
        val |= summary_execute_copy(summaryview);
2793
        val |= summary_execute_delete(summaryview);
2794

    
2795
        statusbar_pop_all();
2796
        STATUSBAR_POP(summaryview->mainwin);
2797

    
2798
        summary_remove_invalid_messages(summaryview);
2799

    
2800
        gtk_clist_thaw(clist);
2801

    
2802
        summary_unlock(summaryview);
2803

    
2804
        if (val != 0) {
2805
                alertpanel_error(_("Error occurred while processing messages."));
2806
        }
2807

    
2808
        return TRUE;
2809
}
2810

    
2811
static void summary_remove_invalid_messages(SummaryView *summaryview)
2812
{
2813
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2814
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
2815
        GtkCTreeNode *node, *next;
2816
        GtkCTreeNode *new_selected = NULL;
2817

    
2818
        gtk_clist_freeze(clist);
2819

    
2820
        if (summaryview->folder_item->threaded)
2821
                summary_unthread_for_exec(summaryview);
2822

    
2823
        node = GTK_CTREE_NODE(clist->row_list);
2824
        for (; node != NULL; node = next) {
2825
                MsgInfo *msginfo;
2826

    
2827
                next = gtkut_ctree_node_next(ctree, node);
2828

    
2829
                msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2830
                if (!msginfo || !MSG_IS_INVALID(msginfo->flags))
2831
                        continue;
2832

    
2833
                if (node == summaryview->displayed) {
2834
                        messageview_clear(summaryview->messageview);
2835
                        summaryview->displayed = NULL;
2836
                }
2837
                if (GTK_CTREE_ROW(node)->children != NULL) {
2838
                        g_warning("summary_execute(): children != NULL\n");
2839
                        continue;
2840
                }
2841

    
2842
                if (!new_selected &&
2843
                    gtkut_ctree_node_is_selected(ctree, node)) {
2844
                        gtk_sctree_unselect_all(GTK_SCTREE(ctree));
2845
                        new_selected = summary_find_next_msg(summaryview, node);
2846
                        if (!new_selected)
2847
                                new_selected = summary_find_prev_msg
2848
                                        (summaryview, node);
2849
                }
2850

    
2851
                if (msginfo->msgid && *msginfo->msgid &&
2852
                    node == g_hash_table_lookup(summaryview->msgid_table,
2853
                                                msginfo->msgid))
2854
                        g_hash_table_remove(summaryview->msgid_table,
2855
                                            msginfo->msgid);
2856

    
2857
                gtk_ctree_remove_node(ctree, node);
2858
                procmsg_msginfo_free(msginfo);
2859
        }
2860

    
2861
        if (new_selected) {
2862
                gtk_sctree_select
2863
                        (GTK_SCTREE(ctree),
2864
                         summaryview->displayed ? summaryview->displayed
2865
                         : new_selected);
2866
        }
2867

    
2868
        if (summaryview->folder_item->threaded)
2869
                summary_thread_build(summaryview);
2870

    
2871
        summaryview->selected = clist->selection ?
2872
                GTK_CTREE_NODE(clist->selection->data) : NULL;
2873

    
2874
        if (!GTK_CLIST(summaryview->ctree)->row_list) {
2875
                menu_set_insensitive_all
2876
                        (GTK_MENU_SHELL(summaryview->popupmenu));
2877
                gtk_widget_grab_focus(summaryview->folderview->ctree);
2878
        } else
2879
                gtk_widget_grab_focus(summaryview->ctree);
2880

    
2881
        summary_write_cache(summaryview);
2882

    
2883
        summary_update_status(summaryview);
2884
        summary_status_show(summaryview);
2885

    
2886
        gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
2887

    
2888
        gtk_clist_thaw(clist);
2889
}
2890

    
2891
static gint summary_execute_move(SummaryView *summaryview)
2892
{
2893
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2894
        gint val = 0;
2895

    
2896
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
2897

    
2898
        /* search moving messages and execute */
2899
        gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
2900
                                summaryview);
2901

    
2902
        if (summaryview->mlist) {
2903
                summaryview->mlist = g_slist_reverse(summaryview->mlist);
2904
                val = procmsg_move_messages(summaryview->mlist);
2905

    
2906
                folder_item_scan_foreach(summaryview->folder_table);
2907
                folderview_update_item_foreach(summaryview->folder_table,
2908
                                               FALSE);
2909

    
2910
                g_slist_free(summaryview->mlist);
2911
                summaryview->mlist = NULL;
2912
        }
2913

    
2914
        g_hash_table_destroy(summaryview->folder_table);
2915
        summaryview->folder_table = NULL;
2916

    
2917
        return val;
2918
}
2919

    
2920
static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
2921
                                      gpointer data)
2922
{
2923
        SummaryView *summaryview = data;
2924
        MsgInfo *msginfo;
2925

    
2926
        msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2927

    
2928
        if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
2929
                g_hash_table_insert(summaryview->folder_table,
2930
                                    msginfo->to_folder, GINT_TO_POINTER(1));
2931

    
2932
                summaryview->mlist =
2933
                        g_slist_prepend(summaryview->mlist, msginfo);
2934

    
2935
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2936
                summary_set_row_marks(summaryview, node);
2937
        }
2938
}
2939

    
2940
static gint summary_execute_copy(SummaryView *summaryview)
2941
{
2942
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2943
        gint val = 0;
2944

    
2945
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
2946

    
2947
        /* search copying messages and execute */
2948
        gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
2949
                                summaryview);
2950

    
2951
        if (summaryview->mlist) {
2952
                summaryview->mlist = g_slist_reverse(summaryview->mlist);
2953
                val = procmsg_copy_messages(summaryview->mlist);
2954

    
2955
                folder_item_scan_foreach(summaryview->folder_table);
2956
                folderview_update_item_foreach(summaryview->folder_table,
2957
                                               FALSE);
2958

    
2959
                g_slist_free(summaryview->mlist);
2960
                summaryview->mlist = NULL;
2961
        }
2962

    
2963
        g_hash_table_destroy(summaryview->folder_table);
2964
        summaryview->folder_table = NULL;
2965

    
2966
        return val;
2967
}
2968

    
2969
static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
2970
                                      gpointer data)
2971
{
2972
        SummaryView *summaryview = data;
2973
        MsgInfo *msginfo;
2974

    
2975
        msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2976

    
2977
        if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
2978
                g_hash_table_insert(summaryview->folder_table,
2979
                                    msginfo->to_folder, GINT_TO_POINTER(1));
2980

    
2981
                summaryview->mlist =
2982
                        g_slist_prepend(summaryview->mlist, msginfo);
2983

    
2984
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2985
                summary_set_row_marks(summaryview, node);
2986
        }
2987
}
2988

    
2989
static gint summary_execute_delete(SummaryView *summaryview)
2990
{
2991
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2992
        FolderItem *trash;
2993
        gint val;
2994

    
2995
        trash = summaryview->folder_item->folder->trash;
2996
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) {
2997
                g_return_val_if_fail(trash != NULL, 0);
2998
        }
2999

    
3000
        /* search deleting messages and execute */
3001
        gtk_ctree_pre_recursive
3002
                (ctree, NULL, summary_execute_delete_func, summaryview);
3003

    
3004
        if (!summaryview->mlist) return 0;
3005

    
3006
        summaryview->mlist = g_slist_reverse(summaryview->mlist);
3007

    
3008
        if (summaryview->folder_item != trash)
3009
                val = folder_item_move_msgs(trash, summaryview->mlist);
3010
        else
3011
                val = folder_item_remove_msgs(trash, summaryview->mlist);
3012

    
3013
        g_slist_free(summaryview->mlist);
3014
        summaryview->mlist = NULL;
3015

    
3016
        if (summaryview->folder_item != trash) {
3017
                folder_item_scan(trash);
3018
                folderview_update_item(trash, FALSE);
3019
        }
3020

    
3021
        return val == -1 ? -1 : 0;
3022
}
3023

    
3024
static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
3025
                                        gpointer data)
3026
{
3027
        SummaryView *summaryview = data;
3028
        MsgInfo *msginfo;
3029

    
3030
        msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3031

    
3032
        if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
3033
                summaryview->mlist =
3034
                        g_slist_prepend(summaryview->mlist, msginfo);
3035
        }
3036
}
3037

    
3038
/* thread functions */
3039

    
3040
void summary_thread_build(SummaryView *summaryview)
3041
{
3042
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3043
        GtkCTreeNode *node;
3044
        GtkCTreeNode *next;
3045
        GtkCTreeNode *parent;
3046
        MsgInfo *msginfo;
3047

    
3048
        summary_lock(summaryview);
3049

    
3050
        debug_print(_("Building threads..."));
3051
        STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
3052
        main_window_cursor_wait(summaryview->mainwin);
3053

    
3054
        g_signal_handlers_block_by_func(G_OBJECT(ctree),
3055
                                        G_CALLBACK(summary_tree_expanded),
3056
                                        summaryview);
3057
        gtk_clist_freeze(GTK_CLIST(ctree));
3058

    
3059
        node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3060
        while (node) {
3061
                next = GTK_CTREE_ROW(node)->sibling;
3062

    
3063
                msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3064
                if (msginfo && msginfo->inreplyto) {
3065
                        parent = g_hash_table_lookup(summaryview->msgid_table,
3066
                                                     msginfo->inreplyto);
3067
                        if (parent && parent != node) {
3068
                                gtk_ctree_move(ctree, node, parent, NULL);
3069
                                gtk_ctree_expand(ctree, node);
3070
                        }
3071
                }
3072

    
3073
                node = next;
3074
        }
3075

    
3076
        node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3077

    
3078
        while (node) {
3079
                next = GTK_CTREE_NODE_NEXT(node);
3080
                if (prefs_common.expand_thread)
3081
                        gtk_ctree_expand(ctree, node);
3082
                if (prefs_common.bold_unread &&
3083
                    GTK_CTREE_ROW(node)->children)
3084
                        summary_set_row_marks(summaryview, node);
3085
                node = next;
3086
        }
3087

    
3088
        gtk_clist_thaw(GTK_CLIST(ctree));
3089
        g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
3090
                                          G_CALLBACK(summary_tree_expanded),
3091
                                          summaryview);
3092

    
3093
        debug_print(_("done.\n"));
3094
        STATUSBAR_POP(summaryview->mainwin);
3095
        main_window_cursor_normal(summaryview->mainwin);
3096

    
3097
        summary_unlock(summaryview);
3098
}
3099

    
3100
static void summary_thread_init(SummaryView *summaryview)
3101
{
3102
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3103
        GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3104
        GtkCTreeNode *next;
3105

    
3106
        if (prefs_common.expand_thread) {
3107
                while (node) {
3108
                        next = GTK_CTREE_ROW(node)->sibling;
3109
                        if (GTK_CTREE_ROW(node)->children)
3110
                                gtk_ctree_expand(ctree, node);
3111
                        node = next;
3112
                }
3113
        } else if (prefs_common.bold_unread) {
3114
                while (node) {
3115
                        next = GTK_CTREE_ROW(node)->sibling;
3116
                        if (GTK_CTREE_ROW(node)->children)
3117
                                summary_set_row_marks(summaryview, node);
3118
                        node = next;
3119
                }
3120
        }
3121
}
3122

    
3123
void summary_unthread(SummaryView *summaryview)
3124
{
3125
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3126
        GtkCTreeNode *node;
3127
        GtkCTreeNode *child;
3128
        GtkCTreeNode *sibling;
3129
        GtkCTreeNode *next_child;
3130

    
3131
        summary_lock(summaryview);
3132

    
3133
        debug_print(_("Unthreading..."));
3134
        STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
3135
        main_window_cursor_wait(summaryview->mainwin);
3136

    
3137
        g_signal_handlers_block_by_func(G_OBJECT(ctree),
3138
                                        G_CALLBACK(summary_tree_collapsed),
3139
                                        summaryview);
3140
        gtk_clist_freeze(GTK_CLIST(ctree));
3141

    
3142
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3143
             node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
3144
                child = GTK_CTREE_ROW(node)->children;
3145
                sibling = GTK_CTREE_ROW(node)->sibling;
3146

    
3147
                while (child != NULL) {
3148
                        next_child = GTK_CTREE_ROW(child)->sibling;
3149
                        gtk_ctree_move(ctree, child, NULL, sibling);
3150
                        child = next_child;
3151
                }
3152
        }
3153

    
3154
        gtk_clist_thaw(GTK_CLIST(ctree));
3155
        g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
3156
                                          G_CALLBACK(summary_tree_collapsed),
3157
                                          summaryview);
3158

    
3159
        debug_print(_("done.\n"));
3160
        STATUSBAR_POP(summaryview->mainwin);
3161
        main_window_cursor_normal(summaryview->mainwin);
3162

    
3163
        summary_unlock(summaryview);
3164
}
3165

    
3166
static void summary_unthread_for_exec(SummaryView *summaryview)
3167
{
3168
        GtkCTreeNode *node;
3169
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3170

    
3171
        summary_lock(summaryview);
3172

    
3173
        debug_print(_("Unthreading for execution..."));
3174

    
3175
        gtk_clist_freeze(GTK_CLIST(ctree));
3176

    
3177
        for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3178
             node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
3179
                summary_unthread_for_exec_func(ctree, node, NULL);
3180
        }
3181

    
3182
        gtk_clist_thaw(GTK_CLIST(ctree));
3183

    
3184
        debug_print(_("done.\n"));
3185

    
3186
        summary_unlock(summaryview);
3187
}
3188

    
3189
static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
3190
                                           gpointer data)
3191
{
3192
        MsgInfo *msginfo;
3193
        GtkCTreeNode *top_parent;
3194
        GtkCTreeNode *child;
3195
        GtkCTreeNode *sibling;
3196

    
3197
        msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3198

    
3199
        if (!msginfo || !MSG_IS_INVALID(msginfo->flags))
3200
                return;
3201
        child = GTK_CTREE_ROW(node)->children;
3202
        if (!child) return;
3203

    
3204
        for (top_parent = node;
3205
             GTK_CTREE_ROW(top_parent)->parent != NULL;
3206
             top_parent = GTK_CTREE_ROW(top_parent)->parent)
3207
                ;
3208
        sibling = GTK_CTREE_ROW(top_parent)->sibling;
3209

    
3210
        while (child != NULL) {
3211
                GtkCTreeNode *next_child;
3212

    
3213
                next_child = GTK_CTREE_ROW(child)->sibling;
3214
                gtk_ctree_move(ctree, child, NULL, sibling);
3215
                child = next_child;
3216
        }
3217
}
3218

    
3219
void summary_expand_threads(SummaryView *summaryview)
3220
{
3221
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3222
        GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3223

    
3224
        gtk_clist_freeze(GTK_CLIST(ctree));
3225

    
3226
        while (node) {
3227
                if (GTK_CTREE_ROW(node)->children)
3228
                        gtk_ctree_expand(ctree, node);
3229
                node = GTK_CTREE_NODE_NEXT(node);
3230
        }
3231

    
3232
        gtk_clist_thaw(GTK_CLIST(ctree));
3233

    
3234
        gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
3235
}
3236

    
3237
void summary_collapse_threads(SummaryView *summaryview)
3238
{
3239
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3240
        GtkCTreeNode *node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3241

    
3242
        gtk_clist_freeze(GTK_CLIST(ctree));
3243

    
3244
        while (node) {
3245
                if (GTK_CTREE_ROW(node)->children)
3246
                        gtk_ctree_collapse(ctree, node);
3247
                node = GTK_CTREE_ROW(node)->sibling;
3248
        }
3249

    
3250
        gtk_clist_thaw(GTK_CLIST(ctree));
3251

    
3252
        gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
3253
}
3254

    
3255
void summary_filter(SummaryView *summaryview, gboolean selected_only)
3256
{
3257
        if (!prefs_common.fltlist) return;
3258

    
3259
        summary_lock(summaryview);
3260

    
3261
        STATUSBAR_POP(summaryview->mainwin);
3262

    
3263
        debug_print(_("filtering..."));
3264
        STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
3265
        main_window_cursor_wait(summaryview->mainwin);
3266

    
3267
        gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
3268

    
3269
        summaryview->filtered = 0;
3270

    
3271
        if (selected_only) {
3272
                GList *cur;
3273

    
3274
                for (cur = GTK_CLIST(summaryview->ctree)->selection;
3275
                     cur != NULL; cur = cur->next) {
3276
                        summary_filter_func(GTK_CTREE(summaryview->ctree),
3277
                                            GTK_CTREE_NODE(cur->data),
3278
                                            summaryview);
3279
                }
3280
        } else {
3281
                gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3282
                                        GTK_CTREE_FUNC(summary_filter_func),
3283
                                        summaryview);
3284
        }
3285

    
3286
        summary_unlock(summaryview);
3287

    
3288
        if (prefs_common.immediate_exec)
3289
                summary_execute(summaryview);
3290
        else
3291
                summary_status_show(summaryview);
3292

    
3293
        folderview_update_all_updated(FALSE);
3294

    
3295
        gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3296

    
3297
        debug_print(_("done.\n"));
3298
        STATUSBAR_POP(summaryview->mainwin);
3299
        main_window_cursor_normal(summaryview->mainwin);
3300

    
3301
        if (summaryview->filtered > 0) {
3302
                gchar result_msg[BUFFSIZE];
3303
                g_snprintf(result_msg, sizeof(result_msg),
3304
                           _("%d message(s) have been filtered."),
3305
                           summaryview->filtered);
3306
                STATUSBAR_PUSH(summaryview->mainwin, result_msg);
3307
        }
3308
        summaryview->filtered = 0;
3309
}
3310

    
3311
static void summary_filter_func(GtkCTree *ctree, GtkCTreeNode *node,
3312
                                gpointer data)
3313
{
3314
        MsgInfo *msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3315
        SummaryView *summaryview = (SummaryView *)data;
3316
        FilterInfo *fltinfo;
3317

    
3318
        fltinfo = filter_info_new();
3319
        fltinfo->flags = msginfo->flags;
3320
        filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo);
3321
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
3322
            fltinfo->actions[FLT_ACTION_COPY] ||
3323
            fltinfo->actions[FLT_ACTION_DELETE] ||
3324
            fltinfo->actions[FLT_ACTION_EXEC] ||
3325
            fltinfo->actions[FLT_ACTION_EXEC_ASYNC] ||
3326
            fltinfo->actions[FLT_ACTION_MARK] ||
3327
            fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
3328
            fltinfo->actions[FLT_ACTION_MARK_READ] ||
3329
            fltinfo->actions[FLT_ACTION_FORWARD] ||
3330
            fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] ||
3331
            fltinfo->actions[FLT_ACTION_REDIRECT])
3332
                summaryview->filtered++;
3333

    
3334
        if ((fltinfo->actions[FLT_ACTION_MARK] ||
3335
             fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
3336
             fltinfo->actions[FLT_ACTION_MARK_READ])) {
3337
                msginfo->flags = fltinfo->flags;
3338
                summary_set_row_marks(summaryview, node);
3339
        }
3340

    
3341
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
3342
                summary_move_row_to(summaryview, node, fltinfo->move_dest);
3343
        else if (fltinfo->actions[FLT_ACTION_DELETE])
3344
                summary_delete_row(summaryview, node);
3345

    
3346
        filter_info_free(fltinfo);
3347
}
3348

    
3349
void summary_filter_open(SummaryView *summaryview, PrefsFilterType type)
3350
{
3351
        MsgInfo *msginfo;
3352
        gchar *header = NULL;
3353
        gchar *key = NULL;
3354

    
3355
        if (!summaryview->selected) return;
3356

    
3357
        msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
3358
                                              summaryview->selected);
3359
        if (!msginfo) return;
3360

    
3361
        procmsg_get_filter_keyword(msginfo, &header, &key, type);
3362
        prefs_filter_open(msginfo, header);
3363

    
3364
        g_free(header);
3365
        g_free(key);
3366
}
3367

    
3368
void summary_reply(SummaryView *summaryview, ComposeMode mode)
3369
{
3370
        GList *sel = GTK_CLIST(summaryview->ctree)->selection;
3371
        GSList *mlist = NULL;
3372
        MsgInfo *msginfo;
3373
        MsgInfo *displayed_msginfo = NULL;
3374
        gchar *text = NULL;
3375

    
3376
        for (; sel != NULL; sel = sel->next) {
3377
                mlist = g_slist_append(mlist,
3378
                                       gtk_ctree_node_get_row_data
3379
                                        (GTK_CTREE(summaryview->ctree),
3380
                                         GTK_CTREE_NODE(sel->data)));
3381
        }
3382
        if (!mlist) return;
3383
        msginfo = (MsgInfo *)mlist->data;
3384

    
3385
        if (summaryview->displayed) {
3386
                displayed_msginfo = gtk_ctree_node_get_row_data
3387
                        (GTK_CTREE(summaryview->ctree), summaryview->displayed);
3388
        }
3389

    
3390
        /* use selection only if the displayed message is selected */
3391
        if (!mlist->next && msginfo == displayed_msginfo) {
3392
                text = gtkut_editable_get_selection
3393
                        (GTK_EDITABLE(summaryview->messageview->textview->text));
3394
                if (text && *text == '\0') {
3395
                        g_free(text);
3396
                        text = NULL;
3397
                }
3398
        }
3399

    
3400
        if (!COMPOSE_QUOTE_MODE(mode))
3401
                mode |= prefs_common.reply_with_quote
3402
                        ? COMPOSE_WITH_QUOTE : COMPOSE_WITHOUT_QUOTE;
3403

    
3404
        switch (COMPOSE_MODE(mode)) {
3405
        case COMPOSE_REPLY:
3406
        case COMPOSE_REPLY_TO_SENDER:
3407
        case COMPOSE_REPLY_TO_ALL:
3408
        case COMPOSE_REPLY_TO_LIST:
3409
                compose_reply(msginfo, summaryview->folder_item, mode, text);
3410
                break;
3411
        case COMPOSE_FORWARD:
3412
                compose_forward(mlist, summaryview->folder_item, FALSE, text);
3413
                break;
3414
        case COMPOSE_FORWARD_AS_ATTACH:
3415
                compose_forward(mlist, summaryview->folder_item, TRUE, NULL);
3416
                break;
3417
        case COMPOSE_REDIRECT:
3418
                compose_redirect(msginfo, summaryview->folder_item);
3419
                break;
3420
        default:
3421
                g_warning("summary_reply(): invalid mode: %d\n", mode);
3422
        }
3423

    
3424
        summary_set_marks_selected(summaryview);
3425
        g_free(text);
3426
        g_slist_free(mlist);
3427
}
3428

    
3429
/* color label */
3430

    
3431
#define N_COLOR_LABELS colorlabel_get_color_count()
3432

    
3433
static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
3434
                                                     gpointer data)
3435
{
3436
        guint color = GPOINTER_TO_UINT(data);
3437
        SummaryView *summaryview;
3438

    
3439
        summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
3440
        g_return_if_fail(summaryview != NULL);
3441

    
3442
        /* "dont_toggle" state set? */
3443
        if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
3444
                              "dont_toggle"))
3445
                return;
3446

    
3447
        summary_set_colorlabel(summaryview, color, NULL);
3448
}
3449

    
3450
/* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
3451
 * for the messsage; not the color index */
3452
void summary_set_colorlabel_color(GtkCTree *ctree, GtkCTreeNode *node,
3453
                                  guint labelcolor)
3454
{
3455
        GdkColor color;
3456
        GtkStyle *style, *prev_style, *ctree_style;
3457
        MsgInfo *msginfo;
3458
        gint color_index;
3459

    
3460
        msginfo = gtk_ctree_node_get_row_data(ctree, node);
3461
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_CLABEL_FLAG_MASK);
3462
        MSG_SET_COLORLABEL_VALUE(msginfo->flags, labelcolor);
3463

    
3464
        color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
3465
        ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
3466
        prev_style = gtk_ctree_node_get_row_style(ctree, node);
3467

    
3468
        if (color_index < 0 || color_index >= N_COLOR_LABELS) {
3469
                if (!prev_style) return;
3470
                style = gtk_style_copy(prev_style);
3471
                color = ctree_style->fg[GTK_STATE_NORMAL];
3472
                style->fg[GTK_STATE_NORMAL] = color;
3473
                color = ctree_style->fg[GTK_STATE_SELECTED];
3474
                style->fg[GTK_STATE_SELECTED] = color;
3475
        } else {
3476
                if (prev_style)
3477
                        style = gtk_style_copy(prev_style);
3478
                else
3479
                        style = gtk_style_copy(ctree_style);
3480
                color = colorlabel_get_color(color_index);
3481
                style->fg[GTK_STATE_NORMAL] = color;
3482
                /* get the average of label color and selected fg color
3483
                   for visibility */
3484
                style->fg[GTK_STATE_SELECTED].red   = (color.red   + ctree_style->fg[GTK_STATE_SELECTED].red  ) / 2;
3485
                style->fg[GTK_STATE_SELECTED].green = (color.green + ctree_style->fg[GTK_STATE_SELECTED].green) / 2;
3486
                style->fg[GTK_STATE_SELECTED].blue  = (color.blue  + ctree_style->fg[GTK_STATE_SELECTED].blue ) / 2;
3487
        }
3488

    
3489
        gtk_ctree_node_set_row_style(ctree, node, style);
3490
}
3491

    
3492
void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
3493
                            GtkWidget *widget)
3494
{
3495
        GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3496
        GtkCList *clist = GTK_CLIST(summaryview->ctree);
3497
        GList *cur;
3498

    
3499
        for (cur = clist->selection; cur != NULL; cur = cur->next)
3500
                summary_set_colorlabel_color(ctree, GTK_CTREE_NODE(cur->data),
3501
                                             labelcolor);
3502
}
3503

    
3504
static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menuitem,
3505
                                                          gpointer data)
3506
{
3507
        SummaryView *summaryview;
3508
        GtkMenuShell *menu;
3509
        GtkCheckMenuItem **items;
3510
        gint n;
3511
        GList *cur, *sel;
3512

    
3513
        summaryview = (SummaryView *)data;
3514
        g_return_if_fail(summaryview != NULL);
3515

    
3516
        sel = GTK_CLIST(summaryview->ctree)->selection;
3517
        if (!sel) return;
3518

    
3519
        menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
3520
        g_return_if_fail(menu != NULL);
3521

    
3522
        Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
3523

    
3524
        /* NOTE: don't return prematurely because we set the "dont_toggle"
3525
         * state for check menu items */
3526
        g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(1));
3527

    
3528
        /* clear items. get item pointers. */
3529
        for (n = 0, cur = menu->children; cur != NULL; cur = cur->next) {
3530
                if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
3531
                        gtk_check_menu_item_set_state
3532
                                (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
3533
                        items[n] = GTK_CHECK_MENU_ITEM(cur->data);
3534
                        n++;
3535
                }
3536
        }
3537

    
3538
        if (n == (N_COLOR_LABELS + 1)) {
3539
                /* iterate all messages and set the state of the appropriate
3540
                 * items */
3541
                for (; sel != NULL; sel = sel->next) {
3542
                        MsgInfo *msginfo;
3543
                        gint clabel;
3544

    
3545
                        msginfo = gtk_ctree_node_get_row_data
3546
                                (GTK_CTREE(summaryview->ctree),
3547
                                 GTK_CTREE_NODE(sel->data));
3548
                        if (msginfo) {
3549
                                clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
3550
                                if (!items[clabel]->active)
3551
                                        gtk_check_menu_item_set_state
3552
                                                (items[clabel], TRUE);
3553
                        }
3554
                }
3555
        } else
3556
                g_warning("invalid number of color elements (%d)\n", n);
3557

    
3558
        /* reset "dont_toggle" state */
3559
        g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(0));
3560
}
3561

    
3562
static void summary_colorlabel_menu_create(SummaryView *summaryview)
3563
{
3564
        GtkWidget *label_menuitem;
3565
        GtkWidget *menu;
3566
        GtkWidget *item;
3567
        gint i;
3568

    
3569
        label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
3570
                                                   "/Color label");
3571
        g_signal_connect(G_OBJECT(label_menuitem), "activate",
3572
                         G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
3573
                         summaryview);
3574
        gtk_widget_show(label_menuitem);
3575

    
3576
        menu = gtk_menu_new();
3577

    
3578
        /* create sub items. for the menu item activation callback we pass the
3579
         * index of label_colors[] as data parameter. for the None color we
3580
         * pass an invalid (high) value. also we attach a data pointer so we
3581
         * can always get back the SummaryView pointer. */
3582

    
3583
        item = gtk_check_menu_item_new_with_label(_("None"));
3584
        gtk_menu_append(GTK_MENU(menu), item);
3585
        g_signal_connect(G_OBJECT(item), "activate",
3586
                         G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
3587
                         GUINT_TO_POINTER(0));
3588
        g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
3589
        gtk_widget_show(item);
3590

    
3591
        item = gtk_menu_item_new();
3592
        gtk_menu_append(GTK_MENU(menu), item);
3593
        gtk_widget_show(item);
3594

    
3595
        /* create pixmap/label menu items */
3596
        for (i = 0; i < N_COLOR_LABELS; i++) {
3597
                item = colorlabel_create_check_color_menu_item(i);
3598
                gtk_menu_append(GTK_MENU(menu), item);
3599
                g_signal_connect(G_OBJECT(item), "activate",
3600
                                 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
3601
                                 GUINT_TO_POINTER(i + 1));
3602
                g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
3603
                gtk_widget_show(item);
3604
        }
3605

    
3606
        gtk_widget_show(menu);
3607
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
3608
        summaryview->colorlabel_menu = menu;
3609
}
3610

    
3611
static GtkWidget *summary_ctree_create(SummaryView *summaryview)
3612
{
3613
        GtkWidget *ctree;
3614
        gint *col_pos = summaryview->col_pos;
3615
        SummaryColumnState *col_state;
3616
        gchar *titles[N_SUMMARY_COLS];
3617
        SummaryColumnType type;
3618
        gint pos;
3619

    
3620
        memset(titles, 0, sizeof(titles));
3621

    
3622
        col_state = prefs_summary_column_get_config();
3623
        for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
3624
                summaryview->col_state[pos] = col_state[pos];
3625
                type = col_state[pos].type;
3626
                col_pos[type] = pos;
3627
        }
3628
        col_state = summaryview->col_state;
3629

    
3630
        ctree = gtk_sctree_new_with_titles
3631
                (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
3632

    
3633
        gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_EXTENDED);
3634
        gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MARK],
3635
                                           GTK_JUSTIFY_CENTER);
3636
        gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_UNREAD],
3637
                                           GTK_JUSTIFY_CENTER);
3638
        gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MIME],
3639
                                           GTK_JUSTIFY_CENTER);
3640
        gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
3641
                                           GTK_JUSTIFY_RIGHT);
3642
        gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
3643
                                           GTK_JUSTIFY_RIGHT);
3644
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MARK],
3645
                                   SUMMARY_COL_MARK_WIDTH);
3646
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_UNREAD],
3647
                                   SUMMARY_COL_UNREAD_WIDTH);
3648
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MIME],
3649
                                   SUMMARY_COL_MIME_WIDTH);
3650
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SUBJECT],
3651
                                   prefs_common.summary_col_size[S_COL_SUBJECT]);
3652
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_FROM],
3653
                                   prefs_common.summary_col_size[S_COL_FROM]);
3654
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_DATE],
3655
                                   prefs_common.summary_col_size[S_COL_DATE]);
3656
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
3657
                                   prefs_common.summary_col_size[S_COL_SIZE]);
3658
        gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
3659
                                   prefs_common.summary_col_size[S_COL_NUMBER]);
3660
        gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
3661
        gtk_ctree_set_expander_style(GTK_CTREE(ctree),
3662
                                     GTK_CTREE_EXPANDER_SQUARE);
3663
#if 0
3664
        gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
3665
        gtk_ctree_set_expander_style(GTK_CTREE(ctree),
3666
                                     GTK_CTREE_EXPANDER_TRIANGLE);
3667
#endif
3668
        gtk_ctree_set_indent(GTK_CTREE(ctree), 16);
3669
        g_object_set_data(G_OBJECT(ctree), "user_data", summaryview);
3670

    
3671
        for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
3672
                GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[pos].button,
3673
                                       GTK_CAN_FOCUS);
3674
                gtk_clist_set_column_visibility
3675
                        (GTK_CLIST(ctree), pos, col_state[pos].visible);
3676
        }
3677

    
3678
        /* connect signal to the buttons for sorting */
3679
#define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
3680
        g_signal_connect \
3681
                (G_OBJECT(GTK_CLIST(ctree)->column[col_pos[col]].button), \
3682
                 "clicked", \
3683
                 G_CALLBACK(func), \
3684
                 summaryview)
3685

    
3686
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked);
3687
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_UNREAD , summary_unread_clicked);
3688
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked);
3689
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked);
3690
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked);
3691
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked);
3692
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked);
3693
        CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
3694

    
3695
#undef CLIST_BUTTON_SIGNAL_CONNECT
3696

    
3697
        g_signal_connect(G_OBJECT(ctree), "tree_select_row",
3698
                         G_CALLBACK(summary_selected), summaryview);
3699
        g_signal_connect(G_OBJECT(ctree), "button_press_event",
3700
                         G_CALLBACK(summary_button_pressed), summaryview);
3701
        g_signal_connect(G_OBJECT(ctree), "button_release_event",
3702
                         G_CALLBACK(summary_button_released), summaryview);
3703
        g_signal_connect(G_OBJECT(ctree), "key_press_event",
3704
                         G_CALLBACK(summary_key_pressed), summaryview);
3705
        g_signal_connect(G_OBJECT(ctree), "resize_column",
3706
                         G_CALLBACK(summary_col_resized), summaryview);
3707
        g_signal_connect(G_OBJECT(ctree), "open_row",
3708
                         G_CALLBACK(summary_open_row), summaryview);
3709

    
3710
        g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
3711
                               G_CALLBACK(summary_tree_expanded),
3712
                               summaryview);
3713
        g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
3714
                               G_CALLBACK(summary_tree_collapsed),
3715
                               summaryview);
3716

    
3717
        g_signal_connect(G_OBJECT(ctree), "start_drag",
3718
                         G_CALLBACK(summary_start_drag), summaryview);
3719
        g_signal_connect(G_OBJECT(ctree), "drag_data_get",
3720
                         G_CALLBACK(summary_drag_data_get), summaryview);
3721

    
3722
        return ctree;
3723
}
3724

    
3725
void summary_set_column_order(SummaryView *summaryview)
3726
{
3727
        GtkWidget *ctree;
3728
        GtkWidget *scrolledwin = summaryview->scrolledwin;
3729
        GtkWidget *pixmap;
3730
        FolderItem *item;
3731

    
3732
        item = summaryview->folder_item;
3733
        summary_clear_all(summaryview);
3734
        gtk_widget_destroy(summaryview->ctree);
3735

    
3736
        summaryview->ctree = ctree = summary_ctree_create(summaryview);
3737
        pixmap = gtk_pixmap_new(clipxpm, clipxpmmask);
3738
        gtk_clist_set_column_widget(GTK_CLIST(ctree),
3739
                                    summaryview->col_pos[S_COL_MIME], pixmap);
3740
        gtk_widget_show(pixmap);
3741
        gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
3742
                                            GTK_CLIST(ctree)->hadjustment);
3743
        gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
3744
                                            GTK_CLIST(ctree)->vadjustment);
3745
        gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
3746
        gtk_widget_show(ctree);
3747

    
3748
        summary_show(summaryview, item, FALSE);
3749
}
3750

    
3751

    
3752
/* callback functions */
3753

    
3754
static gboolean summary_toggle_pressed(GtkWidget *eventbox,
3755
                                       GdkEventButton *event,
3756
                                       SummaryView *summaryview)
3757
{
3758
        if (!event) return FALSE;
3759

    
3760
        summary_toggle_view(summaryview);
3761
        return FALSE;
3762
}
3763

    
3764
static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
3765
                                       SummaryView *summaryview)
3766
{
3767
        if (!event) return FALSE;
3768

    
3769
        if (event->button == 3) {
3770
                /* right clicked */
3771
                gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
3772
                               NULL, NULL, event->button, event->time);
3773
        } else if (event->button == 2) {
3774
                summaryview->display_msg = TRUE;
3775
        } else if (event->button == 1) {
3776
                if (!prefs_common.emulate_emacs &&
3777
                    messageview_is_visible(summaryview->messageview))
3778
                        summaryview->display_msg = TRUE;
3779
        }
3780

    
3781
        return FALSE;
3782
}
3783

    
3784
static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
3785
                                        SummaryView *summaryview)
3786
{
3787
        return FALSE;
3788
}
3789

    
3790
void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
3791
{
3792
        summary_key_pressed(summaryview->ctree, event, summaryview);
3793
}
3794

    
3795
#define BREAK_ON_MODIFIER_KEY() \
3796
        if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
3797

    
3798
static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
3799
                                    SummaryView *summaryview)
3800
{
3801
        GtkCTree *ctree = GTK_CTREE(widget);
3802
        GtkCTreeNode *node;
3803
        MessageView *messageview;
3804
        TextView *textview;
3805
        GtkAdjustment *adj;
3806
        gboolean mod_pressed;
3807

    
3808
        if (summary_is_locked(summaryview)) return FALSE;
3809
        if (!event) return FALSE;
3810

    
3811
        switch (event->keyval) {
3812
        case GDK_Left:                /* Move focus */
3813
                adj = gtk_scrolled_window_get_hadjustment
3814
                        (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
3815
                if (adj->lower != adj->value)
3816
                        break;
3817
                /* FALLTHROUGH */
3818
        case GDK_Escape:
3819
                gtk_widget_grab_focus(summaryview->folderview->ctree);
3820
                return FALSE;
3821
        default:
3822
                break;
3823
        }
3824

    
3825
        if (!summaryview->selected) {
3826
                node = gtk_ctree_node_nth(ctree, 0);
3827
                if (node)
3828
                        gtk_sctree_select(GTK_SCTREE(ctree), node);
3829
                else
3830
                        return FALSE;
3831
        }
3832

    
3833
        messageview = summaryview->messageview;
3834
        if (messageview->type == MVIEW_MIME &&
3835
            gtk_notebook_get_current_page
3836
                (GTK_NOTEBOOK(messageview->mimeview->notebook)) == 1)
3837
                textview = messageview->mimeview->textview;
3838
        else
3839
                textview = messageview->textview;
3840

    
3841
        switch (event->keyval) {
3842
        case GDK_space:                /* Page down or go to the next */
3843
                if (summaryview->displayed != summaryview->selected) {
3844
                        summary_display_msg(summaryview,
3845
                                            summaryview->selected);
3846
                        break;
3847
                }
3848
                mod_pressed =
3849
                        ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
3850
                if (mod_pressed) {
3851
                        if (!textview_scroll_page(textview, TRUE))
3852
                                summary_select_prev_unread(summaryview);
3853
                } else {
3854
                        if (!textview_scroll_page(textview, FALSE))
3855
                                summary_select_next_unread(summaryview);
3856
                }
3857
                break;
3858
        case GDK_BackSpace:        /* Page up */
3859
                textview_scroll_page(textview, TRUE);
3860
                break;
3861
        case GDK_Return:        /* Scroll up/down one line */
3862
                if (summaryview->displayed != summaryview->selected) {
3863
                        summary_display_msg(summaryview,
3864
                                            summaryview->selected);
3865
                        break;
3866
                }
3867
                textview_scroll_one_line
3868
                        (textview, (event->state &
3869
                                    (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
3870
                break;
3871
        case GDK_Delete:
3872
                BREAK_ON_MODIFIER_KEY();
3873
                summary_delete(summaryview);
3874
                break;
3875
        default:
3876
                break;
3877
        }
3878

    
3879
        return FALSE;
3880
}
3881

    
3882
static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
3883
{
3884
        if (summaryview->folder_item->stype == F_OUTBOX ||
3885
            summaryview->folder_item->stype == F_DRAFT  ||
3886
            summaryview->folder_item->stype == F_QUEUE)
3887
                summary_reedit(summaryview);
3888
        else
3889
                summary_open_msg(summaryview);
3890

    
3891
        summaryview->display_msg = FALSE;
3892
}
3893

    
3894
static void summary_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
3895
                                  SummaryView *summaryview)
3896
{
3897
        summary_set_row_marks(summaryview, node);
3898
}
3899

    
3900
static void summary_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
3901
                                   SummaryView *summaryview)
3902
{
3903
        summary_set_row_marks(summaryview, node);
3904
}
3905

    
3906
static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
3907
                             gint column, SummaryView *summaryview)
3908
{
3909
        MsgInfo *msginfo;
3910

    
3911
        summary_status_show(summaryview);
3912

    
3913
        if (GTK_CLIST(ctree)->selection &&
3914
            GTK_CLIST(ctree)->selection->next) {
3915
                summaryview->display_msg = FALSE;
3916
                summary_set_menu_sensitive(summaryview);
3917
                main_window_set_toolbar_sensitive(summaryview->mainwin);
3918
                return;
3919
        }
3920

    
3921
        summaryview->selected = row;
3922

    
3923
        msginfo = gtk_ctree_node_get_row_data(ctree, row);
3924
        g_return_if_fail(msginfo != NULL);
3925

    
3926
        switch (column < 0 ? column : summaryview->col_state[column].type) {
3927
        case S_COL_MARK:
3928
                if (!MSG_IS_DELETED(msginfo->flags) &&
3929
                    !MSG_IS_MOVE(msginfo->flags) &&
3930
                    !MSG_IS_COPY(msginfo->flags)) {
3931
                        if (MSG_IS_MARKED(msginfo->flags)) {
3932
                                summary_unmark_row(summaryview, row);
3933
                                if (MSG_IS_IMAP(msginfo->flags))
3934
                                        imap_msg_unset_perm_flags(msginfo,
3935
                                                                  MSG_MARKED);
3936
                        } else {
3937
                                summary_mark_row(summaryview, row);
3938
                                if (MSG_IS_IMAP(msginfo->flags))
3939
                                        imap_msg_set_perm_flags(msginfo,
3940
                                                                MSG_MARKED);
3941
                        }
3942
                }
3943
                break;
3944
        case S_COL_UNREAD:
3945
                if (MSG_IS_UNREAD(msginfo->flags)) {
3946
                        summary_mark_row_as_read(summaryview, row);
3947
                        if (MSG_IS_IMAP(msginfo->flags))
3948
                                imap_msg_unset_perm_flags
3949
                                        (msginfo, MSG_NEW | MSG_UNREAD);
3950
                        summary_status_show(summaryview);
3951
                } else if (!MSG_IS_REPLIED(msginfo->flags) &&
3952
                         !MSG_IS_FORWARDED(msginfo->flags)) {
3953
                        summary_mark_row_as_unread(summaryview, row);
3954
                        if (MSG_IS_IMAP(msginfo->flags))
3955
                                imap_msg_set_perm_flags(msginfo, MSG_UNREAD);
3956
                        summary_status_show(summaryview);
3957
                }
3958
                break;
3959
        default:
3960
                break;
3961
        }
3962

    
3963
        if (summaryview->display_msg ||
3964
            (prefs_common.always_show_msg &&
3965
             messageview_is_visible(summaryview->messageview))) {
3966
                summaryview->display_msg = FALSE;
3967
                if (summaryview->displayed != row) {
3968
                        summary_display_msg(summaryview, row);
3969
                        return;
3970
                }
3971
        }
3972

    
3973
        summary_set_menu_sensitive(summaryview);
3974
        main_window_set_toolbar_sensitive(summaryview->mainwin);
3975
}
3976

    
3977
static void summary_col_resized(GtkCList *clist, gint column, gint width,
3978
                                SummaryView *summaryview)
3979
{
3980
        SummaryColumnType type = summaryview->col_state[column].type;
3981

    
3982
        prefs_common.summary_col_size[type] = width;
3983
}
3984

    
3985
static void summary_reply_cb(SummaryView *summaryview, guint action,
3986
                             GtkWidget *widget)
3987
{
3988
        summary_reply(summaryview, (ComposeMode)action);
3989
}
3990

    
3991
static void summary_show_all_header_cb(SummaryView *summaryview,
3992
                                       guint action, GtkWidget *widget)
3993
{
3994
        summary_display_msg_selected(summaryview,
3995
                                     GTK_CHECK_MENU_ITEM(widget)->active);
3996
}
3997

    
3998
static void summary_add_address_cb(SummaryView *summaryview,
3999
                                   guint action, GtkWidget *widget)
4000
{
4001
        summary_add_address(summaryview);
4002
}
4003

    
4004
static void summary_sort_by_column_click(SummaryView *summaryview,
4005
                                         FolderSortKey sort_key)
4006
{
4007
        FolderItem *item = summaryview->folder_item;
4008

    
4009
        if (!item) return;
4010

    
4011
        if (item->sort_key == sort_key)
4012
                summary_sort(summaryview, sort_key,
4013
                             item->sort_type == SORT_ASCENDING
4014
                             ? SORT_DESCENDING : SORT_ASCENDING);
4015
        else
4016
                summary_sort(summaryview, sort_key, SORT_ASCENDING);
4017
}
4018

    
4019
static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
4020
{
4021
        summary_sort_by_column_click(summaryview, SORT_BY_MARK);
4022
}
4023

    
4024
static void summary_unread_clicked(GtkWidget *button, SummaryView *summaryview)
4025
{
4026
        summary_sort_by_column_click(summaryview, SORT_BY_UNREAD);
4027
}
4028

    
4029
static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
4030
{
4031
        summary_sort_by_column_click(summaryview, SORT_BY_MIME);
4032
}
4033

    
4034
static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
4035
{
4036
        summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
4037
}
4038

    
4039
static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
4040
{
4041
        summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
4042
}
4043

    
4044
static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
4045
{
4046
        summary_sort_by_column_click(summaryview, SORT_BY_DATE);
4047
}
4048

    
4049
static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
4050
{
4051
        summary_sort_by_column_click(summaryview, SORT_BY_FROM);
4052
}
4053

    
4054
static void summary_subject_clicked(GtkWidget *button,
4055
                                    SummaryView *summaryview)
4056
{
4057
        summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
4058
}
4059

    
4060
static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
4061
                               SummaryView *summaryview)
4062
{
4063
        GtkTargetList *list;
4064
        GdkDragContext *context;
4065

    
4066
        g_return_if_fail(summaryview != NULL);
4067
        g_return_if_fail(summaryview->folder_item != NULL);
4068
        g_return_if_fail(summaryview->folder_item->folder != NULL);
4069
        if (summaryview->selected == NULL) return;
4070

    
4071
        list = gtk_target_list_new(summary_drag_types, 1);
4072

    
4073
        if (FOLDER_ITEM_CAN_ADD(summaryview->folder_item)) {
4074
                context = gtk_drag_begin
4075
                        (widget, list,
4076
                         GDK_ACTION_MOVE | GDK_ACTION_COPY, button, event);
4077
        } else {
4078
                context = gtk_drag_begin(widget, list, GDK_ACTION_COPY,
4079
                                         button, event);
4080
        }
4081
        gtk_drag_set_icon_default(context);
4082
}
4083

    
4084
static void summary_drag_data_get(GtkWidget        *widget,
4085
                                  GdkDragContext   *drag_context,
4086
                                  GtkSelectionData *selection_data,
4087
                                  guint             info,
4088
                                  guint             time,
4089
                                  SummaryView      *summaryview)
4090
{
4091
        if (info == TARGET_MAIL_URI_LIST) {
4092
                GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4093
                GList *cur;
4094
                MsgInfo *msginfo;
4095
                gchar *mail_list = NULL, *tmp1, *tmp2;
4096

    
4097
                for (cur = GTK_CLIST(ctree)->selection;
4098
                     cur != NULL; cur = cur->next) {
4099
                        msginfo = gtk_ctree_node_get_row_data
4100
                                (ctree, GTK_CTREE_NODE(cur->data));
4101
                        tmp2 = procmsg_get_message_file(msginfo);
4102
                        if (!tmp2) continue;
4103
                        tmp1 = g_strconcat("file://", tmp2, NULL);
4104
                        g_free(tmp2);
4105

    
4106
                        if (!mail_list) {
4107
                                mail_list = tmp1;
4108
                        } else {
4109
                                tmp2 = g_strconcat(mail_list, tmp1, NULL);
4110
                                g_free(mail_list);
4111
                                g_free(tmp1);
4112
                                mail_list = tmp2;
4113
                        }
4114
                }
4115

    
4116
                if (mail_list != NULL) {
4117
                        gtk_selection_data_set(selection_data,
4118
                                               selection_data->target, 8,
4119
                                               mail_list, strlen(mail_list));
4120
                        g_free(mail_list);
4121
                } 
4122
        } else if (info == TARGET_DUMMY) {
4123
                if (GTK_CLIST(summaryview->ctree)->selection)
4124
                        gtk_selection_data_set(selection_data,
4125
                                               selection_data->target, 8,
4126
                                               "Dummy", 6);
4127
        }
4128
}
4129

    
4130

    
4131
/* custom compare functions for sorting */
4132

    
4133
#define CMP_FUNC_DEF(func_name, val)                                         \
4134
static gint func_name(GtkCList *clist,                                         \
4135
                      gconstpointer ptr1, gconstpointer ptr2)                 \
4136
{                                                                         \
4137
        MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;                 \
4138
        MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;                 \
4139
                                                                         \
4140
        if (!msginfo1 || !msginfo2)                                         \
4141
                return -1;                                                 \
4142
                                                                         \
4143
        return (val);                                                         \
4144
}
4145

    
4146
CMP_FUNC_DEF(summary_cmp_by_mark,
4147
             MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
4148
CMP_FUNC_DEF(summary_cmp_by_unread,
4149
             MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags))
4150
CMP_FUNC_DEF(summary_cmp_by_mime,
4151
             MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags))
4152
CMP_FUNC_DEF(summary_cmp_by_label,
4153
             MSG_GET_COLORLABEL(msginfo1->flags) -
4154
             MSG_GET_COLORLABEL(msginfo2->flags))
4155

    
4156
CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
4157
CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
4158
CMP_FUNC_DEF(summary_cmp_by_date, msginfo1->date_t - msginfo2->date_t)
4159

    
4160
#undef CMP_FUNC_DEF
4161
#define CMP_FUNC_DEF(func_name, var_name)                                 \
4162
static gint func_name(GtkCList *clist,                                         \
4163
                      gconstpointer ptr1, gconstpointer ptr2)                 \
4164
{                                                                         \
4165
        MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;                 \
4166
        MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;                 \
4167
                                                                         \
4168
        if (!msginfo1->var_name)                                         \
4169
                return (msginfo2->var_name != NULL);                         \
4170
        if (!msginfo2->var_name)                                         \
4171
                return -1;                                                 \
4172
                                                                         \
4173
        return strcasecmp(msginfo1->var_name, msginfo2->var_name);         \
4174
}
4175

    
4176
CMP_FUNC_DEF(summary_cmp_by_from, fromname)
4177
CMP_FUNC_DEF(summary_cmp_by_to, to);
4178

    
4179
#undef CMP_FUNC_DEF
4180

    
4181
static gint summary_cmp_by_subject(GtkCList *clist,                         \
4182
                                   gconstpointer ptr1,                         \
4183
                                   gconstpointer ptr2)                         \
4184
{                                                                         \
4185
        MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;                 \
4186
        MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;                 \
4187
                                                                         \
4188
        if (!msginfo1->subject)                                                 \
4189
                return (msginfo2->subject != NULL);                         \
4190
        if (!msginfo2->subject)                                                 \
4191
                return -1;                                                 \
4192
                                                                         \
4193
        return subject_compare_for_sort                                         \
4194
                (msginfo1->subject, msginfo2->subject);                         \
4195
}