Statistics
| Revision:

root / src / summaryview.c @ 3159

History | View | Annotate | Download (182 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2012 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 <glib/gi18n.h>
24
#include <gdk/gdkkeysyms.h>
25
#include <gtk/gtkversion.h>
26
#include <gtk/gtkwidget.h>
27
#include <gtk/gtklabel.h>
28
#include <gtk/gtkoptionmenu.h>
29
#include <gtk/gtkentry.h>
30
#include <gtk/gtktooltips.h>
31
#include <gtk/gtkscrolledwindow.h>
32
#include <gtk/gtktreestore.h>
33
#include <gtk/gtktreeview.h>
34
#include <gtk/gtktreeselection.h>
35
#include <gtk/gtkcellrendererpixbuf.h>
36
#include <gtk/gtkcellrenderertext.h>
37
#include <gtk/gtksignal.h>
38
#include <gtk/gtkmenu.h>
39
#include <gtk/gtkmenuitem.h>
40
#include <gtk/gtkcheckmenuitem.h>
41
#include <gtk/gtkitemfactory.h>
42
#include <gtk/gtkvbox.h>
43
#include <gtk/gtkhbox.h>
44
#include <gtk/gtkwindow.h>
45
#include <gtk/gtkhseparator.h>
46
#include <gtk/gtkarrow.h>
47
#include <gtk/gtkeventbox.h>
48
#include <gtk/gtkstatusbar.h>
49
#include <gtk/gtkstock.h>
50
#include <gtk/gtknotebook.h>
51

    
52
#include <stdio.h>
53
#include <stdlib.h>
54
#include <string.h>
55
#include <ctype.h>
56
#include <unistd.h>
57

    
58
#include "main.h"
59
#include "menu.h"
60
#include "mainwindow.h"
61
#include "folderview.h"
62
#include "summaryview.h"
63
#include "messageview.h"
64
#include "foldersel.h"
65
#include "procmsg.h"
66
#include "procheader.h"
67
#include "sourcewindow.h"
68
#include "prefs_common.h"
69
#include "prefs_summary_column.h"
70
#include "prefs_filter.h"
71
#include "account.h"
72
#include "compose.h"
73
#include "utils.h"
74
#include "gtkutils.h"
75
#include "stock_pixmap.h"
76
#include "filesel.h"
77
#include "alertpanel.h"
78
#include "inputdialog.h"
79
#include "statusbar.h"
80
#include "trayicon.h"
81
#include "printing.h"
82
#include "filter.h"
83
#include "folder.h"
84
#include "colorlabel.h"
85
#include "inc.h"
86
#include "imap.h"
87
#include "plugin.h"
88

    
89
#define STATUSBAR_PUSH(mainwin, str) \
90
{ \
91
        gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
92
                           mainwin->summaryview_cid, str); \
93
        gtkut_widget_draw_now(mainwin->statusbar); \
94
}
95

    
96
#define STATUSBAR_POP(mainwin) \
97
{ \
98
        gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
99
                          mainwin->summaryview_cid); \
100
}
101

    
102
#define GET_MSG_INFO(msginfo, iter__) \
103
{ \
104
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter__, \
105
                           S_COL_MSG_INFO, &msginfo, -1); \
106
}
107

    
108
#define SORT_BLOCK(key)                                                         \
109
        if (summaryview->folder_item->sort_key == key) {                 \
110
                sort_key = key;                                                 \
111
                sort_type = summaryview->folder_item->sort_type;         \
112
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); \
113
        }
114

    
115
#define SORT_UNBLOCK(key)                                        \
116
        if (sort_key == key)                                        \
117
                summary_sort(summaryview, sort_key, sort_type);
118

    
119
#define SUMMARY_DISPLAY_TOTAL_NUM(item) \
120
        (summaryview->on_filter ? summaryview->flt_msg_total : item->total)
121

    
122
#ifdef G_OS_WIN32
123
#  define SUMMARY_COL_MARK_WIDTH        23
124
#  define SUMMARY_COL_UNREAD_WIDTH        26
125
#  define SUMMARY_COL_MIME_WIDTH        21
126
#else
127
#  define SUMMARY_COL_MARK_WIDTH        21
128
#  define SUMMARY_COL_UNREAD_WIDTH        24
129
#  define SUMMARY_COL_MIME_WIDTH        19
130
#endif
131

    
132
static GdkPixbuf *mark_pixbuf;
133
static GdkPixbuf *deleted_pixbuf;
134

    
135
static GdkPixbuf *mail_pixbuf;
136
static GdkPixbuf *new_pixbuf;
137
static GdkPixbuf *unread_pixbuf;
138
static GdkPixbuf *replied_pixbuf;
139
static GdkPixbuf *forwarded_pixbuf;
140

    
141
static GdkPixbuf *clip_pixbuf;
142

    
143
static void summary_clear_list_full        (SummaryView                *summaryview,
144
                                         gboolean                 is_refresh);
145

    
146
static GList *summary_get_selected_rows        (SummaryView                *summaryview);
147
static void summary_selection_list_free        (SummaryView                *summaryview);
148

    
149
static GSList *summary_get_tmp_marked_msg_list
150
                                        (SummaryView        *summaryview);
151
static void summary_restore_tmp_marks        (SummaryView        *summaryview,
152
                                         GSList                *save_mark_mlist);
153

    
154
static void summary_update_msg_list        (SummaryView                *summaryview);
155

    
156
static void summary_msgid_table_create        (SummaryView                *summaryview);
157
static void summary_msgid_table_destroy        (SummaryView                *summaryview);
158

    
159
static void summary_set_menu_sensitive        (SummaryView                *summaryview);
160

    
161
static void summary_scroll_to_selected        (SummaryView                *summaryview,
162
                                         gboolean                 align_center);
163

    
164
static MsgInfo *summary_get_msginfo        (SummaryView                *summaryview,
165
                                         GtkTreeRowReference        *row);
166
static guint summary_get_msgnum                (SummaryView                *summaryview,
167
                                         GtkTreeRowReference        *row);
168

    
169
static gboolean summary_find_prev_msg        (SummaryView                *summaryview,
170
                                         GtkTreeIter                *prev,
171
                                         GtkTreeIter                *iter);
172
static gboolean summary_find_next_msg        (SummaryView                *summaryview,
173
                                         GtkTreeIter                *next,
174
                                         GtkTreeIter                *iter);
175

    
176
static gboolean summary_find_nearest_msg(SummaryView                *summaryview,
177
                                         GtkTreeIter                *target,
178
                                         GtkTreeIter                *iter);
179

    
180
static gboolean summary_find_prev_flagged_msg
181
                                        (SummaryView        *summaryview,
182
                                         GtkTreeIter        *prev,
183
                                         GtkTreeIter        *iter,
184
                                         MsgPermFlags         flags,
185
                                         gboolean         start_from_prev);
186
static gboolean summary_find_next_flagged_msg
187
                                        (SummaryView        *summaryview,
188
                                         GtkTreeIter        *next,
189
                                         GtkTreeIter        *iter,
190
                                         MsgPermFlags         flags,
191
                                         gboolean         start_from_next);
192

    
193
static gboolean summary_find_msg_by_msgnum
194
                                        (SummaryView                *summaryview,
195
                                         guint                         msgnum,
196
                                         GtkTreeIter                *found);
197

    
198
static void summary_update_display_state(SummaryView                *summaryview,
199
                                         guint                         disp_msgnum,
200
                                         guint                         sel_msgnum);
201

    
202
static void summary_update_status        (SummaryView                *summaryview);
203

    
204
/* display functions */
205
static void summary_status_show                (SummaryView                *summaryview);
206
static void summary_set_row                (SummaryView                *summaryview,
207
                                         GtkTreeIter                *iter,
208
                                         MsgInfo                *msginfo);
209
static void summary_set_tree_model_from_list
210
                                        (SummaryView                *summaryview,
211
                                         GSList                        *mlist);
212
static gboolean summary_row_is_displayed(SummaryView                *summaryview,
213
                                         GtkTreeIter                *iter);
214
static void summary_display_msg                (SummaryView                *summaryview,
215
                                         GtkTreeIter                *iter);
216
static void summary_display_msg_full        (SummaryView                *summaryview,
217
                                         GtkTreeIter                *iter,
218
                                         gboolean                 new_window,
219
                                         gboolean                 all_headers,
220
                                         gboolean                 redisplay);
221

    
222
static void summary_activate_selected        (SummaryView                *summaryview);
223

    
224
/* message handling */
225
static void summary_mark_row                (SummaryView                *summaryview,
226
                                         GtkTreeIter                *iter);
227
static void summary_mark_row_as_read        (SummaryView                *summaryview,
228
                                         GtkTreeIter                *iter);
229
static void summary_mark_row_as_unread        (SummaryView                *summaryview,
230
                                         GtkTreeIter                *iter);
231
static void summary_delete_row                (SummaryView                *summaryview,
232
                                         GtkTreeIter                *iter);
233
static void summary_unmark_row                (SummaryView                *summaryview,
234
                                         GtkTreeIter                *iter);
235
static void summary_move_row_to                (SummaryView                *summaryview,
236
                                         GtkTreeIter                *iter,
237
                                         FolderItem                *to_folder);
238
static void summary_copy_row_to                (SummaryView                *summaryview,
239
                                         GtkTreeIter                *iter,
240
                                         FolderItem                *to_folder);
241

    
242
static void summary_remove_invalid_messages
243
                                        (SummaryView                *summaryview);
244

    
245
static gint summary_execute_move        (SummaryView                *summaryview);
246
static gint summary_execute_copy        (SummaryView                *summaryview);
247
static gint summary_execute_delete        (SummaryView                *summaryview);
248

    
249
static void summary_modify_threads        (SummaryView                *summaryview);
250

    
251
static void summary_colorlabel_menu_item_activate_cb
252
                                        (GtkWidget        *widget,
253
                                         gpointer         data);
254
static void summary_colorlabel_menu_item_activate_item_cb
255
                                        (GtkMenuItem        *label_menu_item,
256
                                         gpointer         data);
257
static void summary_colorlabel_menu_create
258
                                        (SummaryView        *summaryview);
259

    
260
static GtkWidget *summary_tree_view_create
261
                                        (SummaryView        *summaryview);
262

    
263
/* callback functions */
264
static gboolean summary_toggle_pressed        (GtkWidget                *eventbox,
265
                                         GdkEventButton                *event,
266
                                         SummaryView                *summaryview);
267
static gboolean summary_button_pressed        (GtkWidget                *treeview,
268
                                         GdkEventButton                *event,
269
                                         SummaryView                *summaryview);
270
static gboolean summary_button_released        (GtkWidget                *treeview,
271
                                         GdkEventButton                *event,
272
                                         SummaryView                *summaryview);
273
static gboolean summary_key_pressed        (GtkWidget                *treeview,
274
                                         GdkEventKey                *event,
275
                                         SummaryView                *summaryview);
276

    
277
static void summary_row_expanded        (GtkTreeView                *treeview,
278
                                         GtkTreeIter                *iter,
279
                                         GtkTreePath                *path,
280
                                         SummaryView                *summaryview);
281
static void summary_row_collapsed        (GtkTreeView                *treeview,
282
                                         GtkTreeIter                *iter,
283
                                         GtkTreePath                *path,
284
                                         SummaryView                *summaryview);
285

    
286
static void summary_columns_changed        (GtkTreeView                *treeview,
287
                                         SummaryView                *summaryview);
288

    
289
static gboolean summary_select_func        (GtkTreeSelection        *selection,
290
                                         GtkTreeModel                *model,
291
                                         GtkTreePath                *path,
292
                                         gboolean                 cur_selected,
293
                                         gpointer                 data);
294

    
295
static void summary_selection_changed        (GtkTreeSelection        *selection,
296
                                         SummaryView                *summaryview);
297

    
298
static void summary_col_resized                (GtkWidget                *widget,
299
                                         GtkAllocation                *allocation,
300
                                         SummaryView                *summaryview);
301

    
302
static void summary_reply_cb                (SummaryView                *summaryview,
303
                                         guint                         action,
304
                                         GtkWidget                *widget);
305
static void summary_show_all_header_cb        (SummaryView                *summaryview,
306
                                         guint                         action,
307
                                         GtkWidget                *widget);
308

    
309
static void summary_add_address_cb        (SummaryView                *summaryview,
310
                                         guint                         action,
311
                                         GtkWidget                *widget);
312
static void summary_create_filter_cb        (SummaryView                *summaryview,
313
                                         guint                         action,
314
                                         GtkWidget                *widget);
315

    
316
static void summary_column_clicked        (GtkWidget                *button,
317
                                         SummaryView                *summaryview);
318

    
319
static void summary_drag_begin                (GtkWidget          *widget,
320
                                         GdkDragContext          *drag_context,
321
                                         SummaryView          *summaryview);
322
static void summary_drag_end                (GtkWidget          *widget,
323
                                         GdkDragContext          *drag_context,
324
                                         SummaryView          *summaryview);
325
static void summary_drag_data_get       (GtkWidget        *widget,
326
                                         GdkDragContext   *drag_context,
327
                                         GtkSelectionData *selection_data,
328
                                         guint             info,
329
                                         guint             time,
330
                                         SummaryView      *summaryview);
331

    
332
static void summary_text_adj_value_changed
333
                                        (GtkAdjustment                *adj,
334
                                         SummaryView                *summaryview);
335

    
336
/* custom compare functions for sorting */
337
static gint summary_cmp_by_mark                (GtkTreeModel                *model,
338
                                         GtkTreeIter                *a,
339
                                         GtkTreeIter                *b,
340
                                         gpointer                 data);
341
static gint summary_cmp_by_unread        (GtkTreeModel                *model,
342
                                         GtkTreeIter                *a,
343
                                         GtkTreeIter                *b,
344
                                         gpointer                 data);
345
static gint summary_cmp_by_mime                (GtkTreeModel                *model,
346
                                         GtkTreeIter                *a,
347
                                         GtkTreeIter                *b,
348
                                         gpointer                 data);
349
static gint summary_cmp_by_num                (GtkTreeModel                *model,
350
                                         GtkTreeIter                *a,
351
                                         GtkTreeIter                *b,
352
                                         gpointer                 data);
353
static gint summary_cmp_by_size                (GtkTreeModel                *model,
354
                                         GtkTreeIter                *a,
355
                                         GtkTreeIter                *b,
356
                                         gpointer                 data);
357
static gint summary_cmp_by_date                (GtkTreeModel                *model,
358
                                         GtkTreeIter                *a,
359
                                         GtkTreeIter                *b,
360
                                         gpointer                 data);
361
static gint summary_cmp_by_thread_date        (GtkTreeModel                *model,
362
                                         GtkTreeIter                *a,
363
                                         GtkTreeIter                *b,
364
                                         gpointer                 data);
365
static gint summary_cmp_by_from                (GtkTreeModel                *model,
366
                                         GtkTreeIter                *a,
367
                                         GtkTreeIter                *b,
368
                                         gpointer                 data);
369
static gint summary_cmp_by_label        (GtkTreeModel                *model,
370
                                         GtkTreeIter                *a,
371
                                         GtkTreeIter                *b,
372
                                         gpointer                 data);
373
static gint summary_cmp_by_to                (GtkTreeModel                *model,
374
                                         GtkTreeIter                *a,
375
                                         GtkTreeIter                *b,
376
                                         gpointer                 data);
377
static gint summary_cmp_by_subject        (GtkTreeModel                *model,
378
                                         GtkTreeIter                *a,
379
                                         GtkTreeIter                *b,
380
                                         gpointer                 data);
381

    
382

    
383
/* must be synched with FolderSortKey */
384
static SummaryColumnType sort_key_to_col[] = {
385
        -1,
386
        S_COL_NUMBER,
387
        S_COL_SIZE,
388
        S_COL_DATE,
389
        S_COL_TDATE,
390
        S_COL_FROM,
391
        S_COL_SUBJECT,
392
        -1,
393
        S_COL_LABEL,
394
        S_COL_MARK,
395
        S_COL_UNREAD,
396
        S_COL_MIME,
397
        S_COL_TO
398
};
399

    
400
/* must be synched with SummaryColumnType */
401
static FolderSortKey col_to_sort_key[] = {
402
        SORT_BY_MARK,
403
        SORT_BY_UNREAD,
404
        SORT_BY_MIME,
405
        SORT_BY_SUBJECT,
406
        SORT_BY_FROM,
407
        SORT_BY_DATE,
408
        SORT_BY_SIZE,
409
        SORT_BY_NUMBER,
410
        SORT_BY_TO
411
};
412

    
413
enum
414
{
415
        DRAG_TYPE_TEXT,
416
        DRAG_TYPE_RFC822,
417
        DRAG_TYPE_URI_LIST,
418

    
419
        N_DRAG_TYPES
420
};
421

    
422
static GtkTargetEntry summary_drag_types[] =
423
{
424
        {"text/plain", GTK_TARGET_SAME_APP, DRAG_TYPE_TEXT},
425
        {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
426
        {"text/uri-list", 0, DRAG_TYPE_URI_LIST}
427
};
428

    
429
static GtkItemFactoryEntry summary_popup_entries[] =
430
{
431
        {N_("/_Reply"),                        NULL, summary_reply_cb,        COMPOSE_REPLY, NULL},
432
        {N_("/Repl_y to"),                NULL, NULL,                0, "<Branch>"},
433
        {N_("/Repl_y to/_all"),                NULL, summary_reply_cb,        COMPOSE_REPLY_TO_ALL, NULL},
434
        {N_("/Repl_y to/_sender"),        NULL, summary_reply_cb,        COMPOSE_REPLY_TO_SENDER, NULL},
435
        {N_("/Repl_y to/mailing _list"),
436
                                        NULL, summary_reply_cb,        COMPOSE_REPLY_TO_LIST, NULL},
437
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
438
        {N_("/_Forward"),                NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
439
        {N_("/For_ward as attachment"),        NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
440
        {N_("/Redirec_t"),                NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL},
441
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
442
        {N_("/M_ove..."),                NULL, summary_move_to,        0, NULL},
443
        {N_("/_Copy..."),                NULL, summary_copy_to,        0, NULL},
444
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
445
        {N_("/_Mark"),                        NULL, NULL,                0, "<Branch>"},
446
        {N_("/_Mark/Set _flag"),        NULL, summary_mark,        0, NULL},
447
        {N_("/_Mark/_Unset flag"),        NULL, summary_unmark,        0, NULL},
448
        {N_("/_Mark/---"),                NULL, NULL,                0, "<Separator>"},
449
        {N_("/_Mark/Mark as unr_ead"),        NULL, summary_mark_as_unread, 0, NULL},
450
        {N_("/_Mark/Mark as rea_d"),
451
                                        NULL, summary_mark_as_read, 0, NULL},
452
        {N_("/_Mark/Mark _thread as read"),
453
                                        NULL, summary_mark_thread_as_read, 0, NULL},
454
        {N_("/_Mark/Mark all _read"),        NULL, summary_mark_all_read, 0, NULL},
455
        {N_("/Color la_bel"),                NULL, NULL,                0, NULL},
456
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
457
        {N_("/_Delete"),                NULL, summary_delete,        0, NULL},
458
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
459
        {N_("/Set as _junk mail"),        NULL, summary_junk,        0, NULL},
460
        {N_("/Set as not j_unk mail"),        NULL, summary_not_junk,        0, NULL},
461
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
462
        {N_("/Re-_edit"),                NULL, summary_reedit,   0, NULL},
463
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
464
        {N_("/Add sender to address boo_k..."),
465
                                        NULL, summary_add_address_cb, 0, NULL},
466
        {N_("/Create f_ilter rule"),        NULL, NULL,                0, "<Branch>"},
467
        {N_("/Create f_ilter rule/_Automatically"),
468
                                        NULL, summary_create_filter_cb, FLT_BY_AUTO, NULL},
469
        {N_("/Create f_ilter rule/by _From"),
470
                                        NULL, summary_create_filter_cb, FLT_BY_FROM, NULL},
471
        {N_("/Create f_ilter rule/by _To"),
472
                                        NULL, summary_create_filter_cb, FLT_BY_TO, NULL},
473
        {N_("/Create f_ilter rule/by _Subject"),
474
                                        NULL, summary_create_filter_cb, FLT_BY_SUBJECT, NULL},
475
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
476
        {N_("/_View"),                        NULL, NULL,                0, "<Branch>"},
477
        {N_("/_View/Open in new _window"),
478
                                        NULL, summary_open_msg,        0, NULL},
479
        {N_("/_View/Mess_age source"),        NULL, summary_view_source, 0, NULL},
480
        {N_("/_View/All _headers"),        NULL, summary_show_all_header_cb, 0, "<ToggleItem>"},
481
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
482
        {N_("/_Print..."),                NULL, summary_print,        0, NULL}
483
};
484

    
485
SummaryView *summary_create(void)
486
{
487
        SummaryView *summaryview;
488
        GtkWidget *vbox;
489
        GtkWidget *scrolledwin;
490
        GtkWidget *treeview;
491
        GtkTreeStore *store;
492
        GtkTreeSelection *selection;
493
        GtkWidget *hseparator;
494
        GtkWidget *hbox;
495
        GtkWidget *statlabel_folder;
496
        GtkWidget *statlabel_select;
497
        GtkWidget *statlabel_msgs;
498
        GtkWidget *toggle_eventbox;
499
        GtkWidget *toggle_arrow;
500
        GtkTooltips *tip;
501
        GtkWidget *popupmenu;
502
        GtkItemFactory *popupfactory;
503
        gint n_entries;
504
        GList *child;
505

    
506
        debug_print(_("Creating summary view...\n"));
507
        summaryview = g_new0(SummaryView, 1);
508

    
509
        vbox = gtk_vbox_new(FALSE, 1);
510

    
511
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
512
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
513
                                       GTK_POLICY_AUTOMATIC,
514
                                       GTK_POLICY_ALWAYS);
515
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
516
                                            GTK_SHADOW_IN);
517
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
518
        gtk_widget_set_size_request(vbox,
519
                                    prefs_common.summaryview_width,
520
                                    prefs_common.summaryview_height);
521

    
522
        treeview = summary_tree_view_create(summaryview);
523
        gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
524

    
525
        store = GTK_TREE_STORE
526
                (gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)));
527
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
528

    
529
        /* create status label */
530
        hseparator = gtk_hseparator_new();
531
        gtk_box_pack_end(GTK_BOX(vbox), hseparator, FALSE, FALSE, 0);
532

    
533
        hbox = gtk_hbox_new(FALSE, 0);
534
        gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
535

    
536
        statlabel_folder = gtk_label_new("");
537
        gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2);
538
        statlabel_select = gtk_label_new("");
539
        gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 12);
540

    
541
        /* toggle view button */
542
        toggle_eventbox = gtk_event_box_new();
543
        gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
544
        toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
545
        gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
546
        g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
547
                         G_CALLBACK(summary_toggle_pressed), summaryview);
548
        tip = gtk_tooltips_new();
549
        gtk_tooltips_set_tip(tip, toggle_eventbox, _("Toggle message view"),
550
                             NULL);
551

    
552
        statlabel_msgs = gtk_label_new("");
553
        gtk_misc_set_alignment(GTK_MISC(statlabel_msgs), 1, 0.5);
554
#if GTK_CHECK_VERSION(2, 6, 0)
555
        gtk_label_set_ellipsize(GTK_LABEL(statlabel_msgs),
556
                                PANGO_ELLIPSIZE_START);
557
#endif
558
        gtk_box_pack_start(GTK_BOX(hbox), statlabel_msgs, TRUE, TRUE, 2);
559

    
560
        /* create popup menu */
561
        n_entries = sizeof(summary_popup_entries) /
562
                sizeof(summary_popup_entries[0]);
563
        popupmenu = menu_create_items(summary_popup_entries, n_entries,
564
                                      "<SummaryView>", &popupfactory,
565
                                      summaryview);
566

    
567
        summaryview->vbox = vbox;
568
        summaryview->scrolledwin = scrolledwin;
569
        summaryview->treeview = treeview;
