Statistics
| Revision:

root / src / summaryview.c @ 3258

History | View | Annotate | Download (183 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2013 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
        FileselFileType types[4] = {{NULL, NULL}};
3895
        gint selected_type = 0;
3896
        gint result;
3897
        gboolean all_headers;
3898

    
3899
        if (!summaryview->selected) return;
3900
        if (!gtkut_tree_row_reference_get_iter
3901
                (GTK_TREE_MODEL(summaryview->store),
3902
                 summaryview->selected, &iter))
3903
                return;
3904

    
3905
        GET_MSG_INFO(msginfo, &iter);
3906
        if (!msginfo) return;
3907

    
3908
        if (msginfo->subject && *msginfo->subject) {
3909
                filename = g_strdup_printf("%s.eml", msginfo->subject);
3910
        } else {
3911
                filename = g_strdup_printf("%u.eml", msginfo->msgnum);
3912
        }
3913
        subst_for_filename(filename);
3914

    
3915
        types[0].type = _("Original (EML/RFC 822)");
3916
        types[0].ext = "eml";
3917
        types[1].type = _("Text");
3918
        types[1].ext = "txt";
3919
        types[2].type = _("Text (UTF-8)");
3920
        types[2].ext = "txt";
3921
        
3922
        dest = filesel_save_as_type(filename, types, 0, &selected_type);
3923

    
3924
        g_free(filename);
3925
        if (!dest)
3926
                return;
3927

    
3928
        debug_print("summary_save_as: selected_type: %d\n", selected_type);
3929

    
3930
        all_headers = summaryview->messageview->textview->show_all_headers;
3931

    
3932
        if (selected_type == 1) {
3933
                result = procmsg_save_message_as_text(msginfo, dest, conv_get_locale_charset_str(), all_headers);
3934
        } else if (selected_type == 2) {
3935
                result = procmsg_save_message_as_text(msginfo, dest, NULL, all_headers);
3936
        } else {
3937
                src = procmsg_get_message_file(msginfo);
3938
                result = copy_file(src, dest, TRUE);
3939
                g_free(src);
3940
        }
3941

    
3942
        if (result < 0) {
3943
                gchar *utf8_dest;
3944

    
3945
                utf8_dest = conv_filename_to_utf8(dest);
3946
                alertpanel_error(_("Can't save the file `%s'."),
3947
                                 g_basename(utf8_dest));
3948
                g_free(utf8_dest);
3949
        }
3950

    
3951
        g_free(dest);
3952
}
3953

    
3954
void summary_print(SummaryView *summaryview)
3955
{
3956
        GSList *mlist;
3957
        gboolean all_headers;
3958

    
3959
        all_headers = summaryview->messageview->textview->show_all_headers;
3960
        mlist = summary_get_selected_msg_list(summaryview);
3961
        if (!mlist)
3962
                return;
3963
        printing_print_messages(mlist, all_headers);
3964
        g_slist_free(mlist);
3965
}
3966

    
3967
gboolean summary_execute(SummaryView *summaryview)
3968
{
3969
        gint val = 0;
3970

    
3971
        if (!summaryview->folder_item) return FALSE;
3972

    
3973
        if (summary_is_locked(summaryview)) return FALSE;
3974
        summary_lock(summaryview);
3975

    
3976
        val |= summary_execute_move(summaryview);
3977
        val |= summary_execute_copy(summaryview);
3978
        val |= summary_execute_delete(summaryview);
3979

    
3980
        summary_unlock(summaryview);
3981

    
3982
        summary_remove_invalid_messages(summaryview);
3983

    
3984
        statusbar_pop_all();
3985
        STATUSBAR_POP(summaryview->mainwin);
3986

    
3987
        if (val != 0) {
3988
                alertpanel_error(_("Error occurred while processing messages."));
3989
        }
3990

    
3991
        return TRUE;
3992
}
3993

    
3994
static void summary_remove_invalid_messages(SummaryView *summaryview)
3995
{
3996
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3997
        MsgInfo *disp_msginfo = NULL, *msginfo;
3998
        FolderItem *item = summaryview->folder_item;
3999
        GtkTreeIter iter, next;
4000
        GtkTreePath *path;
4001
        gboolean valid;
4002

    
4003
        /* get currently displayed message */
4004
        if (summaryview->displayed) {
4005
                GtkTreeIter displayed;
4006

    
4007
                valid = gtkut_tree_row_reference_get_iter
4008
                        (model, summaryview->displayed, &displayed);
4009
                if (valid) {
4010
                        gtk_tree_model_get(model, &displayed,
4011
                                           S_COL_MSG_INFO, &disp_msginfo, -1);
4012
                        if (MSG_IS_INVALID(disp_msginfo->flags)) {
4013
                                valid = FALSE;
4014
                                disp_msginfo = NULL;
4015
                        }
4016
                }
4017
                if (!valid) {
4018
                        /* g_print("displayed became invalid before removing\n"); */
4019
                        messageview_clear(summaryview->messageview);
4020
                        gtk_tree_row_reference_free(summaryview->displayed);
4021
                        summaryview->displayed = NULL;
4022
                }
4023
        }
4024

    
4025
        if (item->threaded)
4026
                summary_modify_threads(summaryview);
4027

    
4028
        /* update selection */
4029
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
4030
                                                  &iter);