570
        summaryview->store = store;
571
        summaryview->selection = selection;
572
        summaryview->hseparator = hseparator;
573
        summaryview->hbox = hbox;
574
        summaryview->statlabel_folder = statlabel_folder;
575
        summaryview->statlabel_select = statlabel_select;
576
        summaryview->statlabel_msgs = statlabel_msgs;
577
        summaryview->toggle_eventbox = toggle_eventbox;
578
        summaryview->toggle_arrow = toggle_arrow;
579
        summaryview->popupmenu = popupmenu;
580
        summaryview->popupfactory = popupfactory;
581
        summaryview->lock_count = 0;
582

    
583
        summaryview->reedit_menuitem =
584
                gtk_item_factory_get_widget(popupfactory, "/Re-edit");
585
        child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
586
                            summaryview->reedit_menuitem);
587
        summaryview->reedit_separator = GTK_WIDGET(child->next->data);
588

    
589
        summaryview->junk_menuitem =
590
                gtk_item_factory_get_widget(popupfactory, "/Set as junk mail");
591
        summaryview->nojunk_menuitem =
592
                gtk_item_factory_get_widget(popupfactory,
593
                                            "/Set as not junk mail");
594
        child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
595
                            summaryview->nojunk_menuitem);
596
        summaryview->junk_separator = GTK_WIDGET(child->next->data);
597

    
598
        gtk_widget_show_all(vbox);
599

    
600
        return summaryview;
601
}
602

    
603
void summary_init(SummaryView *summaryview)
604
{
605
        GtkWidget *pixmap;
606
        PangoFontDescription *font_desc;
607
        gint size;
608
        TextView *textview;
609
        GtkAdjustment *adj;
610

    
611
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MARK,
612
                         &mark_pixbuf);
613
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_DELETED,
614
                         &deleted_pixbuf);
615
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MAIL_SMALL,
616
                         &mail_pixbuf);
617
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_NEW,
618
                         &new_pixbuf);
619
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_UNREAD,
620
                         &unread_pixbuf);
621
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_REPLIED,
622
                         &replied_pixbuf);
623
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_FORWARDED,
624
                         &forwarded_pixbuf);
625
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_CLIP,
626
                         &clip_pixbuf);
627

    
628
        font_desc = pango_font_description_new();
629
        size = pango_font_description_get_size
630
                (summaryview->statlabel_folder->style->font_desc);
631
        pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
632
        gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
633
        gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
634
        gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
635
        pango_font_description_free(font_desc);
636

    
637
        pixmap = stock_pixbuf_widget(summaryview->hbox,
638
                                     STOCK_PIXMAP_FOLDER_OPEN);
639
        gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
640
        gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
641
        gtk_widget_show(pixmap);
642

    
643
        summary_clear_list(summaryview);
644
        summary_set_column_order(summaryview);
645
        summary_colorlabel_menu_create(summaryview);
646
        summary_set_menu_sensitive(summaryview);
647

    
648
        textview = summaryview->messageview->textview;
649
        adj = GTK_TEXT_VIEW(textview->text)->vadjustment;
650
        g_signal_connect(adj, "value-changed",
651
                         G_CALLBACK(summary_text_adj_value_changed),
652
                         summaryview);
653
        textview = summaryview->messageview->mimeview->textview;
654
        adj = GTK_TEXT_VIEW(textview->text)->vadjustment;
655
        g_signal_connect(adj, "value-changed",
656
                         G_CALLBACK(summary_text_adj_value_changed),
657
                         summaryview);
658
}
659

    
660
static void get_msg_list_func(Folder *folder, FolderItem *item, gpointer data)
661
{
662
        SummaryView *summary = (SummaryView *)folder->data;
663
        gint count = GPOINTER_TO_INT(data);
664
        static GTimeVal tv_prev = {0, 0};
665
        GTimeVal tv_cur;
666

    
667
        g_get_current_time(&tv_cur);
668

    
669
        if (tv_prev.tv_sec == 0 ||
670
            (tv_cur.tv_sec - tv_prev.tv_sec) * G_USEC_PER_SEC +
671
            tv_cur.tv_usec - tv_prev.tv_usec > 100 * 1000) {
672
                gchar buf[256];
673

    
674
                g_snprintf(buf, sizeof(buf), _("Scanning folder (%s) (%d)..."),
675
                           item->path, count);
676
                STATUSBAR_POP(summary->mainwin);
677
                STATUSBAR_PUSH(summary->mainwin, buf);
678
                tv_prev = tv_cur;
679
        }
680
}
681

    
682
gboolean summary_show(SummaryView *summaryview, FolderItem *item,
683
                      gboolean update_cache)
684
{
685
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
686
        GtkTreeIter iter;
687
        GSList *mlist;
688
        gchar *buf;
689
        gboolean is_refresh;
690
        guint selected_msgnum = 0;
691
        guint displayed_msgnum = 0;
692
        gboolean moved;
693
        gboolean selection_done = FALSE;
694
        gboolean do_qsearch = FALSE;
695
        gboolean set_column_order_required = FALSE;
696
        const gchar *key = NULL;
697
        gpointer save_data;
698
        GSList *save_mark_mlist = NULL;
699

    
700
        if (summary_is_locked(summaryview)) return FALSE;
701

    
702
        inc_lock();
703
        summary_lock(summaryview);
704

    
705
        STATUSBAR_POP(summaryview->mainwin);
706

    
707
        is_refresh = (item == summaryview->folder_item &&
708
                      update_cache == FALSE) ? TRUE : FALSE;
709
        selected_msgnum = summary_get_msgnum(summaryview,
710
                                             summaryview->selected);
711
        displayed_msgnum = summary_get_msgnum(summaryview,
712
                                              summaryview->displayed);
713
        if (summaryview->folder_item)
714
                summaryview->folder_item->last_selected = selected_msgnum;
715
        if (!is_refresh) {
716
                if (item)
717
                        selected_msgnum = item->last_selected;
718
                else
719
                        selected_msgnum = 0;
720
                displayed_msgnum = 0;
721
        }
722

    
723
        /* process the marks if any */
724
        if (summaryview->mainwin->lock_count == 0 && !is_refresh &&
725
            (summaryview->moved > 0 || summaryview->copied > 0)) {
726
                AlertValue val;
727

    
728
                val = alertpanel(_("Process mark"),
729
                                 _("Some marks are left. Process it?"),
730
                                 GTK_STOCK_YES, GTK_STOCK_NO, GTK_STOCK_CANCEL);
731
                if (G_ALERTDEFAULT == val) {
732
                        summary_unlock(summaryview);
733
                        summary_execute(summaryview);
734
                        summary_lock(summaryview);
735
                        GTK_EVENTS_FLUSH();
736
                } else if (G_ALERTALTERNATE == val) {
737
                        summary_write_cache(summaryview);
738
                        GTK_EVENTS_FLUSH();
739
                } else {
740
                        summary_unlock(summaryview);
741
                        inc_unlock();
742
                        return FALSE;
743
                }
744
        } else {
745
                /* save temporary move/copy marks */
746
                if (is_refresh &&
747
                    (summaryview->moved > 0 || summaryview->copied > 0))
748
                        save_mark_mlist = summary_get_tmp_marked_msg_list(summaryview);
749

    
750
                summary_write_cache(summaryview);
751
        }
752

    
753
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item) !=
754
            FOLDER_ITEM_IS_SENT_FOLDER(item))
755
                set_column_order_required = TRUE;
756

    
757
        folderview_set_opened_item(summaryview->folderview, item);
758

    
759
        summary_clear_list_full(summaryview, is_refresh);
760

    
761
        buf = NULL;
762
        if (!item || !item->path || !item->parent || item->no_select ||
763
            (FOLDER_TYPE(item->folder) == F_MH && item->stype != F_VIRTUAL &&
764
             ((buf = folder_item_get_path(item)) == NULL ||
765
              change_dir(buf) < 0))) {
766
                g_free(buf);
767
                debug_print("empty folder\n\n");
768
                summary_clear_all(summaryview);
769
                summaryview->folder_item = item;
770
                if (item)
771
                        item->qsearch_cond_type = QS_ALL;
772
                if (set_column_order_required)
773
                        summary_set_column_order(summaryview);
774
                if (save_mark_mlist)
775
                        procmsg_msg_list_free(save_mark_mlist);
776
                summary_unlock(summaryview);
777
                inc_unlock();
778
                return TRUE;
779
        }
780
        g_free(buf);
781

    
782
        if (!is_refresh)
783
                messageview_clear(summaryview->messageview);
784

    
785
        summaryview->folder_item = item;
786
        if (set_column_order_required)
787
                summary_set_column_order(summaryview);
788

    
789
        g_signal_handlers_block_matched(G_OBJECT(treeview),
790
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
791
                                        0, 0, NULL, NULL, summaryview);
792

    
793
        buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
794
        debug_print("%s\n", buf);
795
        STATUSBAR_PUSH(summaryview->mainwin, buf);
796
        g_free(buf);
797

    
798
        main_window_cursor_wait(summaryview->mainwin);
799

    
800
        save_data = item->folder->data;
801
        item->folder->data = summaryview;
802
        folder_set_ui_func(item->folder, get_msg_list_func, NULL);
803

    
804
        mlist = folder_item_get_msg_list(item, !update_cache);
805

    
806
        folder_set_ui_func(item->folder, NULL, NULL);
807
        item->folder->data = save_data;
808

    
809
        statusbar_pop_all();
810
        STATUSBAR_POP(summaryview->mainwin);
811

    
812
        summaryview->all_mlist = mlist;
813

    
814
        /* restore temporary move/copy marks */
815
        if (save_mark_mlist) {
816
                summary_restore_tmp_marks(summaryview, save_mark_mlist);
817
                save_mark_mlist = NULL;
818
        }
819

    
820
        /* set tree store and hash table from the msginfo list, and
821
           create the thread */
822
        if (prefs_common.show_searchbar &&
823
            (prefs_common.persist_qsearch_filter || is_refresh)) {
824
                if (item->qsearch_cond_type > QS_ALL)
825
                        do_qsearch = TRUE;
826
                if (is_refresh && summaryview->qsearch->entry_entered) {
827
                        key = gtk_entry_get_text
828
                                (GTK_ENTRY(summaryview->qsearch->entry));
829
                        if (key && *key != '\0')
830
                                do_qsearch = TRUE;
831
                        else
832
                                key = NULL;
833
                }
834
        }
835

    
836
        if (do_qsearch) {
837
                gint index;
838
                QSearchCondType type = item->qsearch_cond_type;
839

    
840
                index = menu_find_option_menu_index
841
                        (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
842
                         GINT_TO_POINTER(type), NULL);
843
                if (index > 0) {
844
                        gtk_option_menu_set_history
845
                                (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
846
                                 index);
847
                } else {
848
                        gtk_option_menu_set_history
849
                                (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
850
                                 0);
851
                        type = QS_ALL;
852
                }
853

    
854
                if (type > QS_ALL || key) {
855
                        summaryview->flt_mlist =
856
                                quick_search_filter(summaryview->qsearch,
857
                                                    type, key);
858
                        summaryview->on_filter = TRUE;
859
                        summary_set_tree_model_from_list
860
                                (summaryview, summaryview->flt_mlist);
861
                        summary_update_status(summaryview);
862
                } else {
863
                        item->qsearch_cond_type = QS_ALL;
864
                        summary_set_tree_model_from_list(summaryview, mlist);
865
                }
866
        } else {
867
                item->qsearch_cond_type = QS_ALL;
868
                summary_set_tree_model_from_list(summaryview, mlist);
869
        }
870

    
871
        if (mlist)
872
                gtk_widget_grab_focus(GTK_WIDGET(treeview));
873

    
874
        summary_write_cache(summaryview);
875

    
876
        item->opened = TRUE;
877

    
878
        g_signal_handlers_unblock_matched(G_OBJECT(treeview),
879
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
880
                                          0, 0, NULL, NULL, summaryview);
881

    
882
        if (is_refresh) {
883
                summary_update_display_state(summaryview, displayed_msgnum,
884
                                             selected_msgnum);
885

    
886
                if (!summaryview->selected) {
887
                        /* no selected message - select first unread
888
                           message, but do not display it */
889
                        if (summary_find_next_flagged_msg
890
                                (summaryview, &iter, NULL, MSG_UNREAD, FALSE)) {
891
                                summary_select_row(summaryview, &iter,
892
                                                   FALSE, TRUE);
893
                        } else if (item->sort_type == SORT_ASCENDING &&
894
                                   SUMMARY_DISPLAY_TOTAL_NUM(item) > 1) {
895
                                g_signal_emit_by_name
896
                                        (treeview, "move-cursor",
897
                                         GTK_MOVEMENT_BUFFER_ENDS, 1, &moved);
898
                                GTK_EVENTS_FLUSH();
899
                                summary_scroll_to_selected(summaryview, TRUE);
900
                        } else if (gtk_tree_model_get_iter_first
901
                                        (GTK_TREE_MODEL(summaryview->store),
902
                                         &iter)) {
903
                                summary_select_row(summaryview, &iter,
904
                                                   FALSE, TRUE);
905
                        }
906
                }
907
                selection_done = TRUE;
908
        } else if (prefs_common.remember_last_selected) {
909
                summary_unlock(summaryview);
910
                summary_select_by_msgnum(summaryview, selected_msgnum);
911
                summary_lock(summaryview);
912

    
913
                if (summaryview->selected)
914
                        selection_done = TRUE;
915
        }
916

    
917
        if (!selection_done) {
918
                /* select first unread message */
919
                if (summary_find_next_flagged_msg(summaryview, &iter, NULL,
920
                                                  MSG_UNREAD, FALSE)) {
921
                        if (prefs_common.open_unread_on_enter ||
922
                            prefs_common.always_show_msg) {
923
                                summary_unlock(summaryview);
924
                                summary_select_row(summaryview, &iter,
925
                                                   TRUE, TRUE);
926
                                summary_lock(summaryview);
927
                        } else {
928
                                summary_select_row(summaryview, &iter,
929
                                                   FALSE, TRUE);
930
                        }
931
                } else {
932
                        summary_unlock(summaryview);
933
                        if (item->sort_type == SORT_ASCENDING &&
934
                            SUMMARY_DISPLAY_TOTAL_NUM(item) > 1) {
935
                                g_signal_emit_by_name
936
                                        (treeview, "move-cursor",
937
                                         GTK_MOVEMENT_BUFFER_ENDS, 1, &moved);
938
                        } else if (gtk_tree_model_get_iter_first
939
                                        (GTK_TREE_MODEL(summaryview->store),
940
                                         &iter)) {
941
                                summary_select_row(summaryview, &iter,
942
                                                   FALSE, TRUE);
943
                        }
944
                        summary_lock(summaryview);
945
                        GTK_EVENTS_FLUSH();
946
                        summary_scroll_to_selected(summaryview, TRUE);
947
                }
948
        }
949

    
950
        summary_status_show(summaryview);
951
        summary_set_menu_sensitive(summaryview);
952
        main_window_set_toolbar_sensitive(summaryview->mainwin);
953

    
954
        debug_print("\n");
955
        STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
956

    
957
        main_window_cursor_normal(summaryview->mainwin);
958

    
959
        if (prefs_common.online_mode) {
960
                if (FOLDER_IS_REMOTE(item->folder) &&
961
                    REMOTE_FOLDER(item->folder)->session == NULL) {
962
                        alertpanel_error(_("Could not establish a connection to the server."));
963
                }
964
        }
965

    
966
        summary_unlock(summaryview);
967
        inc_unlock();
968

    
969
        return TRUE;
970
}
971

    
972
static void summary_unset_sort_column_id(SummaryView *summaryview)
973
{
974
        gint id;
975
        GtkSortType order;
976

    
977
        if (gtk_tree_sortable_get_sort_column_id
978
                (GTK_TREE_SORTABLE(summaryview->store), &id, &order) &&
979
            id >= 0 && id < N_SUMMARY_VISIBLE_COLS) {
980
                GtkTreeViewColumn *column = summaryview->columns[id];
981
                column->sort_column_id = -1;
982
                gtk_tree_view_column_set_sort_indicator(column, FALSE);
983
        }
984

    
985
        gtkut_tree_sortable_unset_sort_column_id
986
                (GTK_TREE_SORTABLE(summaryview->store));
987
}
988

    
989
void summary_clear_list(SummaryView *summaryview)
990
{
991
        summary_clear_list_full(summaryview, FALSE);
992
}
993

    
994
static void summary_clear_list_full(SummaryView *summaryview,
995
                                    gboolean is_refresh)
996
{
997
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
998
        GtkAdjustment *adj;
999

    
1000
        if (summaryview->folder_item) {
1001
                folder_item_close(summaryview->folder_item);
1002
                summaryview->folder_item = NULL;
1003
        }
1004

    
1005
        summaryview->display_msg = FALSE;
1006

    
1007
        summaryview->selected = NULL;
1008
        summaryview->displayed = NULL;
1009

    
1010
        summary_selection_list_free(summaryview);
1011

    
1012
        summaryview->total_size = 0;
1013
        summaryview->deleted = summaryview->moved = 0;
1014
        summaryview->copied = 0;
1015

    
1016
        summary_msgid_table_destroy(summaryview);
1017

    
1018
        summaryview->tmp_mlist = NULL;
1019
        summaryview->to_folder = NULL;
1020
        if (summaryview->folder_table) {
1021
                g_hash_table_destroy(summaryview->folder_table);
1022
                summaryview->folder_table = NULL;
1023
        }
1024
        summaryview->filtered = 0;
1025
        summaryview->flt_count = 0;
1026
        summaryview->flt_total = 0;
1027

    
1028
        summaryview->on_button_press = FALSE;
1029
        summaryview->can_toggle_selection = TRUE;
1030
        summaryview->on_drag = FALSE;
1031
        if (summaryview->pressed_path) {
1032
                gtk_tree_path_free(summaryview->pressed_path);
1033
                summaryview->pressed_path = NULL;
1034
        }
1035
        if (summaryview->drag_list) {
1036
                g_free(summaryview->drag_list);
1037
                summaryview->drag_list = NULL;
1038
        }
1039

    
1040
        if (summaryview->flt_mlist) {
1041
                g_slist_free(summaryview->flt_mlist);
1042
                summaryview->flt_mlist = NULL;
1043
        }
1044
        summaryview->total_flt_msg_size = 0;
1045
        summaryview->flt_msg_total = 0;
1046
        summaryview->flt_deleted = summaryview->flt_moved = 0;
1047
        summaryview->flt_copied = 0;
1048
        summaryview->flt_new = summaryview->flt_unread = 0;
1049
        if (!is_refresh) {
1050
                quick_search_clear_entry(summaryview->qsearch);
1051
                gtk_option_menu_set_history
1052
                        (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0);
1053
        }
1054
        summaryview->on_filter = FALSE;
1055

    
1056
        procmsg_msg_list_free(summaryview->all_mlist);
1057
        summaryview->all_mlist = NULL;
1058

    
1059
        gtkut_tree_view_fast_clear(treeview, summaryview->store);
1060

    
1061
        /* ensure that the "value-changed" signal is always emitted */
1062
        adj = gtk_tree_view_get_vadjustment(treeview);
1063
        adj->value = 0.0;
1064

    
1065
        summary_unset_sort_column_id(summaryview);
1066
}
1067

    
1068
void summary_clear_all(SummaryView *summaryview)
1069
{
1070
        messageview_clear(summaryview->messageview);
1071
        summary_clear_list(summaryview);
1072
        summary_set_menu_sensitive(summaryview);
1073
        main_window_set_toolbar_sensitive(summaryview->mainwin);
1074
        summary_status_show(summaryview);
1075
}
1076

    
1077
void summary_show_queued_msgs(SummaryView *summaryview)
1078
{
1079
        FolderItem *item;
1080
        GSList *qlist, *cur;
1081
        MsgInfo *msginfo;
1082
        GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
1083
        GtkTreeIter iter;
1084

    
1085
        if (summary_is_locked(summaryview))
1086
                return;
1087

    
1088
        item = summaryview->folder_item;
1089
        if (!item || !item->path || !item->cache_queue ||
1090
            item->stype == F_VIRTUAL)
1091
                return;
1092

    
1093
        debug_print("summary_show_queued_msgs: appending queued messages to summary (%s)\n", item->path);
1094

    
1095
        qlist = g_slist_reverse(item->cache_queue);
1096
        item->cache_queue = NULL;
1097
        if (item->mark_queue) {
1098
                procmsg_flaginfo_list_free(item->mark_queue);
1099
                item->mark_queue = NULL;
1100
        }
1101

    
1102
        for (cur = qlist; cur != NULL; cur = cur->next) {
1103
                msginfo = (MsgInfo *)cur->data;
1104

    
1105
                debug_print("summary_show_queued_msgs: appending msg %u\n",
1106
                            msginfo->msgnum);
1107
                msginfo->folder = item;
1108
                gtk_tree_store_append(store, &iter, NULL);
1109
                summary_set_row(summaryview, &iter, msginfo);
1110

    
1111
                if (cur == qlist) {
1112
                        GtkTreePath *path;
1113

    
1114
                        path = gtk_tree_model_get_path(GTK_TREE_MODEL(store),
1115
                                                       &iter);
1116
                        gtk_tree_view_scroll_to_cell
1117
                                (GTK_TREE_VIEW(summaryview->treeview), path,
1118
                                 NULL, FALSE, 0.0, 0.0);
1119
                        gtk_tree_path_free(path);
1120
                }
1121

    
1122
                summaryview->total_size += msginfo->size;
1123
        }
1124

    
1125
        summaryview->all_mlist = g_slist_concat(summaryview->all_mlist, qlist);
1126

    
1127
        item->cache_dirty = TRUE;
1128
        summary_selection_list_free(summaryview);
1129

    
1130
        summary_status_show(summaryview);
1131

    
1132
        debug_print("summary_show_queued_msgs: done.\n");
1133
}
1134

    
1135
void summary_lock(SummaryView *summaryview)
1136
{
1137
        summaryview->lock_count++;
1138
        summaryview->write_lock_count++;
1139
        /* g_print("summary_lock: %d\n", summaryview->lock_count); */
1140
}
1141

    
1142
void summary_unlock(SummaryView *summaryview)
1143
{
1144
        /* g_print("summary_unlock: %d\n", summaryview->lock_count); */
1145
        if (summaryview->lock_count)
1146
                summaryview->lock_count--;
1147
        if (summaryview->write_lock_count)
1148
                summaryview->write_lock_count--;
1149
}
1150

    
1151
gboolean summary_is_locked(SummaryView *summaryview)
1152
{
1153
        return summaryview->lock_count > 0 || summaryview->write_lock_count > 0;
1154
}
1155

    
1156
gboolean summary_is_read_locked(SummaryView *summaryview)
1157
{
1158
        return summaryview->lock_count > 0;
1159
}
1160

    
1161
void summary_write_lock(SummaryView *summaryview)
1162
{
1163
        summaryview->write_lock_count++;
1164
}
1165

    
1166
void summary_write_unlock(SummaryView *summaryview)
1167
{
1168
        if (summaryview->write_lock_count)
1169
                summaryview->write_lock_count--;
1170
}
1171

    
1172
gboolean summary_is_write_locked(SummaryView *summaryview)
1173
{
1174
        return summaryview->write_lock_count > 0;
1175
}
1176

    
1177
FolderItem *summary_get_current_folder(SummaryView *summaryview)
1178
{
1179
        return summaryview->folder_item;
1180
}
1181

    
1182
SummarySelection summary_get_selection_type(SummaryView *summaryview)
1183
{
1184
        SummarySelection selection;
1185
        GList *rows;
1186

    
1187
        rows = summary_get_selected_rows(summaryview);
1188

    
1189
        if (!summaryview->folder_item || summaryview->folder_item->total == 0)
1190
                selection = SUMMARY_NONE;
1191
        else if (!rows)
1192
                selection = SUMMARY_SELECTED_NONE;
1193
        else if (rows && !rows->next)
1194
                selection = SUMMARY_SELECTED_SINGLE;
1195
        else
1196
                selection = SUMMARY_SELECTED_MULTIPLE;
1197

    
1198
        return selection;
1199
}
1200

    
1201
static GList *summary_get_selected_rows(SummaryView *summaryview)
1202
{
1203
        if (!summaryview->selection_list)
1204
                summaryview->selection_list =
1205
                        gtk_tree_selection_get_selected_rows
1206
                                (summaryview->selection, NULL);
1207

    
1208
        return summaryview->selection_list;
1209
}
1210

    
1211
static void summary_selection_list_free(SummaryView *summaryview)
1212
{
1213
        if (summaryview->selection_list) {
1214
                g_list_foreach(summaryview->selection_list,
1215
                               (GFunc)gtk_tree_path_free, NULL);
1216
                g_list_free(summaryview->selection_list);
1217
                summaryview->selection_list = NULL;
1218
        }
1219
}
1220

    
1221
GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1222
{
1223
        GSList *mlist = NULL;
1224
        GList *rows;
1225
        GList *cur;
1226
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1227
        GtkTreeIter iter;
1228
        MsgInfo *msginfo;
1229

    
1230
        rows = summary_get_selected_rows(summaryview);
1231
        for (cur = rows; cur != NULL; cur = cur->next) {
1232
                gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data);
1233
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1234
                mlist = g_slist_prepend(mlist, msginfo);
1235
        }