4031
        if (valid) {
4032
                valid = summary_find_nearest_msg(summaryview, &next, &iter);
4033
                if (valid) {
4034
                        gboolean display;
4035

    
4036
                        gtk_tree_model_get(model, &next,
4037
                                           S_COL_MSG_INFO, &msginfo, -1);
4038
                        if (disp_msginfo && disp_msginfo == msginfo) {
4039
                                /* g_print("replace displayed\n"); */
4040
                                path = gtk_tree_model_get_path(model, &next);
4041
                                gtk_tree_row_reference_free
4042
                                        (summaryview->displayed);
4043
                                summaryview->displayed =
4044
                                        gtk_tree_row_reference_new(model, path);
4045
                                gtk_tree_path_free(path);
4046
                        }
4047
                        display = prefs_common.immediate_exec &&
4048
                                messageview_is_visible
4049
                                        (summaryview->messageview);
4050
                        summary_select_row
4051
                                (summaryview, &next, display, FALSE);
4052
                        if (display)
4053
                                summary_mark_displayed_read(summaryview, &next);
4054
                }
4055
        }
4056
        if (!valid)
4057
                summary_unselect_all(summaryview);
4058

    
4059
        for (valid = gtk_tree_model_get_iter_first(model, &iter);
4060
             valid == TRUE; iter = next) {
4061
                next = iter;
4062
                valid = gtkut_tree_model_next(model, &next);
4063

    
4064
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4065
                if (!MSG_IS_INVALID(msginfo->flags))
4066
                        continue;
4067

    
4068
                if (gtk_tree_model_iter_has_child(model, &iter)) {
4069
                        g_warning("summary_remove_invalid_messages(): "
4070
                                  "tried to remove row which has child\n");
4071
                        continue;
4072
                }
4073

    
4074
                gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
4075
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4076
                                                        msginfo);
4077
                if (summaryview->flt_mlist)
4078
                        summaryview->flt_mlist =
4079
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4080
                procmsg_msginfo_free(msginfo);
4081

    
4082
                item->cache_dirty = TRUE;
4083
        }
4084

    
4085
        /* selection list becomes invalid if modified */
4086
        if (item->cache_dirty)
4087
                summary_selection_list_free(summaryview);
4088

    
4089
        if (summaryview->displayed &&
4090
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4091
                /* g_print("displayed became invalid after removing. searching disp_msginfo...\n"); */
4092
                if (disp_msginfo &&
4093
                    gtkut_tree_model_find_by_column_data
4094
                        (model, &iter, NULL, S_COL_MSG_INFO, disp_msginfo)) {
4095
                        /* g_print("replace displayed\n"); */
4096
                        path = gtk_tree_model_get_path(model, &iter);
4097
                        gtk_tree_row_reference_free(summaryview->displayed);
4098
                        summaryview->displayed =
4099
                                gtk_tree_row_reference_new(model, path);
4100
                        gtk_tree_path_free(path);
4101
                } else {
4102
                        messageview_clear(summaryview->messageview);
4103
                        gtk_tree_row_reference_free(summaryview->displayed);
4104
                        summaryview->displayed = NULL;
4105
                }
4106
        }
4107

    
4108
        if (gtk_tree_model_get_iter_first(model, &iter))
4109
                gtk_widget_grab_focus(summaryview->treeview);
4110
        else {
4111
                menu_set_insensitive_all
4112
                        (GTK_MENU_SHELL(summaryview->popupmenu));
4113
                gtk_widget_grab_focus(summaryview->folderview->treeview);
4114
        }
4115

    
4116
        summary_write_cache(summaryview);
4117

    
4118
        summary_update_status(summaryview);
4119
        summary_status_show(summaryview);
4120
}
4121

    
4122
static gboolean summary_execute_move_func(GtkTreeModel *model,
4123
                                          GtkTreePath *path, GtkTreeIter *iter,
4124
                                          gpointer data)
4125
{
4126
        SummaryView *summaryview = data;
4127
        MsgInfo *msginfo;
4128

    
4129
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4130

    
4131
        if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
4132
                g_hash_table_insert(summaryview->folder_table,
4133
                                    msginfo->to_folder, GINT_TO_POINTER(1));
4134

    
4135
                summaryview->tmp_mlist =
4136
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4137

    
4138
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
4139
                summary_set_row(summaryview, iter, msginfo);
4140
        }
4141

    
4142
        return FALSE;
4143
}
4144

    
4145
static gint summary_execute_move(SummaryView *summaryview)
4146
{
4147
        gint val = 0;
4148

    
4149
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
4150

    
4151
        /* search moving messages and execute */
4152
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4153
                               summary_execute_move_func, summaryview);
4154

    
4155
        if (summaryview->tmp_mlist) {
4156
                summaryview->tmp_mlist =
4157
                        g_slist_reverse(summaryview->tmp_mlist);
4158
                val = procmsg_move_messages(summaryview->tmp_mlist);
4159

    
4160
                folderview_update_item_foreach(summaryview->folder_table,
4161
                                               FALSE);
4162

    
4163
                g_slist_free(summaryview->tmp_mlist);
4164
                summaryview->tmp_mlist = NULL;
4165
        }
4166

    
4167
        g_hash_table_destroy(summaryview->folder_table);
4168
        summaryview->folder_table = NULL;
4169

    
4170
        return val;
4171
}
4172

    
4173
static gboolean summary_execute_copy_func(GtkTreeModel *model,
4174
                                          GtkTreePath *path, GtkTreeIter *iter,
4175
                                          gpointer data)
4176
{
4177
        SummaryView *summaryview = data;
4178
        MsgInfo *msginfo;
4179

    
4180
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4181

    
4182
        if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
4183
                g_hash_table_insert(summaryview->folder_table,
4184
                                    msginfo->to_folder, GINT_TO_POINTER(1));
4185

    
4186
                summaryview->tmp_mlist =
4187
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4188

    
4189
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
4190
                summary_set_row(summaryview, iter, msginfo);
4191
        }
4192

    
4193
        return FALSE;
4194
}
4195

    
4196
static gint summary_execute_copy(SummaryView *summaryview)
4197
{
4198
        gint val = 0;
4199

    
4200
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
4201

    
4202
        /* search copying messages and execute */
4203
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4204
                               summary_execute_copy_func, summaryview);
4205

    
4206
        if (summaryview->tmp_mlist) {
4207
                summaryview->tmp_mlist =
4208
                        g_slist_reverse(summaryview->tmp_mlist);
4209
                val = procmsg_copy_messages(summaryview->tmp_mlist);
4210

    
4211
                folderview_update_item_foreach(summaryview->folder_table,
4212
                                               FALSE);
4213

    
4214
                g_slist_free(summaryview->tmp_mlist);
4215
                summaryview->tmp_mlist = NULL;
4216
        }
4217

    
4218
        g_hash_table_destroy(summaryview->folder_table);
4219
        summaryview->folder_table = NULL;
4220

    
4221
        return val;
4222
}
4223

    
4224
static gboolean summary_execute_delete_func(GtkTreeModel *model,
4225
                                            GtkTreePath *path,
4226
                                            GtkTreeIter *iter, gpointer data)
4227
{
4228
        SummaryView *summaryview = data;
4229
        MsgInfo *msginfo;
4230

    
4231
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4232

    
4233
        if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
4234
                summaryview->tmp_mlist =
4235
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4236
        }
4237

    
4238
        return FALSE;
4239
}
4240

    
4241
static gint summary_execute_delete(SummaryView *summaryview)
4242
{
4243
        FolderItem *trash = NULL;
4244
        PrefsAccount *ac;
4245
        gint val = 0;
4246

    
4247
        ac = account_find_from_item_property(summaryview->folder_item);
4248
        if (ac && ac->set_trash_folder && ac->trash_folder)
4249
                trash = folder_find_item_from_identifier(ac->trash_folder);
4250
        if (!trash)
4251
                trash = summaryview->folder_item->folder->trash;
4252
        if (!trash)
4253
                folder_get_default_trash();
4254

    
4255
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) {
4256
                g_return_val_if_fail(trash != NULL, 0);
4257
        }
4258

    
4259
        /* search deleting messages and execute */
4260
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4261
                               summary_execute_delete_func, summaryview);
4262

    
4263
        if (!summaryview->tmp_mlist) return 0;
4264

    
4265
        summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist);
4266

    
4267
        if (summaryview->folder_item != trash)
4268
                val = folder_item_move_msgs(trash, summaryview->tmp_mlist);
4269
        else
4270
                val = folder_item_remove_msgs(trash, summaryview->tmp_mlist);
4271

    
4272
        g_slist_free(summaryview->tmp_mlist);
4273
        summaryview->tmp_mlist = NULL;
4274

    
4275
        if (summaryview->folder_item != trash) {
4276
                folder_item_scan(trash);
4277
                folderview_update_item(trash, FALSE);
4278
        }
4279

    
4280
        return val == -1 ? -1 : 0;
4281
}
4282

    
4283
/* thread functions */
4284

    
4285
void summary_thread_build(SummaryView *summaryview)
4286
{
4287
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4288
        GtkTreeStore *store = summaryview->store;
4289
        GtkTreeIter iter, next;
4290
        GtkTreePath *path;
4291
        GSList *mlist;
4292
        GNode *root, *node;
4293
        GHashTable *node_table;
4294
        MsgInfo *msginfo;
4295
        gboolean valid;
4296
        FolderSortKey sort_key = SORT_BY_NONE;
4297
        FolderSortType sort_type = SORT_ASCENDING;
4298
        MsgInfo *selected_msg, *displayed_msg;
4299

    
4300
        if (!summaryview->folder_item)
4301
                return;
4302

    
4303
        summary_lock(summaryview);
4304

    
4305
        debug_print(_("Building threads..."));
4306
        STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
4307
        main_window_cursor_wait(summaryview->mainwin);
4308

    
4309
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4310
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4311
                                        0, 0, NULL, NULL, summaryview);
4312

    
4313
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4314
        displayed_msg = summary_get_msginfo
4315
                (summaryview, summaryview->displayed);
4316

    
4317
        summaryview->folder_item->threaded = TRUE;
4318

    
4319
        mlist = summary_get_msg_list(summaryview);
4320
        root = procmsg_get_thread_tree(mlist);
4321
        node_table = g_hash_table_new(NULL, NULL);
4322
        for (node = root->children; node != NULL; node = node->next) {
4323
                g_hash_table_insert(node_table, node->data, node);
4324
        }
4325

    
4326
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4327
                sort_key = summaryview->folder_item->sort_key;
4328
                sort_type = summaryview->folder_item->sort_type;
4329
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4330
        }
4331

    
4332
        valid = gtk_tree_model_get_iter_first(model, &next);