1236

    
1237
        mlist = g_slist_reverse(mlist);
1238

    
1239
        return mlist;
1240
}
1241

    
1242
GSList *summary_get_changed_msg_list(SummaryView *summaryview)
1243
{
1244
        MsgInfo *msginfo;
1245
        GSList *mlist = NULL;
1246
        GSList *cur;
1247

    
1248
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1249
                msginfo = (MsgInfo *)cur->data;
1250
                if (MSG_IS_FLAG_CHANGED(msginfo->flags))
1251
                        mlist = g_slist_prepend(mlist, msginfo);
1252
        }
1253

    
1254
        return g_slist_reverse(mlist);
1255
}
1256

    
1257
GSList *summary_get_msg_list(SummaryView *summaryview)
1258
{
1259
        if (summaryview->on_filter)
1260
                return g_slist_copy(summaryview->flt_mlist);
1261
        else
1262
                return g_slist_copy(summaryview->all_mlist);
1263
}
1264

    
1265
GSList *summary_get_flagged_msg_list(SummaryView *summaryview,
1266
                                     MsgPermFlags flags)
1267
{
1268
        MsgInfo *msginfo;
1269
        GSList *list, *cur;
1270
        GSList *mlist = NULL;
1271

    
1272
        if (summaryview->on_filter)
1273
                list = summaryview->flt_mlist;
1274
        else
1275
                list = summaryview->all_mlist;
1276

    
1277
        for (cur = list; cur != NULL; cur = cur->next) {
1278
                msginfo = (MsgInfo *)cur->data;
1279
                if ((msginfo->flags.perm_flags & flags) != 0)
1280
                        mlist = g_slist_prepend(mlist, msginfo);
1281
        }
1282

    
1283
        return g_slist_reverse(mlist);
1284
}
1285

    
1286
/* return list of copied MsgInfo */
1287
static GSList *summary_get_tmp_marked_msg_list(SummaryView *summaryview)
1288
{
1289
        MsgInfo *msginfo, *markinfo;
1290
        GSList *mlist = NULL;
1291
        GSList *cur;
1292

    
1293
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1294
                msginfo = (MsgInfo *)cur->data;
1295
                if (MSG_IS_MOVE(msginfo->flags) ||
1296
                    MSG_IS_COPY(msginfo->flags)) {
1297
                        markinfo = g_new0(MsgInfo, 1);
1298
                        markinfo->msgnum = msginfo->msgnum;
1299
                        markinfo->flags = msginfo->flags;
1300
                        markinfo->folder = msginfo->folder;
1301
                        markinfo->to_folder = msginfo->to_folder;
1302
                        mlist = g_slist_prepend(mlist, markinfo);
1303
                }
1304
        }
1305

    
1306
        return g_slist_reverse(mlist);
1307
}
1308

    
1309
static void summary_restore_tmp_marks(SummaryView *summaryview,
1310
                                      GSList *save_mark_mlist)
1311
{
1312
        GSList *cur, *scur;
1313
        MsgInfo *msginfo, *markinfo;
1314

    
1315
        debug_print("summary_restore_tmp_marks: restoring temporary marks\n");
1316

    
1317
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1318
                msginfo = (MsgInfo *)cur->data;
1319
                for (scur = save_mark_mlist; scur != NULL; scur = scur->next) {
1320
                        markinfo = (MsgInfo *)scur->data;
1321
                        if (msginfo->msgnum == markinfo->msgnum &&
1322
                            msginfo->folder == markinfo->folder) {
1323
                                msginfo->flags.tmp_flags |= (markinfo->flags.tmp_flags & (MSG_MOVE|MSG_COPY));
1324
                                msginfo->to_folder = markinfo->to_folder;
1325
                                save_mark_mlist = g_slist_remove
1326
                                        (save_mark_mlist, markinfo);
1327
                                g_free(markinfo);
1328
                                if (!save_mark_mlist)
1329
                                        return;
1330
                                break;
1331
                        }
1332
                }
1333
        }
1334

    
1335
        if (save_mark_mlist)
1336
                procmsg_msg_list_free(save_mark_mlist);
1337
}
1338

    
1339
static void summary_update_msg_list(SummaryView *summaryview)
1340
{
1341
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1342
        GtkTreeIter iter;
1343
        GSList *mlist = NULL;
1344
        MsgInfo *msginfo;
1345
        gboolean valid;
1346

    
1347
        if (summaryview->on_filter)
1348
                return;
1349

    
1350
        g_slist_free(summaryview->all_mlist);
1351
        summaryview->all_mlist = NULL;
1352

    
1353
        valid = gtk_tree_model_get_iter_first(model, &iter);
1354

    
1355
        while (valid) {
1356
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1357
                mlist = g_slist_prepend(mlist, msginfo);
1358
                valid = gtkut_tree_model_next(model, &iter);
1359
        }
1360

    
1361
        summaryview->all_mlist = g_slist_reverse(mlist);
1362

    
1363
}
1364

    
1365
static gboolean summary_msgid_table_create_func(GtkTreeModel *model,
1366
                                                GtkTreePath *path,
1367
                                                GtkTreeIter *iter,
1368
                                                gpointer data)
1369
{
1370
        GHashTable *msgid_table = (GHashTable *)data;
1371
        MsgInfo *msginfo;
1372
        GtkTreeIter *iter_;
1373

    
1374
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
1375

    
1376
        if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1377
            !MSG_IS_DELETED(msginfo->flags) &&
1378
            msginfo->msgid && msginfo->msgid[0] != '\0') {
1379
                iter_ = gtk_tree_iter_copy(iter);
1380
                g_hash_table_replace(msgid_table, msginfo->msgid, iter_);
1381
        }
1382

    
1383
        return FALSE;
1384
}
1385

    
1386
static void summary_msgid_table_create(SummaryView *summaryview)
1387
{
1388
        GHashTable *msgid_table;
1389

    
1390
        g_return_if_fail(summaryview->msgid_table == NULL);
1391

    
1392
        msgid_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1393
                                            (GDestroyNotify)gtk_tree_iter_free);
1394

    
1395
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
1396
                               summary_msgid_table_create_func, msgid_table);
1397

    
1398
        summaryview->msgid_table = msgid_table;
1399
}
1400

    
1401
static void summary_msgid_table_destroy(SummaryView *summaryview)
1402
{
1403
        if (!summaryview->msgid_table)
1404
                return;
1405

    
1406
        g_hash_table_destroy(summaryview->msgid_table);
1407
        summaryview->msgid_table = NULL;
1408
}
1409

    
1410
static void summary_set_menu_sensitive(SummaryView *summaryview)
1411
{
1412
        GtkItemFactory *ifactory = summaryview->popupfactory;
1413
        SummarySelection selection;
1414
        GtkWidget *menuitem;
1415
        gboolean sens;
1416

    
1417
        selection = summary_get_selection_type(summaryview);
1418
        sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1419

    
1420
        main_window_set_menu_sensitive(summaryview->mainwin);
1421

    
1422
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) {
1423
                gtk_widget_show(summaryview->reedit_menuitem);
1424
                gtk_widget_show(summaryview->reedit_separator);
1425
                menu_set_sensitive(ifactory, "/Re-edit", sens);
1426
        } else {
1427
                gtk_widget_hide(summaryview->reedit_menuitem);
1428
                gtk_widget_hide(summaryview->reedit_separator);
1429
                menu_set_sensitive(ifactory, "/Re-edit", FALSE);
1430
        }
1431

    
1432
        if (selection == SUMMARY_NONE) {
1433
                menu_set_insensitive_all
1434
                        (GTK_MENU_SHELL(summaryview->popupmenu));
1435
                return;
1436
        }
1437

    
1438
        if (summaryview->folder_item &&
1439
            FOLDER_TYPE(summaryview->folder_item->folder) != F_NEWS) {
1440
                menu_set_sensitive(ifactory, "/Move...", TRUE);
1441
                menu_set_sensitive(ifactory, "/Delete", TRUE);
1442
        } else {
1443
                menu_set_sensitive(ifactory, "/Move...", FALSE);
1444
                menu_set_sensitive(ifactory, "/Delete", FALSE);
1445
        }
1446

    
1447
        menu_set_sensitive(ifactory, "/Copy...", TRUE);
1448

    
1449
        menu_set_sensitive(ifactory, "/Mark", TRUE);
1450
        menu_set_sensitive(ifactory, "/Mark/Set flag",   TRUE);
1451
        menu_set_sensitive(ifactory, "/Mark/Unset flag", TRUE);
1452

    
1453
        menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
1454
        menu_set_sensitive(ifactory, "/Mark/Mark as read",   TRUE);
1455
        menu_set_sensitive(ifactory, "/Mark/Mark all read",  TRUE);
1456

    
1457
        if (prefs_common.enable_junk) {
1458
                gtk_widget_show(summaryview->junk_menuitem);
1459
                gtk_widget_show(summaryview->nojunk_menuitem);
1460
                gtk_widget_show(summaryview->junk_separator);
1461
                menu_set_sensitive(ifactory, "/Set as junk mail", TRUE);
1462
                menu_set_sensitive(ifactory, "/Set as not junk mail", TRUE);
1463
        } else {
1464
                gtk_widget_hide(summaryview->junk_menuitem);
1465
                gtk_widget_hide(summaryview->nojunk_menuitem);
1466
                gtk_widget_hide(summaryview->junk_separator);
1467
                menu_set_sensitive(ifactory, "/Set as junk mail", FALSE);
1468
                menu_set_sensitive(ifactory, "/Set as not junk mail", FALSE);
1469
        }
1470

    
1471
        menu_set_sensitive(ifactory, "/Color label", TRUE);
1472

    
1473
        menu_set_sensitive(ifactory, "/Reply",                          sens);
1474
        menu_set_sensitive(ifactory, "/Reply to",                  sens);
1475
        menu_set_sensitive(ifactory, "/Reply to/all",                  sens);
1476
        menu_set_sensitive(ifactory, "/Reply to/sender",          sens);
1477
        menu_set_sensitive(ifactory, "/Reply to/mailing list",          sens);
1478
        menu_set_sensitive(ifactory, "/Forward",                  TRUE);
1479
        menu_set_sensitive(ifactory, "/Forward as attachment",          TRUE);
1480
        menu_set_sensitive(ifactory, "/Redirect",                  sens);
1481

    
1482
        menu_set_sensitive(ifactory, "/Add sender to address book...", sens);
1483
        menu_set_sensitive(ifactory, "/Create filter rule", sens);
1484

    
1485
        menu_set_sensitive(ifactory, "/View", sens);
1486
        menu_set_sensitive(ifactory, "/View/Open in new window", sens);
1487
        menu_set_sensitive(ifactory, "/View/Message source", sens);
1488
        menu_set_sensitive(ifactory, "/View/All headers", sens);
1489

    
1490
        menu_set_sensitive(ifactory, "/Print...",   TRUE);
1491

    
1492
        summary_lock(summaryview);
1493
        menuitem = gtk_item_factory_get_widget(ifactory, "/View/All headers");
1494
        gtk_check_menu_item_set_active
1495
                (GTK_CHECK_MENU_ITEM(menuitem),
1496
                 summaryview->messageview->textview->show_all_headers);
1497
        summary_unlock(summaryview);
1498
}
1499

    
1500
static void summary_select_prev_flagged(SummaryView *summaryview,
1501
                                        MsgPermFlags flags,
1502
                                        const gchar *title,
1503
                                        const gchar *ask_msg,
1504
                                        const gchar *notice)
1505
{
1506
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1507
        GtkTreeIter prev, iter;
1508
        gboolean start_from_prev = FALSE;
1509
        gboolean found;
1510

    
1511
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1512
                                               &iter))
1513
                return;
1514

    
1515
        if (!messageview_is_visible(summaryview->messageview) ||
1516
            summary_row_is_displayed(summaryview, &iter))
1517
                start_from_prev = TRUE;
1518

    
1519
        found = summary_find_prev_flagged_msg
1520
                (summaryview, &prev, &iter, flags, start_from_prev);
1521

    
1522
        if (!found) {
1523
                AlertValue val;
1524

    
1525
                val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO,
1526
                                 NULL);
1527
                if (val != G_ALERTDEFAULT) return;
1528
                found = summary_find_prev_flagged_msg(summaryview, &prev, NULL,
1529
                                                      flags, start_from_prev);
1530
        }
1531

    
1532
        if (!found) {
1533
                if (notice)
1534
                        alertpanel_notice("%s", notice);
1535
        } else {
1536
                gboolean visible;
1537
                visible = messageview_is_visible(summaryview->messageview);
1538
                summary_select_row(summaryview, &prev, visible, FALSE);
1539
                if (visible)
1540
                        summary_mark_displayed_read(summaryview, &prev);
1541
        }
1542
}
1543

    
1544
static void summary_select_next_flagged(SummaryView *summaryview,
1545
                                        MsgPermFlags flags,
1546
                                        const gchar *title,
1547
                                        const gchar *ask_msg,
1548
                                        const gchar *notice)
1549
{
1550
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1551
        GtkTreeIter next, iter;
1552
        gboolean start_from_next = FALSE;
1553
        gboolean found;
1554

    
1555
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1556
                                               &iter)) {
1557
                if (!gtk_tree_model_get_iter_first(model, &iter))
1558
                        return;
1559
        }
1560

    
1561
        if (!messageview_is_visible(summaryview->messageview) ||
1562
            summary_row_is_displayed(summaryview, &iter))
1563
                start_from_next = TRUE;
1564

    
1565
        found = summary_find_next_flagged_msg
1566
                (summaryview, &next, &iter, flags, start_from_next);
1567

    
1568
        if (!found) {
1569
                AlertValue val;
1570

    
1571
                val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO,
1572
                                 NULL);
1573
                if (val != G_ALERTDEFAULT) return;
1574
                found = summary_find_next_flagged_msg(summaryview, &next, NULL,
1575
                                                      flags, start_from_next);
1576
        }
1577

    
1578
        if (!found) {
1579
                if (notice)
1580
                        alertpanel_notice("%s", notice);
1581
        } else {
1582
                gboolean visible;
1583
                visible = messageview_is_visible(summaryview->messageview);
1584
                summary_select_row(summaryview, &next, visible, FALSE);
1585
                if (visible)
1586
                        summary_mark_displayed_read(summaryview, &next);
1587
        }
1588
}
1589

    
1590
static void summary_select_next_flagged_or_folder(SummaryView *summaryview,
1591
                                                  MsgPermFlags flags,
1592
                                                  const gchar *title,
1593
                                                  const gchar *ask_msg,
1594
                                                  const gchar *notice)
1595
{
1596
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1597
        GtkTreeIter iter, next;
1598
        gboolean start_from_next = FALSE;
1599
        gboolean visible;
1600

    
1601
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1602
                                               &iter)) {
1603
                if (!gtk_tree_model_get_iter_first(model, &iter))
1604
                        return;
1605
        }
1606

    
1607
        if (!messageview_is_visible(summaryview->messageview) ||
1608
            summary_row_is_displayed(summaryview, &iter))
1609
                start_from_next = TRUE;
1610

    
1611
        while (summary_find_next_flagged_msg
1612
                (summaryview, &next, &iter, flags, start_from_next) == FALSE) {
1613
                AlertValue val;
1614

    
1615
                val = alertpanel(title, ask_msg,
1616
                                 GTK_STOCK_YES, GTK_STOCK_NO,
1617
                                 _("_Search again"));
1618

    
1619
                if (val == G_ALERTDEFAULT) {
1620
                        folderview_select_next_unread(summaryview->folderview);
1621
                        return;
1622
                } else if (val == G_ALERTOTHER) {
1623
                        start_from_next = FALSE;
1624
                        if (!gtk_tree_model_get_iter_first(model, &iter))
1625
                                return;
1626
                } else
1627
                        return;
1628
        }
1629

    
1630
        visible = messageview_is_visible(summaryview->messageview);
1631
        summary_select_row(summaryview, &next, visible, FALSE);
1632
        if (visible)
1633
                summary_mark_displayed_read(summaryview, &next);
1634
}
1635

    
1636
void summary_select_prev_unread(SummaryView *summaryview)
1637
{
1638
        summary_select_prev_flagged(summaryview, MSG_UNREAD,
1639
                                    _("No more unread messages"),
1640
                                    _("No unread message found. "
1641
                                      "Search from the end?"),
1642
                                    _("No unread messages."));
1643
}
1644

    
1645
void summary_select_next_unread(SummaryView *summaryview)
1646
{
1647
        summary_select_next_flagged_or_folder(summaryview, MSG_UNREAD,
1648
                                              _("No more unread messages"),
1649
                                              _("No unread message found. "
1650
                                                "Go to next unread folder?"),
1651
                                              NULL);
1652
}
1653

    
1654
void summary_select_prev_new(SummaryView *summaryview)
1655
{
1656
        summary_select_prev_flagged(summaryview, MSG_NEW,
1657
                                    _("No more new messages"),
1658
                                    _("No new message found. "
1659
                                      "Search from the end?"),
1660
                                    _("No new messages."));
1661
}
1662

    
1663
void summary_select_next_new(SummaryView *summaryview)
1664
{
1665
        summary_select_next_flagged_or_folder(summaryview, MSG_NEW,
1666
                                              _("No more new messages"),
1667
                                              _("No new message found. "
1668
                                                "Go to next folder which has new messages?"),
1669
                                              NULL);
1670
}
1671

    
1672
void summary_select_prev_marked(SummaryView *summaryview)
1673
{
1674
        summary_select_prev_flagged(summaryview, MSG_MARKED,
1675
                                    _("No more marked messages"),
1676
                                    _("No marked message found. "
1677
                                      "Search from the end?"),
1678
                                    _("No marked messages."));
1679
}
1680

    
1681
void summary_select_next_marked(SummaryView *summaryview)
1682
{
1683
        summary_select_next_flagged(summaryview, MSG_MARKED,
1684
                                    _("No more marked messages"),
1685
                                    _("No marked message found. "
1686
                                      "Search from the beginning?"),
1687
                                    _("No marked messages."));
1688
}
1689

    
1690
void summary_select_prev_labeled(SummaryView *summaryview)
1691
{
1692
        summary_select_prev_flagged(summaryview, MSG_CLABEL_FLAG_MASK,
1693
                                    _("No more labeled messages"),
1694
                                    _("No labeled message found. "
1695
                                      "Search from the end?"),
1696
                                    _("No labeled messages."));
1697
}
1698

    
1699
void summary_select_next_labeled(SummaryView *summaryview)
1700
{
1701
        summary_select_next_flagged(summaryview, MSG_CLABEL_FLAG_MASK,
1702
                                    _("No more labeled messages"),
1703
                                    _("No labeled message found. "
1704
                                      "Search from the beginning?"),
1705
                                    _("No labeled messages."));
1706
}
1707

    
1708
void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1709
{
1710
        GtkTreeIter iter;
1711

    
1712
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1713
                summary_select_row(summaryview, &iter, FALSE, TRUE);
1714
}
1715

    
1716
gboolean summary_select_by_msginfo(SummaryView *summaryview, MsgInfo *msginfo)
1717
{
1718
        GtkTreeIter iter;
1719

    
1720
        if (summaryview->folder_item != msginfo->folder)
1721
                return FALSE;
1722

    
1723
        if (summary_find_msg_by_msgnum(summaryview, msginfo->msgnum, &iter)) {
1724
                summary_select_row(summaryview, &iter,
1725
                           messageview_is_visible(summaryview->messageview),
1726
                           TRUE);
1727
                return TRUE;
1728
        }
1729

    
1730
        return FALSE;
1731
}
1732

    
1733
MsgInfo *summary_get_msginfo_by_msgnum(SummaryView *summaryview, guint msgnum)
1734
{
1735
        GtkTreeIter iter;
1736
        MsgInfo *msginfo = NULL;
1737

    
1738
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1739
                GET_MSG_INFO(msginfo, &iter);
1740

    
1741
        return msginfo;
1742
}
1743

    
1744
static gboolean summary_select_true_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean cur_selected, gpointer data)
1745
{
1746
        return TRUE;
1747
}
1748

    
1749
/**
1750
 * summary_select_row:
1751
 * @summaryview: Summary view.
1752
 * @node: Summary tree node.
1753
 * @display_msg: TRUE to display the selected message.
1754
 * @do_refresh: TRUE to refresh the widget.
1755
 *
1756
 * Select @node (bringing it into view by scrolling and expanding its
1757
 * thread, if necessary) and unselect all others.  If @display_msg is
1758
 * TRUE, display the corresponding message in the message view.
1759
 * If @do_refresh is TRUE, the widget is refreshed.
1760
 **/
1761
void summary_select_row(SummaryView *summaryview, GtkTreeIter *iter,
1762
                         gboolean display_msg, gboolean do_refresh)
1763
{
1764
        GtkTreePath *path;
1765

    
1766
        if (!iter)
1767
                return;
1768

    
1769
        gtkut_tree_view_expand_parent_all
1770
                (GTK_TREE_VIEW(summaryview->treeview), iter);
1771

    
1772
        summaryview->display_msg = display_msg;
1773
        path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
1774
                                       iter);
1775
        if (!display_msg)
1776
                gtk_tree_selection_set_select_function(summaryview->selection,
1777
                                                       summary_select_true_func,
1778
                                                       summaryview, NULL);
1779
        gtk_tree_view_set_cursor(GTK_TREE_VIEW(summaryview->treeview), path,
1780
                                 NULL, FALSE);
1781
        if (!display_msg)
1782
                gtk_tree_selection_set_select_function(summaryview->selection,
1783
                                                       summary_select_func,
1784
                                                       summaryview, NULL);
1785
        if (do_refresh) {
1786
                GTK_EVENTS_FLUSH();
1787
                gtk_tree_view_scroll_to_cell
1788
                        (GTK_TREE_VIEW(summaryview->treeview),
1789
                         path, NULL, TRUE, 0.5, 0.0);
1790
        } else {
1791
                gtkut_tree_view_scroll_to_cell
1792
                        (GTK_TREE_VIEW(summaryview->treeview), path,
1793
                         !summaryview->on_button_press);
1794
        }
1795

    
1796
        gtk_tree_path_free(path);
1797
}
1798

    
1799
static void summary_scroll_to_selected(SummaryView *summaryview,
1800
                                       gboolean align_center)