4333
        while (valid) {
4334
                iter = next;
4335
                valid = gtk_tree_model_iter_next(model, &next);
4336

    
4337
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4338
                node = g_hash_table_lookup(node_table, msginfo);
4339
                if (node) {
4340
                        GNode *cur;
4341
                        GtkTreeIter child;
4342
                        guint tdate;
4343

    
4344
                        for (cur = node->children; cur != NULL;
4345
                             cur = cur->next) {
4346
                                summary_insert_gnode(summaryview, store, &child,
4347
                                                     &iter, NULL, cur);
4348
                        }
4349

    
4350
                        tdate = procmsg_get_thread_date(node);
4351
                        gtk_tree_store_set(store, &iter, S_COL_TDATE, tdate, -1);
4352
                } else
4353
                        gtk_tree_store_remove(store, &iter);
4354
        }
4355

    
4356
        if (sort_key != SORT_BY_NONE)
4357
                summary_sort(summaryview, sort_key, sort_type);
4358

    
4359
        g_hash_table_destroy(node_table);
4360
        g_node_destroy(root);
4361
        g_slist_free(mlist);
4362

    
4363
        if (prefs_common.expand_thread)
4364
                gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4365

    
4366
        if (!summaryview->selected ||
4367
            (summaryview->selected &&
4368
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4369
                if (selected_msg &&
4370
                    gtkut_tree_model_find_by_column_data
4371
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4372
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4373
                }
4374
        } else
4375
                summary_scroll_to_selected(summaryview, TRUE);
4376

    
4377
        if (summaryview->displayed &&
4378
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4379
                if (displayed_msg &&
4380
                    gtkut_tree_model_find_by_column_data
4381
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4382
                        path = gtk_tree_model_get_path(model, &iter);
4383
                        gtk_tree_row_reference_free(summaryview->displayed);
4384
                        summaryview->displayed =
4385
                                gtk_tree_row_reference_new(model, path);
4386
                        gtk_tree_path_free(path);
4387
                } else {
4388
                        messageview_clear(summaryview->messageview);
4389
                        gtk_tree_row_reference_free(summaryview->displayed);
4390
                        summaryview->displayed = NULL;
4391
                }
4392
        }
4393

    
4394
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4395
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4396
                                          0, 0, NULL, NULL, summaryview);
4397

    
4398
        debug_print(_("done.\n"));
4399
        STATUSBAR_POP(summaryview->mainwin);
4400
        main_window_cursor_normal(summaryview->mainwin);
4401

    
4402
        summary_unlock(summaryview);
4403
}
4404

    
4405
static void summary_unthread_node_recursive(SummaryView *summaryview,
4406
                                            GtkTreeIter *iter,
4407
                                            GtkTreeIter *sibling)
4408
{
4409
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4410
        GtkTreeIter iter_, child;
4411
        MsgInfo *msginfo;
4412
        gboolean valid;
4413

    
4414
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4415
        gtk_tree_store_insert_after(GTK_TREE_STORE(model), &iter_,
4416
                                    NULL, sibling);
4417
        summary_set_row(summaryview, &iter_, msginfo);
4418
        *sibling = iter_;
4419

    
4420
        valid = gtk_tree_model_iter_children(model, &child, iter);
4421
        while (valid) {
4422
                summary_unthread_node_recursive(summaryview, &child, sibling);
4423
                valid = gtk_tree_model_iter_next(model, &child);
4424
        }
4425
}
4426

    
4427
static void summary_unthread_node(SummaryView *summaryview, GtkTreeIter *iter)
4428
{
4429
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4430
        GtkTreeIter child, sibling, next;
4431
        gboolean valid;
4432

    
4433
        sibling = *iter;
4434

    
4435
        valid = gtk_tree_model_iter_children(model, &next, iter);
4436
        while (valid) {
4437
                child = next;
4438
                valid = gtk_tree_model_iter_next(model, &next);
4439
                summary_unthread_node_recursive(summaryview, &child, &sibling);
4440
                gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
4441
        }
4442
}
4443

    
4444
void summary_unthread(SummaryView *summaryview)
4445
{
4446
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4447
        GtkTreeIter iter, next;
4448
        GtkTreePath *path;
4449
        gboolean valid;
4450
        FolderSortKey sort_key = SORT_BY_NONE;
4451
        FolderSortType sort_type = SORT_ASCENDING;
4452
        MsgInfo *selected_msg, *displayed_msg;
4453

    
4454
        summary_lock(summaryview);
4455

    
4456
        debug_print(_("Unthreading..."));
4457
        STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
4458
        main_window_cursor_wait(summaryview->mainwin);
4459

    
4460
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4461
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4462
                                        0, 0, NULL, NULL, summaryview);
4463

    
4464
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4465
        displayed_msg = summary_get_msginfo
4466
                (summaryview, summaryview->displayed);
4467

    
4468
        if (summaryview->folder_item)
4469
                summaryview->folder_item->threaded = FALSE;
4470

    
4471
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4472
                sort_key = summaryview->folder_item->sort_key;
4473
                sort_type = summaryview->folder_item->sort_type;
4474
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4475
        }
4476

    
4477
        valid = gtk_tree_model_get_iter_first(model, &next);
4478
        while (valid) {
4479
                iter = next;
4480
                valid = gtk_tree_model_iter_next(model, &next);
4481
                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
4482
                                   S_COL_TDATE, 0, -1);
4483
        }
4484

    
4485
        valid = gtk_tree_model_get_iter_first(model, &next);
4486
        while (valid) {
4487
                iter = next;
4488
                valid = gtk_tree_model_iter_next(model, &next);
4489
                summary_unthread_node(summaryview, &iter);
4490
        }
4491

    
4492
        if (sort_key != SORT_BY_NONE)
4493
                summary_sort(summaryview, sort_key, sort_type);