1801
{
1802
        GtkTreePath *path;
1803

    
1804
        if (!summaryview->selected)
1805
                return;
1806

    
1807
        path = gtk_tree_row_reference_get_path(summaryview->selected);
1808
        if (path) {
1809
                if (align_center)
1810
                        gtk_tree_view_scroll_to_cell
1811
                                (GTK_TREE_VIEW(summaryview->treeview),
1812
                                 path, NULL, TRUE, 0.5, 0.0);
1813
                else
1814
                        gtkut_tree_view_scroll_to_cell
1815
                                (GTK_TREE_VIEW(summaryview->treeview), path,
1816
                                 FALSE);
1817
                gtk_tree_path_free(path);
1818
        }
1819
}
1820

    
1821
static MsgInfo *summary_get_msginfo(SummaryView *summaryview,
1822
                                    GtkTreeRowReference *row)
1823
{
1824
        GtkTreeIter iter;
1825
        MsgInfo *msginfo = NULL;
1826

    
1827
        if (!row)
1828
                return 0;
1829
        if (!gtkut_tree_row_reference_get_iter
1830
                (GTK_TREE_MODEL(summaryview->store), row, &iter))
1831
                return 0;
1832

    
1833
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), &iter,
1834
                           S_COL_MSG_INFO, &msginfo, -1);
1835

    
1836
        return msginfo;
1837
}
1838

    
1839
static guint summary_get_msgnum(SummaryView *summaryview,
1840
                                GtkTreeRowReference *row)
1841
{
1842
        MsgInfo *msginfo;
1843

    
1844
        msginfo = summary_get_msginfo(summaryview, row);
1845
        if (!msginfo)
1846
                return 0;
1847

    
1848
        return msginfo->msgnum;
1849
}
1850

    
1851
static gboolean summary_find_prev_msg(SummaryView *summaryview,
1852
                                      GtkTreeIter *prev, GtkTreeIter *iter)
1853
{
1854
        GtkTreeIter iter_;
1855
        MsgInfo *msginfo;
1856
        gboolean valid = TRUE;
1857

    
1858
        if (!iter)
1859
                return FALSE;
1860

    
1861
        iter_ = *iter;
1862

    
1863
        while (valid) {
1864
                GET_MSG_INFO(msginfo, &iter_);
1865
                if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1866
                    !MSG_IS_DELETED(msginfo->flags)) {
1867
                        *prev = iter_;
1868
                        return TRUE;
1869
                }
1870
                valid = gtkut_tree_model_prev
1871
                        (GTK_TREE_MODEL(summaryview->store), &iter_);
1872
        }
1873

    
1874
        return FALSE;
1875
}
1876

    
1877
static gboolean summary_find_next_msg(SummaryView *summaryview,
1878
                                      GtkTreeIter *next, GtkTreeIter *iter)
1879
{
1880
        GtkTreeIter iter_;
1881
        MsgInfo *msginfo;
1882
        gboolean valid = TRUE;
1883

    
1884
        if (!iter)
1885
                return FALSE;
1886

    
1887
        iter_ = *iter;
1888

    
1889
        while (valid) {
1890
                GET_MSG_INFO(msginfo, &iter_);
1891
                if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1892
                    !MSG_IS_DELETED(msginfo->flags)) {
1893
                        *next = iter_;
1894
                        return TRUE;
1895
                }
1896
                valid = gtkut_tree_model_next
1897
                        (GTK_TREE_MODEL(summaryview->store), &iter_);
1898
        }
1899

    
1900
        return FALSE;
1901
}
1902

    
1903
static gboolean summary_find_nearest_msg(SummaryView *summaryview,
1904
                                         GtkTreeIter *target, GtkTreeIter *iter)
1905
{
1906
        gboolean valid;
1907

    
1908
        valid = summary_find_next_msg(summaryview, target, iter);
1909
        if (!valid)
1910
                valid = summary_find_prev_msg(summaryview, target, iter);
1911

    
1912
        return valid;
1913
}
1914

    
1915
static gboolean summary_find_prev_flagged_msg(SummaryView *summaryview,
1916
                                              GtkTreeIter *prev,
1917
                                              GtkTreeIter *iter,
1918
                                              MsgPermFlags flags,
1919
                                              gboolean start_from_prev)
1920
{
1921
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1922
        GtkTreeIter iter_;
1923
        MsgInfo *msginfo;
1924
        gboolean valid = TRUE;
1925

    
1926
        if (iter) {
1927
                iter_ = *iter;
1928
                if (start_from_prev)
1929
                        valid = gtkut_tree_model_prev(model, &iter_);
1930
        } else
1931
                valid = gtkut_tree_model_get_iter_last(model, &iter_);
1932

    
1933
        for (; valid == TRUE; valid = gtkut_tree_model_prev(model, &iter_)) {
1934
                GET_MSG_INFO(msginfo, &iter_);
1935
                if (msginfo && (msginfo->flags.perm_flags & flags) != 0) {
1936
                        *prev = iter_;
1937
                        return TRUE;
1938
                }
1939
        }
1940

    
1941
        return FALSE;
1942
}
1943

    
1944
static gboolean summary_find_next_flagged_msg(SummaryView *summaryview,
1945
                                              GtkTreeIter *next,
1946
                                              GtkTreeIter *iter,
1947
                                              MsgPermFlags flags,
1948
                                              gboolean start_from_next)
1949
{
1950
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1951
        GtkTreeIter iter_;
1952
        MsgInfo *msginfo;
1953
        gboolean valid = TRUE;
1954

    
1955
        if (iter) {
1956
                iter_ = *iter;
1957
                if (start_from_next)
1958
                        valid = gtkut_tree_model_next(model, &iter_);
1959
        } else
1960
                valid = gtk_tree_model_get_iter_first(model, &iter_);
1961

    
1962
        for (; valid == TRUE; valid = gtkut_tree_model_next(model, &iter_)) {
1963
                GET_MSG_INFO(msginfo, &iter_);
1964
                if (msginfo && (msginfo->flags.perm_flags & flags) != 0) {
1965
                        *next = iter_;
1966
                        return TRUE;
1967
                }
1968
        }
1969

    
1970
        return FALSE;
1971
}
1972

    
1973
static gboolean summary_find_msg_by_msgnum(SummaryView *summaryview,
1974
                                           guint msgnum, GtkTreeIter *found)
1975
{
1976
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1977
        GtkTreeIter iter;
1978
        MsgInfo *msginfo;
1979
        gboolean valid;
1980

    
1981
        for (valid = gtk_tree_model_get_iter_first(model, &iter);
1982
             valid == TRUE; valid = gtkut_tree_model_next(model, &iter)) {
1983
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1984
                if (msginfo && msginfo->msgnum == msgnum) {
1985
                        *found = iter;
1986
                        return TRUE;
1987
                }
1988
        }
1989

    
1990
        return FALSE;
1991
}
1992

    
1993
static void summary_update_display_state(SummaryView *summaryview,
1994
                                         guint disp_msgnum, guint sel_msgnum)
1995
{
1996
        GtkTreeIter iter;
1997

    
1998
        if (summary_find_msg_by_msgnum(summaryview, disp_msgnum, &iter)) {
1999
                GtkTreePath *path;
2000
                GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2001

    
2002
                path = gtk_tree_model_get_path(model, &iter);
2003
                gtk_tree_row_reference_free(summaryview->displayed);
2004
                summaryview->displayed =
2005
                        gtk_tree_row_reference_new(model, path);
2006
                gtk_tree_path_free(path);
2007
        } else
2008
                messageview_clear(summaryview->messageview);
2009

    
2010
        summary_select_by_msgnum(summaryview, sel_msgnum);
2011
}
2012

    
2013
static guint attract_hash_func(gconstpointer key)
2014
{
2015
        gchar str[BUFFSIZE];
2016
        gchar *p;
2017
        guint h;
2018

    
2019
        strncpy2(str, (const gchar *)key, sizeof(str));
2020
        trim_subject_for_compare(str);
2021

    
2022
        p = str;
2023
        h = *p;
2024

    
2025
        if (h) {
2026
                for (p += 1; *p != '\0'; p++)
2027
                        h = (h << 5) - h + *p;
2028
        }
2029

    
2030
        return h;
2031
}
2032

    
2033
static gint attract_compare_func(gconstpointer a, gconstpointer b)
2034
{
2035
        return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2036
}
2037

    
2038
void summary_attract_by_subject(SummaryView *summaryview)
2039
{
2040
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2041
        GtkTreeIter iter;
2042
        MsgInfo *msginfo, *dest_msginfo;
2043
        GHashTable *subject_table, *order_table;
2044
        GSList *mlist = NULL, *list, *dest, *last = NULL, *next = NULL;
2045
        gboolean valid;
2046
        gint count, i;
2047
        gint *new_order;
2048

    
2049
        if (!summaryview->folder_item)
2050
                return;
2051
        if (summaryview->folder_item->sort_key != SORT_BY_NONE)
2052
                return;
2053

    
2054
        valid = gtk_tree_model_get_iter_first(model, &iter);
2055
        if (!valid)
2056
                return;
2057

    
2058
        debug_print("Attracting messages by subject...");
2059
        STATUSBAR_PUSH(summaryview->mainwin,
2060
                       _("Attracting messages by subject..."));
2061

    
2062
        main_window_cursor_wait(summaryview->mainwin);
2063

    
2064
        order_table = g_hash_table_new(NULL, NULL);
2065

    
2066
        for (count = 1; valid == TRUE; ++count) {
2067
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
2068
                g_hash_table_insert(order_table, msginfo,
2069
                                    GINT_TO_POINTER(count));
2070
                mlist = g_slist_prepend(mlist, msginfo);
2071
                valid = gtk_tree_model_iter_next(model, &iter);
2072
        }
2073
        --count;
2074

    
2075
        mlist = g_slist_reverse(mlist);
2076

    
2077
        subject_table = g_hash_table_new(attract_hash_func,
2078
                                         attract_compare_func);
2079

    
2080
        for (list = mlist; list != NULL; list = next) {
2081
                msginfo = (MsgInfo *)list->data;
2082

    
2083
                next = list->next;
2084

    
2085
                if (!msginfo->subject) {
2086
                        last = list;
2087
                        continue;
2088
                }
2089

    
2090
                /* find attracting node */
2091
                dest = g_hash_table_lookup(subject_table, msginfo->subject);
2092

    
2093
                if (dest) {
2094
                        dest_msginfo = (MsgInfo *)dest->data;
2095

    
2096
                        /* if the time difference is more than 30 days,
2097
                           don't attract */
2098
                        if (ABS(msginfo->date_t - dest_msginfo->date_t)
2099
                            > 60 * 60 * 24 * 30) {
2100
                                last = list;
2101
                                continue;
2102
                        }
2103

    
2104
                        if (dest->next != list) {
2105
                                last->next = list->next;
2106
                                list->next = dest->next;
2107
                                dest->next = list;
2108
                        } else
2109
                                last = list;
2110
                } else
2111
                        last = list;
2112

    
2113
                g_hash_table_replace(subject_table, msginfo->subject, list);
2114
        }
2115

    
2116
        g_hash_table_destroy(subject_table);
2117

    
2118
        new_order = g_new(gint, count);
2119
        for (list = mlist, i = 0; list != NULL; list = list->next, ++i) {
2120
                gint old_pos;
2121

    
2122
                msginfo = (MsgInfo *)list->data;
2123

    
2124
                old_pos = GPOINTER_TO_INT
2125
                        (g_hash_table_lookup(order_table, msginfo));
2126
                new_order[i] = old_pos - 1;
2127
        }
2128
        gtk_tree_store_reorder(GTK_TREE_STORE(model), NULL, new_order);
2129
        g_free(new_order);
2130

    
2131
        g_slist_free(mlist);
2132
        g_hash_table_destroy(order_table);
2133

    
2134
        summaryview->folder_item->cache_dirty = TRUE;
2135
        summary_selection_list_free(summaryview);
2136
        summary_update_msg_list(summaryview);
2137

    
2138
        summary_scroll_to_selected(summaryview, TRUE);
2139

    
2140
        debug_print("done.\n");
2141
        STATUSBAR_POP(summaryview->mainwin);
2142

    
2143
        main_window_cursor_normal(summaryview->mainwin);
2144
}
2145

    
2146
static void summary_update_status(SummaryView *summaryview)
2147
{
2148
        GSList *cur;
2149
        MsgInfo *msginfo;
2150
        gint64 total_size = 0;
2151
        gint deleted = 0, moved = 0, copied = 0;
2152
        gint64 flt_total_size = 0;
2153
        gint flt_deleted = 0, flt_moved = 0, flt_copied = 0;
2154
        gint flt_new = 0, flt_unread = 0, flt_total = 0;
2155

    
2156
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2157
                msginfo = (MsgInfo *)cur->data;
2158

    
2159
                if (MSG_IS_DELETED(msginfo->flags))
2160
                        deleted++;
2161
                if (MSG_IS_MOVE(msginfo->flags))
2162
                        moved++;
2163
                if (MSG_IS_COPY(msginfo->flags))
2164
                        copied++;
2165
                total_size += msginfo->size;
2166
        }
2167

    
2168
        for (cur = summaryview->flt_mlist; cur != NULL; cur = cur->next) {
2169
                msginfo = (MsgInfo *)cur->data;
2170

    
2171
                if (MSG_IS_DELETED(msginfo->flags))
2172
                        flt_deleted++;
2173
                if (MSG_IS_MOVE(msginfo->flags))
2174
                        flt_moved++;
2175
                if (MSG_IS_COPY(msginfo->flags))
2176
                        flt_copied++;
2177
                if (MSG_IS_NEW(msginfo->flags))
2178
                        flt_new++;
2179
                if (MSG_IS_UNREAD(msginfo->flags))
2180
                        flt_unread++;
2181
                flt_total++;
2182
                flt_total_size += msginfo->size;
2183
        }
2184

    
2185
        summaryview->total_size = total_size;
2186
        summaryview->deleted = deleted;
2187
        summaryview->moved = moved;
2188
        summaryview->copied = copied;
2189
        summaryview->total_flt_msg_size = flt_total_size;
2190
        summaryview->flt_msg_total = flt_total;
2191
        summaryview->flt_deleted = flt_deleted;
2192
        summaryview->flt_moved = flt_moved;
2193
        summaryview->flt_copied = flt_copied;
2194
        summaryview->flt_new = flt_new;
2195
        summaryview->flt_unread = flt_unread;
2196
}
2197

    
2198
static void summary_status_show(SummaryView *summaryview)
2199
{
2200
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2201
        GString *str;
2202
        gchar *name;
2203
        GList *rowlist, *cur;
2204
        guint n_selected = 0;
2205
        gint64 sel_size = 0;
2206
        MsgInfo *msginfo;
2207
        gint deleted, moved, copied;
2208
        gint new, unread, total;
2209
        gint64 total_size;
2210

    
2211
        if (!summaryview->folder_item) {
2212
                gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
2213
                gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
2214
                gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
2215
                return;
2216
        }
2217

    
2218
        rowlist = summary_get_selected_rows(summaryview);
2219
        for (cur = rowlist; cur != NULL; cur = cur->next) {
2220
                GtkTreeIter iter;
2221
                GtkTreePath *path = (GtkTreePath *)cur->data;
2222

    
2223
                if (gtk_tree_model_get_iter(model, &iter, path)) {
2224
                        gtk_tree_model_get(model, &iter,
2225
                                           S_COL_MSG_INFO, &msginfo, -1);
2226
                        sel_size += msginfo->size;
2227
                        n_selected++;
2228
                }
2229
        }
2230

    
2231
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) {
2232
                gchar *group;
2233
                group = get_abbrev_newsgroup_name
2234
                        (g_basename(summaryview->folder_item->path),
2235
                         prefs_common.ng_abbrev_len);
2236
                name = trim_string_before(group, 32);
2237
                g_free(group);
2238
        } else
2239
                name = trim_string_before(summaryview->folder_item->path, 32);
2240
        gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), name);
2241
        g_free(name);
2242

    
2243
        if (summaryview->on_filter) {
2244
                deleted = summaryview->flt_deleted;
2245
                moved = summaryview->flt_moved;
2246
                copied = summaryview->flt_copied;
2247
        } else {
2248
                deleted = summaryview->deleted;
2249
                moved = summaryview->moved;
2250
                copied = summaryview->copied;
2251
        }
2252

    
2253
        str = g_string_sized_new(128);
2254

    
2255
        if (n_selected)
2256
                g_string_append_printf(str, "%d%s (%s)", n_selected,
2257
                                       _(" item(s) selected"),
2258
                                       to_human_readable(sel_size));
2259
        if (str->len > 0 && (deleted || moved || copied))
2260
                g_string_append(str, "    ");
2261
        if (deleted)
2262
                g_string_append_printf(str, _("%d deleted"), deleted);
2263
        if (moved)
2264
                g_string_append_printf(str, _("%s%d moved"),
2265
                                       deleted ? _(", ") : "", moved);
2266
        if (copied)
2267
                g_string_append_printf(str, _("%s%d copied"),
2268
                                       deleted || moved ? _(", ") : "", copied);
2269

    
2270
        gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str->str);
2271
        g_string_truncate(str, 0);
2272

    
2273
        new = summaryview->folder_item->new;
2274
        unread = summaryview->folder_item->unread;
2275
        total = summaryview->folder_item->total;
2276
        total_size = summaryview->total_size;
2277

    
2278
        if (summaryview->on_filter) {
2279
                gint f_new, f_unread, f_total;
2280
                gint64 f_total_size;
2281
                gchar f_ts[16], ts[16];
2282

    
2283
                f_new = summaryview->flt_new;
2284
                f_unread = summaryview->flt_unread;
2285
                f_total = summaryview->flt_msg_total;
2286
                f_total_size = summaryview->total_flt_msg_size;
2287

    
2288
                g_string_printf(str, _("%d/%d new, %d/%d unread, %d/%d total"),
2289
                                f_new, new, f_unread, unread, f_total, total);
2290
                if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
2291
                        g_string_append_printf(str, " (%s/%s)",
2292
                                               to_human_readable_buf(f_ts, sizeof(f_ts), f_total_size), to_human_readable_buf(ts, sizeof(ts), total_size));
2293
                }
2294
        } else {
2295
                if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
2296
                        g_string_printf(str,
2297
                                        _("%d new, %d unread, %d total (%s)"),
2298
                                        new, unread, total,
2299
                                        to_human_readable(total_size));
2300
                } else {
2301
                        g_string_printf(str, _("%d new, %d unread, %d total"),
2302
                                        new, unread, total);
2303
                }
2304
        }
2305

    
2306
        gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str->str);
2307
        g_string_free(str, TRUE);
2308

    
2309
        folderview_update_opened_msg_num(summaryview->folderview);
2310
}
2311

    
2312
void summary_sort(SummaryView *summaryview,
2313
                  FolderSortKey sort_key, FolderSortType sort_type)
2314
{
2315
        FolderItem *item = summaryview->folder_item;
2316
        GtkTreeSortable *sortable = GTK_TREE_SORTABLE(summaryview->store);
2317
        SummaryColumnType col_type, prev_col_type;
2318
        GtkTreeViewColumn *column;
2319

    
2320
        g_return_if_fail(sort_key >= SORT_BY_NONE && sort_key <= SORT_BY_TO);
2321

    
2322
        if (!item || !item->path || !item->parent || item->no_select) return;
2323

    
2324
        if (item->sort_key != sort_key || item->sort_type != sort_type)
2325
                item->cache_dirty = TRUE;
2326

    
2327
        col_type = sort_key_to_col[sort_key];
2328
        prev_col_type = sort_key_to_col[item->sort_key];
2329

    
2330
        if (col_type == -1) {
2331
                item->sort_key = SORT_BY_NONE;
2332
                item->sort_type = SORT_ASCENDING;
2333
                summary_unset_sort_column_id(summaryview);
2334
                summary_set_menu_sensitive(summaryview);
2335
                return;
2336
        }
2337

    
2338
        debug_print("Sorting summary by key: %d...\n", sort_key);
2339
        STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
2340

    
2341
        main_window_cursor_wait(summaryview->mainwin);
2342

    
2343
        item->sort_key = sort_key;
2344
        item->sort_type = sort_type;
2345

    
2346
        gtk_tree_sortable_set_sort_column_id(sortable, col_type,
2347
                                             (GtkSortType)sort_type);
2348

    
2349
        if (prev_col_type != -1 && col_type != prev_col_type &&
2350
            prev_col_type < N_SUMMARY_VISIBLE_COLS) {
2351
                column = summaryview->columns[prev_col_type];
2352
                column->sort_column_id = -1;
2353
                gtk_tree_view_column_set_sort_indicator(column, FALSE);
2354
        }
2355
        if (col_type != S_COL_MARK && col_type != S_COL_UNREAD &&
2356
            col_type != S_COL_MIME && col_type < N_SUMMARY_VISIBLE_COLS) {
2357
                column = summaryview->columns[col_type];
2358
                column->sort_column_id = col_type;
2359
                gtk_tree_view_column_set_sort_indicator(column, TRUE);
2360
                gtk_tree_view_column_set_sort_order(column,
2361
                                                    (GtkSortType)sort_type);
2362
        }
2363

    
2364
        summary_selection_list_free(summaryview);
2365
        if (summaryview->all_mlist)
2366
                summary_update_msg_list(summaryview);
2367

    
2368
        summary_set_menu_sensitive(summaryview);
2369

    
2370
        summary_scroll_to_selected(summaryview, TRUE);
2371

    
2372
        debug_print("done.\n");
2373
        STATUSBAR_POP(summaryview->mainwin);
2374

    
2375
        main_window_cursor_normal(summaryview->mainwin);
2376
}
2377

    
2378
static gboolean summary_have_unread_children(SummaryView *summaryview,
2379
                                             GtkTreeIter *iter)
2380
{
2381
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2382
        GtkTreeIter iter_;
2383
        MsgInfo *msginfo;
2384
        gboolean valid;
2385

    
2386
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
2387
        if (MSG_IS_UNREAD(msginfo->flags))
2388
                return TRUE;
2389

    
2390
        valid = gtk_tree_model_iter_children(model, &iter_, iter);
2391

    
2392
        while (valid) {
2393
                if (summary_have_unread_children(summaryview, &iter_))
2394
                        return TRUE;
2395

    
2396
                valid = gtk_tree_model_iter_next(model, &iter_);
2397
        }
2398

    
2399
        return FALSE;
2400
}
2401

    
2402
static void summary_set_row(SummaryView *summaryview, GtkTreeIter *iter,
2403
                            MsgInfo *msginfo)