4494

    
4495
        if (!summaryview->selected ||
4496
            (summaryview->selected &&
4497
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4498
                if (selected_msg &&
4499
                    gtkut_tree_model_find_by_column_data
4500
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4501
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4502
                }
4503
        } else
4504
                summary_scroll_to_selected(summaryview, TRUE);
4505

    
4506
        if (summaryview->displayed &&
4507
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4508
                if (displayed_msg &&
4509
                    gtkut_tree_model_find_by_column_data
4510
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4511
                        path = gtk_tree_model_get_path(model, &iter);
4512
                        gtk_tree_row_reference_free(summaryview->displayed);
4513
                        summaryview->displayed =
4514
                                gtk_tree_row_reference_new(model, path);
4515
                        gtk_tree_path_free(path);
4516
                } else {
4517
                        messageview_clear(summaryview->messageview);
4518
                        gtk_tree_row_reference_free(summaryview->displayed);
4519
                        summaryview->displayed = NULL;
4520
                }
4521
        }
4522

    
4523
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4524
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4525
                                          0, 0, NULL, NULL, summaryview);
4526

    
4527
        debug_print(_("done.\n"));
4528
        STATUSBAR_POP(summaryview->mainwin);
4529
        main_window_cursor_normal(summaryview->mainwin);
4530

    
4531
        summary_unlock(summaryview);
4532
}
4533

    
4534
static gboolean summary_has_invalid_node(GtkTreeModel *model, GtkTreeIter *iter)
4535
{
4536
        GtkTreeIter child;
4537
        MsgInfo *msginfo;
4538
        gboolean valid;
4539

    
4540
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4541
        if (MSG_IS_INVALID(msginfo->flags))
4542
                return TRUE;
4543

    
4544
        valid = gtk_tree_model_iter_children(model, &child, iter);
4545
        while (valid) {
4546
                if (summary_has_invalid_node(model, &child))
4547
                        return TRUE;
4548
                valid = gtk_tree_model_iter_next(model, &child);
4549
        }
4550

    
4551
        return FALSE;
4552
}
4553

    
4554
static GNode *summary_get_modified_node(SummaryView *summaryview,
4555
                                        GtkTreeIter *iter,
4556
                                        GNode *parent, GNode *sibling)
4557
{
4558
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4559
        GNode *node = NULL, *new_sibling;
4560
        GtkTreeIter child;
4561
        MsgInfo *msginfo;
4562
        gboolean valid;
4563

    
4564
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4565

    
4566
        if (!MSG_IS_INVALID(msginfo->flags)) {
4567
                node = g_node_new(msginfo);
4568
                g_node_insert_after(parent, sibling, node);
4569
                parent = node;
4570
                sibling = NULL;
4571
        } else {
4572
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4573
                                                        msginfo);
4574
                if (summaryview->flt_mlist)
4575
                        summaryview->flt_mlist =
4576
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4577
                procmsg_msginfo_free(msginfo);
4578
        }
4579

    
4580
        valid = gtk_tree_model_iter_children(model, &child, iter);
4581

    
4582
        while (valid) {
4583
                new_sibling = summary_get_modified_node(summaryview, &child,
4584
                                                        parent, sibling);
4585
                if (new_sibling) {
4586
                        sibling = new_sibling;
4587
                        if (!node)
4588
                                node = sibling;
4589
                }
4590
                valid = gtk_tree_model_iter_next(model, &child);
4591
        }
4592

    
4593
        return node;
4594
}
4595

    
4596
#if 0
4597
static gboolean traverse(GNode *node, gpointer data)
4598
{
4599
        gint i;
4600

4601
        if (!node->data)
4602
                return FALSE;
4603
        for (i = 0; i < g_node_depth(node); i++)
4604
                g_print(" ");
4605
        g_print("%s\n", ((MsgInfo *)node->data)->subject);
4606
        return FALSE;
4607
}
4608
#endif
4609

    
4610
static void summary_modify_node(SummaryView *summaryview, GtkTreeIter *iter,
4611
                                GtkTreeIter *selected)