2404
{
2405
        GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2406
        gchar date_modified[80];
2407
        const gchar *date_s;
2408
        gchar *sw_from_s = NULL;
2409
        gchar *subject_s = NULL;
2410
        gchar *to_s = NULL;
2411
        GdkPixbuf *mark_pix = NULL;
2412
        GdkPixbuf *unread_pix = NULL;
2413
        GdkPixbuf *mime_pix = NULL;
2414
        GdkColor *foreground = NULL;
2415
        PangoWeight weight = PANGO_WEIGHT_NORMAL;
2416
        MsgFlags flags;
2417
        GdkColor color;
2418
        gint color_val;
2419

    
2420
        if (!msginfo) {
2421
                GET_MSG_INFO(msginfo, iter);
2422
        }
2423

    
2424
        if (msginfo->date_t) {
2425
                procheader_date_get_localtime(date_modified,
2426
                                              sizeof(date_modified),
2427
                                              msginfo->date_t);
2428
                date_s = date_modified;
2429
        } else if (msginfo->date)
2430
                date_s = msginfo->date;
2431
        else
2432
                date_s = _("(No Date)");
2433
        if (prefs_common.swap_from && msginfo->from && msginfo->to) {
2434
                gchar from[BUFFSIZE];
2435

    
2436
                strncpy2(from, msginfo->from, sizeof(from));
2437
                extract_address(from);
2438
                if (account_address_exist(from))
2439
                        sw_from_s = g_strconcat("-->", msginfo->to, NULL);
2440
        }
2441

    
2442
        if (msginfo->subject && *msginfo->subject) {
2443
                if (msginfo->folder && msginfo->folder->trim_summary_subject) {
2444
                        subject_s = g_strdup(msginfo->subject);
2445
                        trim_subject(subject_s);
2446
                }
2447
        }
2448

    
2449
        if (msginfo->to)
2450
                to_s = procheader_get_toname(msginfo->to);
2451

    
2452
        flags = msginfo->flags;
2453

    
2454
        /* set flag pixbufs */
2455
        if (MSG_IS_DELETED(flags)) {
2456
                mark_pix = deleted_pixbuf;
2457
                foreground = &summaryview->color_dim;
2458
        } else if (MSG_IS_MOVE(flags)) {
2459
                /* mark_pix = move_pixbuf; */
2460
                foreground = &summaryview->color_marked;
2461
        } else if (MSG_IS_COPY(flags)) {
2462
                /* mark_pix = copy_pixbuf; */
2463
                foreground = &summaryview->color_marked;
2464
        } else if (MSG_IS_MARKED(flags))
2465
                mark_pix = mark_pixbuf;
2466

    
2467
        if (MSG_IS_NEW(flags))
2468
                unread_pix = new_pixbuf;
2469
        else if (MSG_IS_UNREAD(flags))
2470
                unread_pix = unread_pixbuf;
2471
        else if (MSG_IS_REPLIED(flags))
2472
                unread_pix = replied_pixbuf;
2473
        else if (MSG_IS_FORWARDED(flags))
2474
                unread_pix = forwarded_pixbuf;
2475

    
2476
        if (MSG_IS_MIME(flags))
2477
                mime_pix = clip_pixbuf;
2478

    
2479
        if (prefs_common.bold_unread) {
2480
                if (MSG_IS_UNREAD(flags))
2481
                        weight = PANGO_WEIGHT_BOLD;
2482
                else if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store),
2483
                                                       iter)) {
2484
                        GtkTreePath *path;
2485

    
2486
                        path = gtk_tree_model_get_path
2487
                                (GTK_TREE_MODEL(store), iter);
2488
                        if (!gtk_tree_view_row_expanded
2489
                                (GTK_TREE_VIEW(summaryview->treeview), path) &&
2490
                            summary_have_unread_children(summaryview, iter))
2491
                                weight = PANGO_WEIGHT_BOLD;
2492
                        gtk_tree_path_free(path);
2493
                }
2494
        }
2495

    
2496
        color_val = MSG_GET_COLORLABEL_VALUE(flags);
2497
        if (color_val != 0) {
2498
                color = colorlabel_get_color(color_val - 1);
2499
                foreground = &color;
2500
        }
2501

    
2502
        gtk_tree_store_set(store, iter,
2503
                           S_COL_MARK, mark_pix,
2504
                           S_COL_UNREAD, unread_pix,
2505
                           S_COL_MIME, mime_pix,
2506
                           S_COL_SUBJECT, subject_s ? subject_s :
2507
                                             msginfo->subject && *msginfo->subject ? msginfo->subject :
2508
                                          _("(No Subject)"),
2509
                           S_COL_FROM, sw_from_s ? sw_from_s :
2510
                                          msginfo->fromname ? msginfo->fromname :
2511
                                       _("(No From)"),
2512
                           S_COL_DATE, date_s,
2513
                           S_COL_SIZE, to_human_readable(msginfo->size),
2514
                           S_COL_NUMBER, msginfo->msgnum,
2515
                           S_COL_TO, to_s ? to_s : "",
2516

    
2517
                           S_COL_MSG_INFO, msginfo,
2518

    
2519
                           S_COL_LABEL, color_val,
2520

    
2521
                           S_COL_FOREGROUND, foreground,
2522
                           S_COL_BOLD, weight,
2523
                           -1);
2524

    
2525
        if (to_s)
2526
                g_free(to_s);
2527
        if (subject_s)
2528
                g_free(subject_s);
2529
        if (sw_from_s)
2530
                g_free(sw_from_s);
2531
}
2532

    
2533
static void summary_insert_gnode(SummaryView *summaryview, GtkTreeStore *store,
2534
                                 GtkTreeIter *iter, GtkTreeIter *parent,
2535
                                 GtkTreeIter *sibling, GNode *gnode)
2536
{
2537
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
2538

    
2539
        if (parent && !sibling)
2540
                gtk_tree_store_append(store, iter, parent);
2541
        else
2542
                gtk_tree_store_insert_after(store, iter, parent, sibling);
2543

    
2544
        summary_set_row(summaryview, iter, msginfo);
2545

    
2546
        if (!parent) {
2547
                guint tdate;
2548

    
2549
                tdate = procmsg_get_thread_date(gnode);
2550
                gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2551
        }
2552

    
2553
        for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2554
                GtkTreeIter child;
2555

    
2556
                summary_insert_gnode(summaryview, store, &child, iter, NULL,
2557
                                     gnode);
2558
        }
2559
}
2560

    
2561
static void summary_insert_gnode_before(SummaryView *summaryview,
2562
                                        GtkTreeStore *store,
2563
                                        GtkTreeIter *iter, GtkTreeIter *parent,
2564
                                        GtkTreeIter *sibling, GNode *gnode)
2565
{
2566
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
2567

    
2568
        gtk_tree_store_insert_before(store, iter, parent, sibling);
2569

    
2570
        summary_set_row(summaryview, iter, msginfo);
2571

    
2572
        if (!parent) {
2573
                guint tdate;
2574

    
2575
                tdate = procmsg_get_thread_date(gnode);
2576
                gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2577
        }
2578

    
2579
        for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2580
                GtkTreeIter child;
2581

    
2582
                summary_insert_gnode_before(summaryview, store, &child, iter,
2583
                                            NULL, gnode);
2584
        }
2585
}
2586

    
2587
static void summary_set_tree_model_from_list(SummaryView *summaryview,
2588
                                             GSList *mlist)
2589
{
2590
        GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2591
        GtkTreeIter iter;
2592
        MsgInfo *msginfo;
2593
        GSList *cur;
2594

    
2595
        debug_print(_("\tSetting summary from message data..."));
2596
        STATUSBAR_PUSH(summaryview->mainwin,
2597
                       _("Setting summary from message data..."));
2598
        gdk_flush();
2599

    
2600
        /* temporarily remove the model for speed up */
2601
        gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL);
2602

    
2603
        if (summaryview->folder_item->threaded) {
2604
                GNode *root, *gnode;
2605

    
2606
                root = procmsg_get_thread_tree(mlist);
2607

    
2608
                for (gnode = root->children; gnode != NULL;
2609
                     gnode = gnode->next) {
2610
                        summary_insert_gnode
2611
                                (summaryview, store, &iter, NULL, NULL, gnode);
2612
                        if (gnode->children && !prefs_common.expand_thread &&
2613
                            prefs_common.bold_unread &&
2614
                            summary_have_unread_children(summaryview, &iter)) {
2615
                                gtk_tree_store_set(store, &iter,
2616
                                                   S_COL_BOLD,
2617
                                                   PANGO_WEIGHT_BOLD, -1);
2618
                        }
2619
                }
2620

    
2621
                g_node_destroy(root);
2622

    
2623
                for (cur = mlist; cur != NULL; cur = cur->next) {
2624
                        msginfo = (MsgInfo *)cur->data;
2625

    
2626
                        if (MSG_IS_DELETED(msginfo->flags))
2627
                                summaryview->deleted++;
2628
                        if (MSG_IS_MOVE(msginfo->flags))
2629
                                summaryview->moved++;
2630
                        if (MSG_IS_COPY(msginfo->flags))
2631
                                summaryview->copied++;
2632
                        summaryview->total_size += msginfo->size;
2633
                }
2634
        } else {
2635
                GSList *rev_mlist;
2636
                GtkTreeIter iter;
2637

    
2638
                rev_mlist = g_slist_reverse(g_slist_copy(mlist));
2639
                for (cur = rev_mlist; cur != NULL; cur = cur->next) {
2640
                        msginfo = (MsgInfo *)cur->data;
2641

    
2642
                        gtk_tree_store_prepend(store, &iter, NULL);
2643
                        summary_set_row(summaryview, &iter, msginfo);
2644

    
2645
                        if (MSG_IS_DELETED(msginfo->flags))
2646
                                summaryview->deleted++;
2647
                        if (MSG_IS_MOVE(msginfo->flags))
2648
                                summaryview->moved++;
2649
                        if (MSG_IS_COPY(msginfo->flags))
2650
                                summaryview->copied++;
2651
                        summaryview->total_size += msginfo->size;
2652
                }
2653
                g_slist_free(rev_mlist);
2654
        }
2655

    
2656
        gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview),
2657
                                GTK_TREE_MODEL(store));
2658

    
2659
        if (summaryview->folder_item->threaded && prefs_common.expand_thread)
2660
                gtk_tree_view_expand_all
2661
                        (GTK_TREE_VIEW(summaryview->treeview));
2662

    
2663
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
2664
                summary_sort(summaryview, summaryview->folder_item->sort_key,
2665
                             summaryview->folder_item->sort_type);
2666
        }
2667

    
2668
        debug_print(_("done.\n"));
2669
        STATUSBAR_POP(summaryview->mainwin);
2670
}
2671

    
2672
struct wcachefp
2673
{
2674
        FILE *cache_fp;
2675
        FILE *mark_fp;
2676
};
2677

    
2678
gint summary_write_cache(SummaryView *summaryview)
2679
{
2680
        struct wcachefp fps;
2681
        FolderItem *item;
2682
        gchar *buf;
2683
        GSList *cur;
2684

    
2685
        item = summaryview->folder_item;
2686
        if (!item || !item->path)
2687
                return -1;
2688
        if (item->mark_queue)
2689
                item->mark_dirty = TRUE;
2690
        if (!item->cache_dirty && !item->mark_dirty)
2691
                return 0;
2692

    
2693
        if (item->cache_dirty) {
2694
                fps.cache_fp = procmsg_open_cache_file(item, DATA_WRITE);
2695
                if (fps.cache_fp == NULL)
2696
                        return -1;
2697
                item->mark_dirty = TRUE;
2698
        } else
2699
                fps.cache_fp = NULL;
2700

    
2701
        if (item->mark_dirty && item->stype != F_VIRTUAL) {
2702
                fps.mark_fp = procmsg_open_mark_file(item, DATA_WRITE);
2703
                if (fps.mark_fp == NULL) {
2704
                        if (fps.cache_fp)
2705
                                fclose(fps.cache_fp);
2706
                        return -1;
2707
                }
2708
        } else
2709
                fps.mark_fp = NULL;
2710

    
2711
        if (item->cache_dirty) {
2712
                buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2713
                                      item->path);
2714
                debug_print("%s", buf);
2715
                STATUSBAR_PUSH(summaryview->mainwin, buf);
2716
                gdk_flush();
2717
                g_free(buf);
2718
        }
2719

    
2720
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2721
                MsgInfo *msginfo = (MsgInfo *)cur->data;
2722

    
2723
                if (msginfo->folder && msginfo->folder->mark_queue != NULL) {
2724
                        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
2725
                }
2726
                if (fps.cache_fp)
2727
                        procmsg_write_cache(msginfo, fps.cache_fp);
2728
                if (fps.mark_fp)
2729
                        procmsg_write_flags(msginfo, fps.mark_fp);
2730
        }
2731

    
2732
        if (item->cache_queue)
2733
                procmsg_flush_cache_queue(item, fps.cache_fp);
2734
        if (item->mark_queue)
2735
                procmsg_flush_mark_queue(item, fps.mark_fp);
2736

    
2737
        item->unmarked_num = 0;
2738

    
2739
        if (fps.cache_fp)
2740
                fclose(fps.cache_fp);
2741
        if (fps.mark_fp)
2742
                fclose(fps.mark_fp);
2743

    
2744
        if (item->stype == F_VIRTUAL) {
2745
                GSList *mlist;
2746

    
2747
                mlist = summary_get_changed_msg_list(summaryview);
2748
                if (mlist) {
2749
                        procmsg_write_flags_for_multiple_folders(mlist);
2750
                        g_slist_free(mlist);
2751
                        folderview_update_all_updated(FALSE);
2752
                }
2753
        }
2754

    
2755
        debug_print(_("done.\n"));
2756

    
2757
        if (item->cache_dirty) {
2758
                STATUSBAR_POP(summaryview->mainwin);
2759
        }
2760

    
2761
        item->cache_dirty = item->mark_dirty = FALSE;
2762

    
2763
        return 0;
2764
}
2765

    
2766
static gboolean summary_row_is_displayed(SummaryView *summaryview,
2767
                                         GtkTreeIter *iter)
2768
{
2769
        GtkTreePath *disp_path, *path;
2770
        gint ret;
2771

    
2772
        if (!summaryview->displayed || !iter)
2773
                return FALSE;
2774

    
2775
        disp_path = gtk_tree_row_reference_get_path(summaryview->displayed);
2776
        if (!disp_path)
2777
                return FALSE;
2778

    
2779
        path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
2780
                                       iter);
2781
        if (!path) {
2782
                gtk_tree_path_free(disp_path);
2783
                return FALSE;
2784
        }
2785

    
2786
        ret = gtk_tree_path_compare(disp_path, path);
2787
        gtk_tree_path_free(path);
2788
        gtk_tree_path_free(disp_path);
2789

    
2790
        return (ret == 0);
2791
}
2792

    
2793
static void summary_display_msg(SummaryView *summaryview, GtkTreeIter *iter)
2794
{
2795
        summary_display_msg_full(summaryview, iter, FALSE, FALSE, FALSE);
2796
}
2797

    
2798
static void summary_display_msg_full(SummaryView *summaryview,
2799
                                     GtkTreeIter *iter,
2800
                                     gboolean new_window, gboolean all_headers,
2801
                                     gboolean redisplay)
2802
{
2803
        GtkTreePath *path;
2804
        MsgInfo *msginfo = NULL;
2805
        gint val;
2806
        gboolean do_mark_read = FALSE;
2807

    
2808
        g_return_if_fail(iter != NULL);
2809

    
2810
        if (!new_window && !redisplay &&
2811
            summary_row_is_displayed(summaryview, iter))
2812
                return;
2813

    
2814
        if (summary_is_read_locked(summaryview)) return;
2815
        summary_lock(summaryview);
2816

    
2817
        STATUSBAR_POP(summaryview->mainwin);
2818

    
2819
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter,
2820
                           S_COL_MSG_INFO, &msginfo, -1);
2821

    
2822
        do_mark_read = prefs_common.always_mark_read_on_show_msg;
2823

    
2824
        if (new_window) {
2825
                MessageView *msgview;
2826

    
2827
                msgview = messageview_create_with_new_window();
2828
                val = messageview_show(msgview, msginfo, all_headers);
2829
                do_mark_read = TRUE;
2830
        } else {
2831
                MessageView *msgview = summaryview->messageview;
2832
                gboolean prev_mimeview;
2833

    
2834
                if (!messageview_is_visible(msgview)) {
2835
                        main_window_toggle_message_view(summaryview->mainwin);
2836
                        GTK_EVENTS_FLUSH();
2837
                }
2838
                prev_mimeview =
2839
                        messageview_get_selected_mime_part(msgview) != NULL;
2840

    
2841
                val = messageview_show(msgview, msginfo, all_headers);
2842
                if (prev_mimeview &&
2843
                    !messageview_get_selected_mime_part(msgview))
2844
                        gtk_widget_grab_focus(summaryview->treeview);
2845
        }
2846

    
2847
        if (val == 0 && do_mark_read) {
2848
                if (MSG_IS_NEW(msginfo->flags) ||
2849
                    MSG_IS_UNREAD(msginfo->flags)) {
2850
                        summary_mark_row_as_read(summaryview, iter);
2851
                        if (MSG_IS_IMAP(msginfo->flags))
2852
                                imap_msg_unset_perm_flags
2853
                                        (msginfo, MSG_NEW | MSG_UNREAD);
2854
                        summary_set_row(summaryview, iter, msginfo);
2855
                        summary_status_show(summaryview);
2856
                }
2857
        }
2858

    
2859
        path = gtk_tree_model_get_path
2860
                (GTK_TREE_MODEL(summaryview->store), iter);
2861
        if (!new_window) {
2862
                gtk_tree_row_reference_free(summaryview->displayed);
2863
                summaryview->displayed =
2864
                        gtk_tree_row_reference_new
2865
                                (GTK_TREE_MODEL(summaryview->store), path);
2866
        }
2867
        gtkut_tree_view_scroll_to_cell
2868
                (GTK_TREE_VIEW(summaryview->treeview), path,
2869
                 !summaryview->on_button_press);
2870
        gtk_tree_path_free(path);
2871

    
2872
        if (summaryview->folder_item->sort_key == SORT_BY_UNREAD)
2873
                summary_selection_list_free(summaryview);
2874

    
2875
        summary_set_menu_sensitive(summaryview);
2876
        main_window_set_toolbar_sensitive(summaryview->mainwin);
2877

    
2878
        trayicon_set_tooltip(NULL);
2879
        trayicon_set_notify(FALSE);
2880

    
2881
        statusbar_pop_all();
2882

    
2883
        summary_unlock(summaryview);
2884
}
2885

    
2886
void summary_display_msg_selected(SummaryView *summaryview,
2887
                                  gboolean new_window, gboolean all_headers)
2888
{
2889
        GtkTreeIter iter;
2890

    
2891
        if (summary_is_read_locked(summaryview)) return;
2892

    
2893
        if (summaryview->selected) {
2894
                if (gtkut_tree_row_reference_get_iter
2895
                        (GTK_TREE_MODEL(summaryview->store),
2896
                         summaryview->selected, &iter)) {
2897
                        summary_display_msg_full(summaryview, &iter,
2898
                                                 new_window, all_headers, TRUE);
2899
                }
2900
        }
2901
}
2902

    
2903
void summary_redisplay_msg(SummaryView *summaryview)
2904
{
2905
        GtkTreeIter iter;
2906

    
2907
        if (summaryview->displayed) {
2908
                if (gtkut_tree_row_reference_get_iter
2909
                        (GTK_TREE_MODEL(summaryview->store),
2910
                         summaryview->displayed, &iter)) {
2911
                        summary_display_msg_full(summaryview, &iter,
2912
                                                 FALSE, FALSE, TRUE);
2913
                }
2914
        }
2915
}
2916

    
2917
void summary_open_msg(SummaryView *summaryview)
2918
{
2919
        summary_display_msg_selected(summaryview, TRUE, FALSE);
2920
}
2921

    
2922
static void summary_activate_selected(SummaryView *summaryview)
2923
{
2924
        if (!summaryview->folder_item)
2925
                return;
2926

    
2927
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item))
2928
                summary_reedit(summaryview);
2929
        else
2930
                summary_open_msg(summaryview);
2931

    
2932
        summaryview->display_msg = FALSE;
2933
}
2934

    
2935
void summary_view_source(SummaryView *summaryview)
2936
{
2937
        GtkTreeIter iter;
2938
        MsgInfo *msginfo;
2939
        SourceWindow *srcwin;
2940

    
2941
        if (summaryview->selected) {
2942
                if (gtkut_tree_row_reference_get_iter
2943
                        (GTK_TREE_MODEL(summaryview->store),
2944
                         summaryview->selected, &iter)) {
2945
                        GET_MSG_INFO(msginfo, &iter);
2946

    
2947
                        srcwin = source_window_create();
2948
                        source_window_show_msg(srcwin, msginfo);
2949
                        source_window_show(srcwin);
2950
                }
2951
        }
2952
}
2953

    
2954
void summary_reedit(SummaryView *summaryview)
2955
{
2956
        GtkTreeIter iter;
2957
        MsgInfo *msginfo;
2958

    
2959
        if (!summaryview->selected) return;
2960
        if (!FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) return;
2961

    
2962
        if (gtkut_tree_row_reference_get_iter
2963
                (GTK_TREE_MODEL(summaryview->store),
2964
                 summaryview->selected, &iter)) {
2965
                GET_MSG_INFO(msginfo, &iter);
2966
                compose_reedit(msginfo);
2967
        }
2968
}
2969

    
2970
gboolean summary_step(SummaryView *summaryview, GtkScrollType type)
2971
{
2972
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2973
        GtkTreeIter iter;
2974

    
2975
        if (summary_is_read_locked(summaryview)) return FALSE;
2976

    
2977
        if (!gtkut_tree_row_reference_get_iter
2978
                (model, summaryview->selected, &iter))
2979
                return FALSE;
2980

    
2981
        if (type == GTK_SCROLL_STEP_FORWARD) {
2982
                if (!gtkut_tree_model_next(model, &iter))
2983
                        return FALSE;
2984
        } else {
2985
                if (!gtkut_tree_model_prev(model, &iter))
2986
                        return FALSE;
2987
        }
2988

    
2989
        summary_select_row(summaryview, &iter,
2990
                           messageview_is_visible(summaryview->messageview),
2991
                           FALSE);
2992

    
2993
        return TRUE;
2994
}
2995

    
2996
void summary_toggle_view(SummaryView *summaryview)
2997
{
2998
        if (!messageview_is_visible(summaryview->messageview) &&
2999
            summaryview->selected) {
3000
                summary_display_msg_selected(summaryview, FALSE, FALSE);
3001
                summary_mark_displayed_read(summaryview, NULL);
3002
        } else
3003
                main_window_toggle_message_view(summaryview->mainwin);
3004
}
3005

    
3006
void summary_update_selected_rows(SummaryView *summaryview)
3007
{
3008
        GList *rows, *cur;
3009
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3010
        GtkTreeIter iter;
3011
        GtkTreePath *path;
3012

    
3013
        rows = summary_get_selected_rows(summaryview);
3014
        for (cur = rows; cur != NULL; cur = cur->next) {
3015
                path = (GtkTreePath *)cur->data;
3016
                gtk_tree_model_get_iter(model, &iter, path);
3017
                summary_set_row(summaryview, &iter, NULL);
3018
        }
3019
}
3020

    
3021
void summary_update_by_msgnum(SummaryView *summaryview, guint msgnum)
3022
{
3023
        GtkTreeIter iter;
3024

    
3025
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
3026
                summary_set_row(summaryview, &iter, NULL);
3027
}
3028

    
3029
static void summary_mark_row(SummaryView *summaryview, GtkTreeIter *iter)
3030
{
3031
        MsgInfo *msginfo = NULL;
3032

    
3033
        GET_MSG_INFO(msginfo, iter);
3034

    
3035
        msginfo->to_folder = NULL;
3036
        if (MSG_IS_DELETED(msginfo->flags)) {
3037
                summaryview->deleted--;
3038
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3039
        }
3040
        if (MSG_IS_MOVE(msginfo->flags))
3041
                summaryview->moved--;
3042
        if (MSG_IS_COPY(msginfo->flags))
3043
                summaryview->copied--;
3044
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3045
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
3046
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3047
        summaryview->folder_item->mark_dirty = TRUE;
3048
        summary_set_row(summaryview, iter, msginfo);
3049

    
3050
        debug_print(_("Message %d is marked\n"), msginfo->msgnum);
3051
}
3052

    
3053
void summary_mark(SummaryView *summaryview)
3054
{
3055
        GList *rows, *cur;
3056
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3057
        GtkTreeIter iter;
3058
        FolderSortKey sort_key = SORT_BY_NONE;
3059
        FolderSortType sort_type = SORT_ASCENDING;
3060

    
3061
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3062
            summary_is_read_locked(summaryview))
3063
                return;
3064

    
3065
        summary_lock(summaryview);
3066
        SORT_BLOCK(SORT_BY_MARK);
3067

    
3068
        rows = summary_get_selected_rows(summaryview);
3069
        for (cur = rows; cur != NULL; cur = cur->next) {
3070
                GtkTreePath *path = (GtkTreePath *)cur->data;
3071
                gtk_tree_model_get_iter(model, &iter, path);
3072
                summary_mark_row(summaryview, &iter);
3073
        }
3074

    
3075
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3076
                GSList *msglist;
3077

    
3078
                msglist = summary_get_selected_msg_list(summaryview);
3079
                imap_msg_list_set_perm_flags(msglist, MSG_MARKED);
3080
                g_slist_free(msglist);
3081
        }
3082

    
3083
        SORT_UNBLOCK(SORT_BY_MARK);
3084
        summary_unlock(summaryview);
3085

    
3086
        summary_status_show(summaryview);
3087
}
3088

    
3089
static void summary_mark_row_as_read(SummaryView *summaryview,
3090
                                     GtkTreeIter *iter)
3091
{
3092
        MsgInfo *msginfo = NULL;
3093

    
3094
        GET_MSG_INFO(msginfo, iter);
3095

    
3096
        if (MSG_IS_NEW(msginfo->flags)) {
3097
                if (summaryview->folder_item->new > 0)
3098
                        summaryview->folder_item->new--;
3099
                if (summaryview->on_filter && summaryview->flt_new > 0)
3100
                        summaryview->flt_new--;
3101
                inc_block_notify(TRUE);
3102
        }
3103
        if (MSG_IS_UNREAD(msginfo->flags)) {
3104
                if (summaryview->folder_item->unread > 0)
3105
                        summaryview->folder_item->unread--;
3106
                if (summaryview->on_filter && summaryview->flt_unread > 0)
3107
                        summaryview->flt_unread--;
3108
        }
3109

    
3110
        if (summaryview->folder_item->stype == F_VIRTUAL) {
3111
                if (MSG_IS_NEW(msginfo->flags) && msginfo->folder->new > 0)
3112
                        msginfo->folder->new--;
3113
                if (MSG_IS_UNREAD(msginfo->flags) &&
3114
                    msginfo->folder->unread > 0)
3115
                        msginfo->folder->unread--;
3116
                folderview_update_item(msginfo->folder, FALSE);
3117
        }
3118

    
3119
        if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3120
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
3121
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3122
                summaryview->folder_item->mark_dirty = TRUE;
3123
                summary_set_row(summaryview, iter, msginfo);
3124
                debug_print(_("Message %d is marked as being read\n"),
3125
                            msginfo->msgnum);
3126
        }
3127
}
3128

    
3129
void summary_mark_as_read(SummaryView *summaryview)
3130
{
3131
        GList *rows, *cur;
3132
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3133
        GtkTreeIter iter;
3134
        FolderSortKey sort_key = SORT_BY_NONE;
3135
        FolderSortType sort_type = SORT_ASCENDING;
3136

    
3137
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3138
            summary_is_read_locked(summaryview))
3139
                return;
3140

    
3141
        summary_lock(summaryview);
3142
        SORT_BLOCK(SORT_BY_UNREAD);
3143

    
3144
        rows = summary_get_selected_rows(summaryview);
3145

    
3146
        for (cur = rows; cur != NULL; cur = cur->next) {
3147
                GtkTreePath *path = (GtkTreePath *)cur->data;
3148

    
3149
                gtk_tree_model_get_iter(model, &iter, path);
3150
                summary_mark_row_as_read(summaryview, &iter);
3151
        }
3152

    
3153
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3154
                GSList *msglist;
3155

    
3156
                msglist = summary_get_selected_msg_list(summaryview);
3157
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3158
                g_slist_free(msglist);
3159
        }
3160

    
3161
        SORT_UNBLOCK(SORT_BY_UNREAD);
3162
        summary_unlock(summaryview);
3163

    
3164
        trayicon_set_tooltip(NULL);
3165
        trayicon_set_notify(FALSE);
3166
        summary_status_show(summaryview);
3167
}
3168

    
3169
static gboolean prepend_thread_rows_func(GtkTreeModel *model, GtkTreePath *path,
3170
                                         GtkTreeIter *iter, gpointer data)
3171
{
3172
        GSList **thr_rows = (GSList **)data;
3173
        GtkTreePath *top_path;
3174

    
3175
        top_path = gtk_tree_path_copy(path);
3176
        *thr_rows = g_slist_prepend(*thr_rows, top_path);
3177
        return FALSE;
3178
}
3179

    
3180
void summary_mark_thread_as_read(SummaryView *summaryview)
3181
{
3182
        GList *rows, *cur;
3183
        GSList *thr_rows = NULL, *top_rows = NULL, *s_cur;
3184
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3185
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3186
        GtkTreeIter iter, parent;
3187
        GtkTreePath *path, *top_path;
3188
        GHashTable *row_table;
3189
        MsgInfo *msginfo;
3190
        GSList *msglist = NULL;
3191
        FolderSortKey sort_key = SORT_BY_NONE;
3192
        FolderSortType sort_type = SORT_ASCENDING;
3193

    
3194
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3195
            summary_is_read_locked(summaryview))
3196
                return;
3197

    
3198
        summary_lock(summaryview);
3199
        SORT_BLOCK(SORT_BY_UNREAD);
3200

    
3201
        rows = summary_get_selected_rows(summaryview);
3202

    
3203
        row_table = g_hash_table_new(NULL, NULL);
3204

    
3205
        for (cur = rows; cur != NULL; cur = cur->next) {
3206
                path = (GtkTreePath *)cur->data;
3207
                gtk_tree_model_get_iter(model, &iter, path);
3208
                while (gtk_tree_model_iter_parent(model, &parent, &iter))
3209
                        iter = parent;
3210
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3211
                if (!g_hash_table_lookup(row_table, msginfo)) {
3212
                        g_hash_table_insert(row_table, msginfo,
3213
                                            GINT_TO_POINTER(1));
3214
                        top_path = gtk_tree_model_get_path(model, &iter);
3215
                        top_rows = g_slist_prepend(top_rows, top_path);
3216
                        gtkut_tree_model_foreach(model, &iter,
3217
                                                 prepend_thread_rows_func,
3218
                                                 &thr_rows);
3219
                }
3220
        }
3221
        top_rows = g_slist_reverse(top_rows);
3222
        thr_rows = g_slist_reverse(thr_rows);
3223

    
3224
        g_hash_table_destroy(row_table);
3225

    
3226
        for (s_cur = thr_rows; s_cur != NULL; s_cur = s_cur->next) {
3227
                path = (GtkTreePath *)s_cur->data;
3228
                gtk_tree_model_get_iter(model, &iter, path);
3229
                summary_mark_row_as_read(summaryview, &iter);
3230
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3231
                msglist = g_slist_prepend(msglist, msginfo);
3232
        }
3233

    
3234
        if (prefs_common.bold_unread) {
3235
                for (s_cur = top_rows; s_cur != NULL; s_cur = s_cur->next) {
3236
                        path = (GtkTreePath *)s_cur->data;
3237
                        gtk_tree_model_get_iter(model, &iter, path);
3238
                        if (gtk_tree_model_iter_has_child(model, &iter) &&
3239
                            !gtk_tree_view_row_expanded(treeview, path) &&
3240
                            !summary_have_unread_children(summaryview, &iter)) {
3241
                                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
3242
                                                   S_COL_BOLD,
3243
                                                   PANGO_WEIGHT_NORMAL, -1);
3244
                        }
3245
                }
3246
        }
3247
        msglist = g_slist_reverse(msglist);
3248

    
3249
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3250
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3251
        }
3252

    
3253
        g_slist_free(msglist);
3254
        g_slist_foreach(top_rows, (GFunc)gtk_tree_path_free, NULL);
3255
        g_slist_free(top_rows);
3256
        g_slist_foreach(thr_rows, (GFunc)gtk_tree_path_free, NULL);
3257
        g_slist_free(thr_rows);
3258

    
3259
        SORT_UNBLOCK(SORT_BY_UNREAD);
3260
        summary_unlock(summaryview);
3261

    
3262
        trayicon_set_tooltip(NULL);
3263
        trayicon_set_notify(FALSE);
3264
        summary_status_show(summaryview);
3265
}
3266

    
3267
void summary_mark_all_read(SummaryView *summaryview)
3268
{
3269
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3270
        GtkTreeIter iter;
3271
        gboolean valid;
3272
        FolderSortKey sort_key = SORT_BY_NONE;
3273
        FolderSortType sort_type = SORT_ASCENDING;
3274

    
3275
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3276
            summary_is_read_locked(summaryview))
3277
                return;
3278

    
3279
        summary_lock(summaryview);
3280
        SORT_BLOCK(SORT_BY_UNREAD);
3281

    
3282
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3283
                GSList *msglist;
3284
                msglist = summary_get_flagged_msg_list(summaryview,
3285
                                                       MSG_NEW | MSG_UNREAD);
3286
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3287
                g_slist_free(msglist);
3288
        }
3289

    
3290
        valid = gtk_tree_model_get_iter_first(model, &iter);
3291
        while (valid) {
3292
                summary_mark_row_as_read(summaryview, &iter);
3293
                if (prefs_common.bold_unread) {
3294
                        if (gtk_tree_model_iter_has_child(model, &iter)) {
3295
                                GtkTreePath *path;
3296

    
3297
                                path = gtk_tree_model_get_path(model, &iter);
3298
                                if (!gtk_tree_view_row_expanded
3299
                                        (GTK_TREE_VIEW(summaryview->treeview),
3300
                                         path))
3301
                                        gtk_tree_store_set
3302
                                                (GTK_TREE_STORE(model), &iter,
3303
                                                 S_COL_BOLD,
3304
                                                 PANGO_WEIGHT_NORMAL, -1);
3305
                                gtk_tree_path_free(path);
3306
                        }
3307
                }
3308
                valid = gtkut_tree_model_next(model, &iter);
3309
        }
3310

    
3311
        SORT_UNBLOCK(SORT_BY_UNREAD);
3312
        summary_unlock(summaryview);
3313

    
3314
        trayicon_set_tooltip(NULL);
3315
        trayicon_set_notify(FALSE);
3316
        summary_status_show(summaryview);
3317
}
3318

    
3319
static void summary_mark_row_as_unread(SummaryView *summaryview,
3320
                                       GtkTreeIter *iter)
3321
{
3322
        MsgInfo *msginfo = NULL;
3323

    
3324
        GET_MSG_INFO(msginfo, iter);
3325

    
3326
        if (MSG_IS_DELETED(msginfo->flags)) {
3327
                msginfo->to_folder = NULL;
3328
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3329
                summaryview->deleted--;
3330
                summaryview->folder_item->mark_dirty = TRUE;
3331
        }
3332
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
3333
        if (!MSG_IS_UNREAD(msginfo->flags)) {
3334
                MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
3335
                summaryview->folder_item->unread++;
3336
                if (summaryview->on_filter)
3337
                        summaryview->flt_unread++;
3338
                summaryview->folder_item->mark_dirty = TRUE;
3339
                if (summaryview->folder_item->stype == F_VIRTUAL) {
3340
                        msginfo->folder->unread++;
3341
                        folderview_update_item(msginfo->folder, FALSE);
3342
                }
3343
                debug_print(_("Message %d is marked as unread\n"),
3344
                            msginfo->msgnum);
3345
        }
3346
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3347
        summary_set_row(summaryview, iter, msginfo);
3348
}
3349

    
3350
void summary_mark_as_unread(SummaryView *summaryview)
3351
{
3352
        GList *rows, *cur;
3353
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3354
        GtkTreeIter iter;
3355
        FolderSortKey sort_key = SORT_BY_NONE;
3356
        FolderSortType sort_type = SORT_ASCENDING;
3357

    
3358
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3359
            summary_is_read_locked(summaryview))
3360
                return;
3361

    
3362
        summary_lock(summaryview);
3363
        SORT_BLOCK(SORT_BY_UNREAD);
3364

    
3365
        rows = summary_get_selected_rows(summaryview);
3366
        for (cur = rows; cur != NULL; cur = cur->next) {
3367
                GtkTreePath *path = (GtkTreePath *)cur->data;
3368

    
3369
                gtk_tree_model_get_iter(model, &iter, path);
3370
                summary_mark_row_as_unread(summaryview, &iter);
3371
        }
3372

    
3373
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3374
                GSList *msglist;
3375
                msglist = summary_get_selected_msg_list(summaryview);
3376
                imap_msg_list_unset_perm_flags(msglist, MSG_REPLIED);
3377
                imap_msg_list_set_perm_flags(msglist, MSG_UNREAD);
3378
                g_slist_free(msglist);
3379
        }
3380

    
3381
        SORT_UNBLOCK(SORT_BY_UNREAD);
3382
        summary_unlock(summaryview);
3383

    
3384
        summary_status_show(summaryview);
3385
}
3386

    
3387
static void summary_delete_row(SummaryView *summaryview, GtkTreeIter *iter)
3388
{
3389
        MsgInfo *msginfo = NULL;
3390

    
3391
        GET_MSG_INFO(msginfo, iter);
3392

    
3393
        if (MSG_IS_DELETED(msginfo->flags)) return;
3394

    
3395
        msginfo->to_folder = NULL;
3396
        if (MSG_IS_MOVE(msginfo->flags)) {
3397
                summaryview->moved--;
3398
                if (summaryview->on_filter)
3399
                        summaryview->flt_moved--;
3400
        }
3401
        if (MSG_IS_COPY(msginfo->flags)) {
3402
                summaryview->copied--;
3403
                if (summaryview->on_filter)
3404
                        summaryview->flt_copied--;
3405
        }
3406
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3407
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3408
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3409
        summaryview->deleted++;
3410
        if (summaryview->on_filter)
3411
                summaryview->flt_deleted++;
3412
        summaryview->folder_item->mark_dirty = TRUE;
3413

    
3414
        if (!prefs_common.immediate_exec && summaryview->tmp_flag == 0)
3415
                summary_set_row(summaryview, iter, msginfo);
3416

    
3417
        debug_print(_("Message %s/%d is set to delete\n"),
3418
                    msginfo->folder->path, msginfo->msgnum);
3419
}
3420

    
3421
static gboolean summary_delete_foreach_func(GtkTreeModel *model,
3422
                                            GtkTreePath *path,
3423
                                            GtkTreeIter *iter, gpointer data)
3424
{
3425
        summary_delete_row((SummaryView *)data, iter);
3426
        return FALSE;
3427
}
3428

    
3429
void summary_delete(SummaryView *summaryview)
3430
{
3431
        FolderItem *item = summaryview->folder_item;
3432
        GList *rows, *cur;
3433
        GtkTreeIter last_sel, next;
3434
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3435
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3436
        gboolean is_trash;
3437

    
3438
        if (!item || FOLDER_TYPE(item->folder) == F_NEWS) return;
3439

    
3440
        if (summary_is_locked(summaryview)) return;
3441

    
3442
        /* if current folder is trash, ask for confirmation */
3443
        is_trash = folder_item_is_trash(item);
3444
        if (is_trash) {
3445
                AlertValue aval;
3446

    
3447
                aval = alertpanel(_("Delete message(s)"),
3448
                                  _("Do you really want to delete message(s) from the trash?"),
3449
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3450
                if (aval != G_ALERTDEFAULT) return;
3451
        }
3452

    
3453
        rows = summary_get_selected_rows(summaryview);
3454
        if (!rows)
3455
                return;
3456

    
3457
        summaryview->tmp_flag = is_trash ? 1 : 0;
3458

    
3459
        /* next code sets current row focus right. We need to find a row
3460
         * that is not deleted. */
3461
        for (cur = rows; cur != NULL; cur = cur->next) {
3462
                GtkTreePath *path = (GtkTreePath *)cur->data;
3463

    
3464
                gtk_tree_model_get_iter(model, &last_sel, path);
3465
                if (gtk_tree_model_iter_has_child(model, &last_sel) &&
3466
                    !gtk_tree_view_row_expanded(treeview, path)) {
3467
                        gtkut_tree_model_foreach
3468
                                (model, &last_sel, summary_delete_foreach_func,
3469
                                 summaryview);
3470
                } else
3471
                        summary_delete_row(summaryview, &last_sel);
3472
        }
3473

    
3474
        summaryview->tmp_flag = 0;
3475

    
3476
        if (prefs_common.immediate_exec || is_trash) {
3477
                summary_execute(summaryview);
3478
        } else {
3479
                if (summary_find_nearest_msg(summaryview, &next, &last_sel)) {
3480
                        summary_select_row
3481
                                (summaryview, &next,
3482
                                 messageview_is_visible
3483
                                        (summaryview->messageview),
3484
                                 FALSE);
3485
                }
3486
                summary_status_show(summaryview);
3487
        }
3488
}
3489

    
3490
static gboolean summary_delete_duplicated_func(GtkTreeModel *model,
3491
                                               GtkTreePath *path,
3492
                                               GtkTreeIter *iter,
3493
                                               gpointer data)
3494
{
3495
        SummaryView *summaryview = (SummaryView *)data;
3496
        MsgInfo *msginfo;
3497
        GtkTreeIter *found;
3498
        GtkTreePath *found_path;
3499

    
3500
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3501

    
3502
        if (!msginfo || !msginfo->msgid || !*msginfo->msgid)
3503
                return FALSE;
3504

    
3505
        found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
3506

    
3507
        if (found) {
3508
                found_path = gtk_tree_model_get_path(model, found);
3509
                if (gtk_tree_path_compare(path, found_path) != 0)
3510
                        summary_delete_row(summaryview, iter);
3511
                gtk_tree_path_free(found_path);
3512
        }
3513

    
3514
        return FALSE;
3515
}
3516

    
3517
void summary_delete_duplicated(SummaryView *summaryview)
3518
{
3519
        if (!summaryview->folder_item ||
3520
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3521
        if (folder_item_is_trash(summaryview->folder_item)) return;
3522

    
3523
        if (summary_is_locked(summaryview)) return;
3524

    
3525
        main_window_cursor_wait(summaryview->mainwin);
3526
        debug_print("Deleting duplicated messages...");
3527
        STATUSBAR_PUSH(summaryview->mainwin,
3528
                       _("Deleting duplicated messages..."));
3529

    
3530
        summary_msgid_table_create(summaryview);
3531

    
3532
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
3533
                               summary_delete_duplicated_func, summaryview);
3534

    
3535
        summary_msgid_table_destroy(summaryview);
3536

    
3537
        if (prefs_common.immediate_exec)
3538
                summary_execute(summaryview);
3539
        else
3540
                summary_status_show(summaryview);
3541

    
3542
        debug_print("done.\n");
3543
        STATUSBAR_POP(summaryview->mainwin);
3544
        main_window_cursor_normal(summaryview->mainwin);
3545
}
3546

    
3547
static void summary_unmark_row(SummaryView *summaryview, GtkTreeIter *iter)
3548
{
3549
        MsgInfo *msginfo = NULL;
3550

    
3551
        GET_MSG_INFO(msginfo, iter);
3552

    
3553
        msginfo->to_folder = NULL;
3554
        if (MSG_IS_DELETED(msginfo->flags)) {
3555
                summaryview->deleted--;
3556
                if (summaryview->on_filter)
3557
                        summaryview->flt_deleted--;
3558
        }
3559
        if (MSG_IS_MOVE(msginfo->flags)) {
3560
                summaryview->moved--;
3561
                if (summaryview->on_filter)
3562
                        summaryview->flt_moved--;
3563
        }
3564
        if (MSG_IS_COPY(msginfo->flags)) {
3565
                summaryview->copied--;
3566
                if (summaryview->on_filter)
3567
                        summaryview->flt_copied--;
3568
        }
3569
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
3570
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3571
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3572
        summaryview->folder_item->mark_dirty = TRUE;
3573
        summary_set_row(summaryview, iter, msginfo);
3574

    
3575
        debug_print(_("Message %s/%d is unmarked\n"),
3576
                    msginfo->folder->path, msginfo->msgnum);
3577
}
3578

    
3579
void summary_unmark(SummaryView *summaryview)
3580
{
3581
        GList *rows, *cur;
3582
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3583
        GtkTreeIter iter;
3584
        FolderSortKey sort_key = SORT_BY_NONE;
3585
        FolderSortType sort_type = SORT_ASCENDING;
3586

    
3587
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3588
            summary_is_read_locked(summaryview))
3589
                return;
3590

    
3591
        summary_lock(summaryview);
3592
        SORT_BLOCK(SORT_BY_MARK);
3593

    
3594
        rows = summary_get_selected_rows(summaryview);
3595
        for (cur = rows; cur != NULL; cur = cur->next) {
3596
                GtkTreePath *path = (GtkTreePath *)cur->data;
3597
                gtk_tree_model_get_iter(model, &iter, path);
3598
                summary_unmark_row(summaryview, &iter);
3599
        }
3600

    
3601
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3602
                GSList *msglist;
3603

    
3604
                msglist = summary_get_selected_msg_list(summaryview);
3605
                imap_msg_list_unset_perm_flags(msglist, MSG_MARKED);
3606
                g_slist_free(msglist);
3607
        }
3608

    
3609
        SORT_UNBLOCK(SORT_BY_MARK);
3610
        summary_unlock(summaryview);
3611

    
3612
        summary_status_show(summaryview);
3613
}
3614

    
3615
static void summary_move_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3616
                                FolderItem *to_folder)
3617
{
3618
        MsgInfo *msginfo;
3619

    
3620
        g_return_if_fail(to_folder != NULL);
3621
        if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3622
                return;
3623

    
3624
        GET_MSG_INFO(msginfo, iter);
3625

    
3626
        msginfo->to_folder = to_folder;
3627
        if (MSG_IS_DELETED(msginfo->flags)) {
3628
                summaryview->deleted--;
3629
                if (summaryview->on_filter)
3630
                        summaryview->flt_deleted--;
3631
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3632
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3633
        }
3634
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3635
        if (!MSG_IS_MOVE(msginfo->flags)) {
3636
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3637
                summaryview->moved++;
3638
                if (summaryview->on_filter)
3639
                        summaryview->flt_moved++;
3640
        }
3641
        summaryview->folder_item->mark_dirty = TRUE;
3642
        if (!prefs_common.immediate_exec)
3643
                summary_set_row(summaryview, iter, msginfo);
3644

    
3645
        debug_print(_("Message %d is set to move to %s\n"),
3646
                    msginfo->msgnum, to_folder->path);
3647
}
3648

    
3649
static gboolean summary_move_foreach_func(GtkTreeModel *model,
3650
                                          GtkTreePath *path, GtkTreeIter *iter,
3651
                                          gpointer data)