4612
{
4613
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4614
        MsgInfo *msginfo, *sel_msginfo = NULL;
4615
        GNode *root, *cur;
4616
        GtkTreeIter iter_, sibling;
4617
        GtkTreeIter *sibling_p = NULL;
4618
        GtkTreePath *path, *sel_path;
4619
        gboolean found = FALSE;
4620

    
4621
        if (!gtk_tree_model_iter_has_child(model, iter))
4622
                return;
4623
        if (!summary_has_invalid_node(model, iter))
4624
                return;
4625

    
4626
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4627

    
4628
        if (selected) {
4629
                path = gtk_tree_model_get_path(model, iter);
4630
                sel_path = gtk_tree_model_get_path(model, selected);
4631
                if (gtk_tree_path_compare(path, sel_path) == 0 ||
4632
                    gtk_tree_path_is_ancestor(path, sel_path))
4633
                        gtk_tree_model_get(model, selected,
4634
                                           S_COL_MSG_INFO, &sel_msginfo, -1);
4635
                gtk_tree_path_free(sel_path);
4636
                gtk_tree_path_free(path);
4637
        }
4638

    
4639
        root = g_node_new(NULL);
4640
        summary_get_modified_node(summaryview, iter, root, NULL);
4641

    
4642
        /* g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse, NULL); */
4643

    
4644
        sibling = *iter;
4645
        if (gtk_tree_model_iter_next(model, &sibling))
4646
                sibling_p = &sibling;
4647

    
4648
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
4649

    
4650
        for (cur = root->children; cur != NULL; cur = cur->next) {
4651
                summary_insert_gnode_before(summaryview, GTK_TREE_STORE(model),
4652
                                            &iter_, NULL, sibling_p, cur);
4653
                if (summaryview->folder_item->threaded &&
4654
                    prefs_common.expand_thread) {
4655
                        path = gtk_tree_model_get_path(model, &iter_);
4656
                        gtk_tree_view_expand_row
4657
                                (GTK_TREE_VIEW(summaryview->treeview),
4658
                                 path, TRUE);
4659
                        gtk_tree_path_free(path);
4660
                }
4661
                if (sel_msginfo && !found) {
4662
                        found = gtkut_tree_model_find_by_column_data
4663
                                (model, selected, &iter_,
4664
                                 S_COL_MSG_INFO, sel_msginfo);
4665
                }
4666
        }
4667

    
4668
        g_node_destroy(root);
4669

    
4670
        summaryview->folder_item->cache_dirty = TRUE;
4671
}
4672

    
4673
static void summary_modify_threads(SummaryView *summaryview)
4674
{
4675
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4676
        GtkTreeIter iter, next, selected, new_selected;
4677
        GtkTreeIter *selected_p = NULL;
4678
        GtkTreePath *prev_path = NULL;
4679
        gboolean valid, has_selection;
4680

    
4681
        summary_lock(summaryview);
4682

    
4683
        debug_print("Modifying threads for execution...");
4684

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

    
4688
        has_selection = gtkut_tree_row_reference_get_iter
4689
                (model, summaryview->selected, &selected);
4690
        if (has_selection) {
4691
                prev_path = gtk_tree_row_reference_get_path
4692
                        (summaryview->selected);
4693
                if (summary_find_nearest_msg(summaryview, &next, &selected)) {
4694
                        selected = next;
4695
                        selected_p = &selected;
4696
                } else
4697
                        has_selection = FALSE;
4698
        }
4699

    
4700
        valid = gtk_tree_model_get_iter_first(model, &next);
4701
        while (valid) {
4702
                iter = next;
4703
                valid = gtk_tree_model_iter_next(model, &next);
4704
                summary_modify_node(summaryview, &iter, selected_p);
4705
        }
4706

    
4707
        g_signal_handlers_unblock_by_func(summaryview->selection,
4708
                                          summary_selection_changed,
4709
                                          summaryview);
4710

    
4711
        if (summaryview->folder_item->cache_dirty)
4712
                summary_selection_list_free(summaryview);
4713

    
4714
        if (has_selection &&
4715
            !gtk_tree_row_reference_valid(summaryview->selected)) {
4716
                if (prev_path &&
4717
                    gtk_tree_model_get_iter(model, &new_selected, prev_path))
4718
                        selected = new_selected;
4719
                summary_select_row(summaryview, &selected, FALSE, FALSE);
4720
        }
4721
        gtk_tree_path_free(prev_path);
4722

    
4723
        debug_print("done.\n");
4724

    
4725
        summary_unlock(summaryview);
4726
}
4727

    
4728
void summary_expand_threads(SummaryView *summaryview)
4729
{
4730
        gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4731
}
4732

    
4733
void summary_collapse_threads(SummaryView *summaryview)
4734
{
4735
        gtk_tree_view_collapse_all(GTK_TREE_VIEW(summaryview->treeview));
4736
}
4737

    
4738
static gboolean summary_filter_func(GtkTreeModel *model, GtkTreePath *path,
4739
                                    GtkTreeIter *iter, gpointer data)
4740
{
4741
        SummaryView *summaryview = (SummaryView *)data;
4742
        MsgInfo *msginfo;
4743
        FilterInfo *fltinfo;
4744

    
4745
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4746

    
4747
        summaryview->flt_count++;
4748
        {
4749
                gchar msg[1024];
4750
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4751
                           summaryview->flt_count, summaryview->flt_total);
4752
                STATUSBAR_POP(summaryview->mainwin);
4753
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4754
                if ((summaryview->flt_count % 100) == 0) {
4755
                        GTK_EVENTS_FLUSH();
4756
                }
4757
        }
4758

    
4759
        fltinfo = filter_info_new();
4760
        fltinfo->flags = msginfo->flags;
4761
        filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo);
4762
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4763
            fltinfo->actions[FLT_ACTION_COPY] ||
4764
            fltinfo->actions[FLT_ACTION_DELETE] ||
4765
            fltinfo->actions[FLT_ACTION_EXEC] ||
4766
            fltinfo->actions[FLT_ACTION_EXEC_ASYNC] ||
4767
            fltinfo->actions[FLT_ACTION_MARK] ||
4768
            fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
4769
            fltinfo->actions[FLT_ACTION_MARK_READ] ||
4770
            fltinfo->actions[FLT_ACTION_FORWARD] ||
4771
            fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] ||
4772
            fltinfo->actions[FLT_ACTION_REDIRECT])
4773
                summaryview->filtered++;
4774

    
4775
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4776
                msginfo->flags = fltinfo->flags;
4777
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4778
                summaryview->folder_item->mark_dirty = TRUE;
4779
                summary_set_row(summaryview, iter, msginfo);
4780
                if (MSG_IS_IMAP(msginfo->flags)) {
4781
                        if (fltinfo->actions[FLT_ACTION_MARK])
4782
                                imap_msg_set_perm_flags(msginfo, MSG_MARKED);
4783
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4784
                                imap_msg_unset_perm_flags(msginfo,
4785
                                                          MSG_NEW|MSG_UNREAD);
4786
                }