3652
{
3653
        SummaryView *summaryview = (SummaryView *)data;
3654

    
3655
        summary_move_row_to(summaryview, iter, summaryview->to_folder);
3656
        return FALSE;
3657
}
3658

    
3659
void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3660
{
3661
        GList *rows, *cur;
3662
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3663
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3664
        GtkTreeIter iter;
3665

    
3666
        if (!to_folder) return;
3667
        if (!summaryview->folder_item ||
3668
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
3669
                return;
3670
        if (summaryview->folder_item->stype == F_QUEUE ||
3671
            to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3672
                return;
3673

    
3674
        if (summary_is_locked(summaryview)) return;
3675

    
3676
        if (summaryview->folder_item == to_folder) {
3677
                alertpanel_warning(_("Destination is same as current folder."));
3678
                return;
3679
        }
3680

    
3681
        rows = summary_get_selected_rows(summaryview);
3682
        for (cur = rows; cur != NULL; cur = cur->next) {
3683
                GtkTreePath *path = (GtkTreePath *)cur->data;
3684

    
3685
                gtk_tree_model_get_iter(model, &iter, path);
3686
                if (gtk_tree_model_iter_has_child(model, &iter) &&
3687
                    !gtk_tree_view_row_expanded(treeview, path)) {
3688
                        summaryview->to_folder = to_folder;
3689
                        gtkut_tree_model_foreach
3690
                                (model, &iter, summary_move_foreach_func,
3691
                                 summaryview);
3692
                        summaryview->to_folder = NULL;
3693
                } else
3694
                        summary_move_row_to(summaryview, &iter, to_folder);
3695
        }
3696

    
3697
        if (prefs_common.immediate_exec)
3698
                summary_execute(summaryview);
3699
        else {
3700
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3701
                summary_status_show(summaryview);
3702
        }
3703
}
3704

    
3705
void summary_move_to(SummaryView *summaryview)
3706
{
3707
        FolderItem *to_folder;
3708

    
3709
        if (!summaryview->folder_item ||
3710
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3711

    
3712
        to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder,
3713
                                              FOLDER_SEL_MOVE, NULL,
3714
                                              _("Select folder to move"));
3715
        summary_move_selected_to(summaryview, to_folder);
3716
}
3717

    
3718
static void summary_copy_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3719
                                FolderItem *to_folder)
3720
{
3721
        MsgInfo *msginfo;
3722

    
3723
        g_return_if_fail(to_folder != NULL);
3724
        if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3725
                return;
3726

    
3727
        GET_MSG_INFO(msginfo, iter);
3728

    
3729
        msginfo->to_folder = to_folder;
3730
        if (MSG_IS_DELETED(msginfo->flags)) {
3731
                summaryview->deleted--;
3732
                if (summaryview->on_filter)
3733
                        summaryview->flt_deleted--;
3734
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3735
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3736
        }
3737
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3738
        if (!MSG_IS_COPY(msginfo->flags)) {
3739
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3740
                summaryview->copied++;
3741
                if (summaryview->on_filter)
3742
                        summaryview->flt_copied++;
3743
        }
3744
        summaryview->folder_item->mark_dirty = TRUE;
3745
        if (!prefs_common.immediate_exec)
3746
                summary_set_row(summaryview, iter, msginfo);
3747

    
3748
        debug_print(_("Message %d is set to copy to %s\n"),
3749
                    msginfo->msgnum, to_folder->path);
3750
}
3751

    
3752
static gboolean summary_copy_foreach_func(GtkTreeModel *model,
3753
                                          GtkTreePath *path, GtkTreeIter *iter,
3754
                                          gpointer data)
3755
{
3756
        SummaryView *summaryview = (SummaryView *)data;
3757

    
3758
        summary_copy_row_to(summaryview, iter, summaryview->to_folder);
3759
        return FALSE;
3760
}
3761

    
3762
void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3763
{
3764
        GList *rows, *cur;
3765
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3766
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3767
        GtkTreeIter iter;
3768

    
3769
        if (!to_folder) return;
3770
        if (!summaryview->folder_item) return;
3771
        if (summaryview->folder_item->stype == F_QUEUE ||
3772
            to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3773
                return;
3774

    
3775
        if (summary_is_locked(summaryview)) return;
3776

    
3777
        if (summaryview->folder_item == to_folder) {
3778
                alertpanel_warning
3779
                        (_("Destination for copy is same as current folder."));
3780
                return;
3781
        }
3782

    
3783
        rows = summary_get_selected_rows(summaryview);
3784
        for (cur = rows; cur != NULL; cur = cur->next) {
3785
                GtkTreePath *path = (GtkTreePath *)cur->data;
3786

    
3787
                gtk_tree_model_get_iter(model, &iter, path);
3788
                if (gtk_tree_model_iter_has_child(model, &iter) &&
3789
                    !gtk_tree_view_row_expanded(treeview, path)) {
3790
                        summaryview->to_folder = to_folder;
3791
                        gtkut_tree_model_foreach
3792
                                 (model, &iter, summary_copy_foreach_func,
3793
                                  summaryview);
3794
                        summaryview->to_folder = NULL;
3795
                } else
3796
                        summary_copy_row_to(summaryview, &iter, to_folder);
3797
        }
3798

    
3799
        if (prefs_common.immediate_exec)
3800
                summary_execute(summaryview);
3801
        else {
3802
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3803
                summary_status_show(summaryview);
3804
        }
3805
}
3806

    
3807
void summary_copy_to(SummaryView *summaryview)
3808
{
3809
        FolderItem *to_folder;
3810

    
3811
        if (!summaryview->folder_item) return;
3812

    
3813
        to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder,
3814
                                              FOLDER_SEL_COPY, NULL,
3815
                                              _("Select folder to copy"));
3816
        summary_copy_selected_to(summaryview, to_folder);
3817
}
3818

    
3819
void summary_add_address(SummaryView *summaryview)
3820
{
3821
        GtkTreeIter iter;
3822
        MsgInfo *msginfo = NULL;
3823
        gchar from[BUFFSIZE];
3824

    
3825
        if (!summaryview->selected) return;
3826

    
3827
        if (!gtkut_tree_row_reference_get_iter
3828
                (GTK_TREE_MODEL(summaryview->store),
3829
                 summaryview->selected, &iter))
3830
                return;
3831

    
3832
        GET_MSG_INFO(msginfo, &iter);
3833
        strncpy2(from, msginfo->from, sizeof(from));
3834
        eliminate_address_comment(from);
3835
        extract_address(from);
3836
        addressbook_add_contact(msginfo->fromname, from, NULL);
3837
}
3838

    
3839
void summary_select_all(SummaryView *summaryview)
3840
{
3841
        gtk_tree_selection_select_all(summaryview->selection);
3842
}
3843

    
3844
void summary_unselect_all(SummaryView *summaryview)
3845
{
3846
        gtk_tree_selection_unselect_all(summaryview->selection);
3847
}
3848

    
3849
void summary_select_thread(SummaryView *summaryview)
3850
{
3851
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3852
        GtkTreeIter iter, parent, child, next;
3853
        GtkTreePath *start_path, *end_path;
3854
        gboolean valid;
3855

    
3856
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
3857
                                                  &iter);
3858
        if (!valid)
3859
                return;
3860

    
3861
        while (gtk_tree_model_iter_parent(model, &parent, &iter))
3862
                iter = parent;
3863

    
3864
        if (!gtk_tree_model_iter_children(model, &child, &iter))
3865
                return;
3866

    
3867
        start_path = gtk_tree_model_get_path(model, &iter);
3868

    
3869
        for (;;) {
3870
                next = iter = child;
3871
                while (gtk_tree_model_iter_next(model, &next))
3872
                        iter = next;
3873
                if (!gtk_tree_model_iter_children(model, &child, &iter))
3874
                        break;
3875
        }
3876

    
3877
        end_path = gtk_tree_model_get_path(model, &iter);
3878

    
3879
        gtk_tree_view_expand_row(GTK_TREE_VIEW(summaryview->treeview),
3880
                                 start_path, TRUE);
3881
        gtk_tree_selection_select_range(summaryview->selection,
3882
                                        start_path, end_path);
3883

    
3884
        gtk_tree_path_free(end_path);
3885
        gtk_tree_path_free(start_path);
3886
}
3887

    
3888
void summary_save_as(SummaryView *summaryview)
3889
{
3890
        GtkTreeIter iter;
3891
        MsgInfo *msginfo = NULL;
3892
        gchar *filename;
3893
        gchar *src, *dest;
3894

    
3895
        if (!summaryview->selected) return;
3896
        if (!gtkut_tree_row_reference_get_iter
3897
                (GTK_TREE_MODEL(summaryview->store),
3898
                 summaryview->selected, &iter))
3899
                return;
3900

    
3901
        GET_MSG_INFO(msginfo, &iter);
3902
        if (!msginfo) return;
3903

    
3904
        if (msginfo->subject && *msginfo->subject) {
3905
                filename = g_strdup_printf("%s.eml", msginfo->subject);
3906
        } else {
3907
                filename = g_strdup_printf("%u.eml", msginfo->msgnum);
3908
        }
3909
        subst_for_filename(filename);
3910

    
3911
        dest = filesel_save_as(filename);
3912

    
3913
        g_free(filename);
3914
        if (!dest)
3915
                return;
3916

    
3917
        src = procmsg_get_message_file(msginfo);
3918
        if (copy_file(src, dest, TRUE) < 0) {
3919
                gchar *utf8_dest;
3920

    
3921
                utf8_dest = conv_filename_to_utf8(dest);
3922
                alertpanel_error(_("Can't save the file `%s'."),
3923
                                 g_basename(utf8_dest));
3924
                g_free(utf8_dest);
3925
        }
3926
        g_free(src);
3927

    
3928
        g_free(dest);
3929
}
3930

    
3931
void summary_print(SummaryView *summaryview)
3932
{
3933
        GSList *mlist;
3934
        gboolean all_headers;
3935

    
3936
        all_headers = summaryview->messageview->textview->show_all_headers;
3937
        mlist = summary_get_selected_msg_list(summaryview);
3938
        if (!mlist)
3939
                return;
3940
        printing_print_messages(mlist, all_headers);
3941
        g_slist_free(mlist);
3942
}
3943

    
3944
gboolean summary_execute(SummaryView *summaryview)
3945
{
3946
        gint val = 0;
3947

    
3948
        if (!summaryview->folder_item) return FALSE;
3949

    
3950
        if (summary_is_locked(summaryview)) return FALSE;
3951
        summary_lock(summaryview);
3952

    
3953
        val |= summary_execute_move(summaryview);
3954
        val |= summary_execute_copy(summaryview);
3955
        val |= summary_execute_delete(summaryview);
3956

    
3957
        summary_unlock(summaryview);
3958

    
3959
        summary_remove_invalid_messages(summaryview);
3960

    
3961
        statusbar_pop_all();
3962
        STATUSBAR_POP(summaryview->mainwin);
3963

    
3964
        if (val != 0) {
3965
                alertpanel_error(_("Error occurred while processing messages."));
3966
        }
3967

    
3968
        return TRUE;
3969
}
3970

    
3971
static void summary_remove_invalid_messages(SummaryView *summaryview)
3972
{
3973
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3974
        MsgInfo *disp_msginfo = NULL, *msginfo;
3975
        FolderItem *item = summaryview->folder_item;
3976
        GtkTreeIter iter, next;
3977
        GtkTreePath *path;
3978
        gboolean valid;
3979

    
3980
        /* get currently displayed message */
3981
        if (summaryview->displayed) {
3982
                GtkTreeIter displayed;
3983

    
3984
                valid = gtkut_tree_row_reference_get_iter
3985
                        (model, summaryview->displayed, &displayed);
3986
                if (valid) {
3987
                        gtk_tree_model_get(model, &displayed,
3988
                                           S_COL_MSG_INFO, &disp_msginfo, -1);
3989
                        if (MSG_IS_INVALID(disp_msginfo->flags)) {
3990
                                valid = FALSE;
3991
                                disp_msginfo = NULL;
3992
                        }
3993
                }
3994
                if (!valid) {
3995
                        /* g_print("displayed became invalid before removing\n"); */
3996
                        messageview_clear(summaryview->messageview);
3997
                        gtk_tree_row_reference_free(summaryview->displayed);
3998
                        summaryview->displayed = NULL;
3999
                }
4000
        }
4001

    
4002
        if (item->threaded)
4003
                summary_modify_threads(summaryview);
4004

    
4005
        /* update selection */
4006
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
4007
                                                  &iter);
4008
        if (valid) {
4009
                valid = summary_find_nearest_msg(summaryview, &next, &iter);
4010
                if (valid) {
4011
                        gboolean display;
4012

    
4013
                        gtk_tree_model_get(model, &next,
4014
                                           S_COL_MSG_INFO, &msginfo, -1);
4015
                        if (disp_msginfo && disp_msginfo == msginfo) {
4016
                                /* g_print("replace displayed\n"); */
4017
                                path = gtk_tree_model_get_path(model, &next);
4018
                                gtk_tree_row_reference_free
4019
                                        (summaryview->displayed);
4020
                                summaryview->displayed =
4021
                                        gtk_tree_row_reference_new(model, path);
4022
                                gtk_tree_path_free(path);
4023
                        }
4024
                        display = prefs_common.immediate_exec &&
4025
                                messageview_is_visible
4026
                                        (summaryview->messageview);
4027
                        summary_select_row
4028
                                (summaryview, &next, display, FALSE);
4029
                        if (display)
4030
                                summary_mark_displayed_read(summaryview, &next);
4031
                }
4032
        }
4033
        if (!valid)
4034
                summary_unselect_all(summaryview);
4035

    
4036
        for (valid = gtk_tree_model_get_iter_first(model, &iter);
4037
             valid == TRUE; iter = next) {
4038
                next = iter;
4039
                valid = gtkut_tree_model_next(model, &next);
4040

    
4041
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4042
                if (!MSG_IS_INVALID(msginfo->flags))
4043
                        continue;
4044

    
4045
                if (gtk_tree_model_iter_has_child(model, &iter)) {
4046
                        g_warning("summary_remove_invalid_messages(): "
4047
                                  "tried to remove row which has child\n");
4048
                        continue;
4049
                }
4050

    
4051
                gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
4052
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4053
                                                        msginfo);
4054
                if (summaryview->flt_mlist)
4055
                        summaryview->flt_mlist =
4056
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4057
                procmsg_msginfo_free(msginfo);
4058

    
4059
                item->cache_dirty = TRUE;
4060
        }
4061

    
4062
        /* selection list becomes invalid if modified */
4063
        if (item->cache_dirty)
4064
                summary_selection_list_free(summaryview);
4065

    
4066
        if (summaryview->displayed &&
4067
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4068
                /* g_print("displayed became invalid after removing. searching disp_msginfo...\n"); */
4069
                if (disp_msginfo &&
4070
                    gtkut_tree_model_find_by_column_data
4071
                        (model, &iter, NULL, S_COL_MSG_INFO, disp_msginfo)) {
4072
                        /* g_print("replace displayed\n"); */
4073
                        path = gtk_tree_model_get_path(model, &iter);
4074
                        gtk_tree_row_reference_free(summaryview->displayed);
4075
                        summaryview->displayed =
4076
                                gtk_tree_row_reference_new(model, path);
4077
                        gtk_tree_path_free(path);
4078
                } else {
4079
                        messageview_clear(summaryview->messageview);
4080
                        gtk_tree_row_reference_free(summaryview->displayed);
4081
                        summaryview->displayed = NULL;
4082
                }
4083
        }
4084

    
4085
        if (gtk_tree_model_get_iter_first(model, &iter))
4086
                gtk_widget_grab_focus(summaryview->treeview);
4087
        else {
4088
                menu_set_insensitive_all
4089
                        (GTK_MENU_SHELL(summaryview->popupmenu));
4090
                gtk_widget_grab_focus(summaryview->folderview->treeview);
4091
        }
4092

    
4093
        summary_write_cache(summaryview);
4094

    
4095
        summary_update_status(summaryview);
4096
        summary_status_show(summaryview);
4097
}
4098

    
4099
static gboolean summary_execute_move_func(GtkTreeModel *model,
4100
                                          GtkTreePath *path, GtkTreeIter *iter,
4101
                                          gpointer data)
4102
{
4103
        SummaryView *summaryview = data;
4104
        MsgInfo *msginfo;
4105

    
4106
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4107

    
4108
        if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
4109
                g_hash_table_insert(summaryview->folder_table,
4110
                                    msginfo->to_folder, GINT_TO_POINTER(1));
4111

    
4112
                summaryview->tmp_mlist =
4113
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4114

    
4115
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
4116
                summary_set_row(summaryview, iter, msginfo);
4117
        }
4118

    
4119
        return FALSE;
4120
}
4121

    
4122
static gint summary_execute_move(SummaryView *summaryview)
4123
{
4124
        gint val = 0;
4125

    
4126
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
4127

    
4128
        /* search moving messages and execute */
4129
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4130
                               summary_execute_move_func, summaryview);
4131

    
4132
        if (summaryview->tmp_mlist) {
4133
                summaryview->tmp_mlist =
4134
                        g_slist_reverse(summaryview->tmp_mlist);
4135
                val = procmsg_move_messages(summaryview->tmp_mlist);
4136

    
4137
                folderview_update_item_foreach(summaryview->folder_table,
4138
                                               FALSE);
4139

    
4140
                g_slist_free(summaryview->tmp_mlist);
4141
                summaryview->tmp_mlist = NULL;
4142
        }
4143

    
4144
        g_hash_table_destroy(summaryview->folder_table);
4145
        summaryview->folder_table = NULL;
4146

    
4147
        return val;
4148
}
4149

    
4150
static gboolean summary_execute_copy_func(GtkTreeModel *model,
4151
                                          GtkTreePath *path, GtkTreeIter *iter,
4152
                                          gpointer data)
4153
{
4154
        SummaryView *summaryview = data;
4155
        MsgInfo *msginfo;
4156

    
4157
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4158

    
4159
        if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
4160
                g_hash_table_insert(summaryview->folder_table,
4161
                                    msginfo->to_folder, GINT_TO_POINTER(1));
4162

    
4163
                summaryview->tmp_mlist =
4164
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4165

    
4166
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
4167
                summary_set_row(summaryview, iter, msginfo);
4168
        }
4169

    
4170
        return FALSE;
4171
}
4172

    
4173
static gint summary_execute_copy(SummaryView *summaryview)
4174
{
4175
        gint val = 0;
4176

    
4177
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
4178

    
4179
        /* search copying messages and execute */
4180
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4181
                               summary_execute_copy_func, summaryview);
4182

    
4183
        if (summaryview->tmp_mlist) {
4184
                summaryview->tmp_mlist =
4185
                        g_slist_reverse(summaryview->tmp_mlist);
4186
                val = procmsg_copy_messages(summaryview->tmp_mlist);
4187

    
4188
                folderview_update_item_foreach(summaryview->folder_table,
4189
                                               FALSE);
4190

    
4191
                g_slist_free(summaryview->tmp_mlist);
4192
                summaryview->tmp_mlist = NULL;
4193
        }
4194

    
4195
        g_hash_table_destroy(summaryview->folder_table);
4196
        summaryview->folder_table = NULL;
4197

    
4198
        return val;
4199
}
4200

    
4201
static gboolean summary_execute_delete_func(GtkTreeModel *model,
4202
                                            GtkTreePath *path,
4203
                                            GtkTreeIter *iter, gpointer data)
4204
{
4205
        SummaryView *summaryview = data;
4206
        MsgInfo *msginfo;
4207

    
4208
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4209

    
4210
        if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
4211
                summaryview->tmp_mlist =
4212
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4213
        }
4214

    
4215
        return FALSE;
4216
}
4217

    
4218
static gint summary_execute_delete(SummaryView *summaryview)
4219
{
4220
        FolderItem *trash = NULL;
4221
        PrefsAccount *ac;
4222
        gint val = 0;
4223

    
4224
        ac = account_find_from_item_property(summaryview->folder_item);
4225
        if (ac && ac->set_trash_folder && ac->trash_folder)
4226
                trash = folder_find_item_from_identifier(ac->trash_folder);
4227
        if (!trash)
4228
                trash = summaryview->folder_item->folder->trash;
4229
        if (!trash)
4230
                folder_get_default_trash();
4231

    
4232
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) {
4233
                g_return_val_if_fail(trash != NULL, 0);
4234
        }
4235

    
4236
        /* search deleting messages and execute */
4237
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4238
                               summary_execute_delete_func, summaryview);
4239

    
4240
        if (!summaryview->tmp_mlist) return 0;
4241

    
4242
        summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist);
4243

    
4244
        if (summaryview->folder_item != trash)
4245
                val = folder_item_move_msgs(trash, summaryview->tmp_mlist);
4246
        else
4247
                val = folder_item_remove_msgs(trash, summaryview->tmp_mlist);
4248

    
4249
        g_slist_free(summaryview->tmp_mlist);
4250
        summaryview->tmp_mlist = NULL;
4251

    
4252
        if (summaryview->folder_item != trash) {
4253
                folder_item_scan(trash);
4254
                folderview_update_item(trash, FALSE);
4255
        }
4256

    
4257
        return val == -1 ? -1 : 0;
4258
}
4259

    
4260
/* thread functions */
4261

    
4262
void summary_thread_build(SummaryView *summaryview)
4263
{
4264
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4265
        GtkTreeStore *store = summaryview->store;
4266
        GtkTreeIter iter, next;
4267
        GtkTreePath *path;
4268
        GSList *mlist;
4269
        GNode *root, *node;
4270
        GHashTable *node_table;
4271
        MsgInfo *msginfo;
4272
        gboolean valid;
4273
        FolderSortKey sort_key = SORT_BY_NONE;
4274
        FolderSortType sort_type = SORT_ASCENDING;
4275
        MsgInfo *selected_msg, *displayed_msg;
4276

    
4277
        if (!summaryview->folder_item)
4278
                return;
4279

    
4280
        summary_lock(summaryview);
4281

    
4282
        debug_print(_("Building threads..."));
4283
        STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
4284
        main_window_cursor_wait(summaryview->mainwin);
4285

    
4286
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4287
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4288
                                        0, 0, NULL, NULL, summaryview);
4289

    
4290
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4291
        displayed_msg = summary_get_msginfo
4292
                (summaryview, summaryview->displayed);
4293

    
4294
        summaryview->folder_item->threaded = TRUE;
4295

    
4296
        mlist = summary_get_msg_list(summaryview);
4297
        root = procmsg_get_thread_tree(mlist);
4298
        node_table = g_hash_table_new(NULL, NULL);
4299
        for (node = root->children; node != NULL; node = node->next) {
4300
                g_hash_table_insert(node_table, node->data, node);
4301
        }
4302

    
4303
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4304
                sort_key = summaryview->folder_item->sort_key;
4305
                sort_type = summaryview->folder_item->sort_type;
4306
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4307
        }
4308

    
4309
        valid = gtk_tree_model_get_iter_first(model, &next);
4310
        while (valid) {
4311
                iter = next;
4312
                valid = gtk_tree_model_iter_next(model, &next);
4313

    
4314
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4315
                node = g_hash_table_lookup(node_table, msginfo);
4316
                if (node) {
4317
                        GNode *cur;
4318
                        GtkTreeIter child;
4319
                        guint tdate;
4320

    
4321
                        for (cur = node->children; cur != NULL;
4322
                             cur = cur->next) {
4323
                                summary_insert_gnode(summaryview, store, &child,
4324
                                                     &iter, NULL, cur);
4325
                        }
4326

    
4327
                        tdate = procmsg_get_thread_date(node);
4328
                        gtk_tree_store_set(store, &iter, S_COL_TDATE, tdate, -1);
4329
                } else
4330
                        gtk_tree_store_remove(store, &iter);
4331
        }
4332

    
4333
        if (sort_key != SORT_BY_NONE)
4334
                summary_sort(summaryview, sort_key, sort_type);
4335

    
4336
        g_hash_table_destroy(node_table);
4337
        g_node_destroy(root);
4338
        g_slist_free(mlist);
4339

    
4340
        if (prefs_common.expand_thread)
4341
                gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4342

    
4343
        if (!summaryview->selected ||
4344
            (summaryview->selected &&
4345
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4346
                if (selected_msg &&
4347
                    gtkut_tree_model_find_by_column_data
4348
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4349
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4350
                }
4351
        } else
4352
                summary_scroll_to_selected(summaryview, TRUE);
4353

    
4354
        if (summaryview->displayed &&
4355
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4356
                if (displayed_msg &&
4357
                    gtkut_tree_model_find_by_column_data
4358
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4359
                        path = gtk_tree_model_get_path(model, &iter);
4360
                        gtk_tree_row_reference_free(summaryview->displayed);
4361
                        summaryview->displayed =
4362
                                gtk_tree_row_reference_new(model, path);
4363
                        gtk_tree_path_free(path);
4364
                } else {
4365
                        messageview_clear(summaryview->messageview);
4366
                        gtk_tree_row_reference_free(summaryview->displayed);
4367
                        summaryview->displayed = NULL;
4368
                }
4369
        }
4370

    
4371
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4372
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4373
                                          0, 0, NULL, NULL, summaryview);
4374

    
4375
        debug_print(_("done.\n"));
4376
        STATUSBAR_POP(summaryview->mainwin);
4377
        main_window_cursor_normal(summaryview->mainwin);
4378

    
4379
        summary_unlock(summaryview);
4380
}
4381

    
4382
static void summary_unthread_node_recursive(SummaryView *summaryview,
4383
                                            GtkTreeIter *iter,
4384
                                            GtkTreeIter *sibling)