4787
        }
4788

    
4789
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4790
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4791
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4792
                summary_delete_row(summaryview, iter);
4793

    
4794
        filter_info_free(fltinfo);
4795

    
4796
        return FALSE;
4797
}
4798

    
4799
static gboolean summary_filter_junk_func(GtkTreeModel *model, GtkTreePath *path,
4800
                                         GtkTreeIter *iter, gpointer data)
4801
{
4802
        SummaryView *summaryview = (SummaryView *)data;
4803
        MsgInfo *msginfo;
4804
        FilterInfo *fltinfo;
4805

    
4806
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4807

    
4808
        summaryview->flt_count++;
4809
        {
4810
                gchar msg[1024];
4811
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4812
                           summaryview->flt_count, summaryview->flt_total);
4813
                STATUSBAR_POP(summaryview->mainwin);
4814
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4815
                if ((summaryview->flt_count % 100) == 0) {
4816
                        GTK_EVENTS_FLUSH();
4817
                }
4818
        }
4819

    
4820
        fltinfo = filter_info_new();
4821
        fltinfo->flags = msginfo->flags;
4822
        filter_apply_msginfo(summaryview->junk_fltlist, msginfo, fltinfo);
4823

    
4824
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4825
            fltinfo->actions[FLT_ACTION_COPY] ||
4826
            fltinfo->actions[FLT_ACTION_DELETE] ||
4827
            fltinfo->actions[FLT_ACTION_MARK_READ])
4828
                summaryview->filtered++;
4829
        else if (fltinfo->error == FLT_ERROR_EXEC_FAILED ||
4830
                 fltinfo->last_exec_exit_status >= 3) {
4831
                if (summaryview->flt_count == 1) {
4832
                        g_warning("summary_filter_junk_func: junk filter command returned %d", fltinfo->last_exec_exit_status);
4833
                        alertpanel_error
4834
                                (_("Execution of the junk filter command failed.\n"
4835
                                   "Please check the junk mail control setting."));
4836
                }
4837
                return TRUE;
4838
        }
4839

    
4840
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4841
                msginfo->flags = fltinfo->flags;
4842
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4843
                summaryview->folder_item->mark_dirty = TRUE;
4844
                summary_set_row(summaryview, iter, msginfo);
4845
                if (MSG_IS_IMAP(msginfo->flags)) {
4846
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4847
                                imap_msg_unset_perm_flags(msginfo,
4848
                                                          MSG_NEW|MSG_UNREAD);
4849
                }
4850
        }
4851

    
4852
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4853
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4854
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4855
                summary_delete_row(summaryview, iter);
4856

    
4857
        filter_info_free(fltinfo);
4858

    
4859
        return FALSE;
4860
}
4861

    
4862
static void summary_filter_real(SummaryView *summaryview,
4863
                                GtkTreeModelForeachFunc func,
4864
                                gboolean selected_only)
4865
{
4866
        GList *rows;
4867
        FolderSortKey sort_key;
4868
        FolderSortType sort_type;
4869

    
4870
        if (!summaryview->folder_item) return;
4871

    
4872
        if (summary_is_locked(summaryview)) return;
4873
        summary_lock(summaryview);
4874

    
4875
        STATUSBAR_POP(summaryview->mainwin);
4876

    
4877
        debug_print(_("filtering..."));
4878
        STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
4879
        main_window_cursor_wait(summaryview->mainwin);
4880
        GTK_EVENTS_FLUSH();
4881

    
4882
        sort_key = summaryview->folder_item->sort_key;
4883
        sort_type = summaryview->folder_item->sort_type;
4884
        if (sort_key != SORT_BY_NONE)
4885
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4886

    
4887
        summaryview->filtered = 0;
4888
        summaryview->flt_count = 0;
4889

    
4890
        if (selected_only) {
4891
                rows = summary_get_selected_rows(summaryview);
4892
                summaryview->flt_total = g_list_length(rows);
4893

    
4894
                gtk_tree_selection_selected_foreach
4895
                        (summaryview->selection,
4896
                         (GtkTreeSelectionForeachFunc)func,
4897
                         summaryview);
4898
        } else {
4899
                summaryview->flt_total = summaryview->folder_item->total;
4900
                gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4901
                                       func, summaryview);
4902
        }
4903

    
4904
        if (sort_key != SORT_BY_NONE)
4905
                summary_sort(summaryview, sort_key, sort_type);
4906

    
4907
        summary_unlock(summaryview);
4908

    
4909
        if (prefs_common.immediate_exec)
4910
                summary_execute(summaryview);
4911
        else
4912
                summary_status_show(summaryview);
4913

    
4914
        folderview_update_all_updated(FALSE);
4915

    
4916
        debug_print(_("done.\n"));
4917
        STATUSBAR_POP(summaryview->mainwin);
4918
        main_window_cursor_normal(summaryview->mainwin);
4919

    
4920
        if (summaryview->filtered > 0) {
4921
                gchar result_msg[BUFFSIZE];
4922
                g_snprintf(result_msg, sizeof(result_msg),
4923
                           _("%d message(s) have been filtered."),
4924
                           summaryview->filtered);
4925
                STATUSBAR_PUSH(summaryview->mainwin, result_msg);
4926
        }
4927
        summaryview->filtered = 0;
4928
        summaryview->flt_count = 0;
4929
        summaryview->flt_total = 0;