4385
{
4386
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4387
        GtkTreeIter iter_, child;
4388
        MsgInfo *msginfo;
4389
        gboolean valid;
4390

    
4391
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4392
        gtk_tree_store_insert_after(GTK_TREE_STORE(model), &iter_,
4393
                                    NULL, sibling);
4394
        summary_set_row(summaryview, &iter_, msginfo);
4395
        *sibling = iter_;
4396

    
4397
        valid = gtk_tree_model_iter_children(model, &child, iter);
4398
        while (valid) {
4399
                summary_unthread_node_recursive(summaryview, &child, sibling);
4400
                valid = gtk_tree_model_iter_next(model, &child);
4401
        }
4402
}
4403

    
4404
static void summary_unthread_node(SummaryView *summaryview, GtkTreeIter *iter)
4405
{
4406
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4407
        GtkTreeIter child, sibling, next;
4408
        gboolean valid;
4409

    
4410
        sibling = *iter;
4411

    
4412
        valid = gtk_tree_model_iter_children(model, &next, iter);
4413
        while (valid) {
4414
                child = next;
4415
                valid = gtk_tree_model_iter_next(model, &next);
4416
                summary_unthread_node_recursive(summaryview, &child, &sibling);
4417
                gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
4418
        }
4419
}
4420

    
4421
void summary_unthread(SummaryView *summaryview)
4422
{
4423
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4424
        GtkTreeIter iter, next;
4425
        GtkTreePath *path;
4426
        gboolean valid;
4427
        FolderSortKey sort_key = SORT_BY_NONE;
4428
        FolderSortType sort_type = SORT_ASCENDING;
4429
        MsgInfo *selected_msg, *displayed_msg;
4430

    
4431
        summary_lock(summaryview);
4432

    
4433
        debug_print(_("Unthreading..."));
4434
        STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
4435
        main_window_cursor_wait(summaryview->mainwin);
4436

    
4437
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4438
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4439
                                        0, 0, NULL, NULL, summaryview);
4440

    
4441
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4442
        displayed_msg = summary_get_msginfo
4443
                (summaryview, summaryview->displayed);
4444

    
4445
        if (summaryview->folder_item)
4446
                summaryview->folder_item->threaded = FALSE;
4447

    
4448
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4449
                sort_key = summaryview->folder_item->sort_key;
4450
                sort_type = summaryview->folder_item->sort_type;
4451
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4452
        }
4453

    
4454
        valid = gtk_tree_model_get_iter_first(model, &next);
4455
        while (valid) {
4456
                iter = next;
4457
                valid = gtk_tree_model_iter_next(model, &next);
4458
                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
4459
                                   S_COL_TDATE, 0, -1);
4460
        }
4461

    
4462
        valid = gtk_tree_model_get_iter_first(model, &next);
4463
        while (valid) {
4464
                iter = next;
4465
                valid = gtk_tree_model_iter_next(model, &next);
4466
                summary_unthread_node(summaryview, &iter);
4467
        }
4468

    
4469
        if (sort_key != SORT_BY_NONE)
4470
                summary_sort(summaryview, sort_key, sort_type);
4471

    
4472
        if (!summaryview->selected ||
4473
            (summaryview->selected &&
4474
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4475
                if (selected_msg &&
4476
                    gtkut_tree_model_find_by_column_data
4477
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4478
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4479
                }
4480
        } else
4481
                summary_scroll_to_selected(summaryview, TRUE);
4482

    
4483
        if (summaryview->displayed &&
4484
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4485
                if (displayed_msg &&
4486
                    gtkut_tree_model_find_by_column_data
4487
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4488
                        path = gtk_tree_model_get_path(model, &iter);
4489
                        gtk_tree_row_reference_free(summaryview->displayed);
4490
                        summaryview->displayed =
4491
                                gtk_tree_row_reference_new(model, path);
4492
                        gtk_tree_path_free(path);
4493
                } else {
4494
                        messageview_clear(summaryview->messageview);
4495
                        gtk_tree_row_reference_free(summaryview->displayed);
4496
                        summaryview->displayed = NULL;
4497
                }
4498
        }
4499

    
4500
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4501
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4502
                                          0, 0, NULL, NULL, summaryview);
4503

    
4504
        debug_print(_("done.\n"));
4505
        STATUSBAR_POP(summaryview->mainwin);
4506
        main_window_cursor_normal(summaryview->mainwin);
4507

    
4508
        summary_unlock(summaryview);
4509
}
4510

    
4511
static gboolean summary_has_invalid_node(GtkTreeModel *model, GtkTreeIter *iter)
4512
{
4513
        GtkTreeIter child;
4514
        MsgInfo *msginfo;
4515
        gboolean valid;
4516

    
4517
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4518
        if (MSG_IS_INVALID(msginfo->flags))
4519
                return TRUE;
4520

    
4521
        valid = gtk_tree_model_iter_children(model, &child, iter);
4522
        while (valid) {
4523
                if (summary_has_invalid_node(model, &child))
4524
                        return TRUE;
4525
                valid = gtk_tree_model_iter_next(model, &child);
4526
        }
4527

    
4528
        return FALSE;
4529
}
4530

    
4531
static GNode *summary_get_modified_node(SummaryView *summaryview,
4532
                                        GtkTreeIter *iter,
4533
                                        GNode *parent, GNode *sibling)
4534
{
4535
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4536
        GNode *node = NULL, *new_sibling;
4537
        GtkTreeIter child;
4538
        MsgInfo *msginfo;
4539
        gboolean valid;
4540

    
4541
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4542

    
4543
        if (!MSG_IS_INVALID(msginfo->flags)) {
4544
                node = g_node_new(msginfo);
4545
                g_node_insert_after(parent, sibling, node);
4546
                parent = node;
4547
                sibling = NULL;
4548
        } else {
4549
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4550
                                                        msginfo);
4551
                if (summaryview->flt_mlist)
4552
                        summaryview->flt_mlist =
4553
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4554
                procmsg_msginfo_free(msginfo);
4555
        }
4556

    
4557
        valid = gtk_tree_model_iter_children(model, &child, iter);
4558

    
4559
        while (valid) {
4560
                new_sibling = summary_get_modified_node(summaryview, &child,
4561
                                                        parent, sibling);
4562
                if (new_sibling) {
4563
                        sibling = new_sibling;
4564
                        if (!node)
4565
                                node = sibling;
4566
                }
4567
                valid = gtk_tree_model_iter_next(model, &child);
4568
        }
4569

    
4570
        return node;
4571
}
4572

    
4573
#if 0
4574
static gboolean traverse(GNode *node, gpointer data)
4575
{
4576
        gint i;
4577

4578
        if (!node->data)
4579
                return FALSE;
4580
        for (i = 0; i < g_node_depth(node); i++)
4581
                g_print(" ");
4582
        g_print("%s\n", ((MsgInfo *)node->data)->subject);
4583
        return FALSE;
4584
}
4585
#endif
4586

    
4587
static void summary_modify_node(SummaryView *summaryview, GtkTreeIter *iter,
4588
                                GtkTreeIter *selected)
4589
{
4590
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4591
        MsgInfo *msginfo, *sel_msginfo = NULL;
4592
        GNode *root, *cur;
4593
        GtkTreeIter iter_, sibling;
4594
        GtkTreeIter *sibling_p = NULL;
4595
        GtkTreePath *path, *sel_path;
4596
        gboolean found = FALSE;
4597

    
4598
        if (!gtk_tree_model_iter_has_child(model, iter))
4599
                return;
4600
        if (!summary_has_invalid_node(model, iter))
4601
                return;
4602

    
4603
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4604

    
4605
        if (selected) {
4606
                path = gtk_tree_model_get_path(model, iter);
4607
                sel_path = gtk_tree_model_get_path(model, selected);
4608
                if (gtk_tree_path_compare(path, sel_path) == 0 ||
4609
                    gtk_tree_path_is_ancestor(path, sel_path))
4610
                        gtk_tree_model_get(model, selected,
4611
                                           S_COL_MSG_INFO, &sel_msginfo, -1);
4612
                gtk_tree_path_free(sel_path);
4613
                gtk_tree_path_free(path);
4614
        }
4615

    
4616
        root = g_node_new(NULL);
4617
        summary_get_modified_node(summaryview, iter, root, NULL);
4618

    
4619
        /* g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse, NULL); */
4620

    
4621
        sibling = *iter;
4622
        if (gtk_tree_model_iter_next(model, &sibling))
4623
                sibling_p = &sibling;
4624

    
4625
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
4626

    
4627
        for (cur = root->children; cur != NULL; cur = cur->next) {
4628
                summary_insert_gnode_before(summaryview, GTK_TREE_STORE(model),
4629
                                            &iter_, NULL, sibling_p, cur);
4630
                if (summaryview->folder_item->threaded &&
4631
                    prefs_common.expand_thread) {
4632
                        path = gtk_tree_model_get_path(model, &iter_);
4633
                        gtk_tree_view_expand_row
4634
                                (GTK_TREE_VIEW(summaryview->treeview),
4635
                                 path, TRUE);
4636
                        gtk_tree_path_free(path);
4637
                }
4638
                if (sel_msginfo && !found) {
4639
                        found = gtkut_tree_model_find_by_column_data
4640
                                (model, selected, &iter_,
4641
                                 S_COL_MSG_INFO, sel_msginfo);
4642
                }
4643
        }
4644

    
4645
        g_node_destroy(root);
4646

    
4647
        summaryview->folder_item->cache_dirty = TRUE;
4648
}
4649

    
4650
static void summary_modify_threads(SummaryView *summaryview)
4651
{
4652
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4653
        GtkTreeIter iter, next, selected, new_selected;
4654
        GtkTreeIter *selected_p = NULL;
4655
        GtkTreePath *prev_path = NULL;
4656
        gboolean valid, has_selection;
4657

    
4658
        summary_lock(summaryview);
4659

    
4660
        debug_print("Modifying threads for execution...");
4661

    
4662
        g_signal_handlers_block_by_func(summaryview->selection,
4663
                                        summary_selection_changed, summaryview);
4664

    
4665
        has_selection = gtkut_tree_row_reference_get_iter
4666
                (model, summaryview->selected, &selected);
4667
        if (has_selection) {
4668
                prev_path = gtk_tree_row_reference_get_path
4669
                        (summaryview->selected);
4670
                if (summary_find_nearest_msg(summaryview, &next, &selected)) {
4671
                        selected = next;
4672
                        selected_p = &selected;
4673
                } else
4674
                        has_selection = FALSE;
4675
        }
4676

    
4677
        valid = gtk_tree_model_get_iter_first(model, &next);
4678
        while (valid) {
4679
                iter = next;
4680
                valid = gtk_tree_model_iter_next(model, &next);
4681
                summary_modify_node(summaryview, &iter, selected_p);
4682
        }
4683

    
4684
        g_signal_handlers_unblock_by_func(summaryview->selection,
4685
                                          summary_selection_changed,
4686
                                          summaryview);
4687

    
4688
        if (summaryview->folder_item->cache_dirty)
4689
                summary_selection_list_free(summaryview);
4690

    
4691
        if (has_selection &&
4692
            !gtk_tree_row_reference_valid(summaryview->selected)) {
4693
                if (prev_path &&
4694
                    gtk_tree_model_get_iter(model, &new_selected, prev_path))
4695
                        selected = new_selected;
4696
                summary_select_row(summaryview, &selected, FALSE, FALSE);
4697
        }
4698
        gtk_tree_path_free(prev_path);
4699

    
4700
        debug_print("done.\n");
4701

    
4702
        summary_unlock(summaryview);
4703
}
4704

    
4705
void summary_expand_threads(SummaryView *summaryview)
4706
{
4707
        gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4708
}
4709

    
4710
void summary_collapse_threads(SummaryView *summaryview)
4711
{
4712
        gtk_tree_view_collapse_all(GTK_TREE_VIEW(summaryview->treeview));
4713
}
4714

    
4715
static gboolean summary_filter_func(GtkTreeModel *model, GtkTreePath *path,
4716
                                    GtkTreeIter *iter, gpointer data)
4717
{
4718
        SummaryView *summaryview = (SummaryView *)data;
4719
        MsgInfo *msginfo;
4720
        FilterInfo *fltinfo;
4721

    
4722
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4723

    
4724
        summaryview->flt_count++;
4725
        {
4726
                gchar msg[1024];
4727
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4728
                           summaryview->flt_count, summaryview->flt_total);
4729
                STATUSBAR_POP(summaryview->mainwin);
4730
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4731
                if ((summaryview->flt_count % 100) == 0) {
4732
                        GTK_EVENTS_FLUSH();
4733
                }
4734
        }
4735

    
4736
        fltinfo = filter_info_new();
4737
        fltinfo->flags = msginfo->flags;
4738
        filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo);
4739
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4740
            fltinfo->actions[FLT_ACTION_COPY] ||
4741
            fltinfo->actions[FLT_ACTION_DELETE] ||
4742
            fltinfo->actions[FLT_ACTION_EXEC] ||
4743
            fltinfo->actions[FLT_ACTION_EXEC_ASYNC] ||
4744
            fltinfo->actions[FLT_ACTION_MARK] ||
4745
            fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
4746
            fltinfo->actions[FLT_ACTION_MARK_READ] ||
4747
            fltinfo->actions[FLT_ACTION_FORWARD] ||
4748
            fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] ||
4749
            fltinfo->actions[FLT_ACTION_REDIRECT])
4750
                summaryview->filtered++;
4751

    
4752
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4753
                msginfo->flags = fltinfo->flags;
4754
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4755
                summaryview->folder_item->mark_dirty = TRUE;
4756
                summary_set_row(summaryview, iter, msginfo);
4757
                if (MSG_IS_IMAP(msginfo->flags)) {
4758
                        if (fltinfo->actions[FLT_ACTION_MARK])
4759
                                imap_msg_set_perm_flags(msginfo, MSG_MARKED);
4760
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4761
                                imap_msg_unset_perm_flags(msginfo,
4762
                                                          MSG_NEW|MSG_UNREAD);
4763
                }
4764
        }
4765

    
4766
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4767
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4768
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4769
                summary_delete_row(summaryview, iter);
4770

    
4771
        filter_info_free(fltinfo);
4772

    
4773
        return FALSE;
4774
}
4775

    
4776
static gboolean summary_filter_junk_func(GtkTreeModel *model, GtkTreePath *path,
4777
                                         GtkTreeIter *iter, gpointer data)
4778
{
4779
        SummaryView *summaryview = (SummaryView *)data;
4780
        MsgInfo *msginfo;
4781
        FilterInfo *fltinfo;
4782

    
4783
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4784

    
4785
        summaryview->flt_count++;
4786
        {
4787
                gchar msg[1024];
4788
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4789
                           summaryview->flt_count, summaryview->flt_total);
4790
                STATUSBAR_POP(summaryview->mainwin);
4791
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4792
                if ((summaryview->flt_count % 100) == 0) {
4793
                        GTK_EVENTS_FLUSH();
4794
                }
4795
        }
4796

    
4797
        fltinfo = filter_info_new();
4798
        fltinfo->flags = msginfo->flags;
4799
        filter_apply_msginfo(summaryview->junk_fltlist, msginfo, fltinfo);
4800

    
4801
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4802
            fltinfo->actions[FLT_ACTION_COPY] ||
4803
            fltinfo->actions[FLT_ACTION_DELETE] ||
4804
            fltinfo->actions[FLT_ACTION_MARK_READ])
4805
                summaryview->filtered++;
4806
        else if (fltinfo->error == FLT_ERROR_EXEC_FAILED ||
4807
                 fltinfo->last_exec_exit_status >= 3) {
4808
                if (summaryview->flt_count == 1) {
4809
                        g_warning("summary_filter_junk_func: junk filter command returned %d", fltinfo->last_exec_exit_status);
4810
                        alertpanel_error
4811
                                (_("Execution of the junk filter command failed.\n"
4812
                                   "Please check the junk mail control setting."));
4813
                }
4814
                return TRUE;
4815
        }
4816

    
4817
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4818
                msginfo->flags = fltinfo->flags;
4819
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4820
                summaryview->folder_item->mark_dirty = TRUE;
4821
                summary_set_row(summaryview, iter, msginfo);
4822
                if (MSG_IS_IMAP(msginfo->flags)) {
4823
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4824
                                imap_msg_unset_perm_flags(msginfo,
4825
                                                          MSG_NEW|MSG_UNREAD);
4826
                }
4827
        }
4828

    
4829
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4830
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4831
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4832
                summary_delete_row(summaryview, iter);
4833

    
4834
        filter_info_free(fltinfo);
4835

    
4836
        return FALSE;
4837
}
4838

    
4839
static void summary_filter_real(SummaryView *summaryview,
4840
                                GtkTreeModelForeachFunc func,
4841
                                gboolean selected_only)
4842
{
4843
        GList *rows;
4844
        FolderSortKey sort_key;
4845
        FolderSortType sort_type;
4846

    
4847
        if (!summaryview->folder_item) return;
4848

    
4849
        if (summary_is_locked(summaryview)) return;
4850
        summary_lock(summaryview);
4851

    
4852
        STATUSBAR_POP(summaryview->mainwin);
4853

    
4854
        debug_print(_("filtering..."));
4855
        STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
4856
        main_window_cursor_wait(summaryview->mainwin);
4857
        GTK_EVENTS_FLUSH();
4858

    
4859
        sort_key = summaryview->folder_item->sort_key;
4860
        sort_type = summaryview->folder_item->sort_type;
4861
        if (sort_key != SORT_BY_NONE)
4862
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4863

    
4864
        summaryview->filtered = 0;
4865
        summaryview->flt_count = 0;
4866

    
4867
        if (selected_only) {
4868
                rows = summary_get_selected_rows(summaryview);
4869
                summaryview->flt_total = g_list_length(rows);
4870

    
4871
                gtk_tree_selection_selected_foreach
4872
                        (summaryview->selection,
4873
                         (GtkTreeSelectionForeachFunc)func,
4874
                         summaryview);
4875
        } else {
4876
                summaryview->flt_total = summaryview->folder_item->total;
4877
                gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4878
                                       func, summaryview);
4879
        }
4880

    
4881
        if (sort_key != SORT_BY_NONE)
4882
                summary_sort(summaryview, sort_key, sort_type);
4883

    
4884
        summary_unlock(summaryview);
4885

    
4886
        if (prefs_common.immediate_exec)
4887
                summary_execute(summaryview);
4888
        else
4889
                summary_status_show(summaryview);
4890

    
4891
        folderview_update_all_updated(FALSE);
4892

    
4893
        debug_print(_("done.\n"));
4894
        STATUSBAR_POP(summaryview->mainwin);
4895
        main_window_cursor_normal(summaryview->mainwin);
4896

    
4897
        if (summaryview->filtered > 0) {
4898
                gchar result_msg[BUFFSIZE];
4899
                g_snprintf(result_msg, sizeof(result_msg),
4900
                           _("%d message(s) have been filtered."),
4901
                           summaryview->filtered);
4902
                STATUSBAR_PUSH(summaryview->mainwin, result_msg);
4903
        }
4904
        summaryview->filtered = 0;
4905
        summaryview->flt_count = 0;
4906
        summaryview->flt_total = 0;
4907
}
4908

    
4909
void summary_filter(SummaryView *summaryview, gboolean selected_only)
4910
{
4911
        if (prefs_common.fltlist)
4912
                summary_filter_real(summaryview, summary_filter_func,
4913
                                    selected_only);
4914
}
4915

    
4916
void summary_filter_junk(SummaryView *summaryview, gboolean selected_only)
4917
{
4918
        FilterRule *rule;
4919
        GSList junk_fltlist = {NULL, NULL};
4920
        FolderItem *item = summaryview->folder_item;
4921
        FolderItem *junk = NULL;
4922

    
4923
        if (!item)
4924
                return;
4925

    
4926
        if (item->folder)
4927
                junk = folder_get_junk(item->folder);
4928
        rule = filter_junk_rule_create(NULL, junk, TRUE);
4929
        if (rule) {
4930
                junk_fltlist.data = rule;
4931
                summaryview->junk_fltlist = &junk_fltlist;
4932
                summary_filter_real(summaryview, summary_filter_junk_func,
4933
                                    selected_only);
4934
                summaryview->junk_fltlist = NULL;
4935
                filter_rule_free(rule);
4936
        }
4937
}
4938

    
4939
void summary_filter_open(SummaryView *summaryview, FilterCreateType type)
4940
{
4941
        GtkTreeIter iter;
4942
        MsgInfo *msginfo = NULL;
4943
        gchar *header = NULL;
4944
        gchar *key = NULL;
4945

    
4946
        if (!summaryview->selected) return;
4947
        if (!gtkut_tree_row_reference_get_iter
4948
                (GTK_TREE_MODEL(summaryview->store),
4949
                 summaryview->selected, &iter))
4950
                return;
4951

    
4952
        GET_MSG_INFO(msginfo, &iter);
4953
        if (!msginfo) return;
4954

    
4955
        filter_get_keyword_from_msg(msginfo, &header, &key, type);
4956
        prefs_filter_open(msginfo, header, key);
4957

    
4958
        g_free(header);
4959
        g_free(key);
4960
}
4961

    
4962
static void summary_junk_func(GtkTreeModel *model, GtkTreePath *path,
4963
                              GtkTreeIter *iter, gpointer data)
4964
{
4965
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
4966
        FilterAction action1 = {FLT_ACTION_EXEC, NULL, 0};
4967
        FilterAction action2 = {FLT_ACTION_MOVE, NULL, 0};
4968
        FilterAction action3 = {FLT_ACTION_MARK_READ, NULL, 0};
4969
        SummaryView *summaryview = (SummaryView *)data;
4970
        MsgInfo *msginfo;
4971
        FilterInfo *fltinfo;
4972
        gchar *file;
4973
        gchar *junk_id = NULL;
4974
        gint ret;
4975

    
4976
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4977
        file = procmsg_get_message_file(msginfo);
4978
        g_return_if_fail(file != NULL);
4979

    
4980
        if (summaryview->to_folder)
4981
                junk_id = folder_item_get_identifier(summaryview->to_folder);
4982

    
4983
        action1.str_value = prefs_common.junk_learncmd;
4984
        action2.str_value = junk_id;
4985

    
4986
        rule.action_list = g_slist_append(rule.action_list, &action1);
4987
        if (junk_id)
4988
                rule.action_list = g_slist_append(rule.action_list, &action2);
4989
        if (prefs_common.mark_junk_as_read)
4990
                rule.action_list = g_slist_append(rule.action_list, &action3);
4991

    
4992
        fltinfo = filter_info_new();
4993
        fltinfo->flags = msginfo->flags;
4994

    
4995
        ret = filter_action_exec(&rule, msginfo, file, fltinfo);
4996

    
4997
        if (ret < 0 || fltinfo->last_exec_exit_status != 0) {
4998
                g_warning("summary_junk_func: junk filter command returned %d",
4999
                          fltinfo->last_exec_exit_status);
5000
                alertpanel_error
5001
                        (_("Execution of the junk filter command failed.\n"
5002
                           "Please check the junk mail control setting."));
5003
        } else {
5004
                if (ret == 0 &&
5005
                    msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
5006
                        msginfo->flags = fltinfo->flags;
5007
                        summary_set_row(summaryview, iter, msginfo);
5008
                        if (MSG_IS_IMAP(msginfo->flags)) {
5009
                                if (fltinfo->actions[FLT_ACTION_MARK_READ])
5010
                                        imap_msg_unset_perm_flags
5011
                                                (msginfo, MSG_NEW | MSG_UNREAD);
5012
                        }
5013
                }
5014
                if (ret == 0 && fltinfo->actions[FLT_ACTION_MOVE] &&
5015
                    fltinfo->move_dest)
5016
                        summary_move_row_to(summaryview, iter,
5017
                                            fltinfo->move_dest);
5018
        }
5019

    
5020
        filter_info_free(fltinfo);
5021
        g_slist_free(rule.action_list);
5022
        g_free(junk_id);
5023
        g_free(file);
5024
}
5025

    
5026
static void summary_not_junk_func(GtkTreeModel *model, GtkTreePath *path,
5027
                                  GtkTreeIter *iter, gpointer data)
5028
{
5029
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
5030
        FilterAction action = {FLT_ACTION_EXEC, NULL, 0};
5031
        MsgInfo *msginfo;
5032
        FilterInfo *fltinfo;
5033
        gchar *file;
5034
        gint ret;
5035

    
5036
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
5037
        file = procmsg_get_message_file(msginfo);
5038
        g_return_if_fail(file != NULL);
5039

    <