4930
}
4931

    
4932
void summary_filter(SummaryView *summaryview, gboolean selected_only)
4933
{
4934
        if (prefs_common.fltlist)
4935
                summary_filter_real(summaryview, summary_filter_func,
4936
                                    selected_only);
4937
}
4938

    
4939
void summary_filter_junk(SummaryView *summaryview, gboolean selected_only)
4940
{
4941
        FilterRule *rule;
4942
        GSList junk_fltlist = {NULL, NULL};
4943
        FolderItem *item = summaryview->folder_item;
4944
        FolderItem *junk = NULL;
4945

    
4946
        if (!item)
4947
                return;
4948

    
4949
        if (item->folder)
4950
                junk = folder_get_junk(item->folder);
4951
        rule = filter_junk_rule_create(NULL, junk, TRUE);
4952
        if (rule) {
4953
                junk_fltlist.data = rule;
4954
                summaryview->junk_fltlist = &junk_fltlist;
4955
                summary_filter_real(summaryview, summary_filter_junk_func,
4956
                                    selected_only);
4957
                summaryview->junk_fltlist = NULL;
4958
                filter_rule_free(rule);
4959
        }
4960
}
4961

    
4962
void summary_filter_open(SummaryView *summaryview, FilterCreateType type)
4963
{
4964
        GtkTreeIter iter;
4965
        MsgInfo *msginfo = NULL;
4966
        gchar *header = NULL;
4967
        gchar *key = NULL;
4968

    
4969
        if (!summaryview->selected) return;
4970
        if (!gtkut_tree_row_reference_get_iter
4971
                (GTK_TREE_MODEL(summaryview->store),
4972
                 summaryview->selected, &iter))
4973
                return;
4974

    
4975
        GET_MSG_INFO(msginfo, &iter);
4976
        if (!msginfo) return;
4977

    
4978
        filter_get_keyword_from_msg(msginfo, &header, &key, type);
4979
        prefs_filter_open(msginfo, header, key);
4980

    
4981
        g_free(header);
4982
        g_free(key);
4983
}
4984

    
4985
static void summary_junk_func(GtkTreeModel *model, GtkTreePath *path,
4986
                              GtkTreeIter *iter, gpointer data)
4987
{
4988
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
4989
        FilterAction action1 = {FLT_ACTION_EXEC, NULL, 0};
4990
        FilterAction action2 = {FLT_ACTION_MOVE, NULL, 0};
4991
        FilterAction action3 = {FLT_ACTION_MARK_READ, NULL, 0};
4992
        SummaryView *summaryview = (SummaryView *)data;
4993
        MsgInfo *msginfo;
4994
        FilterInfo *fltinfo;
4995
        gchar *file;
4996
        gchar *junk_id = NULL;
4997
        gint ret;
4998

    
4999
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
5000
        file = procmsg_get_message_file(msginfo);
5001
        g_return_if_fail(file != NULL);
5002

    
5003
        if (summaryview->to_folder)
5004
                junk_id = folder_item_get_identifier(summaryview->to_folder);
5005

    
5006
        action1.str_value = prefs_common.junk_learncmd;
5007
        action2.str_value = junk_id;
5008

    
5009
        rule.action_list = g_slist_append(rule.action_list, &action1);
5010
        if (junk_id)
5011
                rule.action_list = g_slist_append(rule.action_list, &action2);
5012
        if (prefs_common.mark_junk_as_read)
5013
                rule.action_list = g_slist_append(rule.action_list, &action3);
5014

    
5015
        fltinfo = filter_info_new();
5016
        fltinfo->flags = msginfo->flags;
5017

    
5018
        ret = filter_action_exec(&rule, msginfo, file, fltinfo);
5019

    
5020
        if (ret < 0 || fltinfo->last_exec_exit_status != 0) {
5021
                g_warning("summary_junk_func: junk filter command returned %d",
5022
                          fltinfo->last_exec_exit_status);
5023
                alertpanel_error
5024
                        (_("Execution of the junk filter command failed.\n"
5025
                           "Please check the junk mail control setting."));
5026
        } else {
5027
                if (ret == 0 &&
5028
                    msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
5029
                        msginfo->flags = fltinfo->flags;
5030
                        summary_set_row(summaryview, iter, msginfo);
5031
                        if (MSG_IS_IMAP(msginfo->flags)) {
5032
                                if (fltinfo->actions[FLT_ACTION_MARK_READ])
5033
                                        imap_msg_unset_perm_flags
5034
                                                (msginfo, MSG_NEW | MSG_UNREAD);
5035
                        }
5036
                }
5037
                if (ret == 0 && fltinfo->actions[FLT_ACTION_MOVE] &&
5038
                    fltinfo->move_dest)
5039
                        summary_move_row_to(summaryview, iter,
5040
                                            fltinfo->move_dest);
5041
        }
5042

    
5043
        filter_info_free(fltinfo);
5044
        g_slist_free(rule.action_list);
5045
        g_free(junk_id);
5046
        g_free(file);
5047
}
5048

    
5049
static void summary_not_junk_func(GtkTreeModel *model, GtkTreePath *path,
5050
                                  GtkTreeIter *iter, gpointer data)
5051
{
5052
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
5053
        FilterAction action = {FLT_ACTION_EXEC, NULL, 0};
5054
        MsgInfo *msginfo;
5055
        FilterInfo *fltinfo;
5056
        gchar *file;
5057
        gint ret;
5058

    
5059
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
5060
        file = procmsg_get_message_file(msginfo);
5061
        g_return_if_fail(file != NULL);